Merge branch 'customcommands'

This commit is contained in:
Zaid Abdulla committed 2013-09-15 23:14:22 +03:00
commit 1586ce92aa
9 files changed
+320 -9

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
*~
*.js
node_modules
+12
View File
@@ -102,6 +102,12 @@ updateDevice = (currentRequest, actions, callback) ->
updates["#{path}_writable"] = p[1] if p[1]?
updates["#{path}_timestamp"] = now
if actions.customCommands?
for p in actions.customCommands
commandName = p[0]
updates["_customCommands.#{commandName}._value"] = p[1]
updates["_customCommands.#{commandName}._timestamp"] = now
if Object.keys(updates).length > 0 or Object.keys(deletes).length > 0
db.devicesCollection.update({'_id' : currentRequest.deviceId}, {'$set' : updates, '$unset' : deletes}, {}, (err, count) ->
if (err)
@@ -174,6 +180,12 @@ runTask = (currentRequest, task, methodResponse) ->
res = tr069.response(task._id, cwmpResponse)
writeResponse(currentRequest, res)
)
when tasks.STATUS_SAVE
db.saveTask(task, (err) ->
throw new Error(err) if err?
res = tr069.response(task._id, cwmpResponse)
writeResponse(currentRequest, res)
)
else
throw Error('Unknown task status')
)
+2
View File
@@ -49,3 +49,5 @@ exports.ALIASES = aliases
# load authentication scripts
exports.auth = require('./config/auth')
exports.CUSTOM_COMMANDS = require('./config/custom_commands')
+3
View File
@@ -0,0 +1,3 @@
{
"seowon_frequencies" : "/^[0-9]+\\-SWU\\%2D9200\\-[0-9A-Z]+$/"
}
@@ -0,0 +1,199 @@
// Change frequency plans for Seowon devices.
// Args format: 3532500 5000,3537500 5000,3542500 5000,3547500 5000,3552500 5000,3557500 5000,3505000 10000,3515000 10000,3525000 10000,3535000 10000,3545000 10000,3555000 10000
var net = require('net');
var PORT = 23;
var USERNAME = 'admin';
var PASSWORD = 'admin';
var FREQUENCIES_REGEX = /\s*([\d]+)\.\s*Frequency\s*:\s*([\d]+)\s\(KHz\),\s*Bandwidth\s*:\s*([\d\.]+)\s*\(MHz\)/gm
var telnetConnect = function(host, callback) {
var client = net.connect(PORT, host, function() {
// enter username
telnetExecute(USERNAME, client, "Password: ", function(err, response) {
if (err) return callback(err, client);
if (!timeout) return;
// enter password
telnetExecute(PASSWORD, client, "MT7109> ", function(err, response) {
if (!timeout) return;
clearTimeout(timeout);
return callback(err, client);
});
});
});
var timeout = setTimeout(function() {
timeout = null;
return callback('timeout', client);
}, 4000);
}
var telnetExecute = function(command, connection, prompt, callback) {
command += "\n";
var response = '';
var listener = function(chunk) {
response += chunk.toString();
if (response.slice(0 - prompt.length) == prompt) {
if (!timeout) return;
clearTimeout(timeout);
connection.removeListener('data', listener);
return callback(null, response.slice(command.length, 0 - prompt.length));
}
};
var end = function() {
if (!timeout) return;
clearTimeout(timeout);
connection.removeListener('end', end);
return callback(null, response.slice(command.length));
};
var timeout = setTimeout(function() {
timeout = null;
return callback('timeout', response);
}, 2000);
if (prompt)
connection.on('data', listener);
else
connection.on('end', end);
if (command)
connection.write(command);
}
var parseFrequencies = function(frequenciesString) {
var a = frequenciesString.split(',')
var l = [];
for (i in a) {
var sp = a[i].trim().split(' ');
var frequency = parseInt(sp[0]);
var bandwidth = parseFloat(sp[1]);
l.push([frequency, bandwidth]);
}
return l;
}
var findFrequency = function(haystack, needle) {
for (i in haystack) {
if (haystack[i][0] == needle[0] && haystack[i][1] == needle[1])
return i;
}
return -1;
}
var getDeviceIp = function(deviceId, callback) {
var db = require('../../db');
var URL = require('url');
db.devicesCollection.findOne({_id : deviceId}, {'InternetGatewayDevice.ManagementServer.ConnectionRequestURL._value' : 1}, function(err, device) {
var connectionRequestUrl = device.InternetGatewayDevice.ManagementServer.ConnectionRequestURL._value;
var url = URL.parse(connectionRequestUrl);
return callback(url.hostname);
});
};
exports.init = function(deviceId, args, callback) {
return exports.get(deviceId, args, callback);
};
exports.get = function(deviceId, args, callback) {
var currentFrequencies = [];
getDeviceIp(deviceId, function(ip) {
telnetConnect(ip, function(err, client) {
// enter privileged mode
telnetExecute("enable", client, "MT7109# ", function(err, response) {
if (err) return callback(err);
// show frequencies
telnetExecute("show wmx freq", client, "MT7109# ", function(err, response) {
if (err) return callback(err);
while (f = FREQUENCIES_REGEX.exec(response)) {
var frequency = parseInt(f[2]);
var bandwidth = Math.floor(parseFloat(f[3]) * 1000);
var freqStr = String(frequency) + ' ' + String(bandwidth);
currentFrequencies.push(freqStr);
}
// log out
telnetExecute("logout", client, null, function(err, response) {
return callback(err, currentFrequencies.join(','));
});
});
});
});
});
};
exports.set = function(deviceId, args, callback) {
var newFrequencies = parseFrequencies(args);
var addFrequencies = [];
var delFrequencies = [];
getDeviceIp(deviceId, function(ip) {
telnetConnect(ip, function(err, client) {
// enter privileged mode
telnetExecute("enable", client, "MT7109# ", function(err, response) {
if (err) return callback(err);
// show frequencies
telnetExecute("show wmx freq", client, "MT7109# ", function(err, response) {
if (err) return callback(err);
while (f = FREQUENCIES_REGEX.exec(response)) {
var index = parseInt(f[1]);
var frequency = parseInt(f[2]);
var bandwidth = Math.floor(parseFloat(f[3]));
var i = findFrequency(newFrequencies, [frequency, bandwidth * 1000]);
if (i == -1)
delFrequencies.push(index);
else
newFrequencies.splice(i, 1);
}
for (f in newFrequencies)
addFrequencies.push(newFrequencies[f]);
if (addFrequencies.length == 0 && delFrequencies.length == 0) {
// log out
telnetExecute("logout", client, null, function(err, response) {
return callback(err, args);
});
return;
}
telnetExecute("wimax", client, "MT7109 (WiMax)# ", function(err, response) {
if (err) return callback(err);
var add, del;
del = function() {
if (delFrequencies.length == 0) {
add();
return;
}
var i = delFrequencies.pop();
telnetExecute("wmx freq del " + i, client, "MT7109 (WiMax)# ", function(err, response) {
if (err) return callback(err);
del();
});
};
add = function() {
if (addFrequencies.length == 0) {
// commit
telnetExecute("commit", client, "MT7109 (WiMax)# ", function(err, response) {
if (err) return callback(err);
// log out
telnetExecute("logout", client, null, function(err, response) {
return callback(null, args);
});
});
return;
}
var i = addFrequencies.pop();
telnetExecute("wmx freq add " + i[0] + ' ' + i[1]/1000, client, "MT7109 (WiMax)# ", function(err, response) {
if (err) return callback(err);
add();
});
};
del();
});
});
});
});
});
};
+27
View File
@@ -0,0 +1,27 @@
config = require './config'
common = require './common'
exports.execute = (deviceId, command, callback) ->
args = command.split(/\s+/)
f = args.shift()
c = args.shift()
file = require "./config/custom_commands/#{f}"
file[c](deviceId, args.join(' '), callback)
exports.getFileCommands = (filename) ->
commands = []
f = require("./config/custom_commands/#{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
+7 -3
View File
@@ -72,9 +72,13 @@ updateTask = (task, callback) ->
saveTask = (task, callback) ->
task._id = mongodb.ObjectID(String(task._id))
tasksCollection.save(task, (err) ->
callback(err)
# task ID can either be a string or a MongoDB ID
id = String(task._id)
task._id = mongodb.ObjectID(id)
tasksCollection.save(task, (mongoErr) ->
memcached.del(id, (memcachedErr, res) ->
callback(mongoErr or memcachedErr)
)
)
+24
View File
@@ -4,6 +4,7 @@ db = require './db'
mongoQuery = require './mongo-query'
query = require './query'
log = require('util').log
customCommands = require './custom-commands'
getPresets = (callback) ->
@@ -65,6 +66,7 @@ matchObject = (object, param) ->
exports.assertPresets = (deviceId, presetsHash, callback) ->
getPresets((presets, objects) ->
deviceCustomCommands = customCommands.getDeviceCustomCommands(deviceId)
# only fetch relevant params
projection = {_id : 1}
for p in presets
@@ -75,6 +77,10 @@ exports.assertPresets = (deviceId, presetsHash, callback) ->
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 'firmware'
projection['InternetGatewayDevice.DeviceInfo.SoftwareVersion'] = 1
when 'add_tag', 'delete_tag'
@@ -120,6 +126,23 @@ exports.assertPresets = (deviceId, presetsHash, callback) ->
getParameterValues.push(c.name)
else
expiry = Math.min(expiry, c.age - timeDiff)
when 'custom_command_value'
[filename, commandName] = c.command.split(' ', 2)
continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
currentValue = try common.getParamValueFromPath(device, "_customCommands.#{filename}")._value catch then undefined
dst = common.matchType(currentValue, c.value)
if currentValue != dst
taskList.push({device : deviceId, name : 'customCommand', command : c.command})
when 'custom_command_age'
[filename, commandName] = c.command.split(' ', 2)
continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
command = common.getParamValueFromPath(device, "_customCommands.#{filename}")
timeDiff = try (now - command._timestamp) / 1000 catch then 9999999
if (c.age - timeDiff < config.PRESETS_TIME_PADDING)
expiry = Math.min(expiry, c.age)
taskList.push({device : deviceId, name : 'customCommand', command : c.command})
else
expiry = Math.min(expiry, c.age - timeDiff)
when 'add_tag'
add_tags.push(c.tag) if not device['_tags']? or c.tag not in device['_tags']
when 'delete_tag'
@@ -200,6 +223,7 @@ getObjectHash = (object) ->
hash += "#{k}=#{object[k]}"
return hash
accumulateConfigurations = (presets, objects) ->
maxWeights = {}
configurations = {}
+43 -6
View File
@@ -2,13 +2,28 @@ config = require './config'
common = require './common'
db = require './db'
mongodb = require 'mongodb'
customCommands = require './custom-commands'
BATCH_SIZE = 16
exports.STATUS_QUEUED = STATUS_QUEUED = 0
exports.STATUS_STARTED = STATUS_STARTED = 1
exports.STATUS_PENDING = STATUS_PENDING = 2
exports.STATUS_FAULT = STATUS_FAULT = 3
exports.STATUS_FINISHED = STATUS_FINISHED = 4
exports.STATUS_SAVE = STATUS_SAVE = 2
exports.STATUS_PENDING = STATUS_PENDING = 3
exports.STATUS_FAULT = STATUS_FAULT = 4
exports.STATUS_FINISHED = STATUS_FINISHED = 5
initCustomCommands = (deviceId, callback) ->
updates = {customCommands : []}
counter = 0
for k,v of customCommands.getDeviceCustomCommands(deviceId)
if 'init' in v
++counter
customCommands.execute(deviceId, "#{k} init", (err, value) ->
updates.customCommands.push([k, value])
callback(updates) if --counter <= 0
)
callback(updates) if counter <= 0
this.init = (task, methodResponse, callback) ->
@@ -16,7 +31,7 @@ this.init = (task, methodResponse, callback) ->
task.subtask = {device : task.device, name : 'getParameterNames', parameterPath : '', nextLevel : false}
if task.subtask.name == 'getParameterNames'
if methodResponse.faultcode?
if methodResponse.faultcode?
task.fault = methodResponse
callback(null, STATUS_FAULT)
return
@@ -42,7 +57,13 @@ this.init = (task, methodResponse, callback) ->
# Ignore GetParameterValues errors. A workaround for the crappy Seewon devices.
methodResponse = {parameterList : {}}
this.getParameterValues(task.subtask, methodResponse, (err, status, cwmpResponse, deviceUpdates) ->
callback(err, status, cwmpResponse, deviceUpdates)
if status is STATUS_FINISHED
initCustomCommands(task.device, (updates) ->
common.extend(deviceUpdates, updates)
callback(err, status, cwmpResponse, deviceUpdates)
)
else
callback(err, status, cwmpResponse, deviceUpdates)
)
else
throw Error('Unexpected subtask name')
@@ -241,7 +262,11 @@ this.addObject = (task, methodResponse, callback) ->
task.subtask = {name : 'getParameterValues', parameterNames : task.parameterNames}
subtask()
else if status is STATUS_STARTED
callback(err, STATUS_STARTED, cwmpResponse, allDeviceUpdates)
if allDeviceUpdates.instanceName?
# Use STATUSS_SAVE to avoid adding duplicate object in case of error
callback(err, STATUS_SAVE, cwmpResponse, allDeviceUpdates)
else
callback(err, STATUS_STARTED, cwmpResponse, allDeviceUpdates)
else
throw Error('Unexpected subtask status')
)
@@ -343,5 +368,17 @@ this.download = (task, methodResponse, callback) ->
callback(null, STATUS_FINISHED)
this.customCommand = (task, methodResponse, callback) ->
# TODO implement timeout
customCommands.execute(task.device, task.command, (err, value) ->
if err?
task.fault = err
callback(null, STATUS_FAULT)
else
commandName = task.command.split(' ', 2)[0]
callback(null, STATUS_FINISHED, null, {customCommands : [[commandName, value]]})
)
exports.task = (task, methodResponse, callback) ->
this[task.name](task, methodResponse, callback)