Implement creating and deleting instances

This commit is contained in:
Zaid Abdulla committed 2016-08-16 13:28:50 -07:00
1 parent ac759e1d04
commit 0a5697f06a
2 files changed
+276 -13

No files matched your search

+124
View File
@@ -0,0 +1,124 @@
/**
* Copyright 2013-2016 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/>.
*/
"use strict";
class InstanceSet {
constructor() {
this.set = new Set();
}
add(instance) {
this.set.add(instance);
}
delete(instance) {
this.set.delete(instance);
}
superset(instance) {
let res = [];
for (let inst of this.set) {
let match = true;
for (let k in instance) {
if (inst[k] !== instance[k]) {
match = false;
break;
}
}
if (match)
res.push(inst);
}
res.sort(function(a, b) {
let keysA = Object.keys(a);
let keysB = Object.keys(b);
if (keysA.length != keysB.length)
return keysB.length - keysA.length;
keysA.sort();
keysB.sort();
for (let i = 0; i < keysA.length; ++ i) {
if (keysA[i] > keysB[i])
return 1;
else if (keysA[i] < keysB[i])
return -1;
else if (a[keysA[i]] > b[keysB[i]])
return 1;
else if (a[keysA[i]] < b[keysB[i]])
return -1;
}
return 0;
});
return res;
}
subset(instance) {
let res = [];
for (let inst of this.set) {
let match = true;
for (let k in inst) {
if (inst[k] !== instance[k]) {
match = false;
break;
}
}
if (match)
res.push(inst);
}
res.sort(function(a, b) {
let keysA = Object.keys(a);
let keysB = Object.keys(b);
if (keysA.length != keysB.length)
return keysA.length - keysB.length;
keysA.sort();
keysB.sort();
for (let i = 0; i < keysA.length; ++ i) {
if (keysA[i] > keysB[i])
return 1;
else if (keysA[i] < keysB[i])
return -1;
else if (a[keysA[i]] > b[keysB[i]])
return 1;
else if (a[keysA[i]] < b[keysB[i]])
return -1;
}
return 0;
});
return res;
}
forEach(callback) {
return this.set.forEach(callback);
}
}
module.exports = InstanceSet;
+152 -13
View File
@@ -26,6 +26,7 @@ cache = require './cache'
extensions = require './extensions'
PathSet = require './path-set'
VersionedMap = require './versioned-map.js'
InstanceSet = require './instance-set'
initDeviceData = () ->
@@ -326,6 +327,9 @@ runVirtualParameters = (sessionData, inception, callback) ->
rpcRequest = (sessionData, declarations, callback) ->
if sessionData.rpcRequest?
return callback(null, generateRpcId(sessionData), sessionData.rpcRequest)
revision = sessionData.revisions.reduce(((a, b) -> a + (b >> 1)), 0) + 1
v.revision = revision for k, v of sessionData.deviceData.timestamps
@@ -448,7 +452,38 @@ generateRpcRequest = (sessionData) ->
rpcReq = null
completed = true
# Delete instance
syncState.instancesToDelete.forEach((instances, parent) ->
instances.forEach((instance) ->
if rpcReq?
completed = false
else
rpcReq = {
type: 'DeleteObject'
objectName: instance.join('.') + '.'
}
)
)
# Create instance
syncState.instancesToCreate.forEach((instances, parent) ->
instances.forEach((instance) ->
if rpcReq?
completed = false
else
rpcReq = {
type: 'AddObject'
objectName: parent.join('.') + '.'
next: 'getInstanceKeys'
instanceValues: instance
}
)
)
if syncState.refreshGpn.size
if rpcReq?
completed = false
else
GET_PARAMETER_NAMES_DEPTH_THRESHOLD =
config.get('GET_PARAMETER_NAMES_DEPTH_THRESHOLD', sessionData.deviceId)
@@ -560,19 +595,6 @@ processVirtualparameterDeclaration = (sessionData, path, timestamps, values) ->
processDeclaration = (sessionData, path, _timestamps, values) ->
sessionData.syncState ?= {
paths: new PathSet()
refreshAttributes: {
exist: new Set()
object: new Set()
writable: new Set()
value: new Set()
}
setValues: new Map()
refreshGpn: new Map()
virtualParameters: {}
}
syncState = sessionData.syncState
deviceData = sessionData.deviceData
@@ -747,12 +769,66 @@ loadPath = (sessionData, path) ->
return false
processInstances = (sessionData, parent, parameters, keys, minInstances, maxInstances) ->
instancesToDelete = sessionData.syncState.instancesToDelete.get(parent)
if not instancesToDelete?
instancesToDelete = new Set()
sessionData.syncState.instancesToDelete.set(parent, instancesToDelete)
instancesToCreate = sessionData.syncState.instancesToCreate.get(parent)
if not instancesToCreate?
instancesToCreate = new InstanceSet()
sessionData.syncState.instancesToCreate.set(parent, instancesToCreate)
counter = 0
for p in parameters
++ counter
if counter > maxInstances
instancesToDelete.add(p)
else if counter <= minInstances
instancesToDelete.delete(p)
superset = instancesToCreate.superset(keys)
for inst in superset
++ counter
if counter > maxInstances
instancesToCreate.delete(inst)
subset = instancesToCreate.subset(keys)
for inst in subset
++ counter
if counter <= minInstances
instancesToCreate.delete(inst)
instancesToCreate.add(JSON.parse(JSON.stringify(keys)))
while counter < minInstances
++ counter
instancesToCreate.add(JSON.parse(JSON.stringify(keys)))
return
applyDeclaration = (sessionData, path, state, timestamps, values) ->
if state == 100
if not values? or Object.keys(values).length == 0
return state
timestamps = {}
sessionData.syncState ?= {
paths: new PathSet()
refreshAttributes: {
exist: new Set()
object: new Set()
writable: new Set()
value: new Set()
}
setValues: new Map()
refreshGpn: new Map()
virtualParameters: {}
instancesToDelete: new Map()
instancesToCreate: new Map()
}
decs = device.getAliasDeclarations(path, timestamps.exist ? 1)
if state <= 1
@@ -777,6 +853,30 @@ applyDeclaration = (sessionData, path, state, timestamps, values) ->
return 2 if not satisfied
unpacked = device.unpack(sessionData.deviceData, path)
if values.exist?
if Array.isArray(values.exist)
minInstances = values.exist[0]
maxInstances = values.exist[1]
else
minInstances = maxInstances = values.exist
parent = path.slice(0, -1)
if Array.isArray(path[path.length - 1])
keys = {}
for p, i in path[path.length - 1] by 2
keys[p] = path[path.length - 1][i + 1]
else if path[path.length - 1] == '*'
keys = {}
if (path.wildcard | path.alias) & ((1 << (path.length - 1)) - 1) == 0
processInstances(sessionData, parent, unpacked, keys, minInstances, maxInstances)
else
parentsUnpacked = device.unpack(sessionData.deviceData, parent)
for parent in parentsUnpacked
processInstances(sessionData, parent, device.unpack(sessionData.deviceData, parent.concat([path[parent.length]])), keys, minInstances, maxInstances)
satisfied = true
for up in unpacked
satisfied &&= processDeclaration(sessionData, path, timestamps, values)
@@ -848,7 +948,37 @@ rpcResponse = (sessionData, id, rpcRes, callback) ->
++ sessionData.rpcCount
rpcReq = sessionData.rpcRequest
if not rpcReq.next?
sessionData.rpcRequest = null
else if rpcReq.next == 'getInstanceKeys'
instanceNumber = rpcRes.instanceNumber
parameterNames = []
instanceValues = {}
for k, v of rpcReq.instanceValues
n = "#{rpcReq.objectName}#{rpcRes.instanceNumber}.#{k}"
parameterNames.push(n)
instanceValues[n] = v
if parameterNames.length == 0
sessionData.rpcRequest = null
else
sessionData.rpcRequest = {
type: 'GetParameterValues'
parameterNames: parameterNames
next: 'setInstanceKeys'
instanceValues: instanceValues
}
else if rpcReq.next == 'setInstanceKeys'
parameterList = []
for p in rpcRes.parameterList
if p[1] != rpcReq.instanceValues[p[0]]
parameterList.push(
[p[0]].concat(device.sanitizeParameterValue([rpcReq.instanceValues[p[0]], p[2]])))
sessionData.rpcRequest = {
type: 'SetParameterValues'
parameterList: parameterList
}
timestamp = sessionData.timestamp
revision = sessionData.revisions.reduce(((a, b) -> a + (b >> 1)), 0) + 1
@@ -918,6 +1048,15 @@ rpcResponse = (sessionData, id, rpcRes, callback) ->
{exist: timestamp, object: timestamp, writable: timestamp, value: timestamp},
{exist: 1, object: 0, writable: 1, value: p.slice(1)})
when 'AddObjectResponse'
device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName + rpcRes.instanceNumber),
{exist: timestamp, object: timestamp},
{exist: 1, object: 1})
when 'DeleteObjectResponse'
device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName.slice(0, -1)),
{exist: timestamp})
else
return callback(new Error('Response type not recognized'))