Accept configuration from environment variables or command line arguments. Unified cluster and HTTP server code for CWMP, NBI, and FS.
This commit is contained in:
1 parent
5d0f398bf7
commit
32c56f8ff1
14 files changed
+511
-485
No files matched your search
@@ -0,0 +1,45 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
config = require './config'
|
||||
cluster = require 'cluster'
|
||||
util = require 'util'
|
||||
|
||||
|
||||
cluster.on('listening', (worker, address) ->
|
||||
util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
|
||||
)
|
||||
|
||||
cluster.on('exit', (worker, code, signal) ->
|
||||
util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
|
||||
cluster.fork()
|
||||
)
|
||||
|
||||
|
||||
start = (service) ->
|
||||
workerProcesses = config["#{service.toUpperCase()}_WORKER_PROCESSES"]
|
||||
|
||||
cluster.setupMaster({
|
||||
exec : 'lib/server',
|
||||
args : [service]
|
||||
})
|
||||
|
||||
for [0 ... workerProcesses]
|
||||
cluster.fork()
|
||||
|
||||
|
||||
exports.start = start
|
||||
+86
-34
@@ -15,43 +15,95 @@
|
||||
# along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||
###
|
||||
|
||||
exports.MONGODB_CONNECTION_URL = 'mongodb://localhost/genieacs'
|
||||
exports.REDIS_DB = 0
|
||||
exports.ACS_PORT = 7547 # CWMP port as assigned by IANA
|
||||
exports.ACS_HTTPS_PORT = 7548
|
||||
exports.ACS_INTERFACE = '0.0.0.0'
|
||||
exports.ACS_HTTPS_INTERFACE = '0.0.0.0'
|
||||
exports.API_PORT = 7557
|
||||
exports.API_INTERFACE = '0.0.0.0'
|
||||
exports.FILES_PORT = 7567
|
||||
exports.FILES_INTERFACE = '0.0.0.0'
|
||||
exports.FILES_IP = '172.240.21.2' # Used when sending download requests to devices
|
||||
exports.CACHE_DURATION = 60 # in seconds
|
||||
exports.PRESETS_CACHE_DURATION = 86400
|
||||
exports.PRESETS_TIME_PADDING = 1
|
||||
exports.MONGODB_SOCKET = '/tmp/mongodb-27017.sock'
|
||||
exports.REDIS_SOCKET = '/tmp/redis.sock'
|
||||
exports.WORKER_RESPAWN_TIME = 60000
|
||||
exports.LOG_INFORMS = false
|
||||
exports.DEBUG_DEVICES = {} # {'202BC1-BM632w-8KA8WA1151100043' : true}
|
||||
exports.DEVICE_ONLINE_THRESHOLD = 4000
|
||||
exports.RETRY_DELAY = 300
|
||||
exports.IGNORE_XML_NAMESPACES = false # Traverse XML using element's local name only
|
||||
exports.LIBXMLJS_OPTIONS = {} # Options passed directly to libxmljs
|
||||
exports.SESSION_TIMEOUT = 30
|
||||
exports.GET_PARAMETER_NAMES_DEPTH_THRESHOLD = 0
|
||||
exports.TASK_PARAMETERS_BATCH_SIZE = 32
|
||||
path = require 'path'
|
||||
common = require './common'
|
||||
|
||||
|
||||
defaults = {
|
||||
CONFIG_DIR : './config',
|
||||
|
||||
MONGODB_CONNECTION_URL : 'mongodb://127.0.0.1/genieacs',
|
||||
REDIS_PORT : 6379,
|
||||
REDIS_HOST : '127.0.0.1',
|
||||
REDIS_DB : 0,
|
||||
|
||||
CWMP_WORKER_PROCESSES : 4,
|
||||
CWMP_PORT : 7547, # CWMP port as assigned by IANA
|
||||
CWMP_INTERFACE : '0.0.0.0',
|
||||
CWMP_SSL : false,
|
||||
NBI_WORKER_PROCESSES : 2,
|
||||
NBI_PORT : 7557,
|
||||
NBI_INTERFACE : '0.0.0.0',
|
||||
NBI_SSL : false,
|
||||
FS_WORKER_PROCESSES : 2,
|
||||
FS_PORT : 7567,
|
||||
FS_INTERFACE : '0.0.0.0',
|
||||
FS_SSL : false,
|
||||
FILES_IP : '192.168.0.1', # Used when sending download requests to devices
|
||||
|
||||
CACHE_DURATION : 60, # in seconds
|
||||
PRESETS_CACHE_DURATION : 86400,
|
||||
PRESETS_TIME_PADDING : 1,
|
||||
|
||||
LOG_INFORMS : true,
|
||||
DEBUG : false,
|
||||
DEVICE_ONLINE_THRESHOLD : 4000,
|
||||
RETRY_DELAY : 300,
|
||||
IGNORE_XML_NAMESPACES : false, # Traverse XML using element's local name only
|
||||
SESSION_TIMEOUT : 30,
|
||||
GET_PARAMETER_NAMES_DEPTH_THRESHOLD : 0,
|
||||
TASK_PARAMETERS_BATCH_SIZE : 32
|
||||
}
|
||||
|
||||
|
||||
# Command line arguments
|
||||
exports.argv = []
|
||||
argv = process.argv[2..]
|
||||
while argv.length
|
||||
arg = argv.shift()
|
||||
if arg[0] == '-'
|
||||
v = argv.shift()
|
||||
exports.argv[arg] = v
|
||||
n = arg[2..].toUpperCase().replace(/-/g, '_')
|
||||
if defaults[n]?
|
||||
exports[n] ?= common.matchType(defaults[n], v)
|
||||
# Save as environmnet variables to pass on to any child processes
|
||||
process.env["GENIEACS_#{n}"] = v
|
||||
else
|
||||
exports.argv.push(arg)
|
||||
|
||||
|
||||
# Environment variable
|
||||
for k, v of process.env
|
||||
k = k[9..] # remove "GENIEACS_" prefix
|
||||
if defaults[k]?
|
||||
exports[k] ?= common.matchType(defaults[k], v)
|
||||
|
||||
|
||||
# Find config dir
|
||||
if exports.argv['--config-dir']?
|
||||
exports.CONFIG_DIR = exports.argv['--config-dir']
|
||||
else if process.env['GENIEACS_CONFIG_DIR']?
|
||||
exports.CONFIG_DIR = process.env['GENIEACS_CONFIG_DIR']
|
||||
else
|
||||
exports.CONFIG_DIR = defaults.CONFIG_DIR
|
||||
|
||||
|
||||
# Configuration file
|
||||
for k, v of require(path.resolve(exports.CONFIG_DIR, 'config'))
|
||||
if defaults[k]?
|
||||
exports[k] ?= common.matchType(defaults[k], v)
|
||||
|
||||
|
||||
# Defaults
|
||||
for k, v of defaults
|
||||
exports[k] ?= defaults[k]
|
||||
|
||||
# load configuration
|
||||
c = require('../config/config')
|
||||
for k, v of c
|
||||
exports[k] = v
|
||||
|
||||
# load parameter configurations
|
||||
exports.PARAMETERS = require('../config/parameters')
|
||||
exports.PARAMETERS = require(path.resolve(exports.CONFIG_DIR, 'parameters'))
|
||||
|
||||
# load authentication scripts
|
||||
exports.auth = require('../config/auth')
|
||||
|
||||
exports.CUSTOM_COMMANDS = require('../config/custom_commands')
|
||||
exports.auth = require(path.resolve(exports.CONFIG_DIR, 'auth'))
|
||||
|
||||
exports.CUSTOM_COMMANDS = require(path.resolve(exports.CONFIG_DIR, 'custom_commands'))
|
||||
+4
-41
@@ -41,14 +41,11 @@
|
||||
config = require './config'
|
||||
common = require './common'
|
||||
util = require 'util'
|
||||
http = require 'http'
|
||||
https = require 'https'
|
||||
soap = require './soap'
|
||||
tasks = require './tasks'
|
||||
db = require './db'
|
||||
presets = require './presets'
|
||||
mongodb = require 'mongodb'
|
||||
fs = require 'fs'
|
||||
apiFunctions = require './api-functions'
|
||||
customCommands = require './custom-commands'
|
||||
crypto = require 'crypto'
|
||||
@@ -61,9 +58,9 @@ holdUntil = Date.now()
|
||||
|
||||
|
||||
writeResponse = (currentRequest, res) ->
|
||||
if config.DEBUG_DEVICES[currentRequest.session.deviceId]
|
||||
if config.DEBUG
|
||||
dump = "# RESPONSE #{new Date(Date.now())}\n" + JSON.stringify(res.headers) + "\n#{res.data}\n\n"
|
||||
fs = require('fs').appendFile("../debug/#{currentRequest.session.deviceId}.dump", dump, (err) ->
|
||||
require('fs').appendFile("../debug/#{currentRequest.session.deviceId}.dump", dump, (err) ->
|
||||
throw err if err
|
||||
)
|
||||
|
||||
@@ -454,7 +451,7 @@ listener = (httpRequest, httpResponse) ->
|
||||
session : session
|
||||
}
|
||||
|
||||
if config.DEBUG_DEVICES[currentRequest.session.deviceId]
|
||||
if config.DEBUG
|
||||
dump = "# REQUEST #{new Date(Date.now())}\n" + JSON.stringify(httpRequest.headers) + "\n#{httpRequest.getBody()}\n\n"
|
||||
require('fs').appendFile("../debug/#{currentRequest.session.deviceId}.dump", dump, (err) ->
|
||||
throw err if err
|
||||
@@ -562,39 +559,5 @@ listener = (httpRequest, httpResponse) ->
|
||||
)
|
||||
)
|
||||
|
||||
cluster = require 'cluster'
|
||||
numCPUs = require('os').cpus().length
|
||||
|
||||
if cluster.isMaster
|
||||
db.connect((err) ->
|
||||
throw err if err
|
||||
db.redisClient.flushdb()
|
||||
db.disconnect()
|
||||
cluster.on('listening', (worker, address) ->
|
||||
util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
|
||||
)
|
||||
|
||||
cluster.on('exit', (worker, code, signal) ->
|
||||
util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
|
||||
setTimeout(()->
|
||||
cluster.fork()
|
||||
, config.WORKER_RESPAWN_TIME)
|
||||
)
|
||||
|
||||
for [1 .. numCPUs]
|
||||
cluster.fork()
|
||||
)
|
||||
else
|
||||
options = {
|
||||
key: fs.readFileSync('../config/httpscert.key'),
|
||||
cert: fs.readFileSync('../config/httpscert.crt')
|
||||
}
|
||||
|
||||
httpServer = http.createServer(listener)
|
||||
httpsServer = https.createServer(options, listener)
|
||||
|
||||
db.connect((err) ->
|
||||
throw err if err
|
||||
httpServer.listen(config.ACS_PORT, config.ACS_INTERFACE)
|
||||
httpsServer.listen(config.ACS_HTTPS_PORT, config.ACS_HTTPS_INTERFACE)
|
||||
)
|
||||
exports.listener = listener
|
||||
+1
-1
@@ -70,7 +70,7 @@ connect = (callback) ->
|
||||
return callback(err)
|
||||
)
|
||||
|
||||
exports.redisClient = redisClient = redis.createClient(config.REDIS_SOCKET)
|
||||
exports.redisClient = redisClient = redis.createClient(config.REDIS_PORT, config.REDIS_HOST)
|
||||
redisClient.select(config.REDIS_DB, (err) ->
|
||||
if --callbackCounter == 0 or err
|
||||
callbackCounter = 0
|
||||
|
||||
+19
-43
@@ -21,53 +21,29 @@
|
||||
###
|
||||
|
||||
config = require './config'
|
||||
util = require 'util'
|
||||
http = require 'http'
|
||||
url = require 'url'
|
||||
mongodb = require 'mongodb'
|
||||
querystring = require 'querystring'
|
||||
db = require './db'
|
||||
|
||||
db = null
|
||||
|
||||
cluster = require 'cluster'
|
||||
numCPUs = require('os').cpus().length
|
||||
listener = (request, response) ->
|
||||
urlParts = url.parse(request.url, true)
|
||||
if request.method == 'GET'
|
||||
filename = querystring.unescape(urlParts.pathname.substring(1))
|
||||
gs = new mongodb.GridStore(db.mongoDb, filename, 'r', {})
|
||||
gs.open((err, gs) ->
|
||||
if err
|
||||
response.writeHead(404)
|
||||
response.end()
|
||||
return
|
||||
stream = gs.stream(true)
|
||||
response.writeHead(200, {'Content-Type' : 'application/octet-stream', 'Content-Length' : gs.length})
|
||||
stream.pipe(response)
|
||||
)
|
||||
else
|
||||
response.writeHead(405, {'Allow': 'GET'})
|
||||
response.end('405 Method Not Allowed')
|
||||
|
||||
if cluster.isMaster
|
||||
cluster.on('listening', (worker, address) ->
|
||||
util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
|
||||
)
|
||||
|
||||
cluster.on('exit', (worker, code, signal) ->
|
||||
util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
|
||||
setTimeout(()->
|
||||
cluster.fork()
|
||||
, config.WORKER_RESPAWN_TIME)
|
||||
)
|
||||
|
||||
for i in [1 .. numCPUs]
|
||||
cluster.fork()
|
||||
else
|
||||
server = http.createServer((request, response) ->
|
||||
urlParts = url.parse(request.url, true)
|
||||
if request.method == 'GET'
|
||||
filename = querystring.unescape(urlParts.pathname.substring(1))
|
||||
gs = new mongodb.GridStore(db, filename, 'r', {})
|
||||
gs.open((err, gs) ->
|
||||
if err
|
||||
response.writeHead(404)
|
||||
response.end()
|
||||
return
|
||||
stream = gs.stream(true)
|
||||
response.writeHead(200, {'Content-Type' : 'application/octet-stream', 'Content-Length' : gs.length})
|
||||
stream.pipe(response)
|
||||
)
|
||||
else
|
||||
response.writeHead(405, {'Allow': 'GET'})
|
||||
response.end('405 Method Not Allowed')
|
||||
)
|
||||
|
||||
mongodb.MongoClient.connect("mongodb://#{config.MONGODB_SOCKET}/#{config.DATABASE_NAME}", config.MONGODB_OPTIONS, (err, _db) ->
|
||||
throw err if err
|
||||
db = _db
|
||||
server.listen config.FILES_PORT, config.FILES_INTERFACE
|
||||
)
|
||||
exports.listener = listener
|
||||
+288
-310
@@ -39,8 +39,6 @@
|
||||
###
|
||||
|
||||
config = require './config'
|
||||
util = require 'util'
|
||||
http = require 'http'
|
||||
url = require 'url'
|
||||
common = require './common'
|
||||
db = require './db'
|
||||
@@ -62,343 +60,323 @@ QUERY_REGEX = /^\/([a-zA-Z0-9_]+s)\/?$/
|
||||
DEVICE_PRESET_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/preset\/?$/
|
||||
|
||||
|
||||
cluster = require 'cluster'
|
||||
numCPUs = require('os').cpus().length
|
||||
listener = (request, response) ->
|
||||
chunks = []
|
||||
bytes = 0
|
||||
|
||||
if cluster.isMaster
|
||||
cluster.on('listening', (worker, address) ->
|
||||
util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
|
||||
)
|
||||
request.addListener 'data', (chunk) ->
|
||||
chunks.push(chunk)
|
||||
bytes += chunk.length
|
||||
|
||||
cluster.on('exit', (worker, code, signal) ->
|
||||
util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
|
||||
setTimeout(()->
|
||||
cluster.fork()
|
||||
, config.WORKER_RESPAWN_TIME)
|
||||
)
|
||||
request.getBody = () ->
|
||||
# Write all chunks into a Buffer
|
||||
body = new Buffer(bytes)
|
||||
offset = 0
|
||||
chunks.forEach((chunk) ->
|
||||
chunk.copy(body, offset, 0, chunk.length)
|
||||
offset += chunk.length
|
||||
)
|
||||
return body
|
||||
|
||||
for [1 .. numCPUs]
|
||||
cluster.fork()
|
||||
else
|
||||
server = http.createServer (request, response) ->
|
||||
chunks = []
|
||||
bytes = 0
|
||||
request.addListener 'end', () ->
|
||||
body = request.getBody()
|
||||
urlParts = url.parse(request.url, true)
|
||||
if PRESETS_REGEX.test(urlParts.pathname)
|
||||
presetName = querystring.unescape(PRESETS_REGEX.exec(urlParts.pathname)[1])
|
||||
if request.method == 'PUT'
|
||||
preset = JSON.parse(body)
|
||||
preset._id = presetName
|
||||
|
||||
request.addListener 'data', (chunk) ->
|
||||
chunks.push(chunk)
|
||||
bytes += chunk.length
|
||||
|
||||
request.getBody = () ->
|
||||
# Write all chunks into a Buffer
|
||||
body = new Buffer(bytes)
|
||||
offset = 0
|
||||
chunks.forEach((chunk) ->
|
||||
chunk.copy(body, offset, 0, chunk.length)
|
||||
offset += chunk.length
|
||||
)
|
||||
return body
|
||||
|
||||
request.addListener 'end', () ->
|
||||
body = request.getBody()
|
||||
urlParts = url.parse(request.url, true)
|
||||
if PRESETS_REGEX.test(urlParts.pathname)
|
||||
presetName = querystring.unescape(PRESETS_REGEX.exec(urlParts.pathname)[1])
|
||||
if request.method == 'PUT'
|
||||
preset = JSON.parse(body)
|
||||
preset._id = presetName
|
||||
|
||||
db.presetsCollection.save(preset, (err) ->
|
||||
db.redisClient.del('presets', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
db.presetsCollection.save(preset, (err) ->
|
||||
db.redisClient.del('presets', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
db.presetsCollection.remove({'_id' : presetName}, (err, removedCount) ->
|
||||
db.redisClient.del('presets', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
db.presetsCollection.remove({'_id' : presetName}, (err, removedCount) ->
|
||||
db.redisClient.del('presets', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'PUT, DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if OBJECTS_REGEX.test(urlParts.pathname)
|
||||
objectName = querystring.unescape(OBJECTS_REGEX.exec(urlParts.pathname)[1])
|
||||
if request.method == 'PUT'
|
||||
object = JSON.parse(body)
|
||||
object._id = objectName
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'PUT, DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if OBJECTS_REGEX.test(urlParts.pathname)
|
||||
objectName = querystring.unescape(OBJECTS_REGEX.exec(urlParts.pathname)[1])
|
||||
if request.method == 'PUT'
|
||||
object = JSON.parse(body)
|
||||
object._id = objectName
|
||||
|
||||
db.objectsCollection.save(object, (err) ->
|
||||
db.redisClient.del('objects', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
db.objectsCollection.save(object, (err) ->
|
||||
db.redisClient.del('objects', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
db.objectsCollection.remove({'_id' : objectName}, (err, removedCount) ->
|
||||
db.redisClient.del('objects', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
db.objectsCollection.remove({'_id' : objectName}, (err, removedCount) ->
|
||||
db.redisClient.del('objects', 'presets_hash', (err) ->
|
||||
throw err if err
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'PUT, DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if TAGS_REGEX.test(urlParts.pathname)
|
||||
r = TAGS_REGEX.exec(urlParts.pathname)
|
||||
deviceId = querystring.unescape(r[1])
|
||||
tag = querystring.unescape(r[2])
|
||||
if request.method == 'POST'
|
||||
db.devicesCollection.update({'_id' : deviceId}, {'$addToSet' : {'_tags' : tag}}, {safe: true}, (err) ->
|
||||
db.redisClient.del("#{deviceId}_presets_hash", (err) ->
|
||||
throw err if err
|
||||
)
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'PUT, DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if TAGS_REGEX.test(urlParts.pathname)
|
||||
r = TAGS_REGEX.exec(urlParts.pathname)
|
||||
deviceId = querystring.unescape(r[1])
|
||||
tag = querystring.unescape(r[2])
|
||||
if request.method == 'POST'
|
||||
db.devicesCollection.update({'_id' : deviceId}, {'$addToSet' : {'_tags' : tag}}, {safe: true}, (err) ->
|
||||
db.redisClient.del("#{deviceId}_presets_hash", (err) ->
|
||||
throw err if err
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
db.devicesCollection.update({'_id' : deviceId}, {'$pull' : {'_tags' : tag}}, {safe: true}, (err) ->
|
||||
db.redisClient.del("#{deviceId}_presets_hash", (err) ->
|
||||
throw err if err
|
||||
)
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
db.devicesCollection.update({'_id' : deviceId}, {'$pull' : {'_tags' : tag}}, {safe: true}, (err) ->
|
||||
db.redisClient.del("#{deviceId}_presets_hash", (err) ->
|
||||
throw err if err
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'POST, DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if DEVICE_TASKS_REGEX.test(urlParts.pathname)
|
||||
if request.method == 'POST'
|
||||
deviceId = querystring.unescape(DEVICE_TASKS_REGEX.exec(urlParts.pathname)[1])
|
||||
if body.length
|
||||
task = JSON.parse(body)
|
||||
task.device = deviceId
|
||||
db.getAliases((aliases) ->
|
||||
apiFunctions.insertTasks(task, aliases, (err) ->
|
||||
db.redisClient.del("#{deviceId}_presets_hash", (err) ->
|
||||
throw err if err
|
||||
)
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
|
||||
if urlParts.query.connection_request?
|
||||
apiFunctions.connectionRequest(deviceId, (err) ->
|
||||
if err
|
||||
response.writeHead(202)
|
||||
response.end()
|
||||
else
|
||||
apiFunctions.watchTask(task._id, config.DEVICE_ONLINE_THRESHOLD, (err) ->
|
||||
if err
|
||||
response.writeHead(202)
|
||||
response.end()
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
)
|
||||
else
|
||||
response.writeHead(202)
|
||||
response.end()
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'POST, DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if DEVICE_TASKS_REGEX.test(urlParts.pathname)
|
||||
if request.method == 'POST'
|
||||
deviceId = querystring.unescape(DEVICE_TASKS_REGEX.exec(urlParts.pathname)[1])
|
||||
if body.length
|
||||
task = JSON.parse(body)
|
||||
task.device = deviceId
|
||||
db.getAliases((aliases) ->
|
||||
apiFunctions.insertTasks(task, aliases, (err) ->
|
||||
db.redisClient.del("#{deviceId}_presets_hash", (err) ->
|
||||
throw err if err
|
||||
)
|
||||
)
|
||||
else if urlParts.query.connection_request?
|
||||
# no task, send connection request only
|
||||
apiFunctions.connectionRequest(deviceId, (err) ->
|
||||
if err
|
||||
response.writeHead 504
|
||||
response.end()
|
||||
return
|
||||
response.writeHead 200
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead(400)
|
||||
response.end()
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'POST'}
|
||||
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)
|
||||
r = TASKS_REGEX.exec(urlParts.pathname)
|
||||
taskId = mongodb.ObjectID(querystring.unescape(r[1]))
|
||||
action = try querystring.unescape(r[2])
|
||||
if not action? or action is '/'
|
||||
if request.method == 'DELETE'
|
||||
db.tasksCollection.remove({'_id' : taskId}, (err, removedCount) ->
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'PUT DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if action is '/retry'
|
||||
if request.method == 'POST'
|
||||
db.tasksCollection.update({_id : taskId}, {$unset : {fault : 1}, $set : {timestamp : new Date()}}, (err, count) ->
|
||||
# TODO need to invalidate presets hash for the device
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'POST'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else
|
||||
response.writeHead(404)
|
||||
response.end()
|
||||
else if FILES_REGEX.test(urlParts.pathname)
|
||||
filename = querystring.unescape(FILES_REGEX.exec(urlParts.pathname)[1])
|
||||
if request.method == 'PUT'
|
||||
metadata = {
|
||||
fileType : request.headers.filetype,
|
||||
manufacturer : request.headers.manufacturer,
|
||||
productClass : request.headers.productclass,
|
||||
version : request.headers.version,
|
||||
}
|
||||
|
||||
gs = new mongodb.GridStore(db.mongoDb, filename, 'w', {metadata : metadata})
|
||||
gs.open((err, gs) ->
|
||||
gs.write(body, (err, res) ->
|
||||
throw err if err
|
||||
gs.close((err) ->
|
||||
throw err if err
|
||||
response.writeHead(201)
|
||||
if urlParts.query.connection_request?
|
||||
apiFunctions.connectionRequest(deviceId, (err) ->
|
||||
if err
|
||||
response.writeHead(202)
|
||||
response.end()
|
||||
else
|
||||
apiFunctions.watchTask(task._id, config.DEVICE_ONLINE_THRESHOLD, (err) ->
|
||||
if err
|
||||
response.writeHead(202)
|
||||
response.end()
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
)
|
||||
else
|
||||
response.writeHead(202)
|
||||
response.end()
|
||||
)
|
||||
)
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
mongodb.GridStore.unlink(db.mongoDb, filename, (err) ->
|
||||
else if urlParts.query.connection_request?
|
||||
# no task, send connection request only
|
||||
apiFunctions.connectionRequest(deviceId, (err) ->
|
||||
if err
|
||||
response.writeHead 504
|
||||
response.end()
|
||||
return
|
||||
response.writeHead 200
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead(400)
|
||||
response.end()
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'POST'}
|
||||
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)
|
||||
r = TASKS_REGEX.exec(urlParts.pathname)
|
||||
taskId = mongodb.ObjectID(querystring.unescape(r[1]))
|
||||
action = try querystring.unescape(r[2])
|
||||
if not action? or action is '/'
|
||||
if request.method == 'DELETE'
|
||||
db.tasksCollection.remove({'_id' : taskId}, (err, removedCount) ->
|
||||
if err
|
||||
response.writeHead(500)
|
||||
response.end(err)
|
||||
return
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'PUT, DELETE'}
|
||||
response.writeHead 405, {'Allow': 'PUT DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if PING_REGEX.test(urlParts.pathname)
|
||||
host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1])
|
||||
require('child_process').exec("ping -w 1 -i 0.2 -c 3 #{host}", (err, stdout, stderr) ->
|
||||
if err?
|
||||
response.writeHead(404, {'Cache-Control' : 'no-cache'})
|
||||
else if action is '/retry'
|
||||
if request.method == 'POST'
|
||||
db.tasksCollection.update({_id : taskId}, {$unset : {fault : 1}, $set : {timestamp : new Date()}}, (err, count) ->
|
||||
# TODO need to invalidate presets hash for the device
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
return
|
||||
response.writeHead(200, {'Content-Type' : 'text/plain', 'Cache-Control' : 'no-cache'})
|
||||
response.end(stdout)
|
||||
)
|
||||
else if QUERY_REGEX.test(urlParts.pathname)
|
||||
collectionName = QUERY_REGEX.exec(urlParts.pathname)[1]
|
||||
if request.method not in ['GET', 'HEAD']
|
||||
response.writeHead 405, {'Allow' : 'GET, HEAD'}
|
||||
response.end('405 Method Not Allowed')
|
||||
return
|
||||
collection = db["#{collectionName}Collection"]
|
||||
if not collection?
|
||||
response.writeHead 404
|
||||
response.end('404 Not Found')
|
||||
return
|
||||
|
||||
func = (aliases) ->
|
||||
if urlParts.query.query?
|
||||
q = JSON.parse(urlParts.query.query)
|
||||
else
|
||||
q = {}
|
||||
q = query.expand(q, aliases) if collectionName is 'devices'
|
||||
|
||||
if urlParts.query.projection?
|
||||
projection = {}
|
||||
for p in urlParts.query.projection.split(',')
|
||||
p = p.trim()
|
||||
projection[p] = 1
|
||||
if collectionName is 'devices'
|
||||
for k,v of aliases
|
||||
if k == p or common.startsWith(k, "#{p}.")
|
||||
projection[a] = 1 for a in v
|
||||
|
||||
cur = collection.find(q, projection, {batchSize : 50})
|
||||
if urlParts.query.sort?
|
||||
s = JSON.parse(urlParts.query.sort)
|
||||
sort = {}
|
||||
for k,v of s
|
||||
if aliases[k]?
|
||||
sort[a] = v for a in aliases[k]
|
||||
else
|
||||
sort[k] = v
|
||||
cur.sort(sort)
|
||||
|
||||
cur.skip(parseInt(urlParts.query.skip)) if urlParts.query.skip?
|
||||
cur.limit(parseInt(urlParts.query.limit)) if urlParts.query.limit?
|
||||
cur.count((err, total) ->
|
||||
response.writeHead(200, {'Content-Type' : 'application/json', 'total' : total})
|
||||
if request.method is 'HEAD'
|
||||
response.end()
|
||||
return
|
||||
response.write("[\n")
|
||||
i = 0
|
||||
cur.each((err, item) ->
|
||||
if item is null
|
||||
response.end("\n]")
|
||||
else
|
||||
response.write(",\n") if i++
|
||||
apiFunctions.addAliases(item, aliases) if collectionName is 'devices'
|
||||
response.write(JSON.stringify(item))
|
||||
)
|
||||
)
|
||||
|
||||
if collectionName is 'devices'
|
||||
db.getAliases((aliases) ->
|
||||
func(aliases)
|
||||
)
|
||||
else
|
||||
func({})
|
||||
response.writeHead 405, {'Allow': 'POST'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else
|
||||
response.writeHead(404)
|
||||
response.end()
|
||||
else if FILES_REGEX.test(urlParts.pathname)
|
||||
filename = querystring.unescape(FILES_REGEX.exec(urlParts.pathname)[1])
|
||||
if request.method == 'PUT'
|
||||
metadata = {
|
||||
fileType : request.headers.filetype,
|
||||
manufacturer : request.headers.manufacturer,
|
||||
productClass : request.headers.productclass,
|
||||
version : request.headers.version,
|
||||
}
|
||||
|
||||
gs = new mongodb.GridStore(db.mongoDb, filename, 'w', {metadata : metadata})
|
||||
gs.open((err, gs) ->
|
||||
gs.write(body, (err, res) ->
|
||||
throw err if err
|
||||
gs.close((err) ->
|
||||
throw err if err
|
||||
response.writeHead(201)
|
||||
response.end()
|
||||
)
|
||||
)
|
||||
)
|
||||
else if request.method == 'DELETE'
|
||||
mongodb.GridStore.unlink(db.mongoDb, filename, (err) ->
|
||||
response.writeHead(200)
|
||||
response.end()
|
||||
)
|
||||
else
|
||||
response.writeHead 405, {'Allow': 'PUT, DELETE'}
|
||||
response.end('405 Method Not Allowed')
|
||||
else if PING_REGEX.test(urlParts.pathname)
|
||||
host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1])
|
||||
require('child_process').exec("ping -w 1 -i 0.2 -c 3 #{host}", (err, stdout, stderr) ->
|
||||
if err?
|
||||
response.writeHead(404, {'Cache-Control' : 'no-cache'})
|
||||
response.end()
|
||||
return
|
||||
response.writeHead(200, {'Content-Type' : 'text/plain', 'Cache-Control' : 'no-cache'})
|
||||
response.end(stdout)
|
||||
)
|
||||
else if QUERY_REGEX.test(urlParts.pathname)
|
||||
collectionName = QUERY_REGEX.exec(urlParts.pathname)[1]
|
||||
if request.method not in ['GET', 'HEAD']
|
||||
response.writeHead 405, {'Allow' : 'GET, HEAD'}
|
||||
response.end('405 Method Not Allowed')
|
||||
return
|
||||
collection = db["#{collectionName}Collection"]
|
||||
if not collection?
|
||||
response.writeHead 404
|
||||
response.end('404 Not Found')
|
||||
return
|
||||
|
||||
db.connect((err) ->
|
||||
throw err if err
|
||||
server.listen config.API_PORT, config.API_INTERFACE
|
||||
)
|
||||
func = (aliases) ->
|
||||
if urlParts.query.query?
|
||||
q = JSON.parse(urlParts.query.query)
|
||||
else
|
||||
q = {}
|
||||
q = query.expand(q, aliases) if collectionName is 'devices'
|
||||
|
||||
if urlParts.query.projection?
|
||||
projection = {}
|
||||
for p in urlParts.query.projection.split(',')
|
||||
p = p.trim()
|
||||
projection[p] = 1
|
||||
if collectionName is 'devices'
|
||||
for k,v of aliases
|
||||
if k == p or common.startsWith(k, "#{p}.")
|
||||
projection[a] = 1 for a in v
|
||||
|
||||
cur = collection.find(q, projection, {batchSize : 50})
|
||||
if urlParts.query.sort?
|
||||
s = JSON.parse(urlParts.query.sort)
|
||||
sort = {}
|
||||
for k,v of s
|
||||
if aliases[k]?
|
||||
sort[a] = v for a in aliases[k]
|
||||
else
|
||||
sort[k] = v
|
||||
cur.sort(sort)
|
||||
|
||||
cur.skip(parseInt(urlParts.query.skip)) if urlParts.query.skip?
|
||||
cur.limit(parseInt(urlParts.query.limit)) if urlParts.query.limit?
|
||||
cur.count((err, total) ->
|
||||
response.writeHead(200, {'Content-Type' : 'application/json', 'total' : total})
|
||||
if request.method is 'HEAD'
|
||||
response.end()
|
||||
return
|
||||
response.write("[\n")
|
||||
i = 0
|
||||
cur.each((err, item) ->
|
||||
if item is null
|
||||
response.end("\n]")
|
||||
else
|
||||
response.write(",\n") if i++
|
||||
apiFunctions.addAliases(item, aliases) if collectionName is 'devices'
|
||||
response.write(JSON.stringify(item))
|
||||
)
|
||||
)
|
||||
|
||||
if collectionName is 'devices'
|
||||
db.getAliases((aliases) ->
|
||||
func(aliases)
|
||||
)
|
||||
else
|
||||
func({})
|
||||
else
|
||||
response.writeHead 404
|
||||
response.end('404 Not Found')
|
||||
|
||||
|
||||
exports.listener = listener
|
||||
@@ -0,0 +1,47 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
config = require './config'
|
||||
db = require './db'
|
||||
|
||||
service = config.argv[0]
|
||||
if not service?
|
||||
throw new Error('Missing argument cwmp, fs, or nbi')
|
||||
|
||||
networkInterface = config["#{service.toUpperCase()}_INTERFACE"]
|
||||
port = config["#{service.toUpperCase()}_PORT"]
|
||||
useHttps = config["#{service.toUpperCase()}_SSL"]
|
||||
|
||||
listener = require("./#{service}").listener
|
||||
|
||||
if useHttps
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
httpsKey = path.resolve(config.CONFIG_DIR, "#{service}.key")
|
||||
httpsCert = path.resolve(config.CONFIG_DIR, "#{service}.crt")
|
||||
options = {
|
||||
key: fs.readFileSync(httpsKey),
|
||||
cert: fs.readFileSync(httpsCert)
|
||||
}
|
||||
server = require('https').createServer(options, listener)
|
||||
else
|
||||
server = require('http').createServer(listener)
|
||||
|
||||
db.connect((err) ->
|
||||
throw err if err
|
||||
server.listen(port, networkInterface)
|
||||
)
|
||||
Reference in new issue
Block a user