Remove obsolete code
This commit is contained in:
1 parent
19fea2ac72
commit
c3a956ad27
8 files changed
+6
-1184
No files matched your search
@@ -164,8 +164,6 @@ exports.PARAMETERS = require(path.resolve(allConfig.CONFIG_DIR, 'parameters'))
|
|||||||
# load authentication scripts
|
# load authentication scripts
|
||||||
exports.auth = require(path.resolve(allConfig.CONFIG_DIR, 'auth'))
|
exports.auth = require(path.resolve(allConfig.CONFIG_DIR, 'auth'))
|
||||||
|
|
||||||
exports.CUSTOM_COMMANDS = require(path.resolve(allConfig.CONFIG_DIR, 'custom_commands'))
|
|
||||||
|
|
||||||
exports.get = get
|
exports.get = get
|
||||||
|
|
||||||
exports.allConfig = allConfig
|
exports.allConfig = allConfig
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
###
|
|
||||||
# Copyright 2013, 2014 Zaid Abdulla
|
|
||||||
#
|
|
||||||
# GenieACS is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# GenieACS is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
###
|
|
||||||
|
|
||||||
path = require 'path'
|
|
||||||
config = require './config'
|
|
||||||
common = require './common'
|
|
||||||
|
|
||||||
|
|
||||||
FILES = {}
|
|
||||||
getFile = (filename) ->
|
|
||||||
if filename not of FILES
|
|
||||||
FILES[filename] = require(path.resolve(config.get('CONFIG_DIR'), "custom_commands/#{filename}"))
|
|
||||||
|
|
||||||
return FILES[filename]
|
|
||||||
|
|
||||||
|
|
||||||
exports.execute = (deviceId, command, callback) ->
|
|
||||||
args = command.split(/\s+/)
|
|
||||||
f = args.shift()
|
|
||||||
c = args.shift()
|
|
||||||
|
|
||||||
file = getFile(f)
|
|
||||||
file[c](deviceId, args.join(' '), callback)
|
|
||||||
|
|
||||||
|
|
||||||
exports.getFileCommands = (filename) ->
|
|
||||||
commands = []
|
|
||||||
f = getFile(filename)
|
|
||||||
for k,v of f
|
|
||||||
commands.push(k)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
exports.getDeviceCustomCommands = (deviceId) ->
|
|
||||||
commands = {}
|
|
||||||
for k,v of config.CUSTOM_COMMANDS
|
|
||||||
if eval(v).test(deviceId)
|
|
||||||
commands[k] = exports.getFileCommands(k)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
exports.getDeviceCustomCommandNames = (deviceId) ->
|
|
||||||
commands = []
|
|
||||||
for k,v of config.CUSTOM_COMMANDS
|
|
||||||
if eval(v).test(deviceId)
|
|
||||||
commands.push(k)
|
|
||||||
return commands
|
|
||||||
+1
-30
@@ -28,29 +28,6 @@ presetsCollection = null
|
|||||||
objectsCollection = null
|
objectsCollection = null
|
||||||
|
|
||||||
|
|
||||||
# Only flush redis DB if aliases or custom commands config has changed
|
|
||||||
flushRedis = (callback) ->
|
|
||||||
redisClient.get('config_hash', (err, res) ->
|
|
||||||
return callback(err) if err
|
|
||||||
|
|
||||||
hash = require('crypto')
|
|
||||||
.createHash('md5')
|
|
||||||
.update(JSON.stringify(config.PARAMETERS))
|
|
||||||
.update(JSON.stringify(config.CUSTOM_COMMANDS))
|
|
||||||
.digest('hex')
|
|
||||||
|
|
||||||
if res == hash
|
|
||||||
return callback()
|
|
||||||
|
|
||||||
redisClient.flushdb((err) ->
|
|
||||||
return callback(err) if err
|
|
||||||
redisClient.set('config_hash', hash, (err, res) ->
|
|
||||||
return callback(err)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
connect = (callback) ->
|
connect = (callback) ->
|
||||||
callbackCounter = 6
|
callbackCounter = 6
|
||||||
mongodb.MongoClient.connect(config.get('MONGODB_CONNECTION_URL'), {db:{w:1},server:{autoReconnect:true}}, (err, db) ->
|
mongodb.MongoClient.connect(config.get('MONGODB_CONNECTION_URL'), {db:{w:1},server:{autoReconnect:true}}, (err, db) ->
|
||||||
@@ -96,15 +73,9 @@ connect = (callback) ->
|
|||||||
|
|
||||||
exports.redisClient = redisClient = redis.createClient(config.get('REDIS_PORT'), config.get('REDIS_HOST'))
|
exports.redisClient = redisClient = redis.createClient(config.get('REDIS_PORT'), config.get('REDIS_HOST'))
|
||||||
redisClient.select(config.get('REDIS_DB'), (err) ->
|
redisClient.select(config.get('REDIS_DB'), (err) ->
|
||||||
if err
|
if --callbackCounter == 0 or err
|
||||||
callbackCounter = 0
|
callbackCounter = 0
|
||||||
return callback(err)
|
return callback(err)
|
||||||
|
|
||||||
flushRedis((err) ->
|
|
||||||
if --callbackCounter == 0 or err
|
|
||||||
callbackCounter = 0
|
|
||||||
return callback(err)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
###
|
|
||||||
# Copyright 2013, 2014 Zaid Abdulla
|
|
||||||
#
|
|
||||||
# GenieACS is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# GenieACS is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# This file incorporates work covered by the following copyright and
|
|
||||||
# permission notice:
|
|
||||||
#
|
|
||||||
# Copyright 2013 Fanoos Telecom
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
###
|
|
||||||
|
|
||||||
common = require './common'
|
|
||||||
|
|
||||||
|
|
||||||
test = (obj, query) ->
|
|
||||||
for k,v of query
|
|
||||||
if k.charAt(0) == '$' # this is a logical operator
|
|
||||||
res = testExpressions(obj, v, k)
|
|
||||||
else
|
|
||||||
value = common.getParamValueFromPath(obj, k)
|
|
||||||
|
|
||||||
if common.typeOf(v) isnt common.OBJECT_TYPE
|
|
||||||
# TODO comparing array to regex, array to array, and object to object
|
|
||||||
if common.typeOf(value) is common.ARRAY_TYPE
|
|
||||||
res = value.indexOf(v) != -1
|
|
||||||
else
|
|
||||||
if common.typeOf(v) is common.REGEXP_TYPE
|
|
||||||
res = v.test(value)
|
|
||||||
else
|
|
||||||
res = v == value
|
|
||||||
else
|
|
||||||
for k2,v2 of v
|
|
||||||
switch k2
|
|
||||||
when '$ne'
|
|
||||||
if common.typeOf(value) is common.ARRAY_TYPE
|
|
||||||
res = value.indexOf(v2) == -1
|
|
||||||
else
|
|
||||||
res = value != v2
|
|
||||||
when '$lt'
|
|
||||||
res = value < v2
|
|
||||||
when '$lte'
|
|
||||||
res = value <= v2
|
|
||||||
when '$gt'
|
|
||||||
res = value > v2
|
|
||||||
when '$gte'
|
|
||||||
res = value >= v2
|
|
||||||
when '$regex'
|
|
||||||
res = v2.test(value)
|
|
||||||
when '$in'
|
|
||||||
throw new Error('Operator not supported')
|
|
||||||
when '$nin'
|
|
||||||
throw new Error('Operator not supported')
|
|
||||||
when '$all'
|
|
||||||
throw new Error('Operator not supported')
|
|
||||||
when '$exists'
|
|
||||||
throw new Error('Operator not supported')
|
|
||||||
else
|
|
||||||
throw new Error('Operator not supported')
|
|
||||||
|
|
||||||
if not res
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
testExpressions = (obj, expressions, lop) ->
|
|
||||||
for f in expressions
|
|
||||||
res = test(obj, f)
|
|
||||||
|
|
||||||
switch lop
|
|
||||||
when '$and'
|
|
||||||
return false if not res
|
|
||||||
when '$or'
|
|
||||||
return true if res
|
|
||||||
when '$nor'
|
|
||||||
return false if res
|
|
||||||
|
|
||||||
return switch lop
|
|
||||||
when '$and' then true
|
|
||||||
when '$or' then false
|
|
||||||
when '$nor' then true
|
|
||||||
else throw new Error('Unknown logical operator')
|
|
||||||
|
|
||||||
|
|
||||||
# generate field projection from a query
|
|
||||||
projection = (query, proj) ->
|
|
||||||
for k,v of query
|
|
||||||
if k.charAt(0) == '$' # this is a logical operator
|
|
||||||
for q in v
|
|
||||||
projection(q, proj)
|
|
||||||
else
|
|
||||||
proj[k] = 1
|
|
||||||
|
|
||||||
# optimize projection by removing overlaps
|
|
||||||
optimizeProjection = (obj) ->
|
|
||||||
keys = Object.keys(obj).sort()
|
|
||||||
return if keys.length <= 1
|
|
||||||
i = 1
|
|
||||||
while i < keys.length
|
|
||||||
a = keys[i-1]
|
|
||||||
b = keys[i]
|
|
||||||
if common.startsWith(b, a)
|
|
||||||
if b.charAt(a.length) == '.' or b.charAt(a.length - 1) == '.'
|
|
||||||
delete obj[b]
|
|
||||||
keys.splice(i--, 1)
|
|
||||||
++ i
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
exports.test = test
|
|
||||||
exports.projection = projection
|
|
||||||
exports.optimizeProjection = optimizeProjection
|
|
||||||
+5
-18
@@ -1,5 +1,5 @@
|
|||||||
###
|
###
|
||||||
# Copyright 2013, 2014 Zaid Abdulla
|
# Copyright 2013-2016 Zaid Abdulla
|
||||||
#
|
#
|
||||||
# GenieACS is free software: you can redistribute it and/or modify
|
# GenieACS is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
@@ -38,15 +38,15 @@
|
|||||||
# THE SOFTWARE.
|
# THE SOFTWARE.
|
||||||
###
|
###
|
||||||
|
|
||||||
config = require './config'
|
|
||||||
url = require 'url'
|
url = require 'url'
|
||||||
common = require './common'
|
|
||||||
db = require './db'
|
|
||||||
mongodb = require 'mongodb'
|
mongodb = require 'mongodb'
|
||||||
querystring = require 'querystring'
|
querystring = require 'querystring'
|
||||||
|
|
||||||
|
config = require './config'
|
||||||
|
common = require './common'
|
||||||
|
db = require './db'
|
||||||
query = require './query'
|
query = require './query'
|
||||||
apiFunctions = require './api-functions'
|
apiFunctions = require './api-functions'
|
||||||
presets = require './presets'
|
|
||||||
|
|
||||||
# regular expression objects
|
# regular expression objects
|
||||||
DEVICE_TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tasks\/?$/
|
DEVICE_TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tasks\/?$/
|
||||||
@@ -57,7 +57,6 @@ OBJECTS_REGEX = /^\/objects\/([a-zA-Z0-9\-\_\%]+)\/?$/
|
|||||||
FILES_REGEX = /^\/files\/([a-zA-Z0-9\%\!\*\'\(\)\;\:\@\&\=\+\$\,\?\#\[\]\-\_\.\~]+)\/?$/
|
FILES_REGEX = /^\/files\/([a-zA-Z0-9\%\!\*\'\(\)\;\:\@\&\=\+\$\,\?\#\[\]\-\_\.\~]+)\/?$/
|
||||||
PING_REGEX = /^\/ping\/([a-zA-Z0-9\-\_\.]+)\/?$/
|
PING_REGEX = /^\/ping\/([a-zA-Z0-9\-\_\.]+)\/?$/
|
||||||
QUERY_REGEX = /^\/([a-zA-Z0-9_]+s)\/?$/
|
QUERY_REGEX = /^\/([a-zA-Z0-9_]+s)\/?$/
|
||||||
DEVICE_PRESET_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/preset\/?$/
|
|
||||||
DELETE_DEVICE_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/?$/
|
DELETE_DEVICE_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/?$/
|
||||||
|
|
||||||
|
|
||||||
@@ -248,18 +247,6 @@ listener = (request, response) ->
|
|||||||
else
|
else
|
||||||
response.writeHead 405, {'Allow': 'POST'}
|
response.writeHead 405, {'Allow': 'POST'}
|
||||||
response.end('405 Method Not Allowed')
|
response.end('405 Method Not Allowed')
|
||||||
else if DEVICE_PRESET_REGEX.test(urlParts.pathname)
|
|
||||||
deviceId = querystring.unescape(DEVICE_PRESET_REGEX.exec(urlParts.pathname)[1])
|
|
||||||
if request.method is 'GET'
|
|
||||||
db.getPresetsObjectsAliases((allPresets, allObjects, allAliases) ->
|
|
||||||
presets.getDevicePreset(deviceId, allPresets, allObjects, allAliases, (devicePreset) ->
|
|
||||||
response.writeHead(200, {'Content-Type' : 'application/json'})
|
|
||||||
response.end(JSON.stringify(devicePreset))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
response.writeHead 405, {'Allow': 'GET'}
|
|
||||||
response.end('405 Method Not Allowed')
|
|
||||||
else if TASKS_REGEX.test(urlParts.pathname)
|
else if TASKS_REGEX.test(urlParts.pathname)
|
||||||
r = TASKS_REGEX.exec(urlParts.pathname)
|
r = TASKS_REGEX.exec(urlParts.pathname)
|
||||||
taskId = mongodb.ObjectID(querystring.unescape(r[1]))
|
taskId = mongodb.ObjectID(querystring.unescape(r[1]))
|
||||||
|
|||||||
@@ -1,336 +0,0 @@
|
|||||||
###
|
|
||||||
# Copyright 2013, 2014 Zaid Abdulla
|
|
||||||
#
|
|
||||||
# GenieACS is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# GenieACS is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# This file incorporates work covered by the following copyright and
|
|
||||||
# permission notice:
|
|
||||||
#
|
|
||||||
# Copyright 2013 Fanoos Telecom
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
###
|
|
||||||
|
|
||||||
config = require './config'
|
|
||||||
common = require './common'
|
|
||||||
db = require './db'
|
|
||||||
mongoQuery = require './mongo-query'
|
|
||||||
query = require './query'
|
|
||||||
log = require('util').log
|
|
||||||
customCommands = require './custom-commands'
|
|
||||||
|
|
||||||
|
|
||||||
exports.calculatePresetsHash = (presets, objects) ->
|
|
||||||
crypto = require('crypto')
|
|
||||||
hash = crypto.createHash('md5').update(JSON.stringify(presets) + JSON.stringify(objects)).digest('hex')
|
|
||||||
return hash
|
|
||||||
|
|
||||||
|
|
||||||
matchObject = (object, param) ->
|
|
||||||
flatObject = common.flattenObject(object)
|
|
||||||
if object._keys?.length
|
|
||||||
keys = object._keys
|
|
||||||
else
|
|
||||||
# Consider all parameters as keys if none are defined
|
|
||||||
keys = (k for k in Object.keys(flatObject) when k[0] != '_')
|
|
||||||
|
|
||||||
for k in keys
|
|
||||||
p = common.getParamValueFromPath(param, k)
|
|
||||||
return false if not p?
|
|
||||||
v = common.matchType(p._value, flatObject[k])
|
|
||||||
|
|
||||||
if p._value != common.matchType(p._value, flatObject[k])
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
exports.getDevicePreset = (deviceId, presets, objects, aliases, callback) ->
|
|
||||||
deviceCustomCommands = customCommands.getDeviceCustomCommands(deviceId)
|
|
||||||
# only fetch relevant params
|
|
||||||
projection = {_id : 1}
|
|
||||||
for p in presets
|
|
||||||
if common.typeOf(p.precondition) is common.STRING_TYPE
|
|
||||||
p.precondition = query.expand(JSON.parse(p.precondition), aliases)
|
|
||||||
else
|
|
||||||
# Accept an object for backward compatiblity
|
|
||||||
p.precondition = query.expand(p.precondition ? {}, aliases)
|
|
||||||
mongoQuery.projection(p.precondition, projection)
|
|
||||||
|
|
||||||
for c in p.configurations
|
|
||||||
switch c.type
|
|
||||||
when 'value', 'age'
|
|
||||||
projection[c.name] = 1
|
|
||||||
when 'custom_command_value', 'custom_command_age'
|
|
||||||
commandfile = c.command.split(' ', 1)
|
|
||||||
if commandfile of deviceCustomCommands
|
|
||||||
projection["_customCommands.#{c.command.split(' ', 1)}"] = 1
|
|
||||||
when 'software_version'
|
|
||||||
projection['Device.DeviceInfo.SoftwareVersion'] = 1
|
|
||||||
projection['InternetGatewayDevice.DeviceInfo.SoftwareVersion'] = 1
|
|
||||||
when 'add_tag', 'delete_tag'
|
|
||||||
projection['_tags'] = 1
|
|
||||||
when 'add_object', 'delete_object'
|
|
||||||
projection[c.name] = 1
|
|
||||||
else
|
|
||||||
throw new Error('Unknown configuration type')
|
|
||||||
|
|
||||||
mongoQuery.optimizeProjection(projection)
|
|
||||||
|
|
||||||
db.devicesCollection.findOne({'_id' : deviceId}, projection, (err, device) ->
|
|
||||||
throw err if err
|
|
||||||
devicePresets = []
|
|
||||||
for p in presets
|
|
||||||
if mongoQuery.test(device, p.precondition)
|
|
||||||
devicePresets.push(p)
|
|
||||||
|
|
||||||
configurations = accumulateConfigurations(devicePresets, objects)
|
|
||||||
|
|
||||||
getParamDetails = (device, path) ->
|
|
||||||
p = common.getParamValueFromPath(device, path)
|
|
||||||
return undefined if not p?
|
|
||||||
details = {}
|
|
||||||
for k,v of p
|
|
||||||
details[k] = v if k[0] == '_'
|
|
||||||
return details
|
|
||||||
|
|
||||||
devicePreset = {}
|
|
||||||
for c in configurations
|
|
||||||
switch c.type
|
|
||||||
when 'value'
|
|
||||||
devicePreset.parameters ?= {}
|
|
||||||
devicePreset.parameters[c.name] ?= {}
|
|
||||||
devicePreset.parameters[c.name].preset ?= {}
|
|
||||||
devicePreset.parameters[c.name].preset.value = c.value
|
|
||||||
devicePreset.parameters[c.name].current = getParamDetails(device, c.name)
|
|
||||||
|
|
||||||
when 'age'
|
|
||||||
devicePreset.parameters ?= {}
|
|
||||||
devicePreset.parameters[c.name] ?= {}
|
|
||||||
devicePreset.parameters[c.name].preset ?= {}
|
|
||||||
devicePreset.parameters[c.name].preset.expiry = parseInt(c.age)
|
|
||||||
devicePreset.parameters[c.name].current = getParamDetails(device, c.name)
|
|
||||||
|
|
||||||
when 'add_tag'
|
|
||||||
devicePreset.tags ?= {}
|
|
||||||
devicePreset.tags[c.tag] = 2 | c.tag in (device._tags ? [])
|
|
||||||
|
|
||||||
when 'delete_tag'
|
|
||||||
devicePreset.tags ?= {}
|
|
||||||
devicePreset.tags[c.tag] = 0 | c.tag in (device._tags ? [])
|
|
||||||
|
|
||||||
when 'add_object'
|
|
||||||
devicePreset.objects ?= {}
|
|
||||||
devicePreset.objects[c.name] ?= {}
|
|
||||||
devicePreset.objects[c.name][c.object] ?= {}
|
|
||||||
devicePreset.objects[c.name][c.object].preset = objects[c.object]
|
|
||||||
continue if not param = common.getParamValueFromPath(device, c.name)
|
|
||||||
devicePreset.objects[c.name][c.object].current ?= {}
|
|
||||||
for k,p of param
|
|
||||||
continue if k[0] == '_'
|
|
||||||
if matchObject(objects[c.object], p)
|
|
||||||
devicePreset.objects[c.name][c.object].current[k] = p
|
|
||||||
|
|
||||||
when 'delete_object'
|
|
||||||
devicePreset.objects ?= {}
|
|
||||||
devicePreset.objects[c.name] ?= {}
|
|
||||||
devicePreset.objects[c.name][c.object] ?= {}
|
|
||||||
continue if not param = common.getParamValueFromPath(device, c.name)
|
|
||||||
devicePreset.objects[c.name][c.object].current ?= {}
|
|
||||||
for k,p of param
|
|
||||||
continue if k[0] == '_'
|
|
||||||
if matchObject(objects[c.object], p)
|
|
||||||
devicePreset.objects[c.name][c.object].current[k] = p
|
|
||||||
|
|
||||||
when 'custom_command_value'
|
|
||||||
[filename, commandName] = c.command.split(' ', 2)
|
|
||||||
devicePreset.customCommands ?= {}
|
|
||||||
devicePreset.customCommands[filename] ?= {}
|
|
||||||
devicePreset.customCommands[filename].preset ?= {}
|
|
||||||
devicePreset.customCommands[filename].preset.value = c.value
|
|
||||||
devicePreset.customCommands[filename].preset.valueCommand = c.command
|
|
||||||
continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
|
|
||||||
if cmd = common.getParamValueFromPath(device, "_customCommands.#{filename}")
|
|
||||||
devicePreset.customCommands[filename].current = cmd
|
|
||||||
else
|
|
||||||
devicePreset.customCommands[filename].current ?= {}
|
|
||||||
|
|
||||||
when 'custom_command_age'
|
|
||||||
[filename, commandName] = c.command.split(' ', 2)
|
|
||||||
devicePreset.customCommands ?= {}
|
|
||||||
devicePreset.customCommands[filename] ?= {}
|
|
||||||
devicePreset.customCommands[filename].preset ?= {}
|
|
||||||
devicePreset.customCommands[filename].preset.expiry = parseInt(c.age)
|
|
||||||
devicePreset.customCommands[filename].preset.expiryCommand = c.command
|
|
||||||
continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
|
|
||||||
if cmd = common.getParamValueFromPath(device, "_customCommands.#{filename}")
|
|
||||||
devicePreset.customCommands[filename].current = cmd
|
|
||||||
else
|
|
||||||
devicePreset.customCommands[filename].current ?= {}
|
|
||||||
|
|
||||||
when 'software_version'
|
|
||||||
devicePreset.softwareVersion ?= {}
|
|
||||||
devicePreset.softwareVersion.preset = c.software_version
|
|
||||||
if device?.Device? # TR-181 data model
|
|
||||||
devicePreset.softwareVersion.current = common.getParamValueFromPath(device, 'Device.DeviceInfo.SoftwareVersion')
|
|
||||||
else # TR-098 data model
|
|
||||||
devicePreset.softwareVersion.current = common.getParamValueFromPath(device, 'InternetGatewayDevice.DeviceInfo.SoftwareVersion')
|
|
||||||
|
|
||||||
else
|
|
||||||
throw new Error('Unknown configuration type')
|
|
||||||
|
|
||||||
callback(devicePreset)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
exports.processDevicePreset = (deviceId, devicePreset, callback) ->
|
|
||||||
PRESETS_TIME_PADDING = config.get('PRESETS_TIME_PADDING')
|
|
||||||
now = new Date()
|
|
||||||
expiry = config.get('PRESETS_CACHE_DURATION', deviceId)
|
|
||||||
getParameterValues = []
|
|
||||||
setParameterValues = []
|
|
||||||
addTags = []
|
|
||||||
deleteTags = []
|
|
||||||
taskList = []
|
|
||||||
|
|
||||||
# Parameters
|
|
||||||
for parameterPath, parameterDetails of devicePreset.parameters ? {}
|
|
||||||
continue if not parameterDetails.current?
|
|
||||||
expiry = Math.min(expiry, parameterDetails.preset.expiry) if parameterDetails.preset.expiry?
|
|
||||||
presetValue = parameterDetails.preset.value
|
|
||||||
currentValue = parameterDetails.current._value
|
|
||||||
if presetValue? and currentValue != (presetValue = common.matchType(currentValue, presetValue))
|
|
||||||
setParameterValues.push([parameterPath, presetValue, parameterDetails.current._type])
|
|
||||||
else if parameterDetails.preset.expiry?
|
|
||||||
diff = parameterDetails.preset.expiry - (now - (parameterDetails.current._timestamp ? 0)) / 1000
|
|
||||||
if diff <= PRESETS_TIME_PADDING
|
|
||||||
if parameterDetails.current._value?
|
|
||||||
getParameterValues.push(parameterPath)
|
|
||||||
else
|
|
||||||
taskList.push({device : deviceId, name : 'refreshObject', objectName : parameterPath})
|
|
||||||
else
|
|
||||||
expiry = Math.min(expiry, diff)
|
|
||||||
|
|
||||||
# Objects
|
|
||||||
for parameterPath, object of devicePreset.objects ? {}
|
|
||||||
for objectName, objectDetails of object
|
|
||||||
continue if not objectDetails.current?
|
|
||||||
if objectDetails.preset?
|
|
||||||
flatObject = common.flattenObject(objectDetails.preset)
|
|
||||||
if Object.keys(objectDetails.current).length > 0
|
|
||||||
for i, obj of objectDetails.current
|
|
||||||
for paramName, paramValue of flatObject
|
|
||||||
continue if paramName[0] == '_'
|
|
||||||
currentValue = common.getParamValueFromPath(obj, paramName)
|
|
||||||
if currentValue?
|
|
||||||
presetValue = common.matchType(currentValue._value, flatObject[paramName])
|
|
||||||
if currentValue._value != presetValue
|
|
||||||
setParameterValues.push(["#{parameterPath}.#{i}.#{paramName}", presetValue, currentValue._type])
|
|
||||||
else
|
|
||||||
vals = []
|
|
||||||
for k,v of flatObject
|
|
||||||
vals.push([k, v]) if k[0] != '_'
|
|
||||||
taskList.push({device : deviceId, name : 'addObject', objectName : parameterPath, parameterValues : vals})
|
|
||||||
else if Object.keys(objectDetails.current).length > 0
|
|
||||||
for i, obj of objectDetails.current
|
|
||||||
taskList.push({device : deviceId, name : 'deleteObject', objectName : "#{parameterPath}.#{i}"})
|
|
||||||
|
|
||||||
# Tags
|
|
||||||
for tag, v of devicePreset.tags ? {}
|
|
||||||
if v == 1
|
|
||||||
deleteTags.push(tag)
|
|
||||||
else if v == 2
|
|
||||||
addTags.push(tag)
|
|
||||||
|
|
||||||
# Custom commands
|
|
||||||
for commandName, commandDetails of devicePreset.customCommands ? {}
|
|
||||||
continue if not commandDetails.current?
|
|
||||||
expiry = Math.min(expiry, commandDetails.preset.expiry) if commandDetails.preset.expiry?
|
|
||||||
currentValue = commandDetails.current._value
|
|
||||||
presetValue = common.matchType(currentValue, commandDetails.preset.value)
|
|
||||||
if commandDetails.preset.value? and currentValue != presetValue
|
|
||||||
taskList.push({device : deviceId, name : 'customCommand', command : commandDetails.preset.valueCommand})
|
|
||||||
else if commandDetails.preset.expiry?
|
|
||||||
diff = commandDetails.preset.expiry - (now - (commandDetails.current._timestamp ? 0)) / 1000
|
|
||||||
if diff <= PRESETS_TIME_PADDING
|
|
||||||
taskList.push({device : deviceId, name : 'customCommand', command : commandDetails.preset.expiryCommand})
|
|
||||||
else
|
|
||||||
expiry = Math.min(expiry, diff)
|
|
||||||
|
|
||||||
if setParameterValues.length
|
|
||||||
taskList.push {device : deviceId, name : 'setParameterValues', parameterValues: setParameterValues, timestamp : now}
|
|
||||||
|
|
||||||
if getParameterValues.length
|
|
||||||
taskList.push {device : deviceId, name : 'getParameterValues', parameterNames: getParameterValues, timestamp : now}
|
|
||||||
|
|
||||||
callback(taskList, addTags, deleteTags, expiry)
|
|
||||||
|
|
||||||
|
|
||||||
getObjectHash = (object) ->
|
|
||||||
if object._keys?.length
|
|
||||||
keys = object._keys
|
|
||||||
else
|
|
||||||
keys = (k for k in Object.keys(object) when k[0] != '_')
|
|
||||||
|
|
||||||
keys.sort()
|
|
||||||
|
|
||||||
hash = []
|
|
||||||
for k in keys
|
|
||||||
hash.push(k)
|
|
||||||
hash.push(common.getParamValueFromPath(object, k))
|
|
||||||
|
|
||||||
return JSON.stringify(hash)
|
|
||||||
|
|
||||||
|
|
||||||
accumulateConfigurations = (presets, objects) ->
|
|
||||||
maxWeights = {}
|
|
||||||
configurations = {}
|
|
||||||
for p in presets
|
|
||||||
for c in p.configurations
|
|
||||||
configurationHash = switch c.type
|
|
||||||
when 'add_tag', 'delete_tag'
|
|
||||||
"tag_#{c.tag}"
|
|
||||||
when 'add_object', 'delete_object'
|
|
||||||
objectHash = getObjectHash(objects[c.object])
|
|
||||||
"object_#{c.name}_#{objectHash}"
|
|
||||||
when 'software_version'
|
|
||||||
'software_version'
|
|
||||||
when 'custom_command_age', 'custom_command_value'
|
|
||||||
[filename, commandName] = c.command.split(' ', 2)
|
|
||||||
"#{c.type}_#{filename}_#{commandName}"
|
|
||||||
else
|
|
||||||
"#{c.type}_#{c.name}"
|
|
||||||
|
|
||||||
if not maxWeights[configurationHash]? or p.weight > maxWeights[configurationHash]
|
|
||||||
configurations[configurationHash] = c
|
|
||||||
maxWeights[configurationHash] = p.weight
|
|
||||||
|
|
||||||
configurationsList = (configurations[c] for c of configurations)
|
|
||||||
return configurationsList
|
|
||||||
@@ -1,379 +0,0 @@
|
|||||||
###
|
|
||||||
# Copyright 2013, 2014 Zaid Abdulla
|
|
||||||
#
|
|
||||||
# GenieACS is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# GenieACS is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# This file incorporates work covered by the following copyright and
|
|
||||||
# permission notice:
|
|
||||||
#
|
|
||||||
# Copyright 2013 Fanoos Telecom
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
###
|
|
||||||
|
|
||||||
config = require './config'
|
|
||||||
common = require './common'
|
|
||||||
db = require './db'
|
|
||||||
mongodb = require 'mongodb'
|
|
||||||
customCommands = require './custom-commands'
|
|
||||||
url = require 'url'
|
|
||||||
|
|
||||||
exports.STATUS_OK = STATUS_OK = 1
|
|
||||||
exports.STATUS_FAULT = STATUS_FAULT = 2
|
|
||||||
exports.STATUS_COMPLETED = STATUS_COMPLETED = 4
|
|
||||||
exports.STATUS_SAVE = STATUS_SAVE = 128 # not mutually exclusive with the rest
|
|
||||||
|
|
||||||
|
|
||||||
this.refreshObject = (task, methodResponse, callback) ->
|
|
||||||
task.session = {} if not task.session?
|
|
||||||
|
|
||||||
if not task.session.subtask?
|
|
||||||
path = task.objectName
|
|
||||||
path += '.' if path != '' and not common.endsWith(path, '.')
|
|
||||||
task.session.subtask = {device : task.device, name : 'getParameterNames', parameterPath : path}
|
|
||||||
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
if task.session.subtask.name is 'getParameterNames'
|
|
||||||
this.getParameterNames(task.session.subtask, methodResponse, (err, status, methodRequest, deviceUpdates) =>
|
|
||||||
task.session.parameterNames ?= []
|
|
||||||
if deviceUpdates?.parameterNames?
|
|
||||||
for p in deviceUpdates.parameterNames
|
|
||||||
if not common.endsWith(p[0], '.')
|
|
||||||
task.session.parameterNames.push(p[0])
|
|
||||||
|
|
||||||
if status & STATUS_COMPLETED
|
|
||||||
task.session.subtask = {device : task.device, name : 'getParameterValues', parameterNames : task.session.parameterNames}
|
|
||||||
delete task.session.parameterNames
|
|
||||||
|
|
||||||
this.getParameterValues(task.session.subtask, {}, (err, status, methodRequest) ->
|
|
||||||
# ignore deviceUpdates returned by first call to getParameterValues
|
|
||||||
callback(err, status, methodRequest, deviceUpdates)
|
|
||||||
)
|
|
||||||
else if status & STATUS_OK
|
|
||||||
callback(err, STATUS_OK, methodRequest, deviceUpdates)
|
|
||||||
else
|
|
||||||
throw new Error('Unexpected subtask status')
|
|
||||||
)
|
|
||||||
else if task.session.subtask.name is 'getParameterValues'
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
return callback(null, STATUS_FAULT)
|
|
||||||
|
|
||||||
this.getParameterValues(task.session.subtask, methodResponse, (err, status, methodRequest, deviceUpdates) ->
|
|
||||||
callback(err, status, methodRequest, deviceUpdates)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
throw Error('Unexpected subtask name')
|
|
||||||
|
|
||||||
|
|
||||||
this.getParameterNames = (task, methodResponse, callback) ->
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
GET_PARAMETER_NAMES_DEPTH_THRESHOLD = config.get('GET_PARAMETER_NAMES_DEPTH_THRESHOLD', task.device)
|
|
||||||
|
|
||||||
getParameterDepth = (param) ->
|
|
||||||
return 0 if !param
|
|
||||||
return (param[...-1] + '.').split('.').length - 1
|
|
||||||
|
|
||||||
if methodResponse.type is 'GetParameterNamesResponse'
|
|
||||||
path = task.session.queue.pop()
|
|
||||||
|
|
||||||
# If parameter depth higher than the threshold, nextLevel was set to false
|
|
||||||
if not task.nextLevel? and getParameterDepth(path) < GET_PARAMETER_NAMES_DEPTH_THRESHOLD
|
|
||||||
for p in methodResponse.parameterList
|
|
||||||
task.session.queue.push(p[0]) if p[0][-1..] == '.'
|
|
||||||
|
|
||||||
deviceUpdates = {parameterNames : methodResponse.parameterList}
|
|
||||||
|
|
||||||
# Make sure that for each parameter, all its parents are explicitly included
|
|
||||||
found = {}
|
|
||||||
found[path] = 0 if !!path
|
|
||||||
for p in deviceUpdates.parameterNames
|
|
||||||
param = p[0]
|
|
||||||
i = param.length
|
|
||||||
while (i = param.lastIndexOf('.', i-1)) > path.length
|
|
||||||
pp = param.slice(0, i + 1)
|
|
||||||
break if found[pp]?
|
|
||||||
found[pp] = 0
|
|
||||||
found[p[0]] = 1
|
|
||||||
|
|
||||||
for k, v of found
|
|
||||||
deviceUpdates.parameterNames.push([k]) if v == 0
|
|
||||||
else
|
|
||||||
task.session = {queue : [task.parameterPath]}
|
|
||||||
|
|
||||||
if task.session.queue.length > 0
|
|
||||||
path = task.session.queue[-1..][0]
|
|
||||||
methodRequest = {
|
|
||||||
type : 'GetParameterNames',
|
|
||||||
parameterPath : path,
|
|
||||||
nextLevel : task.nextLevel ? getParameterDepth(path) < GET_PARAMETER_NAMES_DEPTH_THRESHOLD
|
|
||||||
}
|
|
||||||
return callback(null, STATUS_OK, methodRequest, deviceUpdates)
|
|
||||||
else
|
|
||||||
return callback(null, STATUS_COMPLETED, null, deviceUpdates)
|
|
||||||
|
|
||||||
|
|
||||||
this.getParameterValues = (task, methodResponse, callback) ->
|
|
||||||
task.session = {} if not task.session?
|
|
||||||
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not task.session.currentIndex?
|
|
||||||
task.session.currentIndex = 0
|
|
||||||
else if methodResponse.parameterList?
|
|
||||||
task.session.currentIndex = task.session.nextIndex
|
|
||||||
|
|
||||||
task.session.nextIndex = Math.min(task.session.currentIndex + config.get('TASK_PARAMETERS_BATCH_SIZE', task.device), task.parameterNames.length)
|
|
||||||
names = task.parameterNames.slice(task.session.currentIndex, task.session.nextIndex)
|
|
||||||
|
|
||||||
if methodResponse.type is 'GetParameterValuesResponse'
|
|
||||||
deviceUpdates = {parameterValues : methodResponse.parameterList}
|
|
||||||
|
|
||||||
if names.length == 0
|
|
||||||
callback(null, STATUS_COMPLETED, null, deviceUpdates)
|
|
||||||
else
|
|
||||||
methodRequest = {
|
|
||||||
type : 'GetParameterValues',
|
|
||||||
parameterNames : names
|
|
||||||
}
|
|
||||||
callback(null, STATUS_OK, methodRequest, deviceUpdates)
|
|
||||||
|
|
||||||
|
|
||||||
this.setParameterValues = (task, methodResponse, callback) ->
|
|
||||||
task.session = {} if not task.session?
|
|
||||||
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not task.session.currentIndex?
|
|
||||||
task.session.currentIndex = 0
|
|
||||||
else if methodResponse.type is 'SetParameterValuesResponse'
|
|
||||||
prevValues = task.parameterValues.slice(task.session.currentIndex, task.session.nextIndex)
|
|
||||||
task.session.currentIndex = task.session.nextIndex
|
|
||||||
|
|
||||||
task.session.nextIndex = Math.min(task.session.currentIndex + config.get('TASK_PARAMETERS_BATCH_SIZE', task.device), task.parameterValues.length)
|
|
||||||
values = task.parameterValues.slice(task.session.currentIndex, task.session.nextIndex)
|
|
||||||
|
|
||||||
if prevValues?
|
|
||||||
deviceUpdates = {parameterValues : prevValues}
|
|
||||||
|
|
||||||
if values.length == 0
|
|
||||||
callback(null, STATUS_COMPLETED, null, deviceUpdates)
|
|
||||||
else
|
|
||||||
callback(null, STATUS_OK, {type : 'SetParameterValues', parameterList : values}, deviceUpdates)
|
|
||||||
|
|
||||||
|
|
||||||
this.addObject = (task, methodResponse, callback) ->
|
|
||||||
task.session = {} if not task.session?
|
|
||||||
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
allDeviceUpdates = {}
|
|
||||||
|
|
||||||
if not task.session.instanceNumber?
|
|
||||||
if methodResponse.type is 'AddObjectResponse'
|
|
||||||
status_save = STATUS_SAVE
|
|
||||||
task.session.instanceNumber = methodResponse.instanceNumber
|
|
||||||
# TODO Don't specify nextLevel explicity. Also consider using refreshObject instead.
|
|
||||||
task.session.subtask = {device : task.device, name : 'getParameterNames', parameterPath : "#{task.objectName}.#{task.session.instanceNumber}.", nextLevel : false}
|
|
||||||
task.session.appliedParameterValues = []
|
|
||||||
task.session.parameterNames = []
|
|
||||||
else
|
|
||||||
callback(null, STATUS_OK, {type : 'AddObject', objectName : "#{task.objectName}."})
|
|
||||||
return
|
|
||||||
|
|
||||||
subtask = () =>
|
|
||||||
switch task.session.subtask.name
|
|
||||||
when 'getParameterNames'
|
|
||||||
this.getParameterNames(task.session.subtask, methodResponse, (err, status, methodRequest, deviceUpdates) =>
|
|
||||||
common.extend(allDeviceUpdates, deviceUpdates)
|
|
||||||
if deviceUpdates and deviceUpdates.parameterNames
|
|
||||||
for p in deviceUpdates.parameterNames
|
|
||||||
task.session.parameterNames.push(p[0]) if not common.endsWith(p[0], '.')
|
|
||||||
|
|
||||||
if status & STATUS_COMPLETED
|
|
||||||
task.session.subtask = {device : task.device, name : 'getParameterValues', parameterNames : task.session.parameterNames}
|
|
||||||
subtask()
|
|
||||||
else if status & STATUS_OK
|
|
||||||
# Use STATUS_SAVE to avoid adding duplicate object in case of error
|
|
||||||
callback(err, STATUS_OK | status_save, methodRequest, allDeviceUpdates)
|
|
||||||
else
|
|
||||||
throw Error('Unexpected subtask status')
|
|
||||||
)
|
|
||||||
when 'getParameterValues'
|
|
||||||
this.getParameterValues(task.session.subtask, methodResponse, (err, status, methodRequest, deviceUpdates) =>
|
|
||||||
common.extend(allDeviceUpdates, deviceUpdates)
|
|
||||||
# if values are given, compare with default values
|
|
||||||
if task.parameterValues?
|
|
||||||
if deviceUpdates and deviceUpdates.parameterValues?
|
|
||||||
for p1 in deviceUpdates.parameterValues
|
|
||||||
for p2 in task.parameterValues
|
|
||||||
if p1[0] == "#{task.objectName}.#{task.session.instanceNumber}.#{p2[0]}"
|
|
||||||
t = if p2[2] then p2[2] else p1[2]
|
|
||||||
v = common.matchType(p1[1], p2[1])
|
|
||||||
# TODO only include if writable
|
|
||||||
task.session.appliedParameterValues.push([p1[0], v, t])
|
|
||||||
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
return callback(null, STATUS_FAULT)
|
|
||||||
|
|
||||||
if status & STATUS_COMPLETED and task.session.appliedParameterValues.length > 0
|
|
||||||
task.session.subtask = {device : task.device, name : 'setParameterValues', parameterValues : task.session.appliedParameterValues}
|
|
||||||
subtask()
|
|
||||||
else
|
|
||||||
callback(err, status, methodRequest, allDeviceUpdates)
|
|
||||||
)
|
|
||||||
when 'setParameterValues'
|
|
||||||
this.setParameterValues(task.session.subtask, methodResponse, (err, status, methodRequest, deviceUpdates) =>
|
|
||||||
common.extend(allDeviceUpdates, deviceUpdates)
|
|
||||||
callback(err, status, methodRequest, allDeviceUpdates)
|
|
||||||
)
|
|
||||||
subtask()
|
|
||||||
|
|
||||||
|
|
||||||
this.deleteObject = (task, methodResponse, callback) ->
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
if methodResponse.type is 'DeleteObjectResponse'
|
|
||||||
callback(null, STATUS_COMPLETED, null, {deletedObjects : [task.objectName]})
|
|
||||||
else
|
|
||||||
methodRequest = {
|
|
||||||
type : 'DeleteObject',
|
|
||||||
objectName : "#{task.objectName}."
|
|
||||||
}
|
|
||||||
callback(null, STATUS_OK, methodRequest)
|
|
||||||
|
|
||||||
|
|
||||||
this.reboot = (task, methodResponse, callback) ->
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
if methodResponse.type isnt 'RebootResponse'
|
|
||||||
callback(null, STATUS_OK, {type : 'Reboot'})
|
|
||||||
else
|
|
||||||
callback(null, STATUS_COMPLETED)
|
|
||||||
|
|
||||||
|
|
||||||
this.factoryReset = (task, methodResponse, callback) ->
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
if methodResponse.type isnt 'FactoryResetResponse'
|
|
||||||
callback(null, STATUS_OK, {type : 'FactoryReset'})
|
|
||||||
else
|
|
||||||
callback(null, STATUS_COMPLETED)
|
|
||||||
|
|
||||||
|
|
||||||
this.download = (task, methodResponse, callback) ->
|
|
||||||
if methodResponse.faultcode?
|
|
||||||
task.fault = methodResponse
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
return
|
|
||||||
|
|
||||||
if methodResponse.type isnt 'DownloadResponse'
|
|
||||||
if task.url?
|
|
||||||
methodRequest = {
|
|
||||||
type : 'Download',
|
|
||||||
fileType : task.fileType or '1 Firmware Upgrade Image',
|
|
||||||
url : task.url,
|
|
||||||
username: task.username,
|
|
||||||
password: task.password,
|
|
||||||
successUrl : task.successUrl,
|
|
||||||
failureUrl : task.failureUrl
|
|
||||||
}
|
|
||||||
callback(null, STATUS_OK, methodRequest)
|
|
||||||
else
|
|
||||||
db.filesCollection.findOne({_id : mongodb.ObjectID(String(task.file))}, (err, file) ->
|
|
||||||
if not file?
|
|
||||||
callback('File not found')
|
|
||||||
return
|
|
||||||
else if err?
|
|
||||||
callback(err)
|
|
||||||
return
|
|
||||||
|
|
||||||
l = {
|
|
||||||
protocol : if config.get('FS_SSL') then 'https' else 'http',
|
|
||||||
hostname : config.get('FS_IP'),
|
|
||||||
port : config.get('FS_PORT'),
|
|
||||||
pathname : encodeURIComponent(file.filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
methodRequest = {
|
|
||||||
type : 'Download',
|
|
||||||
fileType : file.metadata.fileType,
|
|
||||||
fileSize : file.length,
|
|
||||||
url : url.format(l),
|
|
||||||
successUrl : task.successUrl,
|
|
||||||
failureUrl : task.failureUrl
|
|
||||||
}
|
|
||||||
callback(null, STATUS_OK, methodRequest)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
callback(null, STATUS_COMPLETED)
|
|
||||||
|
|
||||||
|
|
||||||
this.customCommand = (task, methodResponse, callback) ->
|
|
||||||
# TODO implement timeout
|
|
||||||
customCommands.execute(task.device, task.command, (err, value) ->
|
|
||||||
if err
|
|
||||||
task.fault = {name : err.name, message : err.message}
|
|
||||||
callback(null, STATUS_FAULT)
|
|
||||||
else
|
|
||||||
commandName = task.command.split(' ', 2)[0]
|
|
||||||
callback(null, STATUS_COMPLETED, null, {customCommands : [[commandName, value]]})
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
exports.task = (task, methodResponse, callback) ->
|
|
||||||
this[task.name](task, methodResponse, callback)
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
###
|
|
||||||
# Copyright 2013, 2014 Zaid Abdulla
|
|
||||||
#
|
|
||||||
# GenieACS is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# GenieACS is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
###
|
|
||||||
|
|
||||||
common = require './common'
|
|
||||||
db = require './db'
|
|
||||||
normalize = require('./normalize').normalize
|
|
||||||
mongoQuery = require './mongo-query'
|
|
||||||
parameters = require './parameters'
|
|
||||||
|
|
||||||
OBJECT_REGEX = /\.$/
|
|
||||||
INSTANCE_REGEX = /\.[\d]+\.$/
|
|
||||||
|
|
||||||
|
|
||||||
queueUpdates = (deviceId, deviceUpdates, callback) ->
|
|
||||||
return callback() if not deviceUpdates? or Object.keys(deviceUpdates).length == 0
|
|
||||||
# TODO consider setting expiry
|
|
||||||
db.redisClient.rpush("#{deviceId}_updates", JSON.stringify(deviceUpdates), (err, res) ->
|
|
||||||
return callback(err)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
clearUpdatesQueue = (deviceId, callback) ->
|
|
||||||
db.redisClient.del("#{deviceId}_updates", (err, res) ->
|
|
||||||
return callback(err)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
commitUpdates = (deviceId, deviceUpdates, commitQueue, callback) ->
|
|
||||||
now = new Date(Date.now())
|
|
||||||
update = {'$set' : {}, '$unset' : {}}
|
|
||||||
|
|
||||||
# Used to find parameters that no longer exist in device
|
|
||||||
presentPaths = null
|
|
||||||
projection = null
|
|
||||||
|
|
||||||
# Keep track of alias changes to clear cache if needed
|
|
||||||
newAliases = null
|
|
||||||
|
|
||||||
processBatch = (updatesBatch, cb) ->
|
|
||||||
if updatesBatch.informEvents?
|
|
||||||
update['$set']['_lastInform'] = now
|
|
||||||
update['$set']['_lastBoot'] = now if '1 BOOT' in updatesBatch.informEvents
|
|
||||||
update['$set']['_lastBootstrap'] = now if '0 BOOTSTRAP' in updatesBatch.informEvents
|
|
||||||
|
|
||||||
if updatesBatch.parameterValues?
|
|
||||||
for p in updatesBatch.parameterValues
|
|
||||||
origValue = p[1]
|
|
||||||
v = normalize(p[0], origValue)
|
|
||||||
|
|
||||||
path = if common.endsWith(p[0], '.') then p[0] else "#{p[0]}."
|
|
||||||
if v == origValue
|
|
||||||
update['$unset']["#{path}_orig"] = 1
|
|
||||||
else
|
|
||||||
update['$set']["#{path}_orig"] = origValue
|
|
||||||
update['$set']["#{path}_value"] = v
|
|
||||||
update['$set']["#{path}_timestamp"] = now
|
|
||||||
if p[2]?
|
|
||||||
update['$set']["#{path}_type"] = p[2]
|
|
||||||
|
|
||||||
if updatesBatch.deletedObjects?
|
|
||||||
for p in updatesBatch.deletedObjects
|
|
||||||
update['$unset'][p] = 1
|
|
||||||
|
|
||||||
if updatesBatch.parameterNames?
|
|
||||||
presentPaths ?= {}
|
|
||||||
projection ?= {}
|
|
||||||
|
|
||||||
for p in updatesBatch.parameterNames
|
|
||||||
if OBJECT_REGEX.test(p[0])
|
|
||||||
path = p[0].slice(0, -1)
|
|
||||||
if INSTANCE_REGEX.test(p[0])
|
|
||||||
update['$set']["#{path}._instance"] = true
|
|
||||||
else
|
|
||||||
update['$set']["#{path}._object"] = true
|
|
||||||
else
|
|
||||||
path = p[0]
|
|
||||||
update['$set']["#{path}._writable"] = p[1] if p[1]?
|
|
||||||
update['$set']["#{path}._timestamp"] = now
|
|
||||||
|
|
||||||
# Add parameter (sans any dot suffix) if has an alias
|
|
||||||
if (a = parameters.getAlias(path))?
|
|
||||||
newAliases ?= {}
|
|
||||||
newAliases[path] = a
|
|
||||||
|
|
||||||
projection[path] = 1
|
|
||||||
|
|
||||||
# Store this param path and all its parents for later look up
|
|
||||||
presentPaths[path] = true
|
|
||||||
i = p[0].indexOf('.')
|
|
||||||
while i != -1
|
|
||||||
presentPaths[p[0][0...i]] = true
|
|
||||||
i = p[0].indexOf('.', i + 1)
|
|
||||||
|
|
||||||
if updatesBatch.customCommands?
|
|
||||||
for p in updatesBatch.customCommands
|
|
||||||
commandName = p[0]
|
|
||||||
update['$set']["_customCommands.#{commandName}._value"] = p[1]
|
|
||||||
update['$set']["_customCommands.#{commandName}._timestamp"] = now
|
|
||||||
|
|
||||||
processBatches = (cb) ->
|
|
||||||
if not commitQueue
|
|
||||||
processBatch(deviceUpdates) if deviceUpdates?
|
|
||||||
return cb()
|
|
||||||
|
|
||||||
db.redisClient.lpop("#{deviceId}_updates", (err, res) ->
|
|
||||||
callback(err) if err
|
|
||||||
|
|
||||||
if not res?
|
|
||||||
processBatch(deviceUpdates) if deviceUpdates?
|
|
||||||
return cb()
|
|
||||||
|
|
||||||
processBatch(JSON.parse(res))
|
|
||||||
return processBatches(cb)
|
|
||||||
)
|
|
||||||
|
|
||||||
commit = () ->
|
|
||||||
for k of update
|
|
||||||
delete update[k] if Object.keys(update[k]).length == 0
|
|
||||||
|
|
||||||
if Object.keys(update).length
|
|
||||||
db.devicesCollection.update({'_id' : deviceId}, update, {}, (err, count) ->
|
|
||||||
return callback(err) if err
|
|
||||||
|
|
||||||
# Clear aliases cache if there's a new aliased parameter that has not been included in aliases cache
|
|
||||||
if newAliases?
|
|
||||||
newAliasFound = false
|
|
||||||
db.getAliases((aliases) ->
|
|
||||||
for p, a of newAliases
|
|
||||||
if not aliases[a]? or p not in aliases[a]
|
|
||||||
newAliasFound = true
|
|
||||||
break
|
|
||||||
|
|
||||||
return callback() if not newAliasFound
|
|
||||||
db.redisClient.del('aliases', (err, res) ->
|
|
||||||
return callback(err)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
return callback()
|
|
||||||
)
|
|
||||||
else
|
|
||||||
return callback()
|
|
||||||
|
|
||||||
processBatches(() ->
|
|
||||||
if projection?
|
|
||||||
mongoQuery.optimizeProjection(projection)
|
|
||||||
|
|
||||||
# Fetch device from DB to discover and delete parameters that no longer exist
|
|
||||||
# While we're fetching the object, we might as well use the information
|
|
||||||
# to optimize our update query
|
|
||||||
db.devicesCollection.findOne({'_id' : deviceId}, projection, (err, device) ->
|
|
||||||
return callback(err) if err
|
|
||||||
|
|
||||||
recursive = (obj, prefix) ->
|
|
||||||
for k, v of obj
|
|
||||||
p = prefix + k
|
|
||||||
|
|
||||||
# Mark unset operations we want to keep
|
|
||||||
if update['$unset'][p]?
|
|
||||||
update['$unset'][p] = 2
|
|
||||||
continue
|
|
||||||
|
|
||||||
if k[0] == '_'
|
|
||||||
# Remove set operations for unmodified values for optimization
|
|
||||||
if update['$set'][p] == v
|
|
||||||
delete update['$set'][p]
|
|
||||||
continue
|
|
||||||
|
|
||||||
if presentPaths[p]
|
|
||||||
recursive(v, "#{p}.")
|
|
||||||
else
|
|
||||||
update['$unset'][p] = 2
|
|
||||||
|
|
||||||
# Scan the DB object and remove parameters that no longer exists in device
|
|
||||||
for path of projection
|
|
||||||
root = device
|
|
||||||
rootPath = ''
|
|
||||||
for p in path.split('.')
|
|
||||||
rootPath += "#{p}."
|
|
||||||
root = root[p]
|
|
||||||
break if not root?
|
|
||||||
|
|
||||||
if root
|
|
||||||
recursive(root, rootPath)
|
|
||||||
else
|
|
||||||
if not presentPaths[rootPath.slice(0, -1)]?
|
|
||||||
update['$unset'][rootPath.slice(0, -1)] = 2
|
|
||||||
|
|
||||||
# Remove unnecessary unset updates
|
|
||||||
for k, v of update['$unset']
|
|
||||||
if common.startsWith(k, path + '.') and v != 2
|
|
||||||
delete update['$unset'][k]
|
|
||||||
|
|
||||||
commit()
|
|
||||||
)
|
|
||||||
else
|
|
||||||
commit()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
exports.queueUpdates = queueUpdates
|
|
||||||
exports.clearUpdatesQueue = clearUpdatesQueue
|
|
||||||
exports.commitUpdates = commitUpdates
|
|
||||||
Reference in new issue
Block a user