Applying JSON arguments dbus types parsing to cloudeebus engine.
[contrib/cloudeebus.git] / cloudeebus / cloudeebusengine.py
1 # Cloudeebus
2 #
3 # Copyright 2012 Intel Corporation.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 # Luc Yriarte <luc.yriarte@intel.com>
18 # Christophe Guiraud <christophe.guiraud@intel.com>
19 # Frederic Paut <frederic.paut@intel.com>
20 #
21
22 import dbus
23 import dbus.service
24 import os
25 import re
26 import json
27
28 # enable debug log
29 from twisted.python import log
30
31 # XML parser module
32 from xml.etree.ElementTree import XMLParser
33
34 from twisted.internet import defer
35
36 # The user of cloudeebusengine.py must set this to some object
37 # providing a dispatch(topicUri, event) method as in WampServerFactory
38 factory = None
39
40 if os.environ.get('CLOUDEEBUS_XWALK', False):
41     # Same approach as in autobahn.wamp: add the method name to
42     # decorated methods, which then gets used to identify the methods
43     # that can be called from remote.
44     def exportRpc(arg):
45         arg._xwalk_rpc_id = arg.__name__
46         return arg
47 else:
48     from autobahn.wamp import exportRpc
49
50 VERSION = "0.6.0"
51 OPENDOOR = False
52 SERVICELIST = []
53
54 ###############################################################################
55 class DbusCache:
56     '''
57     Global cache of DBus connexions and signal handlers
58     '''
59     def __init__(self):
60         self.dbusConnexions = {}
61         self.signalHandlers = {}
62
63
64     def reset(self):
65         '''
66         Disconnect signal handlers before resetting cache.
67         '''
68         self.dbusConnexions = {}
69         # disconnect signal handlers
70         for key in self.signalHandlers:
71             self.signalHandlers[key].disconnect()
72         self.signalHandlers = {}
73
74
75     def dbusConnexion(self, busName):
76         if not self.dbusConnexions.has_key(busName):
77             if busName == "session":
78                 self.dbusConnexions[busName] = dbus.SessionBus()
79             elif busName == "system":
80                 self.dbusConnexions[busName] = dbus.SystemBus()
81             else:
82                 raise Exception("Error: invalid bus: %s" % busName)
83         return self.dbusConnexions[busName]
84
85 cache = DbusCache()
86
87
88 ###############################################################################
89 class DbusSignalHandler:
90     '''
91     signal hash id as busName#senderName#objectName#interfaceName#signalName
92     '''
93     def __init__(self, busName, senderName, objectName, interfaceName, signalName):
94         self.id = "#".join([busName, senderName, objectName, interfaceName, signalName])
95         # connect handler to signal
96         self.bus = cache.dbusConnexion(busName)
97         self.bus.add_signal_receiver(self.handleSignal, signalName, interfaceName, senderName, objectName)
98         
99     
100     def disconnect(self):
101         names = self.id.split("#")
102         self.bus.remove_signal_receiver(self.handleSignal, names[4], names[3], names[1], names[2])
103
104
105     def handleSignal(self, *args):
106         '''
107         publish dbus args under topic hash id
108         '''
109         factory.dispatch(self.id, json.dumps(args))
110
111
112
113 ###############################################################################
114 class DbusCallHandler:
115     '''
116     deferred reply to return dbus results
117     '''
118     def __init__(self, method, args):
119         self.pending = False
120         self.request = defer.Deferred()
121         self.method = method
122         self.args = args
123
124
125     def callMethod(self):
126         '''
127         dbus method async call
128         '''
129         self.pending = True
130         self.method(*self.args, reply_handler=self.dbusSuccess, error_handler=self.dbusError)
131         return self.request
132
133
134     def dbusSuccess(self, *result):
135         '''
136         return JSON string result array
137         '''
138         self.request.callback(json.dumps(result))
139         self.pending = False
140
141
142     def dbusError(self, error):
143         '''
144         return dbus error message
145         '''
146         self.request.errback(Exception(error.get_dbus_message()))
147         self.pending = False
148
149
150
151 ################################################################################       
152 class ExecCode:
153     '''
154     Execute DynDBusClass generated code
155     '''
156     def __init__(self, globalCtx, localCtx) :
157         self.exec_string = ""
158         self.exec_code = None
159         self.exec_code_valid = 1
160         self.indent_level = 0
161         self.indent_increment = 1
162         self.line = 0
163         self.localCtx = localCtx
164         self.globalCtx = globalCtx
165         
166
167     def append_stmt(self, stmt) :
168         self.exec_code_valid = 0
169         self.line += 1
170         for x in range(0,self.indent_level):
171             self.exec_string = self.exec_string + ' '            
172         self.exec_string = self.exec_string + stmt + '\n'
173
174     def indent(self) :
175         self.indent_level = self.indent_level + self.indent_increment
176
177     def dedent(self) :
178         self.indent_level = self.indent_level - self.indent_increment
179     
180     # compile : Compile exec_string into exec_code using the builtin
181     # compile function. Skip if already in sync.
182     def compile(self) :
183         if not self.exec_code_valid :
184             self.exec_code = compile(self.exec_string, "<string>", "exec")
185         self.exec_code_valid = True
186
187     def execute(self) :
188         if not self.exec_code_valid :
189             self.compile()
190         exec(self.exec_code, self.globalCtx, self.localCtx)
191
192
193
194 ################################################################################       
195 class XmlCbParser: # The target object of the parser
196     maxDepth = 0
197     depth = 0
198     def __init__(self, dynDBusClass):
199         self.dynDBusClass = dynDBusClass
200         
201     def start(self, tag, attrib):   # Called for each opening tag.
202         if (tag == 'node'):
203             return
204         # Set interface name
205         if (tag == 'interface'):
206             self.dynDBusClass.set_interface(attrib['name'])
207             return
208         # Set method name
209         if (tag == 'method'):
210             self.current = tag
211             self.dynDBusClass.def_method(attrib['name'])
212             return
213         if (tag == 'signal'):
214             self.current = tag
215             self.dynDBusClass.def_signal(attrib['name'])
216             return
217
218         # Set signature (in/out & name) for method
219         if (tag == 'arg'):
220             if (self.current == 'method'):
221                 if (attrib.has_key('direction') == False):
222                     attrib['direction'] = "in"
223                 self.dynDBusClass.add_signature(attrib['name'],
224                                                 attrib['direction'],
225                                                 attrib['type'])
226                 return
227             if (self.current == 'signal'):
228                 if (attrib.has_key('name') == False):
229                     attrib['name'] = 'value'
230                 self.dynDBusClass.add_signature(attrib['name'], 'in',
231                                                 attrib['type'])
232                 return
233     def end(self, tag):             # Called for each closing tag.
234         if (tag == 'method'):
235             self.dynDBusClass.add_dbus_method()
236             self.dynDBusClass.add_body_method()
237             self.dynDBusClass.end_method()
238         if (tag == 'signal'):
239             self.dynDBusClass.add_dbus_signal()
240             self.dynDBusClass.add_body_signal()
241             self.dynDBusClass.end_method()
242            
243     def data(self, data):
244         pass            # We do not need to do anything with data.
245     def close(self):    # Called when all data has been parsed.
246         return self.maxDepth
247
248
249        
250 ###############################################################################
251 def createClassName(objectPath):
252     return re.sub('/', '_', objectPath[1:])
253
254 ################################################################################       
255 class DynDBusClass():
256     def __init__(self, className, globalCtx, localCtx):
257         self.xmlCB = XmlCbParser(self)
258         self.signature = {}
259         self.class_code = ExecCode(globalCtx, localCtx)  
260         self.class_code.indent_increment = 4
261         self.class_code.append_stmt("import dbus")
262         self.class_code.append_stmt("\n")
263         self.class_code.append_stmt("class " + className + "(dbus.service.Object):")
264         self.class_code.indent()
265         
266         ## Overload of __init__ method 
267         self.def_method("__init__")
268         self.add_method("bus, callback=None, objPath='/sample', srvName='org.cloudeebus'")
269         self.add_stmt("self.bus = bus")
270         self.add_stmt("self.objPath = objPath")
271         self.add_stmt("self.srvName = srvName")        
272         self.add_stmt("self.callback = callback")        
273         self.add_stmt("dbus.service.Object.__init__(self, conn=bus, bus_name=srvName)")
274         self.end_method()
275                
276         ## Create 'add_to_connection' method 
277         self.def_method("add_to_connection")
278         self.add_method("connection=None, path=None")
279         self.add_stmt("dbus.service.Object.add_to_connection(self, connection=self.bus, path=self.objPath)")
280         self.end_method()
281                
282         ## Create 'remove_from_connection' method 
283         self.def_method("remove_from_connection")
284         self.add_method("connection=None, path=None")
285         self.add_stmt("dbus.service.Object.remove_from_connection(self, connection=None, path=self.objPath)")
286         self.end_method()
287                
288     def createDBusServiceFromXML(self, xml):
289         self.parser = XMLParser(target=self.xmlCB)
290         self.parser.feed(xml)
291         self.parser.close()
292     
293     def set_interface(self, ifName):
294         self.ifName = ifName
295         
296     def def_method(self, methodName):
297         self.methodToAdd = methodName
298         self.signalToAdd = None
299         self.args_str = str()
300         self.signature = {}
301         self.signature['name'] = str()
302         self.signature['in'] = str()                
303         self.signature['out'] = str()                        
304
305     def def_signal(self, signalName):
306         self.methodToAdd = None
307         self.signalToAdd = signalName
308         self.args_str = str()
309         self.signature = {}
310         self.signature['name'] = str()
311         self.signature['in'] = str()                
312         self.signature['out'] = str()                        
313
314     def add_signature(self, name, direction, signature):
315         if (direction == 'in'):
316             self.signature['in'] += signature
317             if (self.signature['name'] != str()):
318                 self.signature['name'] += ", "
319             self.signature['name'] += name
320         if (direction == 'out'):
321             self.signature['out'] = signature                        
322         
323     def add_method(self, args = None, async_success_cb = None, async_err_cb = None):
324         async_cb_str = str()
325         if (self.methodToAdd != None):
326             name = self.methodToAdd
327         else:
328             name = self.signalToAdd
329         if (args != None):
330             self.args_str = args
331         if (async_success_cb != None):
332             async_cb_str = async_success_cb
333         if (async_err_cb != None):
334             if (async_cb_str != str()):
335                 async_cb_str += ", "
336             async_cb_str += async_err_cb
337                         
338         parameters = self.args_str
339         if (async_cb_str != str()):
340             if (parameters != str()):
341                 parameters += ", "
342             parameters +=async_cb_str       
343         
344         if (parameters != str()):
345             self.class_code.append_stmt("def " + name + "(self, %s):" % parameters)               
346         else:
347             self.class_code.append_stmt("def " + name + "(self):")
348         self.class_code.indent()
349         
350     def end_method(self):
351         self.class_code.append_stmt("\n")
352         self.class_code.dedent()
353         
354     def add_dbus_method(self):
355         decorator = '@dbus.service.method("' + self.ifName + '"'
356         if (self.signature.has_key('in') and self.signature['in'] != str()):
357                 decorator += ", in_signature='" + self.signature['in'] + "'"
358         if (self.signature.has_key('out') and self.signature['out'] != str()):
359                 decorator += ", out_signature='" + self.signature['out'] + "'"
360         decorator += ", async_callbacks=('dbus_async_cb', 'dbus_async_err_cb')"            
361         decorator += ")"
362         self.class_code.append_stmt(decorator)
363         if (self.signature.has_key('name') and self.signature['name'] != str()):
364             self.add_method(self.signature['name'], async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
365         else:
366             self.add_method(async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
367
368     def add_dbus_signal(self):
369         decorator = '@dbus.service.signal("' + self.ifName + '"'
370         if (self.signature.has_key('in') and self.signature['in'] != str()):
371                 decorator += ", signature='" + self.signature['in'] + "'"
372         decorator += ")"            
373         self.class_code.append_stmt(decorator)
374         if (self.signature.has_key('name') and self.signature['name'] != str()):
375             self.add_method(self.signature['name'])
376         else:
377             self.add_method()
378
379     def add_body_method(self):
380         if (self.methodToAdd != None):
381             if (self.args_str != str()):
382                 self.class_code.append_stmt("self.callback(self.srvName,'" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb, %s)" % self.args_str)
383             else:        
384                 self.class_code.append_stmt("self.callback(self.srvName,'" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb)")
385
386     def add_body_signal(self):
387         self.class_code.append_stmt("return") ## TODO: Remove and fix with code ad hoc
388         self.class_code.append_stmt("\n")
389
390     def add_stmt(self, stmt) :
391         self.class_code.append_stmt(stmt)
392         
393     def declare(self) :
394         self.class_code.execute()
395
396
397
398 ###############################################################################
399 class CloudeebusService:
400     '''
401     support for sending DBus messages and registering for DBus signals
402     '''
403     def __init__(self, permissions):
404         self.permissions = {};
405         self.permissions['permissions'] = permissions['permissions']
406         self.permissions['authextra'] = permissions['authextra']
407         self.permissions['services'] = permissions['services']
408         self.proxyObjects = {}
409         self.proxyMethods = {}
410         self.pendingCalls = []
411         self.dynDBusClasses = {} # DBus class source code generated dynamically (a list because one by classname)
412         self.services = {}  # DBus service created
413         self.serviceAgents = {} # Instantiated DBus class previously generated dynamically, for now, one by classname
414         self.servicePendingCalls = {} # JS methods called (and waiting for a Success/error response), containing 'methodId', (successCB, errorCB)
415         self.localCtx = locals()
416         self.globalCtx = globals()
417
418         self.patternDbus        = re.compile('^dbus\.(\w+)')
419         self.patternDbusBoolean = re.compile('^dbus.Boolean\((\w+)\)$')
420         self.patternDbusByte    = re.compile('^dbus.Byte\((\d+)\)$')
421         self.patternDbusInt16   = re.compile('^dbus.Int16\((\d+)\)$')
422         self.patternDbusInt32   = re.compile('^dbus.Int32\((\d+)\)$')
423         self.patternDbusInt64   = re.compile('^dbus.Int64\((\d+)\)$')
424         self.patternDbusUInt16  = re.compile('^dbus.UInt16\((\d+)\)$')
425         self.patternDbusUInt32  = re.compile('^dbus.UInt32\((\d+)\)$')
426         self.patternDbusUInt64  = re.compile('^dbus.UInt64\((\d+)\)$')
427         self.patternDbusDouble  = re.compile('^dbus.Double\((\d+\.\d+)\)$')
428
429     def proxyObject(self, busName, serviceName, objectName):
430         '''
431         object hash id as busName#serviceName#objectName
432         '''
433         id = "#".join([busName, serviceName, objectName])
434         if not self.proxyObjects.has_key(id):
435             if not OPENDOOR:
436                 # check permissions, array.index throws exception
437                 self.permissions['permissions'].index(serviceName)
438             bus = cache.dbusConnexion(busName)
439             self.proxyObjects[id] = bus.get_object(serviceName, objectName)
440         return self.proxyObjects[id]
441
442
443     def proxyMethod(self, busName, serviceName, objectName, interfaceName, methodName):
444         '''
445         method hash id as busName#serviceName#objectName#interfaceName#methodName
446         '''
447         id = "#".join([busName, serviceName, objectName, interfaceName, methodName])
448         if not self.proxyMethods.has_key(id):
449             obj = self.proxyObject(busName, serviceName, objectName)
450             self.proxyMethods[id] = obj.get_dbus_method(methodName, interfaceName)
451         return self.proxyMethods[id]
452
453     def decodeArgs( self, args ):
454         if isinstance( args, list ):
455             newArgs = []
456             for arg in args:
457                 newArgs.append( self.decodeArgs( arg ))
458             return newArgs
459         elif isinstance( args, dict ):
460             newDict = {}
461             for key, value in args.iteritems():
462                 key = self.decodeArgs( key )
463                 newValue = self.decodeArgs( value )
464                 newDict[key] = newValue
465             return newDict
466         elif isinstance( args, basestring ):
467             newArg = self.decodeDbusString( args )
468             return newArg
469         else:
470             return args
471
472     def decodeDbusString( self, dbusString ):
473         matchDbus = self.patternDbus.match( dbusString )
474
475         if not matchDbus:
476             return dbusString
477
478
479         result = {
480            "Boolean" : lambda x : dbus.Boolean(   self.patternDbusBoolean.match( x ).group( 1 ).lower() in ("yes", "true", "t", "1")),
481            "Byte"    : lambda x : dbus.Byte( int( self.patternDbusByte.match(    x ).group( 1 ))),
482            "Int16"   : lambda x : dbus.Int16(     self.patternDbusInt16.match(   x ).group( 1 )),
483            "Int32"   : lambda x : dbus.Int32(     self.patternDbusInt32.match(   x ).group( 1 )),
484            "Int64"   : lambda x : dbus.Int64(     self.patternDbusInt64.match(    x ).group( 1 )),
485            "UInt16"  : lambda x : dbus.UInt16(    self.patternDbusUInt16.match(  x ).group( 1 )),
486            "UInt32"  : lambda x : dbus.UInt32(    self.patternDbusUInt32.match(  x ).group( 1 )),
487            "UInt64"  : lambda x : dbus.UInt64(    self.patternDbusUInt64.match(   x ).group( 1 )),
488            "Double"  : lambda x : dbus.Double(    self.patternDbusDouble.match(  x ).group( 1 ))
489         }[matchDbus.group(1)](dbusString)
490
491         return result
492
493     @exportRpc
494     def dbusRegister(self, list):
495         '''
496         arguments: bus, sender, object, interface, signal
497         '''
498         if len(list) < 5:
499             raise Exception("Error: expected arguments: bus, sender, object, interface, signal)")
500         
501         if not OPENDOOR:
502             # check permissions, array.index throws exception
503             self.permissions['permissions'].index(list[1])
504         
505         # check if a handler exists
506         sigId = "#".join(list)
507         if cache.signalHandlers.has_key(sigId):
508             return sigId
509         
510         # create a handler that will publish the signal
511         dbusSignalHandler = DbusSignalHandler(*list)
512         cache.signalHandlers[sigId] = dbusSignalHandler
513         
514         return dbusSignalHandler.id
515
516
517     @exportRpc
518     def dbusSend(self, list):
519         '''
520         arguments: bus, destination, object, interface, message, [args]
521         '''
522         # clear pending calls
523         for call in self.pendingCalls:
524             if not call.pending:
525                 self.pendingCalls.remove(call)
526         
527         if len(list) < 5:
528             raise Exception("Error: expected arguments: bus, destination, object, interface, message, [args])")
529         
530         # parse JSON arg list
531         args = []
532         if len(list) == 6:
533             jsonArgs = json.loads(list[5])
534             if jsonArgs:
535                 args = self.decodeArgs( jsonArgs )
536         
537         # get dbus proxy method
538         method = self.proxyMethod(*list[0:5])
539         
540         # use a deferred call handler to manage dbus results
541         dbusCallHandler = DbusCallHandler(method, args)
542         self.pendingCalls.append(dbusCallHandler)
543         return dbusCallHandler.callMethod()
544
545
546     @exportRpc
547     def emitSignal(self, list):
548         '''
549         arguments: agentObjectPath, signalName, args (to emit)
550         '''
551         objectPath = list[0]
552         className = re.sub('/', '_', objectPath[1:])
553         signalName = list[1]
554         args = []
555         jsonArgs = json.loads(list[2])
556         print "JSON Arguments:", jsonArgs
557         if jsonArgs:
558             args = self.decodeArgs( jsonArgs )
559             print "Decoded Arguments: ", args
560
561         if (self.serviceAgents.has_key(className) == True):            
562             exe_str = "self.serviceAgents['"+ className +"']."+ signalName + "("
563             if len(args) > 0:
564                 exe_str += json.dumps(args[0])
565                 for idx in args[1:]:
566                     exe_str += "," + json.dumps(idx)
567             exe_str += ")"               
568             eval(exe_str, self.globalCtx, self.localCtx)
569         else:
570             raise Exception("No object path " + objectPath)
571
572     @exportRpc
573     def returnMethod(self, list):
574         '''
575         arguments: methodId, callIndex, success (=true, error otherwise), result (to return)
576         '''
577         methodId = list[0]
578         callIndex = list[1]
579         success = list[2]
580         result = list[3]
581         if (self.servicePendingCalls.has_key(methodId)):
582             cb = self.servicePendingCalls[methodId]['calls'][callIndex]
583             if cb is None:
584                 raise Exception("No pending call " + str(callIndex) + " for methodID " + methodId)
585             if (success):                
586                 successCB = cb["successCB"]
587                 if (result != None):
588                     successCB(result)
589                 else:
590                     successCB()                    
591             else:     
592                 errorCB = cb["errorCB"]        
593                 if (result != None):
594                     errorCB(result)
595                 else:
596                     errorCB()
597             self.servicePendingCalls[methodId]['calls'][callIndex] = None
598             self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] - 1
599             if self.servicePendingCalls[methodId]['count'] == 0:
600                 del self.servicePendingCalls[methodId]
601         else:
602             raise Exception("No methodID " + methodId)
603
604     def srvCB(self, srvName, name, objPath, ifName, async_succes_cb, async_error_cb, *args):
605         methodId = srvName + "#" + objPath + "#" + ifName + "#" + name
606         cb = { 'successCB': async_succes_cb, 
607                'errorCB': async_error_cb}
608         if methodId not in self.servicePendingCalls:
609             self.servicePendingCalls[methodId] = {'count': 0, 'calls': []}
610             
611         try:
612             pendingCallStr = json.dumps({'callIndex': len(self.servicePendingCalls[methodId]['calls']), 'args': args})
613         except Exception, e:                
614             args = eval( str(args).replace("dbus.Byte", "dbus.Int16") )
615             pendingCallStr = json.dumps({'callIndex': len(self.servicePendingCalls[methodId]['calls']), 'args': args})
616                
617         self.servicePendingCalls[methodId]['calls'].append(cb)
618         self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] + 1
619         factory.dispatch(methodId, pendingCallStr)
620                     
621     @exportRpc
622     def serviceAdd(self, list):
623         '''
624         arguments: busName, srvName
625         '''
626         busName = list[0]
627         self.bus =  cache.dbusConnexion( busName )
628         srvName = list[1]
629         if not OPENDOOR and (SERVICELIST == [] or SERVICELIST != [] and self.permissions['services'] == None):
630             SERVICELIST.index(srvName)
631             
632         if (self.services.has_key(srvName) == False):
633             self.services[srvName] = dbus.service.BusName(name = srvName, bus = self.bus)
634         return srvName
635
636     @exportRpc
637     def serviceRelease(self, list):
638         '''
639         arguments: busName, srvName
640         '''
641         srvName = list[0]
642         if (self.services.has_key(srvName) == True):
643             self.services.pop(srvName)
644             return srvName
645         else:
646             raise Exception(srvName + " does not exist")
647                    
648     @exportRpc
649     def serviceAddAgent(self, list):
650         '''
651         arguments: objectPath, xmlTemplate
652         '''
653         srvName = list[0]
654         agentObjectPath = list[1]
655         xmlTemplate = list[2]
656         className = createClassName(agentObjectPath)
657         if (self.dynDBusClasses.has_key(className) == False):
658             self.dynDBusClasses[className] = DynDBusClass(className, self.globalCtx, self.localCtx)
659             self.dynDBusClasses[className].createDBusServiceFromXML(xmlTemplate)
660             self.dynDBusClasses[className].declare()
661
662         ## Class already exist, instanciate it if not already instanciated
663         if (self.serviceAgents.has_key(className) == False):
664             self.serviceAgents[className] = eval(className + "(self.bus, callback=self.srvCB, objPath='" + agentObjectPath + "', srvName='" + srvName + "')", self.globalCtx, self.localCtx)
665             
666         self.serviceAgents[className].add_to_connection()
667         return (agentObjectPath)
668                     
669     @exportRpc
670     def serviceDelAgent(self, list):
671         '''
672         arguments: objectPath, xmlTemplate
673         '''
674         agentObjectPath = list[0]
675         className = createClassName(agentObjectPath)
676         
677         if (self.serviceAgents.has_key(className)):
678             self.serviceAgents[className].remove_from_connection()
679             self.serviceAgents.pop(className)
680         else:
681             raise Exception(agentObjectPath + " doesn't exist!")
682         
683         return (agentObjectPath)
684                     
685     @exportRpc
686     def getVersion(self):
687         '''
688         return current version string
689         '''
690         return VERSION