feat(xo-server,xo-web/menu): proxy upgrade notification (#5930)
See xoa-support#4105
This commit is contained in:
parent
eb238bf107
commit
fc73971d63
@ -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
|
||||
|
||||
|
@ -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')
|
||||
}
|
||||
|
@ -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 -----
|
||||
|
@ -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)
|
||||
|
@ -327,6 +327,10 @@
|
||||
@extend .fa-ticket;
|
||||
}
|
||||
&-circle {
|
||||
@extend .fa;
|
||||
@extend .fa-circle;
|
||||
}
|
||||
&-circle-thin {
|
||||
@extend .fa;
|
||||
@extend .fa-circle-thin;
|
||||
}
|
||||
|
@ -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 && {
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user