fix(sdn-controller): recreate both sides of tunnels when host updated (#4996)
To keep password synchronized.
This commit is contained in:
parent
1da889e420
commit
d547aa8ebd
@ -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
|
||||
|
@ -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(
|
||||
|
@ -18,6 +18,8 @@ export class PrivateNetwork {
|
||||
this.controller = controller
|
||||
this.uuid = uuid
|
||||
this.networks = {}
|
||||
|
||||
this.controller.registerPrivateNetwork(this)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user