fix(sdn-controller): recreate both sides of tunnels when host updated (#4996)

To keep password synchronized.
This commit is contained in:
BenjiReis 2020-05-15 16:05:21 +02:00 committed by GitHub
parent 1da889e420
commit d547aa8ebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 72 deletions

View File

@ -19,6 +19,7 @@
- Fix mounting of NFS remote in FreeBSD (PR [#4988](https://github.com/vatesfr/xen-orchestra/issues/4988))
- [Remotes] Fix "remote is disabled" error on getting the remotes info (commit [eb2f429964d7adc264bf678c37e49a856454388e](https://github.com/vatesfr/xen-orchestra/commit/eb2f429964d7adc264bf678c37e49a856454388e))
- Fix default filters not being set in all tables (PR [#4994](https://github.com/vatesfr/xen-orchestra/pull/4994))
- [SDN Controller] Broken encrypted tunnels after host reboot [#4996](https://github.com/vatesfr/xen-orchestra/pull/4996)
### Released packages
@ -37,6 +38,7 @@
>
> In case of conflict, the highest (lowest in previous list) `$version` wins.
- xo-server-sdn-controller patch
- xo-server-usage-report minor
- @xen-orchestra/fs patch
- xo-server patch

View File

@ -312,7 +312,7 @@ class SDNController extends EventEmitter {
this._getDataDir = getDataDir
this._newHosts = []
this._privateNetworks = {}
this.privateNetworks = {}
this._networks = new Map()
this._starCenters = new Map()
@ -366,7 +366,7 @@ class SDNController extends EventEmitter {
})
const updatedPools = []
await Promise.all(
map(this._privateNetworks, async privateNetworks => {
map(this.privateNetworks, async privateNetworks => {
await Promise.all(
privateNetworks.getPools().map(async pool => {
if (!updatedPools.includes(pool)) {
@ -436,7 +436,7 @@ class SDNController extends EventEmitter {
}
async unload() {
this._privateNetworks = {}
this.privateNetworks = {}
this._newHosts = []
this._networks.clear()
@ -450,6 +450,15 @@ class SDNController extends EventEmitter {
this._unsetApiMethods()
}
// ---------------------------------------------------------------------------
registerPrivateNetwork(privateNetwork) {
this.privateNetworks[privateNetwork.uuid] = privateNetwork
log.info('Private network registered', {
privateNetwork: privateNetwork.uuid,
})
}
// ===========================================================================
async _handleConnectedXapi(xapi) {
@ -491,11 +500,10 @@ class SDNController extends EventEmitter {
return
}
let privateNetwork = this._privateNetworks[uuid]
if (privateNetwork === undefined) {
privateNetwork = new PrivateNetwork(this, uuid)
this._privateNetworks[uuid] = privateNetwork
}
const privateNetwork =
this.privateNetworks[uuid] !== undefined
? this.privateNetworks[uuid]
: new PrivateNetwork(this, uuid)
const vni = otherConfig['xo:sdn-controller:vni']
if (vni === undefined) {
@ -563,7 +571,7 @@ class SDNController extends EventEmitter {
)
// Re-elect a center to apply the VNI
const privateNetwork = this._privateNetworks[
const privateNetwork = this.privateNetworks[
network.other_config['xo:sdn-controller:private-network-uuid']
]
await this._electNewCenter(privateNetwork)
@ -580,7 +588,7 @@ class SDNController extends EventEmitter {
_handleDisconnectedXapi(xapi) {
log.debug('xapi disconnected', { id: xapi.pool.uuid })
try {
forOwn(this._privateNetworks, privateNetwork => {
forOwn(this.privateNetworks, privateNetwork => {
privateNetwork.networks = omitBy(
privateNetwork.networks,
network => network.$pool.uuid === xapi.pool.uuid
@ -591,8 +599,8 @@ class SDNController extends EventEmitter {
}
})
this._privateNetworks = filter(
this._privateNetworks,
this.privateNetworks = filter(
this.privateNetworks,
privateNetwork => Object.keys(privateNetwork.networks).length !== 0
)
} catch (error) {
@ -745,7 +753,7 @@ class SDNController extends EventEmitter {
if (starCenterRef !== undefined) {
this._starCenters.delete(id)
const privateNetworks = filter(
this._privateNetworks,
this.privateNetworks,
privateNetwork => privateNetwork.center?.$ref === starCenterRef
)
for (const privateNetwork of privateNetworks) {
@ -754,19 +762,19 @@ class SDNController extends EventEmitter {
return
}
// If a network is removed, clean this._privateNetworks from it
// If a network is removed, clean this.privateNetworks from it
const networkRef = this._networks.get(id)
if (networkRef !== undefined) {
this._networks.delete(id)
forOwn(this._privateNetworks, privateNetwork => {
forOwn(this.privateNetworks, privateNetwork => {
privateNetwork.networks = omitBy(
privateNetwork.networks,
network => network.$ref === networkRef
)
})
this._privateNetworks = filter(
this._privateNetworks,
this.privateNetworks = filter(
this.privateNetworks,
privateNetwork => Object.keys(privateNetwork.networks).length !== 0
)
}
@ -782,7 +790,7 @@ class SDNController extends EventEmitter {
async _pifUpdated(pif) {
// Only if PIF is in a private network
const privateNetwork = find(
this._privateNetworks,
this.privateNetworks,
privateNetwork =>
privateNetwork.networks[pif.$pool.uuid]?.$ref === pif.network
)
@ -856,7 +864,7 @@ class SDNController extends EventEmitter {
}
const privateNetworks = filter(
this._privateNetworks,
this.privateNetworks,
privateNetwork => privateNetwork[host.$pool.uuid] !== undefined
)
for (const privateNetwork of privateNetworks) {
@ -888,23 +896,23 @@ class SDNController extends EventEmitter {
// ---------------------------------------------------------------------------
async _setPoolControllerIfNeeded(pool) {
if (!isControllerNeeded(pool.$xapi)) {
// Nothing to do
return
}
if (isControllerNeeded(pool.$xapi)) {
const controller = find(pool.$xapi.objects.all, {
$type: 'SDN_controller',
})
if (controller !== undefined) {
await pool.$xapi.call('SDN_controller.forget', controller.$ref)
log.debug('Old SDN controller removed', {
pool: pool.name_label,
})
}
const controller = find(pool.$xapi.objects.all, { $type: 'SDN_controller' })
if (controller !== undefined) {
await pool.$xapi.call('SDN_controller.forget', controller.$ref)
log.debug('Old SDN controller removed', {
await pool.$xapi.call('SDN_controller.introduce', PROTOCOL)
log.debug('SDN controller has been set', {
pool: pool.name_label,
})
}
await pool.$xapi.call('SDN_controller.introduce', PROTOCOL)
log.debug('SDN controller has been set', {
pool: pool.name_label,
})
this._cleaners.push(await this._manageXapi(pool.$xapi))
}
@ -965,7 +973,7 @@ class SDNController extends EventEmitter {
}
const privateNetwork = find(
this._privateNetworks,
this.privateNetworks,
privateNetwork =>
privateNetwork.networks[host.$pool.uuid]?.$ref === network.$ref
)
@ -1041,7 +1049,7 @@ class SDNController extends EventEmitter {
}
async _hostUnreachable(host) {
let privateNetworks = filter(this._privateNetworks, privateNetwork => {
let privateNetworks = filter(this.privateNetworks, privateNetwork => {
return privateNetwork.center.$ref === host.$ref
})
for (const privateNetwork of privateNetworks) {
@ -1055,7 +1063,7 @@ class SDNController extends EventEmitter {
}
privateNetworks = filter(
this._privateNetworks,
this.privateNetworks,
privateNetwork => privateNetwork[host.$pool.uuid] !== undefined
)
await Promise.all(

View File

@ -18,6 +18,8 @@ export class PrivateNetwork {
this.controller = controller
this.uuid = uuid
this.networks = {}
this.controller.registerPrivateNetwork(this)
}
// ---------------------------------------------------------------------------

View File

@ -82,8 +82,16 @@ export class OvsdbClient {
}
const adding = { id: network.uuid, addr: remoteAddress }
this._adding.push(adding)
let socket
try {
socket = await this._connect()
} catch (error) {
this._adding = this._adding.filter(
elem => elem.id !== network.uuid || elem.addr !== remoteAddress
)
return
}
const socket = await this._connect()
const bridge = await this._getBridgeForNetwork(network, socket)
if (bridge.uuid === undefined) {
socket.destroy()
@ -93,17 +101,33 @@ export class OvsdbClient {
return
}
const alreadyExist = await this._interfaceAndPortAlreadyExist(
const port = await this._interfaceAndPortAlreadyExist(
bridge,
remoteAddress,
socket
)
if (alreadyExist) {
socket.destroy()
this._adding = this._adding.filter(
elem => elem.id !== network.uuid || elem.addr !== remoteAddress
)
return bridge.name
if (port !== undefined) {
if (password === undefined) {
socket.destroy()
this._adding = this._adding.filter(
elem => elem.id !== network.uuid || elem.addr !== remoteAddress
)
return bridge.name
}
// Remove port and interface to recreate it with new password
try {
await this._removePortsFromBridge(bridge, port, socket)
} catch (error) {
socket.destroy()
this._adding = this._adding.filter(
elem => elem.id !== network.uuid || elem.addr !== remoteAddress
)
log.error('Error while deleting port for encrypted password update', {
error,
})
return
}
}
const index = ++this._numberOfPortAndInterface
@ -253,34 +277,12 @@ export class OvsdbClient {
return
}
const mutateBridgeOperation = {
op: 'mutate',
table: 'Bridge',
where: [['_uuid', '==', ['uuid', bridge.uuid]]],
mutations: [['ports', 'delete', ['set', portsToDelete]]],
try {
await this._removePortsFromBridge(bridge, ['set', portsToDelete], socket)
} catch (error) {
log.error('Error while deleting ports for network reset', { error })
}
const params = ['Open_vSwitch', mutateBridgeOperation]
const jsonObjects = await this._sendOvsdbTransaction(params, socket)
if (jsonObjects === undefined) {
socket.destroy()
return
}
if (jsonObjects[0].error != null) {
log.error('Error while deleting ports from bridge', {
error: jsonObjects[0].error,
bridge: bridge.name,
host: this.host.name_label,
})
socket.destroy()
return
}
log.debug('Ports deleted from bridge', {
nPorts: jsonObjects[0].result[0].count,
bridge: bridge.name,
host: this.host.name_label,
})
socket.destroy()
}
@ -316,6 +318,39 @@ export class OvsdbClient {
// ---------------------------------------------------------------------------
async _removePortsFromBridge(bridge, portsToDelete, socket) {
const mutateBridgeOperation = {
op: 'mutate',
table: 'Bridge',
where: [['_uuid', '==', ['uuid', bridge.uuid]]],
mutations: [['ports', 'delete', portsToDelete]],
}
const params = ['Open_vSwitch', mutateBridgeOperation]
const jsonObjects = await this._sendOvsdbTransaction(params, socket)
if (jsonObjects === undefined) {
throw Error('Undefined OVSDB result while deleting ports from bridge', {
bridge: bridge.name,
host: this.host.name_label,
portsToDelete,
})
}
if (jsonObjects[0].error != null) {
throw Error('Error while deleting ports from bridge', {
error: jsonObjects[0].error,
bridge: bridge.name,
host: this.host.name_label,
portsToDelete,
})
}
log.debug('Ports deleted from bridge', {
nPorts: jsonObjects[0].result[0].count,
bridge: bridge.name,
host: this.host.name_label,
})
}
async _getBridgeForNetwork(network, socket) {
const where = [
['external_ids', 'includes', toMap({ 'xs-network-uuids': network.uuid })],
@ -340,7 +375,7 @@ export class OvsdbClient {
async _interfaceAndPortAlreadyExist(bridge, remoteAddress, socket) {
const ports = await this._getBridgePorts(bridge, socket)
if (ports === undefined) {
return false
return
}
for (const port of ports) {
@ -358,12 +393,10 @@ export class OvsdbClient {
socket
)
if (hasRemote) {
return true
return port
}
}
}
return false
}
async _getBridgePorts(bridge, socket) {