feat(xo-server-sdn-controller): dynamically handle (dis)connected servers (fixes #4649) (#4677)

* feat(xo-server-sdn-controller): Dynamically monitor xapis dis/connection to XO (fixes #4649)

* fill changelog.unreleased.md

* adapt ro PR comments

* rename methods

* refactor code

* Update packages/xo-server-sdn-controller/src/index.js

Co-Authored-By: badrAZ <azizbibadr@gmail.com>

* refactor

* Update CHANGELOG.unreleased.md

Co-Authored-By: Julien Fontanet <julien.fontanet@isonoe.net>

* Update CHANGELOG.unreleased.md

Co-Authored-By: Julien Fontanet <julien.fontanet@isonoe.net>

* use native array map

* Better name for setControllerNeeded

* add try/catch block

* use correct check on objects

* fix previous commit

* remove useless emit

Co-authored-by: badrAZ <azizbibadr@gmail.com>
Co-authored-by: Julien Fontanet <julien.fontanet@isonoe.net>
This commit is contained in:
BenjiReis
2020-02-13 12:04:22 +01:00
committed by GitHub
parent ab3621fe3c
commit e63d27a035
3 changed files with 179 additions and 121 deletions

View File

@@ -8,12 +8,14 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [New VM] Ability to set VM max vCPUS [#4703](https://github.com/vatesfr/xen-orchestra/issues/4703) (PR [#4729](https://github.com/vatesfr/xen-orchestra/pull/4729))
- [SDN Controller] Automatically handle new, connected and disconnected servers (PR [#4677](https://github.com/vatesfr/xen-orchestra/pull/4677))
### Bug fixes
> Users must be able to say: “I had this issue, happy to know it's fixed”
- [Usage Report] Fix wrong report date [#4779](https://github.com/vatesfr/xen-orchestra/issues/4779) (PR [#4799](https://github.com/vatesfr/xen-orchestra/pull/4799))
- [SDN Controller] Fix plugin stuck loading [#4649](https://github.com/vatesfr/xen-orchestra/issues/4649) (PR [#4677](https://github.com/vatesfr/xen-orchestra/pull/4677))
### Released packages
@@ -23,5 +25,6 @@
> Rule of thumb: add packages on top.
- xo-server-usage-report v0.7.4
- xo-server-sdn-controller v0.4.0
- xo-server v5.57.0
- xo-web v5.57.0

View File

@@ -275,7 +275,7 @@ function getHostTunnelForNetwork(host, networkRef) {
// -----------------------------------------------------------------------------
function setControllerNeeded(xapi) {
function isControllerNeeded(xapi) {
const controller = find(xapi.objects.all, { $type: 'SDN_controller' })
return !(
controller?.protocol === PROTOCOL &&
@@ -418,126 +418,21 @@ class SDNController extends EventEmitter {
},
})
// FIXME: we should monitor when xapis are added/removed
this._xapis = this._xo.getAllXapis()
await Promise.all(
map(this._xapis, async xapi => {
await xapi.objectsFetched
forOwn(this._xo.getAllXapis(), xapi => {
if (xapi.status === 'connected') {
this._handleConnectedXapi(xapi)
}
})
if (setControllerNeeded(xapi)) {
return
}
this._cleaners.push(await this._manageXapi(xapi))
const hosts = filter(xapi.objects.all, { $type: 'host' })
for (const host of hosts) {
this._createOvsdbClient(host)
}
// Add already existing private networks
const networks = filter(xapi.objects.all, { $type: 'network' })
const noVniNetworks = []
await Promise.all(
map(networks, async network => {
// 2019-09-03
// Compatibility code, to be removed in 1 year.
await updateNetworkOtherConfig(network)
network = await network.$xapi.barrier(network.$ref)
let otherConfig = network.other_config
// 2019-10-01
// To be removed in a year
if (otherConfig['xo:sdn-controller:private-pool-wide'] === 'true') {
await updateOldPrivateNetwork(network)
}
network = await network.$xapi.barrier(network.$ref)
otherConfig = network.other_config
const uuid = otherConfig['xo:sdn-controller:private-network-uuid']
if (uuid === undefined) {
return
}
let privateNetwork = this._privateNetworks[uuid]
if (privateNetwork === undefined) {
privateNetwork = new PrivateNetwork(this, uuid)
this._privateNetworks[uuid] = privateNetwork
}
const vni = otherConfig['xo:sdn-controller:vni']
if (vni === undefined) {
noVniNetworks.push(network)
} else {
this._prevVni = Math.max(this._prevVni, +vni)
}
await privateNetwork.addNetwork(network)
// Previously created network didn't store `pif-device`
//
// 2019-08-22
// This is used to add the `pif-device` to networks created before this version. (v0.1.2)
// This will be removed in 1 year.
if (otherConfig['xo:sdn-controller:pif-device'] === undefined) {
const tunnel = getHostTunnelForNetwork(
privateNetwork.center,
network.$ref
)
const pif = xapi.getObjectByRef(tunnel.transport_PIF)
await network.update_other_config(
'xo:sdn-controller:pif-device',
pif.device
)
}
// Previously created network didn't store `vlan`
//
// 2020-01-27
// This is used to add the `vlan` to networks created before this version. (v0.4.0)
// This will be removed in 1 year.
if (otherConfig['xo:sdn-controller:vlan'] === undefined) {
const tunnel = getHostTunnelForNetwork(
privateNetwork.center,
network.$ref
)
const pif = xapi.getObjectByRef(tunnel.transport_PIF)
await network.update_other_config(
'xo:sdn-controller:vlan',
String(pif.VLAN)
)
}
this._networks.set(network.$id, network.$ref)
if (privateNetwork.center !== undefined) {
this._starCenters.set(
privateNetwork.center.$id,
privateNetwork.center.$ref
)
}
})
)
// Add VNI to other config of networks without VNI
//
// 2019-08-22
// This is used to add the VNI to networks created before this version. (v0.1.3)
// This will be removed in 1 year.
await Promise.all(
map(noVniNetworks, async network => {
await network.update_other_config(
'xo:sdn-controller:vni',
String(++this._prevVni)
)
// Re-elect a center to apply the VNI
const privateNetwork = this._privateNetworks[
network.other_config['private-network-uuid']
]
await this._electNewCenter(privateNetwork)
})
)
})
)
const handleConnectedServer = ({ xapi }) => this._handleConnectedXapi(xapi)
const handleDisconnectedServer = ({ xapi }) =>
this._handleDisconnectedXapi(xapi)
this._xo.on('server:connected', handleConnectedServer)
this._xo.on('server:disconnected', handleDisconnectedServer)
this._cleaners.push(() => {
this._xo.removeListener('server:connected', handleConnectedServer)
this._xo.removeListener('server:disconnected', handleDisconnectedServer)
})
}
async unload() {
@@ -557,6 +452,159 @@ class SDNController extends EventEmitter {
// ===========================================================================
async _handleConnectedXapi(xapi) {
log.debug('xapi connected', { id: xapi.pool.uuid })
try {
await xapi.objectsFetched
if (isControllerNeeded(xapi)) {
return
}
this._cleaners.push(await this._manageXapi(xapi))
const hosts = filter(xapi.objects.all, { $type: 'host' })
for (const host of hosts) {
this._createOvsdbClient(host)
}
// Add already existing private networks
const networks = filter(xapi.objects.all, { $type: 'network' })
const noVniNetworks = []
await Promise.all(
networks.map(async network => {
// 2019-09-03
// Compatibility code, to be removed in 1 year.
await updateNetworkOtherConfig(network)
network = await network.$xapi.barrier(network.$ref)
let otherConfig = network.other_config
// 2019-10-01
// To be removed in a year
if (otherConfig['xo:sdn-controller:private-pool-wide'] === 'true') {
await updateOldPrivateNetwork(network)
}
network = await network.$xapi.barrier(network.$ref)
otherConfig = network.other_config
const uuid = otherConfig['xo:sdn-controller:private-network-uuid']
if (uuid === undefined) {
return
}
let privateNetwork = this._privateNetworks[uuid]
if (privateNetwork === undefined) {
privateNetwork = new PrivateNetwork(this, uuid)
this._privateNetworks[uuid] = privateNetwork
}
const vni = otherConfig['xo:sdn-controller:vni']
if (vni === undefined) {
noVniNetworks.push(network)
} else {
this._prevVni = Math.max(this._prevVni, +vni)
}
await privateNetwork.addNetwork(network)
// Previously created network didn't store `pif-device`
//
// 2019-08-22
// This is used to add the pif-device to networks created before this version. (v0.1.2)
// This will be removed in 1 year.
if (otherConfig['xo:sdn-controller:pif-device'] === undefined) {
const tunnel = getHostTunnelForNetwork(
privateNetwork.center,
network.$ref
)
const pif = xapi.getObjectByRef(tunnel.transport_PIF)
await network.update_other_config(
'xo:sdn-controller:pif-device',
pif.device
)
}
// Previously created network didn't store `vlan`
//
// 2020-01-27
// This is used to add the `vlan` to networks created before this version. (v0.4.0)
// This will be removed in 1 year.
if (otherConfig['xo:sdn-controller:vlan'] === undefined) {
const tunnel = getHostTunnelForNetwork(
privateNetwork.center,
network.$ref
)
const pif = xapi.getObjectByRef(tunnel.transport_PIF)
await network.update_other_config(
'xo:sdn-controller:vlan',
String(pif.VLAN)
)
}
this._networks.set(network.$id, network.$ref)
if (privateNetwork.center !== undefined) {
this._starCenters.set(
privateNetwork.center.$id,
privateNetwork.center.$ref
)
}
})
)
// Add VNI to other config of networks without VNI
//
// 2019-08-22
// This is used to add the VNI to networks created before this version. (v0.1.3)
// This will be removed in 1 year.
await Promise.all(
noVniNetworks.map(async network => {
await network.update_other_config(
'xo:sdn-controller:vni',
String(++this._prevVni)
)
// Re-elect a center to apply the VNI
const privateNetwork = this._privateNetworks[
network.other_config['private-network-uuid']
]
await this._electNewCenter(privateNetwork)
})
)
} catch (error) {
log.error('Error while handling xapi connection', {
id: xapi.pool.uuid,
error,
})
}
}
_handleDisconnectedXapi(xapi) {
log.debug('xapi disconnected', { id: xapi.pool.uuid })
try {
forOwn(this._privateNetworks, privateNetwork => {
privateNetwork.networks = omitBy(
privateNetwork.networks,
network => network.$pool.uuid === xapi.pool.uuid
)
if (privateNetwork.center?.$pool.uuid === xapi.pool.uuid) {
this._electNewCenter(privateNetwork)
}
})
this._privateNetworks = filter(
this._privateNetworks,
privateNetwork => Object.keys(privateNetwork.networks).length !== 0
)
} catch (error) {
log.error('Error while handling xapi disconnection', {
id: xapi.pool.uuid,
error,
})
}
}
// ===========================================================================
async _createPrivateNetwork({
poolIds,
pifIds,
@@ -716,6 +764,11 @@ class SDNController extends EventEmitter {
network => network.$ref === networkRef
)
})
this._privateNetworks = filter(
this._privateNetworks,
privateNetwork => Object.keys(privateNetwork.networks).length !== 0
)
}
} catch (error) {
log.error('Error in _objectsRemoved', {
@@ -831,7 +884,7 @@ class SDNController extends EventEmitter {
// ---------------------------------------------------------------------------
async _setPoolControllerIfNeeded(pool) {
if (!setControllerNeeded(pool.$xapi)) {
if (!isControllerNeeded(pool.$xapi)) {
// Nothing to do
return
}

View File

@@ -445,7 +445,9 @@ export default class {
xapi.xo.uninstall()
delete this._xapis[server.id]
delete this._serverIdsByPool[poolId]
this._xo.emit('server:disconnected', { server, xapi })
})
this._xo.emit('server:connected', { server, xapi })
} catch (error) {
delete this._xapis[server.id]
xapi.disconnect()::ignoreErrors()