Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(MOS): support OpenMedia's hot standby #1169

Merged
merged 9 commits into from
Dec 11, 2024
40 changes: 31 additions & 9 deletions packages/mos-gateway/src/$schemas/devices.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,20 @@
"ui:title": "(Optional) MOS Query Port",
"ui:description": "Connect to an alternate port for 'query' port MOS messages",
"default": 10542
}
}
},
"required": ["lower", "upper", "query"],
"required": [
"lower",
"upper",
"query"
],
"additionalProperties": false
}
},
"required": ["id", "host"],
"required": [
"id",
"host"
],
"additionalProperties": false
},
"secondary": {
Expand Down Expand Up @@ -105,6 +112,12 @@
"ui:description": "How often to ping NRCS to determine connection status",
"default": 30000
},
"openMediaHotStandby": {
"type": "boolean",
"ui:title": "Secondary: OpenMedia Hot Standby",
"ui:description": "Is the secondary connection a OpenMedia hot standby for the primary",
"default": false
},
"ports": {
"type": "object",
"ui:title": "Ports",
Expand All @@ -126,16 +139,25 @@
"ui:title": "(Optional) MOS Query Port",
"ui:description": "Connect to an alternate port for 'query' port MOS messages",
"default": 10542
}
}
},
"required": ["lower", "upper", "query"],
"required": [
"lower",
"upper",
"query"
],
"additionalProperties": false
}
}
},
"required": ["id", "host"],
"required": [
"id",
"host"
],
"additionalProperties": false
}
},
"required": ["primary"],
"required": [
"primary"
],
"additionalProperties": false
}
}
50 changes: 37 additions & 13 deletions packages/mos-gateway/src/CoreMosDeviceHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,15 @@ export class CoreMosDeviceHandler {
private _pendingStoryItemChanges: Array<IStoryItemChange> = []
private _pendingChangeTimeout: number = 60 * 1000
private mosTypes: MosTypes
private _openMediaHotStandby: boolean

private _messageQueue: Queue

constructor(parent: CoreHandler, mosDevice: IMOSDevice, mosHandler: MosHandler) {
constructor(parent: CoreHandler, mosDevice: IMOSDevice, mosHandler: MosHandler, openMediaHotStandby: boolean) {
Julusian marked this conversation as resolved.
Show resolved Hide resolved
this._coreParentHandler = parent
this._mosDevice = mosDevice
this._mosHandler = mosHandler
this._openMediaHotStandby = openMediaHotStandby

this._messageQueue = new Queue()

Expand Down Expand Up @@ -138,25 +140,47 @@ export class CoreMosDeviceHandler {
let statusCode: StatusCode
const messages: Array<string> = []

if (connectionStatus.PrimaryConnected) {
if (connectionStatus.SecondaryConnected || !this._mosDevice.idSecondary) {
if (this._openMediaHotStandby) {
// OpenMedia treats secondary server as hot-standby
// And thus is not considered as a warning if it's not connected
if (connectionStatus.PrimaryConnected) {
statusCode = StatusCode.GOOD
} else {
statusCode = StatusCode.WARNING_MINOR
// Primary not connected is only bad if there is no secondary:
if (connectionStatus.SecondaryConnected) {
statusCode = StatusCode.GOOD
messages.push(connectionStatus.SecondaryStatus || 'Running NRCS on hot standby')
} else {
statusCode = StatusCode.BAD
// Send messages for both connections
messages.push(connectionStatus.PrimaryStatus || 'Primary and hot standby are not connected')
messages.push(connectionStatus.SecondaryStatus || 'Primary and hot standby are not connected')
}
}
} else {
if (connectionStatus.SecondaryConnected) {
statusCode = StatusCode.WARNING_MAJOR
if (connectionStatus.PrimaryConnected) {
// ENPS expect both Primary and Secondary to be connected if both of them are configured
if (connectionStatus.SecondaryConnected || !this._mosDevice.idSecondary) {
statusCode = StatusCode.GOOD
} else {
statusCode = StatusCode.WARNING_MINOR
}
} else {
statusCode = StatusCode.BAD
if (connectionStatus.SecondaryConnected) {
// Primary not connected should give a warning if Secondary is used.
statusCode = StatusCode.WARNING_MAJOR
} else {
// If neither Primary nor Secondary is connected, it's a bad state.
statusCode = StatusCode.BAD
}
}
}

if (!connectionStatus.PrimaryConnected) {
messages.push(connectionStatus.PrimaryStatus || 'Primary not connected')
}
if (this._mosDevice.idSecondary && !connectionStatus.SecondaryConnected) {
messages.push(connectionStatus.SecondaryStatus || 'Fallback not connected')
if (!connectionStatus.PrimaryConnected) {
messages.push(connectionStatus.PrimaryStatus || 'Primary not connected')
}
if (this._mosDevice.idSecondary && !connectionStatus.SecondaryConnected) {
messages.push(connectionStatus.SecondaryStatus || 'Fallback not connected')
}
}

this.core
Expand Down
8 changes: 6 additions & 2 deletions packages/mos-gateway/src/coreHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,13 @@ export class CoreHandler {

return options
}
async registerMosDevice(mosDevice: IMOSDevice, mosHandler: MosHandler): Promise<CoreMosDeviceHandler> {
async registerMosDevice(
mosDevice: IMOSDevice,
mosHandler: MosHandler,
openMediaHotStandby: boolean
Julusian marked this conversation as resolved.
Show resolved Hide resolved
): Promise<CoreMosDeviceHandler> {
this.logger.info('registerMosDevice -------------')
const coreMos = new CoreMosDeviceHandler(this, mosDevice, mosHandler)
const coreMos = new CoreMosDeviceHandler(this, mosDevice, mosHandler, openMediaHotStandby)

this._coreMosHandlers.push(coreMos)
return coreMos.init().then(() => {
Expand Down
1 change: 1 addition & 0 deletions packages/mos-gateway/src/generated/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface MosDeviceConfig {
dontUseQueryPort?: boolean
timeout?: number
heartbeatInterval?: number
openMediaHotStandby?: boolean
ports?: {
lower: number
upper: number
Expand Down
14 changes: 10 additions & 4 deletions packages/mos-gateway/src/mosHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,15 @@ export class MosHandler {
private _logger: Winston.Logger
private _disposed = false
private _settings?: MosGatewayConfig
private _openMediaHotStandby: Record<string, boolean>
private _coreHandler: CoreHandler | undefined
private _observers: Array<Observer<any>> = []
private _triggerupdateDevicesTimeout: any = null
private mosTypes: MosTypes

constructor(logger: Winston.Logger) {
this._logger = logger
this._openMediaHotStandby = {}
this.mosTypes = getMosTypes(this.strict) // temporary, another will be set upon init()
}
async init(config: MosConfig, coreHandler: CoreHandler): Promise<void> {
Expand Down Expand Up @@ -101,7 +103,7 @@ export class MosHandler {

this.mosTypes = getMosTypes(this.strict)

await this._initMosConnection()
await this._updateDevices()

if (!this._coreHandler) throw Error('_coreHandler is undefined!')
this._coreHandler.onConnected(() => {
Expand All @@ -110,8 +112,6 @@ export class MosHandler {
this.sendStatusOfAllMosDevices()
})
this.setupObservers()

return this._updateDevices()
}
async dispose(): Promise<void> {
this._disposed = true
Expand Down Expand Up @@ -243,7 +243,11 @@ export class MosHandler {

if (!this._coreHandler) throw Error('_coreHandler is undefined!')

const coreMosHandler = await this._coreHandler.registerMosDevice(mosDevice, this)
const coreMosHandler = await this._coreHandler.registerMosDevice(
mosDevice,
this,
mosDevice.idSecondary ? this._openMediaHotStandby[mosDevice.idSecondary] : false
)
// this._logger.info('mosDevice registered -------------')
// Setup message flow between the devices:

Expand Down Expand Up @@ -420,6 +424,8 @@ export class MosHandler {
for (const [deviceId, device] of Object.entries<{ options: MosDeviceConfig }>(devices)) {
if (device) {
if (device.options.secondary) {
this._openMediaHotStandby[device.options.secondary.id] =
device.options.secondary?.openMediaHotStandby || false
// If the host isn't set, don't use secondary:
if (!device.options.secondary.host || !device.options.secondary.id)
delete device.options.secondary
Expand Down