Merge branch 'future' into agent_future
[contrib/cloudeebus.git] / cloudeebus / cloudeebus.py
1 #!/usr/bin/env python
2
3 # Cloudeebus
4 #
5 # Copyright 2012 Intel Corporation.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19 # Luc Yriarte <luc.yriarte@intel.com>
20 # Christophe Guiraud <christophe.guiraud@intel.com>
21 # Frederic Paut <frederic.paut@intel.com>
22 #
23
24
25 import argparse, dbus, json, sys
26
27 from twisted.internet import glib2reactor
28 # Configure the twisted mainloop to be run inside the glib mainloop.
29 # This must be done before importing the other twisted modules
30 glib2reactor.install()
31 from twisted.internet import reactor, defer
32
33 from autobahn.websocket import listenWS
34 from autobahn.wamp import exportRpc, WampServerFactory, WampCraServerProtocol
35
36 from dbus.mainloop.glib import DBusGMainLoop
37
38 import gobject
39 import re
40 import dbus.service
41 gobject.threads_init()
42
43 from dbus import glib
44 glib.init_threads()
45
46 # enable debug log
47 from twisted.python import log
48
49 # XML parser module
50 from xml.etree.ElementTree import XMLParser
51
52 ###############################################################################
53
54 VERSION = "0.5.0"
55 OPENDOOR = False
56 CREDENTIALS = {}
57 WHITELIST = []
58 NETMASK =  []
59
60 ###############################################################################
61 def ipV4ToHex(mask):
62     ## Convert an ip or an IP mask (such as ip/24 or ip/255.255.255.0) in hex value (32bits)
63     maskHex = 0
64     byte = 0
65     if mask.rfind(".") == -1:
66         if (int(mask) < 32):
67             maskHex = (2**(int(mask))-1)
68             maskHex = maskHex << (32-int(mask))
69         else:
70             raise Exception("Illegal mask (larger than 32 bits) " + mask)
71     else:
72         maskField = mask.split(".")
73         # Check if mask has four fields (byte)
74         if len(maskField) != 4:
75             raise Exception("Illegal ip address / mask (should be 4 bytes) " + mask)
76         for maskQuartet in maskField:
77             byte = int(maskQuartet)
78             # Check if each field is really a byte
79             if byte > 255:
80                 raise Exception("Illegal ip address / mask (digit larger than a byte) " + mask)              
81             maskHex += byte
82             maskHex = maskHex << 8
83         maskHex = maskHex >> 8
84     return maskHex
85
86 ###############################################################################
87 class DbusCache:
88     '''
89     Global cache of DBus connexions and signal handlers
90     '''
91     def __init__(self):
92         self.dbusConnexions = {}
93         self.signalHandlers = {}
94
95
96     def reset(self):
97         '''
98         Disconnect signal handlers before resetting cache.
99         '''
100         self.dbusConnexions = {}
101         # disconnect signal handlers
102         for key in self.signalHandlers:
103             self.signalHandlers[key].disconnect()
104         self.signalHandlers = {}
105
106
107     def dbusConnexion(self, busName):
108         if not self.dbusConnexions.has_key(busName):
109             if busName == "session":
110                 self.dbusConnexions[busName] = dbus.SessionBus()
111             elif busName == "system":
112                 self.dbusConnexions[busName] = dbus.SystemBus()
113             else:
114                 raise Exception("Error: invalid bus: %s" % busName)
115         return self.dbusConnexions[busName]
116
117
118
119 ###############################################################################
120 class DbusSignalHandler:
121     '''
122     signal hash id as busName#senderName#objectName#interfaceName#signalName
123     '''
124     def __init__(self, busName, senderName, objectName, interfaceName, signalName):
125         self.id = "#".join([busName, senderName, objectName, interfaceName, signalName])
126         # connect handler to signal
127         self.bus = cache.dbusConnexion(busName)
128         self.bus.add_signal_receiver(self.handleSignal, signalName, interfaceName, senderName, objectName)
129         
130     
131     def disconnect(self):
132         names = self.id.split("#")
133         self.bus.remove_signal_receiver(self.handleSignal, names[4], names[3], names[1], names[2])
134
135
136     def handleSignal(self, *args):
137         '''
138         publish dbus args under topic hash id
139         '''
140         factory.dispatch(self.id, json.dumps(args))
141
142
143
144 ###############################################################################
145 class DbusCallHandler:
146     '''
147     deferred reply to return dbus results
148     '''
149     def __init__(self, method, args):
150         self.pending = False
151         self.request = defer.Deferred()
152         self.method = method
153         self.args = args
154
155
156     def callMethod(self):
157         '''
158         dbus method async call
159         '''
160         self.pending = True
161         self.method(*self.args, reply_handler=self.dbusSuccess, error_handler=self.dbusError)
162         return self.request
163
164
165     def dbusSuccess(self, *result):
166         '''
167         return JSON string result array
168         '''
169         self.request.callback(json.dumps(result))
170         self.pending = False
171
172
173     def dbusError(self, error):
174         '''
175         return dbus error message
176         '''
177         self.request.errback(Exception(error.get_dbus_message()))
178         self.pending = False
179
180
181
182 ################################################################################       
183 class ExecCode:
184     '''
185     Execute DynDBusClass generated code
186     '''
187     def __init__(self, globalCtx, localCtx) :
188         self.exec_string = ""
189         self.exec_code = None
190         self.exec_code_valid = 1
191         self.indent_level = 0
192         self.indent_increment = 1
193         self.line = 0
194         self.localCtx = localCtx
195         self.globalCtx = globalCtx
196         
197
198     def append_stmt(self, stmt) :
199         self.exec_code_valid = 0
200         self.line += 1
201         for x in range(0,self.indent_level):
202             self.exec_string = self.exec_string + ' '            
203         self.exec_string = self.exec_string + stmt + '\n'
204
205     def indent(self) :
206         self.indent_level = self.indent_level + self.indent_increment
207
208     def dedent(self) :
209         self.indent_level = self.indent_level - self.indent_increment
210     
211     # compile : Compile exec_string into exec_code using the builtin
212     # compile function. Skip if already in sync.
213     def compile(self) :
214         if not self.exec_code_valid :
215             self.exec_code = compile(self.exec_string, "<string>", "exec")
216         self.exec_code_valid = True
217
218     def execute(self) :
219         if not self.exec_code_valid :
220             self.compile()
221         exec(self.exec_code, self.globalCtx, self.localCtx)
222
223
224
225 ################################################################################       
226 class XmlCbParser: # The target object of the parser
227     maxDepth = 0
228     depth = 0
229     def __init__(self, dynDBusClass):
230         self.dynDBusClass = dynDBusClass
231         
232     def start(self, tag, attrib):   # Called for each opening tag.
233         if (tag == 'node'):
234             return
235         # Set interface name
236         if (tag == 'interface'):
237             self.dynDBusClass.set_interface(attrib['name'])
238             return
239         # Set method name
240         if (tag == 'method'):
241             self.current = tag
242             self.dynDBusClass.def_method(attrib['name'])
243             return
244         if (tag == 'signal'):
245             self.current = tag
246             self.dynDBusClass.def_signal(attrib['name'])
247             return
248
249         # Set signature (in/out & name) for method
250         if (tag == 'arg'):
251             if (self.current == 'method'):
252                 if (attrib.has_key('direction') == False):
253                     attrib['direction'] = "in"
254                 self.dynDBusClass.add_signature(attrib['name'],
255                                                 attrib['direction'],
256                                                 attrib['type'])
257                 return
258             if (self.current == 'signal'):
259                 if (attrib.has_key('name') == False):
260                     attrib['name'] = 'value'
261                 self.dynDBusClass.add_signature(attrib['name'], 'in',
262                                                 attrib['type'])
263                 return
264     def end(self, tag):             # Called for each closing tag.
265         if (tag == 'method'):
266             self.dynDBusClass.add_dbus_method()
267             self.dynDBusClass.add_body_method()
268             self.dynDBusClass.end_method()
269         if (tag == 'signal'):
270             self.dynDBusClass.add_dbus_signal()
271             self.dynDBusClass.add_body_signal()
272             self.dynDBusClass.end_method()
273            
274     def data(self, data):
275         pass            # We do not need to do anything with data.
276     def close(self):    # Called when all data has been parsed.
277         return self.maxDepth
278
279
280        
281 ################################################################################       
282 class DynDBusClass():
283     def __init__(self, className, globalCtx, localCtx):
284         self.className = className
285         self.xmlCB = XmlCbParser(self)
286         self.signature = {}
287         self.class_code = ExecCode(globalCtx, localCtx)  
288         self.class_code.indent_increment = 4
289         self.class_code.append_stmt("import dbus")
290         self.class_code.append_stmt("\n")
291         self.class_code.append_stmt("class " + self.className + "(dbus.service.Object):")
292         self.class_code.indent()
293         
294         ## Overload of __init__ method 
295         self.def_method("__init__")
296         self.add_method("bus, callback=None, objPath='/sample', busName='org.cloudeebus'")
297         self.add_stmt("self.bus = bus")
298         self.add_stmt("self.objPath = objPath")
299         self.add_stmt("self.callback = callback")        
300         self.add_stmt("dbus.service.Object.__init__(self, conn=bus, bus_name=busName)")
301         self.end_method()
302                
303         ## Create 'add_to_connection' method 
304         self.def_method("add_to_connection")
305         self.add_method("connection=None, path=None")
306         self.add_stmt("dbus.service.Object.add_to_connection(self, connection=self.bus, path=self.objPath)")
307         self.end_method()
308                
309         ## Create 'remove_from_connection' method 
310         self.def_method("remove_from_connection")
311         self.add_method("connection=None, path=None")
312         self.add_stmt("dbus.service.Object.remove_from_connection(self, connection=None, path=self.objPath)")
313         self.end_method()
314                
315     def createDBusServiceFromXML(self, xml):
316         self.parser = XMLParser(target=self.xmlCB)
317         self.parser.feed(xml)
318         self.parser.close()
319     
320     def set_interface(self, ifName):
321         self.ifName = ifName
322         
323     def def_method(self, methodName):
324         self.methodToAdd = methodName
325         self.signalToAdd = None
326         self.args_str = str()
327         self.signature = {}
328         self.signature['name'] = str()
329         self.signature['in'] = str()                
330         self.signature['out'] = str()                        
331
332     def def_signal(self, signalName):
333         self.methodToAdd = None
334         self.signalToAdd = signalName
335         self.args_str = str()
336         self.signature = {}
337         self.signature['name'] = str()
338         self.signature['in'] = str()                
339         self.signature['out'] = str()                        
340
341     def add_signature(self, name, direction, signature):
342         if (direction == 'in'):
343             self.signature['in'] += signature
344             if (self.signature['name'] != str()):
345                 self.signature['name'] += ", "
346             self.signature['name'] += name
347         if (direction == 'out'):
348             self.signature['out'] = signature                        
349         
350     def add_method(self, args = None, async_success_cb = None, async_err_cb = None):
351         async_cb_str = str()
352         if (self.methodToAdd != None):
353             name = self.methodToAdd
354         else:
355             name = self.signalToAdd
356         if (args != None):
357             self.args_str = args
358         if (async_success_cb != None):
359             async_cb_str = async_success_cb
360         if (async_err_cb != None):
361             if (async_cb_str != str()):
362                 async_cb_str += ", "
363             async_cb_str += async_err_cb
364                         
365         parameters = self.args_str
366         if (async_cb_str != str()):
367             if (parameters != str()):
368                 parameters += ", "
369             parameters +=async_cb_str       
370         
371         if (parameters != str()):
372             self.class_code.append_stmt("def " + name + "(self, %s):" % parameters)               
373         else:
374             self.class_code.append_stmt("def " + name + "(self):")
375         self.class_code.indent()
376         
377     def end_method(self):
378         self.class_code.append_stmt("\n")
379         self.class_code.dedent()
380         
381     def add_dbus_method(self):
382         decorator = '@dbus.service.method("' + self.ifName + '"'
383         if (self.signature.has_key('in') and self.signature['in'] != str()):
384                 decorator += ", in_signature='" + self.signature['in'] + "'"
385         if (self.signature.has_key('out') and self.signature['out'] != str()):
386                 decorator += ", out_signature='" + self.signature['out'] + "'"
387         decorator += ", async_callbacks=('dbus_async_cb', 'dbus_async_err_cb')"            
388         decorator += ")"
389         self.class_code.append_stmt(decorator)
390         if (self.signature.has_key('name') and self.signature['name'] != str()):
391             self.add_method(self.signature['name'], async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
392         else:
393             self.add_method(async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
394
395     def add_dbus_signal(self):
396         decorator = '@dbus.service.signal("' + self.ifName + '"'
397         if (self.signature.has_key('in') and self.signature['in'] != str()):
398                 decorator += ", signature='" + self.signature['in'] + "'"
399         decorator += ")"            
400         self.class_code.append_stmt(decorator)
401         if (self.signature.has_key('name') and self.signature['name'] != str()):
402             self.add_method(self.signature['name'])
403         else:
404             self.add_method()
405
406     def add_body_method(self):
407         if (self.methodToAdd != None):
408             if (self.args_str != str()):
409                 self.class_code.append_stmt("self.callback('" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb, %s)" % self.args_str)
410             else:        
411                 self.class_code.append_stmt("self.callback('" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb)")
412
413     def add_body_signal(self):
414         self.class_code.append_stmt("return") ## TODO: Remove and fix with code ad hoc
415         self.class_code.append_stmt("\n")
416
417     def add_stmt(self, stmt) :
418         self.class_code.append_stmt(stmt)
419         
420     def declare(self) :
421         self.class_code.execute()
422
423
424
425 ###############################################################################
426 class CloudeebusService:
427     '''
428     support for sending DBus messages and registering for DBus signals
429     '''
430     def __init__(self, permissions):
431         self.permissions = {};
432         self.permissions['permissions'] = permissions['permissions']
433         self.permissions['authextra'] = permissions['authextra']
434         self.proxyObjects = {}
435         self.proxyMethods = {}
436         self.pendingCalls = []
437         self.dynDBusClasses = {} # DBus class source code generated dynamically (a list because one by classname)
438         self.services = {}  # DBus service created
439         self.serviceAgents = {} # Instantiated DBus class previously generated dynamically, for now, one by classname
440         self.servicePendingCalls = {} # JS methods called (and waiting for a Success/error response), containing 'methodId', (successCB, errorCB)
441         self.localCtx = locals()
442         self.globalCtx = globals()
443
444
445     def proxyObject(self, busName, serviceName, objectName):
446         '''
447         object hash id as busName#serviceName#objectName
448         '''
449         id = "#".join([busName, serviceName, objectName])
450         if not self.proxyObjects.has_key(id):
451             if not OPENDOOR:
452                 # check permissions, array.index throws exception
453                 self.permissions['permissions'].index(serviceName)
454             bus = cache.dbusConnexion(busName)
455             self.proxyObjects[id] = bus.get_object(serviceName, objectName)
456         return self.proxyObjects[id]
457
458
459     def proxyMethod(self, busName, serviceName, objectName, interfaceName, methodName):
460         '''
461         method hash id as busName#serviceName#objectName#interfaceName#methodName
462         '''
463         id = "#".join([busName, serviceName, objectName, interfaceName, methodName])
464         if not self.proxyMethods.has_key(id):
465             obj = self.proxyObject(busName, serviceName, objectName)
466             self.proxyMethods[id] = obj.get_dbus_method(methodName, interfaceName)
467         return self.proxyMethods[id]
468
469
470     @exportRpc
471     def dbusRegister(self, list):
472         '''
473         arguments: bus, sender, object, interface, signal
474         '''
475         if len(list) < 5:
476             raise Exception("Error: expected arguments: bus, sender, object, interface, signal)")
477         
478         if not OPENDOOR:
479             # check permissions, array.index throws exception
480             self.permissions['permissions'].index(list[1])
481         
482         # check if a handler exists
483         sigId = "#".join(list)
484         if cache.signalHandlers.has_key(sigId):
485             return sigId
486         
487         # create a handler that will publish the signal
488         dbusSignalHandler = DbusSignalHandler(*list)
489         cache.signalHandlers[sigId] = dbusSignalHandler
490         
491         return dbusSignalHandler.id
492
493
494     @exportRpc
495     def dbusSend(self, list):
496         '''
497         arguments: bus, destination, object, interface, message, [args]
498         '''
499         # clear pending calls
500         for call in self.pendingCalls:
501             if not call.pending:
502                 self.pendingCalls.remove(call)
503         
504         if len(list) < 5:
505             raise Exception("Error: expected arguments: bus, destination, object, interface, message, [args])")
506         
507         # parse JSON arg list
508         args = []
509         if len(list) == 6:
510             args = json.loads(list[5])
511         
512         # get dbus proxy method
513         method = self.proxyMethod(*list[0:5])
514         
515         # use a deferred call handler to manage dbus results
516         dbusCallHandler = DbusCallHandler(method, args)
517         self.pendingCalls.append(dbusCallHandler)
518         return dbusCallHandler.callMethod()
519
520
521     @exportRpc
522     def emitSignal(self, list):
523         '''
524         arguments: agentObjectPath, signalName, result (to emit)
525         '''
526         objectPath = list[0]
527         className = re.sub('/', '_', objectPath[1:])
528         signalName = list[1]
529         result = list[2]
530         if (self.serviceAgents.has_key(className) == True):
531             exe_str = "self.serviceAgents['"+ className +"']."+ signalName + "(" + str(result) + ")"
532             eval(exe_str, self.globalCtx, self.localCtx)
533         else:
534             raise Exception("No object path " + objectPath)
535
536     @exportRpc
537     def returnMethod(self, list):
538         '''
539         arguments: methodId, callIndex, success (=true, error otherwise), result (to return)
540         '''
541         methodId = list[0]
542         callIndex = list[1]
543         success = list[2]
544         result = list[3]
545         if (self.servicePendingCalls.has_key(methodId)):
546             cb = self.servicePendingCalls[methodId]['calls'][callIndex]
547             if cb is None:
548                 raise Exception("No pending call " + str(callIndex) + " for methodID " + methodId)
549             if (success):                
550                 successCB = cb["successCB"]
551                 if (result != None):
552                     successCB(result)
553                 else:
554                     successCB()                    
555             else:     
556                 errorCB = cb["errorCB"]        
557                 if (result != None):
558                     errorCB(result)
559                 else:
560                     errorCB()
561             self.servicePendingCalls[methodId]['calls'][callIndex] = None
562             self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] - 1
563             if self.servicePendingCalls[methodId]['count'] == 0:
564                 del self.servicePendingCalls[methodId]
565         else:
566             raise Exception("No methodID " + methodId)
567
568     def srvCB(self, name, objPath, ifName, async_succes_cb, async_error_cb, *args):
569         methodId = self.srvName + "#" + objPath + "#" + ifName + "#" + name
570         cb = { 'successCB': async_succes_cb, 
571                'errorCB': async_error_cb}
572         if methodId not in self.servicePendingCalls:
573             self.servicePendingCalls[methodId] = {'count': 0, 'calls': []}
574         pendingCallStr = json.dumps({'callIndex': len(self.servicePendingCalls[methodId]['calls']), 'args': args})
575         self.servicePendingCalls[methodId]['calls'].append(cb)
576         self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] + 1
577         factory.dispatch(methodId, pendingCallStr)
578                     
579     @exportRpc
580     def serviceAdd(self, list):
581         '''
582         arguments: busName, srvName
583         '''
584         busName = list[0]
585         self.bus =  cache.dbusConnexion( busName['name'] )
586         self.srvName = list[1]
587         if (self.services.has_key(self.srvName) == False):            
588             self.services[self.srvName] = dbus.service.BusName(name = self.srvName, bus = self.bus)
589         return self.srvName
590
591     @exportRpc
592     def serviceRelease(self, list):
593         '''
594         arguments: busName, srvName
595         '''
596         self.srvName = list[0]
597         if (self.services.has_key(self.srvName) == True):
598             self.services.pop(self.srvName)
599             return self.srvName
600         else:
601             raise Exception(self.srvName + " do not exist")
602                    
603     @exportRpc
604     def serviceAddAgent(self, list):
605         '''
606         arguments: objectPath, xmlTemplate
607         '''
608         self.agentObjectPath = list[0]
609         xmlTemplate = list[1]
610         self.className = re.sub('/', '_', self.agentObjectPath[1:])
611         if (self.dynDBusClasses.has_key(self.className) == False):
612             self.dynDBusClasses[self.className] = DynDBusClass(self.className, self.globalCtx, self.localCtx)
613             self.dynDBusClasses[self.className].createDBusServiceFromXML(xmlTemplate)
614             self.dynDBusClasses[self.className].declare()
615
616         ## Class already exist, instanciate it if not already instanciated
617         if (self.serviceAgents.has_key(self.className) == False):
618             self.serviceAgents[self.className] = eval(self.className + "(self.bus, callback=self.srvCB, objPath=self.agentObjectPath, busName=self.srvName)", self.globalCtx, self.localCtx)
619             
620         self.serviceAgents[self.className].add_to_connection()
621         return (self.agentObjectPath)
622                     
623     @exportRpc
624     def serviceDelAgent(self, list):
625         '''
626         arguments: objectPath, xmlTemplate
627         '''
628         agentObjectPath = list[0]
629         className = re.sub('/', '_', agentObjectPath[1:])
630
631         if (self.serviceAgents.has_key(className)):
632             self.serviceAgents[self.className].remove_from_connection()
633             self.serviceAgents.pop(self.className)
634         else:
635             raise Exception(agentObjectPath + " doesn't exist!")
636         
637         return (agentObjectPath)
638                     
639     @exportRpc
640     def getVersion(self):
641         '''
642         return current version string
643         '''
644         return VERSION
645
646
647
648 ###############################################################################
649 class CloudeebusServerProtocol(WampCraServerProtocol):
650     '''
651     connexion and session authentication management
652     '''
653     
654     def onSessionOpen(self):
655         # CRA authentication options
656         self.clientAuthTimeout = 0
657         self.clientAuthAllowAnonymous = OPENDOOR
658         # CRA authentication init
659         WampCraServerProtocol.onSessionOpen(self)
660     
661     
662     def getAuthPermissions(self, key, extra):
663          return {'permissions': extra.get("permissions", None),
664                  'authextra': extra.get("authextra", None)}   
665     
666     def getAuthSecret(self, key):
667         secret = CREDENTIALS.get(key, None)
668         if secret is None:
669             return None
670         # secret must be of str type to be hashed
671         return str(secret)
672     
673
674     def onAuthenticated(self, key, permissions):
675         if not OPENDOOR:
676             # check net filter
677             if NETMASK != []:
678                 ipAllowed = False
679                 for netfilter in NETMASK:
680                     ipHex=ipV4ToHex(self.peer.host)
681                     ipAllowed = (ipHex & netfilter['mask']) == netfilter['ipAllowed'] & netfilter['mask']
682                     if ipAllowed:
683                         break
684                 if not ipAllowed:
685                     raise Exception("host " + self.peer.host + " is not allowed!")
686             # check authentication key
687             if key is None:
688                 raise Exception("Authentication failed")
689             # check permissions, array.index throws exception
690             for req in permissions['permissions']:
691                     WHITELIST.index(req);
692         # create cloudeebus service instance
693         self.cloudeebusService = CloudeebusService(permissions)
694         # register it for RPC
695         self.registerForRpc(self.cloudeebusService)
696         # register for Publish / Subscribe
697         self.registerForPubSub("", True)
698     
699     
700     def connectionLost(self, reason):
701         WampCraServerProtocol.connectionLost(self, reason)
702         if factory.getConnectionCount() == 0:
703             cache.reset()
704
705
706
707 ###############################################################################
708
709 if __name__ == '__main__':
710     
711     cache = DbusCache()
712
713     parser = argparse.ArgumentParser(description='Javascript DBus bridge.')
714     parser.add_argument('-v', '--version', action='store_true', 
715         help='print version and exit')
716     parser.add_argument('-d', '--debug', action='store_true', 
717         help='log debug info on standard output')
718     parser.add_argument('-o', '--opendoor', action='store_true',
719         help='allow anonymous access to all services')
720     parser.add_argument('-p', '--port', default='9000',
721         help='port number')
722     parser.add_argument('-c', '--credentials',
723         help='path to credentials file')
724     parser.add_argument('-w', '--whitelist',
725         help='path to whitelist file')
726     parser.add_argument('-n', '--netmask',
727         help='netmask,IP filter (comma separated.) eg. : -n 127.0.0.1,192.168.2.0/24,10.12.16.0/255.255.255.0')
728     
729     args = parser.parse_args(sys.argv[1:])
730
731     if args.version:
732         print("Cloudeebus version " + VERSION)
733         exit(0)
734     
735     if args.debug:
736         log.startLogging(sys.stdout)
737     
738     OPENDOOR = args.opendoor
739     
740     if args.credentials:
741         jfile = open(args.credentials)
742         CREDENTIALS = json.load(jfile)
743         jfile.close()
744     
745     if args.whitelist:
746         jfile = open(args.whitelist)
747         WHITELIST = json.load(jfile)
748         jfile.close()
749         
750     if args.netmask:
751         iplist = args.netmask.split(",")
752         for ip in iplist:
753             if ip.rfind("/") != -1:
754                 ip=ip.split("/")
755                 ipAllowed = ip[0]
756                 mask = ip[1]
757             else:
758                 ipAllowed = ip
759                 mask = "255.255.255.255" 
760             NETMASK.append( {'ipAllowed': ipV4ToHex(ipAllowed), 'mask' : ipV4ToHex(mask)} )
761     
762     uri = "ws://localhost:" + args.port
763     
764     factory = WampServerFactory(uri, debugWamp = args.debug)
765     factory.protocol = CloudeebusServerProtocol
766     factory.setProtocolOptions(allowHixie76 = True)
767     
768     listenWS(factory)
769     
770     DBusGMainLoop(set_as_default=True)
771     
772     reactor.run()