Better preset loop detection

The RPC and iteration counts are now reset for every cycle.
This commit is contained in:
Zaid Abdulla committed 2017-01-12 23:30:27 -08:00
1 parent 310114a500
commit 2e40ef8622
2 files changed
+65 -32

No files matched your search

+46 -30
View File
@@ -51,6 +51,8 @@ device = require './device'
cache = require './cache' cache = require './cache'
db = require './db' db = require './db'
MAX_CYCLES = 4
throwError = (err, httpResponse) -> throwError = (err, httpResponse) ->
if httpResponse if httpResponse
@@ -213,6 +215,8 @@ appendProvisions = (original, toAppend) ->
applyPresets = (currentRequest) -> applyPresets = (currentRequest) ->
sessionData = currentRequest.sessionData
cache.getPresets((err, presetsHash, presets) -> cache.getPresets((err, presetsHash, presets) ->
return throwError(err, currentRequest.httpResponse) if err return throwError(err, currentRequest.httpResponse) if err
@@ -221,28 +225,28 @@ applyPresets = (currentRequest) ->
whiteList = null whiteList = null
whiteListProvisions = null whiteListProvisions = null
RETRY_DELAY = config.get('RETRY_DELAY', currentRequest.sessionData.deviceId) RETRY_DELAY = config.get('RETRY_DELAY', sessionData.deviceId)
if currentRequest.sessionData.faults? if sessionData.faults?
for channel, fault of currentRequest.sessionData.faults for channel, fault of sessionData.faults
if fault.retryNow if fault.retryNow
retryTimestamp = 0 retryTimestamp = 0
else else
retryTimestamp = fault.timestamp + (RETRY_DELAY * Math.pow(2, currentRequest.sessionData.retries[channel])) * 1000 retryTimestamp = fault.timestamp + (RETRY_DELAY * Math.pow(2, sessionData.retries[channel])) * 1000
if retryTimestamp <= currentRequest.sessionData.timestamp if retryTimestamp <= sessionData.timestamp
whiteList = channel whiteList = channel
whiteListProvisions = fault.provisions whiteListProvisions = fault.provisions
break break
blackList[channel] = if fault.precondition then 1 else 2 blackList[channel] = if fault.precondition then 1 else 2
currentRequest.sessionData.presetsHash = presetsHash sessionData.presetsHash = presetsHash
deviceEvents = {} deviceEvents = {}
iter = currentRequest.sessionData.deviceData.paths.subset(['Events', '*']) iter = sessionData.deviceData.paths.subset(['Events', '*'])
while (p = iter.next().value) while (p = iter.next().value)
if currentRequest.sessionData.timestamp <= currentRequest.sessionData.deviceData.attributes.get(p)?.value?[1][0] if sessionData.timestamp <= sessionData.deviceData.attributes.get(p)?.value?[1][0]
deviceEvents[p[1]] = true deviceEvents[p[1]] = true
parameters = {} parameters = {}
@@ -251,7 +255,7 @@ applyPresets = (currentRequest) ->
for preset in presets for preset in presets
if whiteList? if whiteList?
continue if preset.channel != whiteList continue if preset.channel != whiteList
else if blackList[channel] == 1 else if blackList[preset.channel] == 1
continue continue
eventsMatch = true eventsMatch = true
@@ -261,11 +265,11 @@ applyPresets = (currentRequest) ->
break break
continue if (not eventsMatch) or (preset.schedule and not continue if (not eventsMatch) or (preset.schedule and not
(preset.schedule.schedule and testSchedule(preset.schedule, currentRequest.sessionData.timestamp)[0])) (preset.schedule.schedule and testSchedule(preset.schedule, sessionData.timestamp)[0]))
filteredPresets.push(preset) filteredPresets.push(preset)
for k of preset.precondition for k of preset.precondition
currentRequest.sessionData.channels[preset.channel] = 0 sessionData.channels[preset.channel] = 0
p = k.split(/([^a-zA-Z0-9\-\_\.].*)/, 1)[0] p = k.split(/([^a-zA-Z0-9\-\_\.].*)/, 1)[0]
parameters[p] = common.parsePath(p) parameters[p] = common.parsePath(p)
@@ -273,28 +277,28 @@ applyPresets = (currentRequest) ->
for k, v of parameters for k, v of parameters
declarations.push([v, 1, {value: 1}]) declarations.push([v, 1, {value: 1}])
session.rpcRequest(currentRequest.sessionData, declarations, (err, fault, id, rpcRequest) -> session.rpcRequest(sessionData, declarations, (err, fault, id, rpcRequest) ->
return throwError(err, currentRequest.httpResponse) if err return throwError(err, currentRequest.httpResponse) if err
if fault if fault
recordFault(currentRequest.sessionData, fault, currentRequest.sessionData.provisions, currentRequest.sessionData.channels) recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
session.clearProvisions(currentRequest.sessionData) session.clearProvisions(sessionData)
return applyPresets(currentRequest) return applyPresets(currentRequest)
if rpcRequest? if rpcRequest?
return sendRpcRequest(currentRequest, id, rpcRequest) return sendRpcRequest(currentRequest, id, rpcRequest)
session.clearProvisions(currentRequest.sessionData) session.clearProvisions(sessionData)
for k, v of parameters for k, v of parameters
unpacked = device.unpack(currentRequest.sessionData.deviceData, v) unpacked = device.unpack(sessionData.deviceData, v)
if unpacked[0] and (vv = currentRequest.sessionData.deviceData.attributes.get(unpacked[0]).value?[1])? if unpacked[0] and (vv = sessionData.deviceData.attributes.get(unpacked[0]).value?[1])?
parameters[k] = vv[0] parameters[k] = vv[0]
else else
delete parameters[k] delete parameters[k]
if whiteList? if whiteList?
session.addProvisions(currentRequest.sessionData, whiteList, whiteListProvisions) session.addProvisions(sessionData, whiteList, whiteListProvisions)
appendProvisionsToFaults = {} appendProvisionsToFaults = {}
for p in filteredPresets for p in filteredPresets
@@ -303,27 +307,39 @@ applyPresets = (currentRequest) ->
appendProvisionsToFaults[p.channel] = appendProvisionsToFaults[p.channel] =
(appendProvisionsToFaults[p.channel] or []).concat(p.provisions) (appendProvisionsToFaults[p.channel] or []).concat(p.provisions)
else else
session.addProvisions(currentRequest.sessionData, p.channel, p.provisions) session.addProvisions(sessionData, p.channel, p.provisions)
for channel, provisions of appendProvisionsToFaults for channel, provisions of appendProvisionsToFaults
if appendProvisions(currentRequest.sessionData.faults[channel].provisions, provisions) if appendProvisions(sessionData.faults[channel].provisions, provisions)
currentRequest.sessionData.faultsTouched ?= {} sessionData.faultsTouched ?= {}
currentRequest.sessionData.faultsTouched[channel] = true sessionData.faultsTouched[channel] = true
session.rpcRequest(currentRequest.sessionData, null, (err, fault, id, rpcRequest) -> sessionData.presetCycles = (sessionData.presetCycles or 0) + 1
if sessionData.presetCycles > MAX_CYCLES
fault = {
code: 'endless_cycle'
message: 'The provision seems to be repeating indefinitely'
timestamp: sessionData.timestamp
}
recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
session.clearProvisions(sessionData)
return sendRpcRequest(currentRequest)
session.rpcRequest(sessionData, null, (err, fault, id, rpcRequest) ->
return throwError(err, currentRequest.httpResponse) if err return throwError(err, currentRequest.httpResponse) if err
if fault if fault
recordFault(currentRequest.sessionData, fault, currentRequest.sessionData.provisions, currentRequest.sessionData.channels) recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
session.clearProvisions(currentRequest.sessionData) session.clearProvisions(sessionData)
return applyPresets(currentRequest) return applyPresets(currentRequest)
if not rpcRequest? if not rpcRequest?
for channel, flags of currentRequest.sessionData.channels for channel, flags of sessionData.channels
if channel of currentRequest.sessionData.faults if channel of sessionData.faults
delete currentRequest.sessionData.faults[channel] delete sessionData.faults[channel]
currentRequest.sessionData.faultsTouched ?= {} sessionData.faultsTouched ?= {}
currentRequest.sessionData.faultsTouched[channel] = true sessionData.faultsTouched[channel] = true
if whiteList? if whiteList?
return applyPresets(currentRequest) return applyPresets(currentRequest)
+19 -2
View File
@@ -26,6 +26,8 @@ VersionedMap = require './versioned-map'
InstanceSet = require './instance-set' InstanceSet = require './instance-set'
defaultProvisions = require './default-provisions' defaultProvisions = require './default-provisions'
MAX_ITERATIONS = 64
initDeviceData = () -> initDeviceData = () ->
return { return {
@@ -53,6 +55,7 @@ init = (deviceId, cwmpVersion, timeout, callback) ->
revisions: [0] revisions: [0]
rpcCount: 0 rpcCount: 0
iteration: 0 iteration: 0
cycle: 0
extensionsCache: {} extensionsCache: {}
declarations: [] declarations: []
} }
@@ -105,7 +108,9 @@ loadParameters = (sessionData, callback) ->
generateRpcId = (sessionData) -> generateRpcId = (sessionData) ->
return sessionData.timestamp.toString(16) + "0#{sessionData.rpcCount.toString(16)}".slice(-2) return sessionData.timestamp.toString(16) +
sessionData.cycle.toString(16).slice(-1) +
"0#{sessionData.rpcCount.toString(16)}".slice(-2)
inform = (sessionData, rpcReq, callback) -> inform = (sessionData, rpcReq, callback) ->
@@ -282,6 +287,11 @@ addProvisions = (sessionData, channel, provisions) ->
sessionData.revisions = [0] sessionData.revisions = [0]
sessionData.extensionsCache = {} sessionData.extensionsCache = {}
if sessionData.iteration != sessionData.cycle * MAX_ITERATIONS
sessionData.cycle += 1
sessionData.rpcCount = 0
sessionData.iteration = sessionData.cycle * MAX_ITERATIONS
sessionData.channels[channel] |= 0 sessionData.channels[channel] |= 0
for provision, i in provisions for provision, i in provisions
@@ -307,9 +317,15 @@ clearProvisions = (sessionData) ->
sessionData.deviceData.timestamps.collapse(1) sessionData.deviceData.timestamps.collapse(1)
sessionData.deviceData.attributes.collapse(1) sessionData.deviceData.attributes.collapse(1)
if sessionData.iteration != sessionData.cycle * MAX_ITERATIONS
sessionData.cycle += 1
sessionData.rpcCount = 0
sessionData.iteration = sessionData.cycle * MAX_ITERATIONS
delete sessionData.syncState delete sessionData.syncState
delete sessionData.rpcRequest delete sessionData.rpcRequest
sessionData.provisions = [] sessionData.provisions = []
sessionData.virtualParameters = []
sessionData.channels = {} sessionData.channels = {}
sessionData.declarations = [] sessionData.declarations = []
sessionData.doneProvisions = 0 sessionData.doneProvisions = 0
@@ -588,7 +604,8 @@ rpcRequest = (sessionData, _declarations, callback) ->
if sessionData.rpcCount >= 255 or if sessionData.rpcCount >= 255 or
sessionData.revisions.length >= 8 or sessionData.revisions.length >= 8 or
sessionData.iteration >= 128 sessionData.cycle >= 16 or
sessionData.iteration >= MAX_ITERATIONS * (sessionData.cycle + 1)
fault = { fault = {
code: 'endless_cycle' code: 'endless_cycle'