feat(xo-web/settings/servers): display connection issues (#4310)
Fixes #4300
This commit is contained in:
committed by
Julien Fontanet
parent
863e4f0c19
commit
c7aaeca530
@@ -3,6 +3,7 @@
|
||||
### Enhancements
|
||||
|
||||
- [Stats] Ability to display last day stats [#4160](https://github.com/vatesfr/xen-orchestra/issues/4160) (PR [#4168](https://github.com/vatesfr/xen-orchestra/pull/4168))
|
||||
- [Settings/servers] Display servers connection issues [#4300](https://github.com/vatesfr/xen-orchestra/issues/4300) (PR [#4310](https://github.com/vatesfr/xen-orchestra/pull/4310))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@@ -12,5 +13,6 @@
|
||||
### Released packages
|
||||
|
||||
- xo-server-sdn-controller v0.1.1
|
||||
- xen-api v0.26.0
|
||||
- xo-server v5.45.0
|
||||
- xo-web v5.45.0
|
||||
|
||||
@@ -99,6 +99,9 @@ export class Xapi extends EventEmitter {
|
||||
this._sessionId = undefined
|
||||
this._status = DISCONNECTED
|
||||
|
||||
this._watchEventsError = undefined
|
||||
this._lastEventFetchedTimestamp = undefined
|
||||
|
||||
this._debounce = opts.debounce ?? 200
|
||||
this._objects = new Collection()
|
||||
this._objectsByRef = { __proto__: null }
|
||||
@@ -479,6 +482,14 @@ export class Xapi extends EventEmitter {
|
||||
return this._objectsFetched
|
||||
}
|
||||
|
||||
get lastEventFetchedTimestamp() {
|
||||
return this._lastEventFetchedTimestamp
|
||||
}
|
||||
|
||||
get watchEventsError() {
|
||||
return this._watchEventsError
|
||||
}
|
||||
|
||||
// ensure we have received all events up to this call
|
||||
//
|
||||
// optionally returns the up to date object for the given ref
|
||||
@@ -954,6 +965,8 @@ export class Xapi extends EventEmitter {
|
||||
],
|
||||
EVENT_TIMEOUT * 1e3 * 1.1
|
||||
)
|
||||
this._lastEventFetchedTimestamp = Date.now()
|
||||
this._watchEventsError = undefined
|
||||
} catch (error) {
|
||||
const code = error?.code
|
||||
if (code === 'EVENTS_LOST' || code === 'SESSION_INVALID') {
|
||||
@@ -961,6 +974,7 @@ export class Xapi extends EventEmitter {
|
||||
continue mainLoop
|
||||
}
|
||||
|
||||
this._watchEventsError = error
|
||||
console.warn('_watchEvents', error)
|
||||
await pDelay(this._eventPollDelay)
|
||||
continue
|
||||
|
||||
@@ -29,6 +29,9 @@ guessVhdSizeOnImport = false
|
||||
# be turned for investigation by the administrator.
|
||||
verboseApiLogsOnErrors = false
|
||||
|
||||
# if no events could be fetched during this delay, the server will be marked as disconnected
|
||||
xapiMarkDisconnectedDelay = '5 minutes'
|
||||
|
||||
# https:#github.com/websockets/ws#websocket-compression
|
||||
[apiWebSocketOptions]
|
||||
perMessageDeflate = { threshold = 524288 } # 512kiB
|
||||
|
||||
@@ -108,16 +108,16 @@ set.params = {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export async function connect({ id }) {
|
||||
export async function enable({ id }) {
|
||||
this.updateXenServer(id, { enabled: true })::ignoreErrors()
|
||||
await this.connectXenServer(id)
|
||||
}
|
||||
|
||||
connect.description = 'connect a Xen server'
|
||||
enable.description = 'enable a Xen server'
|
||||
|
||||
connect.permission = 'admin'
|
||||
enable.permission = 'admin'
|
||||
|
||||
connect.params = {
|
||||
enable.params = {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
@@ -125,16 +125,16 @@ connect.params = {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export async function disconnect({ id }) {
|
||||
export async function disable({ id }) {
|
||||
this.updateXenServer(id, { enabled: false })::ignoreErrors()
|
||||
await this.disconnectXenServer(id)
|
||||
}
|
||||
|
||||
disconnect.description = 'disconnect a Xen server'
|
||||
disable.description = 'disable a Xen server'
|
||||
|
||||
disconnect.permission = 'admin'
|
||||
disable.permission = 'admin'
|
||||
|
||||
disconnect.params = {
|
||||
disable.params = {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import { noSuchObject } from 'xo-common/api-errors'
|
||||
import { pDelay, ignoreErrors } from 'promise-toolbox'
|
||||
|
||||
import * as XenStore from '../_XenStore'
|
||||
import parseDuration from '../_parseDuration'
|
||||
import Xapi from '../xapi'
|
||||
import xapiObjectToXo from '../xapi-object-to-xo'
|
||||
import XapiStats from '../xapi-stats'
|
||||
@@ -40,7 +41,10 @@ const log = createLogger('xo:xo-mixins:xen-servers')
|
||||
// - _xapis[server.id] id defined
|
||||
// - _serverIdsByPool[xapi.pool.$id] is server.id
|
||||
export default class {
|
||||
constructor(xo, { guessVhdSizeOnImport, xapiOptions }) {
|
||||
constructor(
|
||||
xo,
|
||||
{ guessVhdSizeOnImport, xapiMarkDisconnectedDelay, xapiOptions }
|
||||
) {
|
||||
this._objectConflicts = { __proto__: null } // TODO: clean when a server is disconnected.
|
||||
const serversDb = (this._servers = new Servers({
|
||||
connection: xo._redis,
|
||||
@@ -55,6 +59,7 @@ export default class {
|
||||
}
|
||||
this._xapis = { __proto__: null }
|
||||
this._xo = xo
|
||||
this._xapiMarkDisconnectedDelay = parseDuration(xapiMarkDisconnectedDelay)
|
||||
|
||||
xo.on('clean', () => serversDb.rebuildIndexes())
|
||||
xo.on('start', async () => {
|
||||
@@ -472,6 +477,14 @@ export default class {
|
||||
const servers = await this._servers.get()
|
||||
const xapis = this._xapis
|
||||
forEach(servers, server => {
|
||||
const lastEventFetchedTimestamp =
|
||||
xapis[server.id]?.lastEventFetchedTimestamp
|
||||
if (
|
||||
lastEventFetchedTimestamp !== undefined &&
|
||||
Date.now() > lastEventFetchedTimestamp + this._xapiMarkDisconnectedDelay
|
||||
) {
|
||||
server.error = xapis[server.id].watchEventsError
|
||||
}
|
||||
server.status = this._getXenServerStatus(server.id)
|
||||
if (server.status === 'connected') {
|
||||
server.poolId = xapis[server.id].pool.uuid
|
||||
|
||||
@@ -3058,9 +3058,6 @@ export default {
|
||||
// Original text: "Enable it if your certificate is rejected, but it's not recommended because your connection will not be secured."
|
||||
serverUnauthorizedCertificatesInfo: undefined,
|
||||
|
||||
// Original text: 'Disconnect server'
|
||||
serverDisconnect: undefined,
|
||||
|
||||
// Original text: 'username'
|
||||
serverPlaceHolderUser: undefined,
|
||||
|
||||
@@ -3091,12 +3088,6 @@ export default {
|
||||
// Original text: 'Connecting…'
|
||||
serverConnecting: undefined,
|
||||
|
||||
// Original text: 'Connected'
|
||||
serverConnected: undefined,
|
||||
|
||||
// Original text: 'Disconnected'
|
||||
serverDisconnected: undefined,
|
||||
|
||||
// Original text: 'Authentication error'
|
||||
serverAuthFailed: undefined,
|
||||
|
||||
|
||||
@@ -3135,9 +3135,6 @@ export default {
|
||||
serverUnauthorizedCertificatesInfo:
|
||||
"Activez ceci si votre certificat est rejeté, mais ce n'est pas recommandé car votre connexion ne sera pas sécurisée.",
|
||||
|
||||
// Original text: "Disconnect server"
|
||||
serverDisconnect: 'Déconnecter le serveur',
|
||||
|
||||
// Original text: "username"
|
||||
serverPlaceHolderUser: "nom d'utilisateur",
|
||||
|
||||
@@ -3169,12 +3166,6 @@ export default {
|
||||
// Original text: "Connecting…"
|
||||
serverConnecting: 'Connexion…',
|
||||
|
||||
// Original text: "Connected"
|
||||
serverConnected: 'Connecté',
|
||||
|
||||
// Original text: "Disconnected"
|
||||
serverDisconnected: 'Déconnecté',
|
||||
|
||||
// Original text: "Authentication error"
|
||||
serverAuthFailed: "Erreur d'authentification",
|
||||
|
||||
|
||||
@@ -2612,9 +2612,6 @@ export default {
|
||||
// Original text: 'Read Only'
|
||||
serverReadOnly: undefined,
|
||||
|
||||
// Original text: 'Disconnect server'
|
||||
serverDisconnect: undefined,
|
||||
|
||||
// Original text: 'username'
|
||||
serverPlaceHolderUser: undefined,
|
||||
|
||||
|
||||
@@ -2909,9 +2909,6 @@ export default {
|
||||
// Original text: "Read Only"
|
||||
serverReadOnly: 'Csak Olvasható',
|
||||
|
||||
// Original text: "Disconnect server"
|
||||
serverDisconnect: 'Szerver Lecsatlakozás',
|
||||
|
||||
// Original text: "username"
|
||||
serverPlaceHolderUser: 'felhasználónév',
|
||||
|
||||
@@ -2939,12 +2936,6 @@ export default {
|
||||
// Original text: "Connecting…"
|
||||
serverConnecting: 'Csatlakozás…',
|
||||
|
||||
// Original text: "Connected"
|
||||
serverConnected: 'Kapcsolódva',
|
||||
|
||||
// Original text: "Disconnected"
|
||||
serverDisconnected: 'Lekapcsolódva',
|
||||
|
||||
// Original text: "Authentication error"
|
||||
serverAuthFailed: 'Bejelentkezési hiba',
|
||||
|
||||
|
||||
@@ -2648,9 +2648,6 @@ export default {
|
||||
// Original text: "Read Only"
|
||||
serverReadOnly: 'Tylko do odczytu',
|
||||
|
||||
// Original text: "Disconnect server"
|
||||
serverDisconnect: 'Rozłącz serwer',
|
||||
|
||||
// Original text: "username"
|
||||
serverPlaceHolderUser: 'Użytkownik',
|
||||
|
||||
|
||||
@@ -2636,9 +2636,6 @@ export default {
|
||||
// Original text: "Read Only"
|
||||
serverReadOnly: 'Modo Leitura',
|
||||
|
||||
// Original text: 'Disconnect server'
|
||||
serverDisconnect: undefined,
|
||||
|
||||
// Original text: 'username'
|
||||
serverPlaceHolderUser: undefined,
|
||||
|
||||
|
||||
@@ -3916,9 +3916,6 @@ export default {
|
||||
serverUnauthorizedCertificatesInfo:
|
||||
'Sertifikanız reddedildiğinde bunu yapın ancak bağlantınız güvenli olmayacağı için tavsiye edilmez.',
|
||||
|
||||
// Original text: "Disconnect server"
|
||||
serverDisconnect: 'Sunucu bağlantısını kes',
|
||||
|
||||
// Original text: "username"
|
||||
serverPlaceHolderUser: 'kullanıcı adı',
|
||||
|
||||
@@ -3949,12 +3946,6 @@ export default {
|
||||
// Original text: "Connecting…"
|
||||
serverConnecting: 'Bağlanıyor...',
|
||||
|
||||
// Original text: "Connected"
|
||||
serverConnected: 'Bağlandı',
|
||||
|
||||
// Original text: "Disconnected"
|
||||
serverDisconnected: 'Bağlantı kesildi',
|
||||
|
||||
// Original text: "Authentication error"
|
||||
serverAuthFailed: 'Kimlik doğrulama hatası',
|
||||
|
||||
|
||||
@@ -1662,7 +1662,6 @@ const messages = {
|
||||
serverAllowUnauthorizedCertificates: 'Allow Unauthorized Certificates',
|
||||
serverUnauthorizedCertificatesInfo:
|
||||
"Enable it if your certificate is rejected, but it's not recommended because your connection will not be secured.",
|
||||
serverDisconnect: 'Disconnect server',
|
||||
serverPlaceHolderUser: 'username',
|
||||
serverPlaceHolderPassword: 'password',
|
||||
serverPlaceHolderAddress: 'address[:port]',
|
||||
@@ -1672,13 +1671,15 @@ const messages = {
|
||||
serverAddFailed: 'Adding server failed',
|
||||
serverStatus: 'Status',
|
||||
serverConnectionFailed: 'Connection failed. Click for more information.',
|
||||
serverConnected: 'Connected',
|
||||
serverDisconnected: 'Disconnected',
|
||||
serverAuthFailed: 'Authentication error',
|
||||
serverUnknownError: 'Unknown error',
|
||||
serverSelfSignedCertError: 'Invalid self-signed certificate',
|
||||
serverSelfSignedCertQuestion:
|
||||
'Do you want to accept self-signed certificate for this server even though it would decrease security?',
|
||||
serverEnable: 'Enable',
|
||||
serverEnabled: 'Enabled',
|
||||
serverDisabled: 'Disabled',
|
||||
serverDisable: 'Disable server',
|
||||
|
||||
// ----- Copy VM -----
|
||||
copyVm: 'Copy VM',
|
||||
|
||||
@@ -536,13 +536,13 @@ export const editServer = (server, props) =>
|
||||
subscribeServers.forceRefresh
|
||||
)
|
||||
|
||||
export const connectServer = server =>
|
||||
_call('server.connect', { id: resolveId(server) })::pFinally(
|
||||
export const enableServer = server =>
|
||||
_call('server.enable', { id: resolveId(server) })::pFinally(
|
||||
subscribeServers.forceRefresh
|
||||
)
|
||||
|
||||
export const disconnectServer = server =>
|
||||
_call('server.disconnect', { id: resolveId(server) })::tap(
|
||||
export const disableServer = server =>
|
||||
_call('server.disable', { id: resolveId(server) })::tap(
|
||||
subscribeServers.forceRefresh
|
||||
)
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import { injectIntl } from 'react-intl'
|
||||
import { noop } from 'lodash'
|
||||
import {
|
||||
addServer,
|
||||
disableServer,
|
||||
editServer,
|
||||
connectServer,
|
||||
disconnectServer,
|
||||
enableServer,
|
||||
removeServer,
|
||||
subscribeServers,
|
||||
} from 'xo'
|
||||
@@ -38,7 +38,7 @@ const showServerError = server => {
|
||||
}).then(
|
||||
() =>
|
||||
editServer(server, { allowUnauthorized: true }).then(() =>
|
||||
connectServer(server)
|
||||
enableServer(server)
|
||||
),
|
||||
noop
|
||||
)
|
||||
@@ -100,17 +100,16 @@ const COLUMNS = [
|
||||
itemRenderer: server => (
|
||||
<div>
|
||||
<StateButton
|
||||
disabledLabel={_('serverDisconnected')}
|
||||
disabledHandler={connectServer}
|
||||
disabledTooltip={_('serverConnect')}
|
||||
enabledLabel={_('serverConnected')}
|
||||
enabledHandler={disconnectServer}
|
||||
enabledTooltip={_('serverDisconnect')}
|
||||
disabledLabel={_('serverDisabled')}
|
||||
disabledHandler={enableServer}
|
||||
disabledTooltip={_('serverEnable')}
|
||||
enabledLabel={_('serverEnabled')}
|
||||
enabledHandler={disableServer}
|
||||
enabledTooltip={_('serverDisable')}
|
||||
handlerParam={server}
|
||||
pending={server.status === 'connecting'}
|
||||
state={server.status === 'connected'}
|
||||
state={server.enabled}
|
||||
/>{' '}
|
||||
{server.error && (
|
||||
{server.error != null && (
|
||||
<Tooltip content={_('serverConnectionFailed')}>
|
||||
<a
|
||||
className='text-danger btn btn-link btn-sm'
|
||||
|
||||
Reference in New Issue
Block a user