diff --git a/packages/xo-web/src/common/intl/locales/fr.js b/packages/xo-web/src/common/intl/locales/fr.js
index 1dc61fcfd..71c33e40a 100644
--- a/packages/xo-web/src/common/intl/locales/fr.js
+++ b/packages/xo-web/src/common/intl/locales/fr.js
@@ -3857,7 +3857,8 @@ export default {
xosanUsedSpace: 'Espace utilisé',
// Original text: "XOSAN pack needs to be installed on each host of the pool."
- xosanNeedPack: 'La pack XOSAN doit être installé sur tous les hôtes du pool.',
+ xosanNeedPack:
+ 'Le pack XOSAN doit être installé et à jour sur tous les hôtes du pool.',
// Original text: "Install it now!"
xosanInstallIt: 'Installer maintenant !',
diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js
index 5f74be8c6..41af58e33 100644
--- a/packages/xo-web/src/common/intl/messages.js
+++ b/packages/xo-web/src/common/intl/messages.js
@@ -1766,7 +1766,8 @@ const messages = {
xosanUsedSpace: 'Used space',
xosanLicense: 'License',
xosanMultipleLicenses: 'This XOSAN has more than 1 license!',
- xosanNeedPack: 'XOSAN pack needs to be installed on each host of the pool.',
+ xosanNeedPack:
+ 'XOSAN pack needs to be installed and up to date on each host of the pool.',
xosanInstallIt: 'Install it now!',
xosanNeedRestart:
'Some hosts need their toolstack to be restarted before you can create an XOSAN',
@@ -1794,6 +1795,14 @@ const messages = {
xosanPbdsDetached: 'Some SRs are detached from the XOSAN',
xosanBadStatus: 'Something is wrong with: {badStatuses}',
xosanRunning: 'Running',
+ xosanUpdatePacks: 'Update packs',
+ xosanPackUpdateChecking: 'Checking for updates',
+ xosanPackUpdateError:
+ 'Error while checking XOSAN packs. Please make sure that the Cloud plugin is installed and loaded and that the updater is reachable.',
+ xosanPackUpdateUnavailable: 'XOSAN resources are unavailable',
+ xosanPackUpdateUnregistered: 'Not registered for XOSAN resources',
+ xosanPackUpdateUpToDate: "✓ This pool's XOSAN packs are up to date!",
+ xosanPackUpdateVersion: 'Update pool with latest pack v{version}',
xosanDelete: 'Delete XOSAN',
xosanFixIssue: 'Fix',
xosanCreatingOn: 'Creating XOSAN on {pool}',
@@ -1810,12 +1819,8 @@ const messages = {
xosanRegister: 'Register your appliance first',
xosanLoading: 'Loading…',
xosanNotAvailable: 'XOSAN is not available at the moment',
- xosanInstallPackOnHosts: 'Install XOSAN pack on these hosts:',
- xosanInstallPack: 'Install {pack} v{version}?',
xosanNoPackFound:
'No compatible XOSAN pack found for your XenServer versions.',
- xosanPackRequirements:
- 'At least one of these version requirements must be satisfied by all the hosts in this pool:',
// SR tab XOSAN
xosanVmsNotRunning: 'Some XOSAN Virtual Machines are not running',
xosanVmsNotFound: 'Some XOSAN Virtual Machines could not be found',
diff --git a/packages/xo-web/src/common/utils.js b/packages/xo-web/src/common/utils.js
index 9a100fa41..9968c9da8 100644
--- a/packages/xo-web/src/common/utils.js
+++ b/packages/xo-web/src/common/utils.js
@@ -20,6 +20,7 @@ import {
mapValues,
replace,
sample,
+ some,
startsWith,
} from 'lodash'
@@ -28,6 +29,7 @@ import * as actions from './store/actions'
import invoke from './invoke'
import store from './store'
import { getObject } from './selectors'
+import { satisfies as versionSatisfies } from 'semver'
export const EMPTY_ARRAY = Object.freeze([])
export const EMPTY_OBJECT = Object.freeze({})
@@ -523,6 +525,40 @@ export const ShortDate = ({ timestamp }) => (
)
+export const findLatestPack = (packs, hostsVersions) => {
+ const checkVersion = version =>
+ !version ||
+ every(hostsVersions, hostVersion => versionSatisfies(hostVersion, version))
+
+ let latestPack = { version: '0' }
+ forEach(packs, pack => {
+ if (
+ pack.type === 'iso' &&
+ compareVersions(pack.version, '>', latestPack.version) &&
+ checkVersion(pack.requirements && pack.requirements.xenserver)
+ ) {
+ latestPack = pack
+ }
+ })
+
+ if (latestPack.version === '0') {
+ // No compatible pack was found
+ return
+ }
+
+ return latestPack
+}
+
+export const isLatestXosanPackInstalled = (latestXosanPack, hosts) =>
+ latestXosanPack !== undefined &&
+ every(hosts, host =>
+ some(
+ host.supplementalPacks,
+ ({ name, version }) =>
+ name === 'XOSAN' && version === latestXosanPack.version
+ )
+ )
+
// ===================================================================
export const getMemoryUsedMetric = ({ memory, memoryFree = memory }) =>
diff --git a/packages/xo-web/src/common/xo/index.js b/packages/xo-web/src/common/xo/index.js
index 41d3c4c67..39dc323bd 100644
--- a/packages/xo-web/src/common/xo/index.js
+++ b/packages/xo-web/src/common/xo/index.js
@@ -2412,20 +2412,6 @@ export const removeXosanBricks = (xosansr, bricks) =>
export const computeXosanPossibleOptions = (lvmSrs, brickSize) =>
_call('xosan.computeXosanPossibleOptions', { lvmSrs, brickSize })
-import InstallXosanPackModal from './install-xosan-pack-modal' // eslint-disable-line import/first
-export const downloadAndInstallXosanPack = pool =>
- confirm({
- title: _('xosanInstallPackTitle', { pool: pool.name_label }),
- icon: 'export',
- body: ,
- }).then(pack =>
- _call('xosan.downloadAndInstallXosanPack', {
- id: pack.id,
- version: pack.version,
- pool: resolveId(pool),
- })
- )
-
export const registerXosan = () =>
_call('cloud.registerResource', { namespace: 'xosan' })::tap(
subscribeResourceCatalog.forceRefresh
@@ -2434,6 +2420,31 @@ export const registerXosan = () =>
export const fixHostNotInXosanNetwork = (xosanSr, host) =>
_call('xosan.fixHostNotInNetwork', { xosanSr, host })
+// XOSAN packs -----------------------------------------------------------------
+
+export const getResourceCatalog = () => _call('cloud.getResourceCatalog')
+
+const downloadAndInstallXosanPack = (pack, pool, { version }) =>
+ _call('xosan.downloadAndInstallXosanPack', {
+ id: resolveId(pack),
+ version,
+ pool: resolveId(pool),
+ })
+
+import UpdateXosanPacksModal from './update-xosan-packs-modal' // eslint-disable-line import/first
+export const updateXosanPacks = pool =>
+ confirm({
+ title: _('xosanUpdatePacks'),
+ icon: 'host-patch-update',
+ body: ,
+ }).then(pack => {
+ if (pack === undefined) {
+ return
+ }
+
+ return downloadAndInstallXosanPack(pack, pool, { version: pack.version })
+ })
+
// Licenses --------------------------------------------------------------------
export const getLicenses = productId => _call('xoa.getLicenses', { productId })
diff --git a/packages/xo-web/src/common/xo/install-xosan-pack-modal/index.js b/packages/xo-web/src/common/xo/install-xosan-pack-modal/index.js
deleted file mode 100644
index c5c1c7202..000000000
--- a/packages/xo-web/src/common/xo/install-xosan-pack-modal/index.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import _ from 'intl'
-import Component from 'base-component'
-import React from 'react'
-import { connectStore, compareVersions, isXosanPack } from 'utils'
-import { subscribeResourceCatalog, subscribePlugins } from 'xo'
-import {
- createGetObjectsOfType,
- createSelector,
- createCollectionWrapper,
-} from 'selectors'
-import { satisfies as versionSatisfies } from 'semver'
-import { every, filter, forEach, map, some } from 'lodash'
-
-const findLatestPack = (packs, hostsVersions) => {
- const checkVersion = version =>
- every(hostsVersions, hostVersion => versionSatisfies(hostVersion, version))
-
- let latestPack = { version: '0' }
- forEach(packs, pack => {
- const xsVersionRequirement =
- pack.requirements && pack.requirements.xenserver
-
- if (
- pack.type === 'iso' &&
- compareVersions(pack.version, latestPack.version) > 0 &&
- (!xsVersionRequirement || checkVersion(xsVersionRequirement))
- ) {
- latestPack = pack
- }
- })
-
- if (latestPack.version === '0') {
- // No compatible pack was found
- return
- }
-
- return latestPack
-}
-
-@connectStore(
- () => ({
- hosts: createGetObjectsOfType('host').filter(
- createSelector(
- (_, { pool }) => pool != null && pool.id,
- poolId =>
- poolId
- ? host =>
- host.$pool === poolId &&
- !some(host.supplementalPacks, isXosanPack)
- : false
- )
- ),
- }),
- { withRef: true }
-)
-export default class InstallXosanPackModal extends Component {
- componentDidMount () {
- this._unsubscribePlugins = subscribePlugins(plugins =>
- this.setState({ plugins })
- )
- this._unsubscribeResourceCatalog = subscribeResourceCatalog(catalog =>
- this.setState({ catalog })
- )
- }
-
- componentWillUnmount () {
- this._unsubscribePlugins()
- this._unsubscribeResourceCatalog()
- }
-
- _getXosanLatestPack = createSelector(
- () => this.state.catalog && this.state.catalog.xosan,
- createSelector(
- () => this.props.hosts,
- createCollectionWrapper(hosts => map(hosts, 'version'))
- ),
- findLatestPack
- )
-
- _getXosanPacks = createSelector(
- () => this.state.catalog && this.state.catalog.xosan,
- packs => filter(packs, ({ type }) => type === 'iso')
- )
-
- get value () {
- return this._getXosanLatestPack()
- }
-
- render () {
- const { hosts } = this.props
- const latestPack = this._getXosanLatestPack()
-
- return (
-
- {latestPack ? (
-
- {_('xosanInstallPackOnHosts')}
-
- {map(hosts, host => - {host.name_label}
)}
-
-
- {_('xosanInstallPack', {
- pack: latestPack.name,
- version: latestPack.version,
- })}
-
-
- ) : (
-
- {_('xosanNoPackFound')}
-
- {_('xosanPackRequirements')}
-
- {map(this._getXosanPacks(), ({ name, requirements }, key) => (
- -
- {_.keyValue(
- name,
- requirements && requirements.xenserver
- ? requirements.xenserver
- : '/'
- )}
-
- ))}
-
-
- )}
-
- )
- }
-}
diff --git a/packages/xo-web/src/common/xo/update-xosan-packs-modal/index.js b/packages/xo-web/src/common/xo/update-xosan-packs-modal/index.js
new file mode 100644
index 000000000..92f099d50
--- /dev/null
+++ b/packages/xo-web/src/common/xo/update-xosan-packs-modal/index.js
@@ -0,0 +1,89 @@
+import _ from 'intl'
+import React from 'react'
+import Component from 'base-component'
+import { createGetObjectsOfType, createSelector } from 'selectors'
+import { map } from 'lodash'
+import { subscribeResourceCatalog } from 'xo'
+import {
+ addSubscriptions,
+ isLatestXosanPackInstalled,
+ connectStore,
+ findLatestPack,
+} from 'utils'
+
+@connectStore(
+ {
+ hosts: createGetObjectsOfType('host').filter((_, { pool }) => host =>
+ host.$pool === pool.id
+ ),
+ },
+ { withRef: true }
+)
+@addSubscriptions(() => ({
+ catalog: subscribeResourceCatalog,
+}))
+export default class UpdateXosanPacksModal extends Component {
+ state = {
+ status: 'checking',
+ }
+
+ get value () {
+ return this.state.pack
+ }
+
+ _getStatus = createSelector(
+ () => this.props.catalog,
+ () => this.props.hosts,
+ (catalog, hosts) => {
+ if (catalog === undefined) {
+ return { status: 'error' }
+ }
+
+ if (catalog._namespaces.xosan === undefined) {
+ return { status: 'unavailable' }
+ }
+
+ if (!catalog._namespaces.xosan.registered) {
+ return { status: 'unregistered' }
+ }
+
+ const pack = findLatestPack(catalog.xosan, map(hosts, 'version'))
+
+ if (pack === undefined) {
+ return { status: 'noPack' }
+ }
+
+ if (isLatestXosanPackInstalled(pack, hosts)) {
+ return { status: 'upToDate' }
+ }
+
+ return { status: 'packFound', pack }
+ }
+ )
+
+ render () {
+ const { status, pack } = this._getStatus()
+ switch (status) {
+ case 'checking':
+ return {_('xosanPackUpdateChecking')}
+ case 'error':
+ return {_('xosanPackUpdateError')}
+ case 'unavailable':
+ return {_('xosanPackUpdateUnavailable')}
+ case 'unregistered':
+ return {_('xosanPackUpdateUnregistered')}
+ case 'noPack':
+ return {_('xosanNoPackFound')}
+ case 'upToDate':
+ return {_('xosanPackUpdateUpToDate')}
+ case 'packFound':
+ return (
+
+ {_('xosanPackUpdateVersion', {
+ version: pack.version,
+ })}
+
+ )
+ }
+ }
+}
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 cf015f268..8709aeaa4 100644
--- a/packages/xo-web/src/xo-app/host/tab-advanced.js
+++ b/packages/xo-web/src/xo-app/host/tab-advanced.js
@@ -1,10 +1,11 @@
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 Upgrade from 'xoa-upgrade'
-import { connectStore } from 'utils'
+import { compareVersions, connectStore } from 'utils'
import { Toggle } from 'form'
import {
enableHost,
@@ -17,7 +18,7 @@ import {
import { FormattedRelative, FormattedTime } from 'react-intl'
import { Container, Row, Col } from 'grid'
import { createGetObjectsOfType, createSelector } from 'selectors'
-import { map, noop } from 'lodash'
+import { forEach, map, noop } from 'lodash'
const ALLOW_INSTALL_SUPP_PACK = process.env.XOA_PLAN > 1
@@ -31,7 +32,9 @@ const formatPack = ({ name, author, description, version }, key) => (
)
-export default connectStore(() => {
+const getPackId = ({ author, name }) => `${author}\0${name}`
+
+@connectStore(() => {
const getPgpus = createGetObjectsOfType('PGPU')
.pick((_, { host }) => host.$PGPUs)
.sort()
@@ -44,207 +47,233 @@ export default connectStore(() => {
pcis: getPcis,
pgpus: getPgpus,
}
-})(({ host, pcis, pgpus }) => (
-
-
-
- {host.power_state === 'Running' && (
-
- )}
- {host.enabled ? (
-
- ) : (
-
- )}
-
- {host.power_state !== 'Running' && (
-
- )}
-
-
-
-
- {_('xenSettingsLabel')}
-
-
-
- {_('uuid')} |
- {host.uuid}
-
-
- {_('hostAddress')} |
- {host.address}
-
-
- {_('hostStatus')} |
-
- {host.enabled
- ? _('hostStatusEnabled')
- : _('hostStatusDisabled')}
- |
-
-
- {_('hostPowerOnMode')} |
-
-
- |
-
-
- {_('hostStartedSince')} |
-
- {_('started', {
- ago: ,
- })}
- |
-
-
- {_('hostStackStartedSince')} |
-
- {_('started', {
- ago: ,
- })}
- |
-
-
- {_('hostXenServerVersion')} |
-
- {host.license_params.sku_marketing_name} {host.version} ({
- host.license_params.sku_type
- })
-
-
-
- {_('hostBuildNumber')} |
- {host.build}
-
-
- {_('hostIscsiName')} |
- {host.iSCSI_name}
-
-
-
-
- {_('hardwareHostSettingsLabel')}
-
-
-
- {_('hostCpusModel')} |
- {host.CPUs.modelname}
-
-
- {_('hostGpus')} |
-
- {map(pgpus, pgpu => pcis[pgpu.pci].device_name).join(', ')}
- |
-
-
- {_('hostCpusNumber')} |
-
- {host.cpus.cores} ({host.cpus.sockets})
- |
-
-
- {_('hostManufacturerinfo')} |
-
- {host.bios_strings['system-manufacturer']} ({
- host.bios_strings['system-product-name']
- })
-
-
-
- {_('hostBiosinfo')} |
-
- {host.bios_strings['bios-vendor']} ({
- host.bios_strings['bios-version']
- })
- |
-
-
-
-
- {_('licenseHostSettingsLabel')}
-
-
-
- {_('hostLicenseType')} |
- {host.license_params.sku_type} |
-
-
- {_('hostLicenseSocket')} |
- {host.license_params.sockets} |
-
-
- {_('hostLicenseExpiry')} |
-
-
-
- |
-
-
-
- {_('supplementalPacks')}
-
-
- {map(host.supplementalPacks, formatPack)}
- {ALLOW_INSTALL_SUPP_PACK && (
-
- {_('supplementalPackNew')} |
-
- installSupplementalPack(host, file)}
- />
- |
-
+})
+export default class extends Component {
+ _getPacks = createSelector(
+ () => this.props.host.supplementalPacks,
+ packs => {
+ const uniqPacks = {}
+ let packId, previousPack
+ forEach(packs, pack => {
+ packId = getPackId(pack)
+ if (
+ (previousPack = uniqPacks[packId]) === undefined ||
+ compareVersions(pack.version, previousPack.version) > 0
+ ) {
+ uniqPacks[packId] = pack
+ }
+ })
+ return uniqPacks
+ }
+ )
+
+ render () {
+ const { host, pcis, pgpus } = this.props
+ return (
+
+
+
+ {host.power_state === 'Running' && (
+
)}
-
-
- {!ALLOW_INSTALL_SUPP_PACK && [
- {_('supplementalPackNew')}
,
-
-
- ,
- ]}
-
-
-
-))
+ {host.enabled ? (
+
+ ) : (
+
+ )}
+
+ {host.power_state !== 'Running' && (
+
+ )}
+
+
+
+
+ {_('xenSettingsLabel')}
+
+
+
+ {_('uuid')} |
+ {host.uuid}
+
+
+ {_('hostAddress')} |
+ {host.address}
+
+
+ {_('hostStatus')} |
+
+ {host.enabled
+ ? _('hostStatusEnabled')
+ : _('hostStatusDisabled')}
+ |
+
+
+ {_('hostPowerOnMode')} |
+
+
+ |
+
+
+ {_('hostStartedSince')} |
+
+ {_('started', {
+ ago: ,
+ })}
+ |
+
+
+ {_('hostStackStartedSince')} |
+
+ {_('started', {
+ ago: (
+
+ ),
+ })}
+ |
+
+
+ {_('hostXenServerVersion')} |
+
+ {host.license_params.sku_marketing_name} {host.version} ({
+ host.license_params.sku_type
+ })
+
+
+
+ {_('hostBuildNumber')} |
+ {host.build}
+
+
+ {_('hostIscsiName')} |
+ {host.iSCSI_name}
+
+
+
+
+ {_('hardwareHostSettingsLabel')}
+
+
+
+ {_('hostCpusModel')} |
+ {host.CPUs.modelname}
+
+
+ {_('hostGpus')} |
+
+ {map(pgpus, pgpu => pcis[pgpu.pci].device_name).join(', ')}
+ |
+
+
+ {_('hostCpusNumber')} |
+
+ {host.cpus.cores} ({host.cpus.sockets})
+ |
+
+
+ {_('hostManufacturerinfo')} |
+
+ {host.bios_strings['system-manufacturer']} ({
+ host.bios_strings['system-product-name']
+ })
+
+
+
+ {_('hostBiosinfo')} |
+
+ {host.bios_strings['bios-vendor']} ({
+ host.bios_strings['bios-version']
+ })
+ |
+
+
+
+
+ {_('licenseHostSettingsLabel')}
+
+
+
+ {_('hostLicenseType')} |
+ {host.license_params.sku_type} |
+
+
+ {_('hostLicenseSocket')} |
+ {host.license_params.sockets} |
+
+
+ {_('hostLicenseExpiry')} |
+
+
+
+ |
+
+
+
+ {_('supplementalPacks')}
+
+
+ {map(this._getPacks(), formatPack)}
+ {ALLOW_INSTALL_SUPP_PACK && (
+
+ {_('supplementalPackNew')} |
+
+ installSupplementalPack(host, file)}
+ />
+ |
+
+ )}
+
+
+ {!ALLOW_INSTALL_SUPP_PACK && [
+ {_('supplementalPackNew')}
,
+
+
+ ,
+ ]}
+
+
+
+ )
+ }
+}
diff --git a/packages/xo-web/src/xo-app/xosan/index.js b/packages/xo-web/src/xo-app/xosan/index.js
index c623c6899..0bd854e06 100644
--- a/packages/xo-web/src/xo-app/xosan/index.js
+++ b/packages/xo-web/src/xo-app/xosan/index.js
@@ -10,24 +10,13 @@ import Tooltip from 'tooltip'
import { Container, Col, Row } from 'grid'
import { get } from 'xo-defined'
import { ignoreErrors } from 'promise-toolbox'
-import {
- every,
- filter,
- find,
- flatten,
- forEach,
- isEmpty,
- map,
- mapValues,
- some,
-} from 'lodash'
+import { every, filter, find, flatten, forEach, isEmpty, map } from 'lodash'
import { createGetObjectsOfType, createSelector, isAdmin } from 'selectors'
import {
addSubscriptions,
connectStore,
cowSet,
formatSize,
- isXosanPack,
ShortDate,
} from 'utils'
import {
@@ -37,6 +26,7 @@ import {
subscribePlugins,
subscribeResourceCatalog,
subscribeVolumeInfo,
+ updateXosanPacks,
} from 'xo'
import NewXosan from './new-xosan'
@@ -208,6 +198,12 @@ const XOSAN_COLUMNS = [
]
const XOSAN_INDIVIDUAL_ACTIONS = [
+ {
+ handler: (xosan, { pools }) => updateXosanPacks(pools[xosan.$pool]),
+ icon: 'host-patch-update',
+ label: _('xosanUpdatePacks'),
+ level: 'primary',
+ },
{
handler: deleteSr,
icon: 'delete',
@@ -221,14 +217,6 @@ const XOSAN_INDIVIDUAL_ACTIONS = [
const getHostsByPool = getHosts.groupBy('$pool')
const getPools = createGetObjectsOfType('pool')
- const noPacksByPool = createSelector(getHostsByPool, hostsByPool =>
- mapValues(
- hostsByPool,
- (poolHosts, poolId) =>
- !every(poolHosts, host => some(host.supplementalPacks, isXosanPack))
- )
- )
-
const getPbdsBySr = createGetObjectsOfType('PBD').groupBy('SR')
const getXosanSrs = createSelector(
createGetObjectsOfType('SR').filter([
@@ -291,7 +279,6 @@ const XOSAN_INDIVIDUAL_ACTIONS = [
isAdmin,
isMasterOfflineByPool: getIsMasterOfflineByPool,
hostsNeedRestartByPool: getHostsNeedRestartByPool,
- noPacksByPool,
poolPredicate: getPoolPredicate,
pools: getPools,
xoaRegistration: state => state.xoaRegisterState,
@@ -419,8 +406,8 @@ export default class Xosan extends Component {
const {
hostsNeedRestartByPool,
isAdmin,
- noPacksByPool,
poolPredicate,
+ pools,
xoaRegistration,
xosanSrs,
} = this.props
@@ -456,7 +443,6 @@ export default class Xosan extends Component {
(this._isXosanRegistered() ? (
diff --git a/packages/xo-web/src/xo-app/xosan/new-xosan.js b/packages/xo-web/src/xo-app/xosan/new-xosan.js
index 0308bfceb..b543a1ab1 100644
--- a/packages/xo-web/src/xo-app/xosan/new-xosan.js
+++ b/packages/xo-web/src/xo-app/xosan/new-xosan.js
@@ -29,15 +29,18 @@ import {
} from 'selectors'
import {
addSubscriptions,
+ isLatestXosanPackInstalled,
compareVersions,
connectStore,
+ findLatestPack,
formatSize,
mapPlus,
} from 'utils'
import {
computeXosanPossibleOptions,
createXosanSR,
- downloadAndInstallXosanPack,
+ updateXosanPacks,
+ getResourceCatalog,
restartHostsAgents,
subscribeResourceCatalog,
} from 'xo'
@@ -76,14 +79,47 @@ export default class NewXosan extends Component {
suggestion: 0,
}
+ _checkPacks = pool =>
+ getResourceCatalog().then(
+ catalog => {
+ if (catalog === undefined || catalog.xosan === undefined) {
+ this.setState({
+ checkPackError: true,
+ })
+ return
+ }
+
+ const hosts = filter(this.props.hosts, { $pool: pool.id })
+ const pack = findLatestPack(catalog.xosan, map(hosts, 'version'))
+
+ if (isLatestXosanPackInstalled(pack, hosts)) {
+ this.setState({
+ needsUpdate: true,
+ })
+ }
+ },
+ () => {
+ this.setState({
+ checkPackError: true,
+ })
+ }
+ )
+
+ _updateXosanPacks = pool =>
+ updateXosanPacks(pool).then(() => this._checkPacks(pool))
+
_selectPool = pool => {
this.setState({
- selectedSrs: {},
brickSize: DEFAULT_BRICKSIZE,
+ checkPackError: false,
memorySize: DEFAULT_MEMORY,
+ needsUpdate: false,
pif: undefined,
pool,
+ selectedSrs: {},
})
+
+ return this._checkPacks(pool)
}
componentDidUpdate () {
@@ -243,10 +279,12 @@ export default class NewXosan extends Component {
const {
brickSize,
+ checkPackError,
customBrickSize,
customIpRange,
ipRange,
memorySize,
+ needsUpdate,
pif,
pool,
selectedSrs,
@@ -256,12 +294,7 @@ export default class NewXosan extends Component {
vlan,
} = this.state
- const {
- hostsNeedRestartByPool,
- noPacksByPool,
- poolPredicate,
- notRegistered,
- } = this.props
+ const { hostsNeedRestartByPool, poolPredicate, notRegistered } = this.props
if (notRegistered) {
return (
@@ -296,9 +329,7 @@ export default class NewXosan extends Component {
{pool != null &&
- noPacksByPool[pool.id] && (
+ (checkPackError ? (
+ {_('xosanPackUpdateError')}
+ ) : needsUpdate ? (
- {_('xosanNeedPack')}
-
-
- {_('xosanInstallIt')}
-
+
+ {_('xosanNeedPack')}
+
+
+ {_('xosanInstallIt')}
+
+
- )}
- {!isEmpty(hostsNeedRestart) && (
-
- {_('xosanNeedRestart')}
-
-
- {_('xosanRestartAgents')}
-
-
- )}
- {pool != null &&
- !noPacksByPool[pool.id] &&
- isEmpty(hostsNeedRestart) && [
+ ) : !isEmpty(hostsNeedRestart) ? (
- {_('xosanSelect2Srs')}
-
-
,
-
- {!isEmpty(suggestions) && (
-
-
{_('xosanSuggestions')}
+
+
{_('xosanNeedRestart')}
+
+
+ {_('xosanRestartAgents')}
+
+
+
+ ) : (
+ [
+
+
+ {_('xosanSelect2Srs')}
- {architecture.layout === 'disperse' && (
-
- )}
-
,
-
-
-
- {_('xosanCreate')}
-
-
-
,
- ]}
+
+ ,
+
+
+
+ {_('xosanCreate')}
+
+
+
,
+ ]
+ ))}
)