feat(XOSAN): allow user to update packs (#2782)
This commit is contained in:
parent
ebab7c0867
commit
114501ebc7
@ -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 !',
|
||||
|
@ -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',
|
||||
|
@ -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 }) => (
|
||||
<FormattedDate value={timestamp} month='short' day='numeric' year='numeric' />
|
||||
)
|
||||
|
||||
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 }) =>
|
||||
|
@ -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: <InstallXosanPackModal pool={pool} />,
|
||||
}).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: <UpdateXosanPacksModal pool={pool} />,
|
||||
}).then(pack => {
|
||||
if (pack === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
return downloadAndInstallXosanPack(pack, pool, { version: pack.version })
|
||||
})
|
||||
|
||||
// Licenses --------------------------------------------------------------------
|
||||
|
||||
export const getLicenses = productId => _call('xoa.getLicenses', { productId })
|
||||
|
@ -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 (
|
||||
<div>
|
||||
{latestPack ? (
|
||||
<div>
|
||||
{_('xosanInstallPackOnHosts')}
|
||||
<ul>
|
||||
{map(hosts, host => <li key={host.id}>{host.name_label}</li>)}
|
||||
</ul>
|
||||
<div className='mt-1'>
|
||||
{_('xosanInstallPack', {
|
||||
pack: latestPack.name,
|
||||
version: latestPack.version,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{_('xosanNoPackFound')}
|
||||
<br />
|
||||
{_('xosanPackRequirements')}
|
||||
<ul>
|
||||
{map(this._getXosanPacks(), ({ name, requirements }, key) => (
|
||||
<li key={key}>
|
||||
{_.keyValue(
|
||||
name,
|
||||
requirements && requirements.xenserver
|
||||
? requirements.xenserver
|
||||
: '/'
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -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 <em>{_('xosanPackUpdateChecking')}</em>
|
||||
case 'error':
|
||||
return <em>{_('xosanPackUpdateError')}</em>
|
||||
case 'unavailable':
|
||||
return <em>{_('xosanPackUpdateUnavailable')}</em>
|
||||
case 'unregistered':
|
||||
return <em>{_('xosanPackUpdateUnregistered')}</em>
|
||||
case 'noPack':
|
||||
return <em>{_('xosanNoPackFound')}</em>
|
||||
case 'upToDate':
|
||||
return <em>{_('xosanPackUpdateUpToDate')}</em>
|
||||
case 'packFound':
|
||||
return (
|
||||
<div>
|
||||
{_('xosanPackUpdateVersion', {
|
||||
version: pack.version,
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -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) => (
|
||||
</tr>
|
||||
)
|
||||
|
||||
export default connectStore(() => {
|
||||
const getPackId = ({ author, name }) => `${author}\0${name}`
|
||||
|
||||
@connectStore(() => {
|
||||
const getPgpus = createGetObjectsOfType('PGPU')
|
||||
.pick((_, { host }) => host.$PGPUs)
|
||||
.sort()
|
||||
@ -44,7 +47,29 @@ export default connectStore(() => {
|
||||
pcis: getPcis,
|
||||
pgpus: getPgpus,
|
||||
}
|
||||
})(({ host, pcis, pgpus }) => (
|
||||
})
|
||||
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 (
|
||||
<Container>
|
||||
<Row>
|
||||
<Col className='text-xs-right'>
|
||||
@ -135,7 +160,9 @@ export default connectStore(() => {
|
||||
<th>{_('hostStackStartedSince')}</th>
|
||||
<td>
|
||||
{_('started', {
|
||||
ago: <FormattedRelative value={host.agentStartTime * 1000} />,
|
||||
ago: (
|
||||
<FormattedRelative value={host.agentStartTime * 1000} />
|
||||
),
|
||||
})}
|
||||
</td>
|
||||
</tr>
|
||||
@ -224,7 +251,7 @@ export default connectStore(() => {
|
||||
<h3>{_('supplementalPacks')}</h3>
|
||||
<table className='table'>
|
||||
<tbody>
|
||||
{map(host.supplementalPacks, formatPack)}
|
||||
{map(this._getPacks(), formatPack)}
|
||||
{ALLOW_INSTALL_SUPP_PACK && (
|
||||
<tr>
|
||||
<th>{_('supplementalPackNew')}</th>
|
||||
@ -247,4 +274,6 @@ export default connectStore(() => {
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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() ? (
|
||||
<NewXosan
|
||||
hostsNeedRestartByPool={hostsNeedRestartByPool}
|
||||
noPacksByPool={noPacksByPool}
|
||||
poolPredicate={poolPredicate}
|
||||
onSrCreationFinished={this._updateLicenses}
|
||||
onSrCreationStarted={this._onSrCreationStarted}
|
||||
@ -498,6 +484,7 @@ export default class Xosan extends Component {
|
||||
isAdmin,
|
||||
licensesByXosan: this._getLicensesByXosan(),
|
||||
licenseError,
|
||||
pools,
|
||||
status: this.state.status,
|
||||
}}
|
||||
/>
|
||||
|
@ -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 {
|
||||
<Col size={4}>
|
||||
<SelectPif
|
||||
disabled={
|
||||
pool == null ||
|
||||
noPacksByPool[pool.id] ||
|
||||
!isEmpty(hostsNeedRestart)
|
||||
pool == null || needsUpdate || !isEmpty(hostsNeedRestart)
|
||||
}
|
||||
onChange={this.linkState('pif')}
|
||||
predicate={this._getPifPredicate()}
|
||||
@ -307,22 +338,26 @@ export default class NewXosan extends Component {
|
||||
</Col>
|
||||
</Row>
|
||||
{pool != null &&
|
||||
noPacksByPool[pool.id] && (
|
||||
(checkPackError ? (
|
||||
<em>{_('xosanPackUpdateError')}</em>
|
||||
) : needsUpdate ? (
|
||||
<Row>
|
||||
<Col>
|
||||
<Icon icon='error' /> {_('xosanNeedPack')}
|
||||
<br />
|
||||
<ActionButton
|
||||
btnStyle='success'
|
||||
handler={downloadAndInstallXosanPack}
|
||||
handler={this._updateXosanPacks}
|
||||
handlerParam={pool}
|
||||
icon='export'
|
||||
>
|
||||
{_('xosanInstallIt')}
|
||||
</ActionButton>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{!isEmpty(hostsNeedRestart) && (
|
||||
) : !isEmpty(hostsNeedRestart) ? (
|
||||
<Row>
|
||||
<Col>
|
||||
<Icon icon='error' /> {_('xosanNeedRestart')}
|
||||
<br />
|
||||
<ActionButton
|
||||
@ -333,12 +368,12 @@ export default class NewXosan extends Component {
|
||||
>
|
||||
{_('xosanRestartAgents')}
|
||||
</ActionButton>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{pool != null &&
|
||||
!noPacksByPool[pool.id] &&
|
||||
isEmpty(hostsNeedRestart) && [
|
||||
) : (
|
||||
[
|
||||
<Row>
|
||||
<Col>
|
||||
<em>{_('xosanSelect2Srs')}</em>
|
||||
<table className='table table-striped'>
|
||||
<thead>
|
||||
@ -380,9 +415,13 @@ export default class NewXosan extends Component {
|
||||
<Tooltip
|
||||
content={_('spaceLeftTooltip', {
|
||||
used: String(
|
||||
Math.round(sr.physical_usage / sr.size * 100)
|
||||
Math.round(
|
||||
sr.physical_usage / sr.size * 100
|
||||
)
|
||||
),
|
||||
free: formatSize(
|
||||
sr.size - sr.physical_usage
|
||||
),
|
||||
free: formatSize(sr.size - sr.physical_usage),
|
||||
})}
|
||||
>
|
||||
<progress
|
||||
@ -398,8 +437,10 @@ export default class NewXosan extends Component {
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Col>
|
||||
</Row>,
|
||||
<Row>
|
||||
<Col>
|
||||
{!isEmpty(suggestions) && (
|
||||
<div>
|
||||
<h3>{_('xosanSuggestions')}</h3>
|
||||
@ -548,6 +589,7 @@ export default class NewXosan extends Component {
|
||||
<hr />
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>,
|
||||
<Row>
|
||||
<Col>
|
||||
@ -561,7 +603,8 @@ export default class NewXosan extends Component {
|
||||
</ActionButton>
|
||||
</Col>
|
||||
</Row>,
|
||||
]}
|
||||
]
|
||||
))}
|
||||
<hr />
|
||||
</Container>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user