feat(xo-server,xo-web/menu): proxy upgrade notification (#5930)

See xoa-support#4105
This commit is contained in:
Rajaa.BARHTAOUI 2021-10-28 10:52:23 +02:00 committed by GitHub
parent eb238bf107
commit fc73971d63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 3 deletions

View File

@ -13,6 +13,7 @@
- [VM/export] Ability to copy the export URL (PR [#5948](https://github.com/vatesfr/xen-orchestra/pull/5948))
- [Servers] Ability to use an HTTP proxy between XO and a server
- [Pool/advanced] Ability to define network for importing/exporting VMs/VDIs (PR [#5957](https://github.com/vatesfr/xen-orchestra/pull/5957))
- [Menu] Notify user when proxies need to be upgraded (PR [#5930](https://github.com/vatesfr/xen-orchestra/pull/5930))
### Bug fixes

View File

@ -23,9 +23,12 @@ import { timeout } from 'promise-toolbox'
import Collection from '../collection/redis.mjs'
import patch from '../patch.mjs'
import { debounceWithKey } from '../_pDebounceWithKey.mjs'
import { extractIpFromVmNetworks } from '../_extractIpFromVmNetworks.mjs'
import { generateToken } from '../utils.mjs'
const DEBOUNCE_TIME_PROXY_STATE = 60000
const extractProperties = _ => _.properties
const omitToken = proxy => omit(proxy, 'authenticationToken')
const synchronizedWrite = synchronized()
@ -191,6 +194,7 @@ export default class Proxy {
await xapi._waitObjectState(vmUuid, vm => extractIpFromVmNetworks(vm.$guest_metrics?.networks) !== undefined)
}
@decorateWith(debounceWithKey, DEBOUNCE_TIME_PROXY_STATE, id => id)
getProxyApplianceUpdaterState(id) {
return this.callProxyMethod(id, 'appliance.updater.getState')
}

View File

@ -2380,6 +2380,7 @@ const messages = {
proxyUpToDate: 'Your proxy is up-to-date',
proxyRunningBackupsMessage:
'The upgrade will interrupt {nJobs, number} running backup job{nJobs, plural, one {} other {s}}. Do you want to continue?',
proxiesNeedUpgrade: 'Some proxies need to be upgraded.',
upgradeNeededForProxies: 'Some proxies need to be upgraded. Click here to get more information.',
// ----- Utils -----

View File

@ -477,6 +477,25 @@ subscribeHostMissingPatches.forceRefresh = host => {
}
}
const proxiesApplianceUpdaterState = {}
export const subscribeProxiesApplianceUpdaterState = (proxyId, cb) => {
if (proxiesApplianceUpdaterState[proxyId] === undefined) {
proxiesApplianceUpdaterState[proxyId] = createSubscription(() => getProxyApplianceUpdaterState(proxyId))
}
return proxiesApplianceUpdaterState[proxyId](cb)
}
subscribeProxiesApplianceUpdaterState.forceRefresh = proxyId => {
if (proxyId === undefined) {
forEach(proxiesApplianceUpdaterState, subscription => subscription.forceRefresh())
return
}
const subscription = proxiesApplianceUpdaterState[proxyId]
if (subscription !== undefined) {
subscription.forceRefresh()
}
}
const volumeInfoBySr = {}
export const subscribeVolumeInfo = ({ sr, infoType }, cb) => {
sr = resolveId(sr)

View File

@ -327,6 +327,10 @@
@extend .fa-ticket;
}
&-circle {
@extend .fa;
@extend .fa-circle;
}
&-circle-thin {
@extend .fa;
@extend .fa-circle-thin;
}

View File

@ -1,7 +1,7 @@
import _ from 'intl'
import classNames from 'classnames'
import Component from 'base-component'
import Icon from 'icon'
import Icon, { StackedIcons } from 'icon'
import Link from 'link'
import React from 'react'
import Tooltip from 'tooltip'
@ -15,6 +15,8 @@ import {
subscribeHostMissingPatches,
subscribeNotifications,
subscribePermissions,
subscribeProxies,
subscribeProxiesApplianceUpdaterState,
subscribeResourceSets,
} from 'xo'
import {
@ -60,6 +62,10 @@ const returnTrue = () => true
@addSubscriptions({
notifications: subscribeNotifications,
permissions: subscribePermissions,
proxyIds: cb =>
subscribeProxies(proxies => {
cb(map(proxies, 'id').sort())
}),
resourceSets: subscribeResourceSets,
})
@injectState
@ -77,18 +83,29 @@ export default class Menu extends Component {
}
this._updateMissingPatchesSubscriptions()
this._updateProxiesSubscriptions()
}
componentWillUnmount() {
this._removeListener()
this._unsubscribeMissingPatches()
this._unsubscribeProxiesApplianceUpdaterState()
}
componentDidUpdate(prevProps) {
if (!isEqual(Object.keys(prevProps.hosts).sort(), Object.keys(this.props.hosts).sort())) {
this._updateMissingPatchesSubscriptions()
}
if (!isEqual(prevProps.proxyIds, this.props.proxyIds)) {
this._updateProxiesSubscriptions()
}
}
_areProxiesOutOfDate = createSelector(
() => this.state.proxyStates,
proxyStates => some(proxyStates, state => state.endsWith('-upgrade-needed'))
)
_checkPermissions = createSelector(
() => this.props.isAdmin,
@ -157,6 +174,29 @@ export default class Menu extends Component {
this._unsubscribeMissingPatches = () => forEach(unsubs, unsub => unsub())
}
_updateProxiesSubscriptions = () => {
this.setState(({ proxyStates }) => ({
proxyStates: pick(proxyStates, this.props.proxyIds),
}))
const unsubs = map(this.props.proxyIds, proxyId =>
subscribeProxiesApplianceUpdaterState(proxyId, ({ state: proxyState = '' }) => {
this.setState(state => ({
proxyStates: {
...state.proxyStates,
[proxyId]: proxyState,
},
}))
})
)
if (this._unsubscribeProxiesApplianceUpdaterState !== undefined) {
this._unsubscribeProxiesApplianceUpdaterState()
}
this._unsubscribeProxiesApplianceUpdaterState = () => forEach(unsubs, unsub => unsub())
}
render() {
const { isAdmin, isPoolAdmin, nTasks, state, status, user, pools, nHosts, srs, xoaState } = this.props
const noOperatablePools = this._getNoOperatablePools()
@ -397,6 +437,18 @@ export default class Menu extends Component {
to: '/proxies',
icon: 'proxy',
label: 'proxies',
extra: [
this._areProxiesOutOfDate() ? (
<Tooltip content={_('proxiesNeedUpgrade')}>
<StackedIcons
icons={[
{ color: 'text-success', icon: 'circle', size: 2 },
{ icon: 'menu-update', size: 1 },
]}
/>
</Tooltip>
) : null,
],
},
isAdmin && { to: '/about', icon: 'menu-about', label: 'aboutPage' },
!noOperatablePools && {

View File

@ -355,7 +355,7 @@ class VifStatus extends BaseComponent {
<StackedIcons
icons={[
{ icon: 'vif-disable', size: 1 },
{ icon: 'circle', size: 2 },
{ icon: 'circle-thin', size: 2 },
]}
/>
</Tooltip>
@ -366,7 +366,7 @@ class VifStatus extends BaseComponent {
<StackedIcons
icons={[
{ icon: 'unlock', size: 1 },
{ icon: 'circle', size: 2 },
{ icon: 'circle-thin', size: 2 },
]}
/>
</Tooltip>