2 # -*- coding: utf-8 -*-
5 import nepi.core.execute
6 from nepi.core.attributes import AttributesMap, Attribute
7 from nepi.util import server, validation
8 from nepi.util.constants import TIME_NOW, ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP, DeploymentConfiguration as DC
21 # PROTOCOL INSTRUCTION MESSAGES
41 DO_CROSS_CONNECT_INIT = 22
51 GET_ATTRIBUTE_LIST = 32
53 DO_CROSS_CONNECT_COMPL = 34
59 GET_TESTBED_VERSION = 40
61 instruction_text = dict({
71 CONFIGURE: "CONFIGURE",
73 CREATE_SET: "CREATE_SET",
74 FACTORY_SET: "FACTORY_SET",
76 CROSS_CONNECT: "CROSS_CONNECT",
77 ADD_TRACE: "ADD_TRACE",
78 ADD_ADDRESS: "ADD_ADDRESS",
79 ADD_ROUTE: "ADD_ROUTE",
81 DO_CREATE: "DO_CREATE",
82 DO_CONNECT_INIT: "DO_CONNECT_INIT",
83 DO_CONNECT_COMPL: "DO_CONNECT_COMPL",
84 DO_CONFIGURE: "DO_CONFIGURE",
85 DO_PRECONFIGURE: "DO_PRECONFIGURE",
86 DO_CROSS_CONNECT_INIT: "DO_CROSS_CONNECT_INIT",
87 DO_CROSS_CONNECT_COMPL: "DO_CROSS_CONNECT_COMPL",
90 GET_ROUTE: "GET_ROUTE",
91 GET_ADDRESS: "GET_ADDRESS",
92 GET_ATTRIBUTE_LIST: "GET_ATTRIBUTE_LIST",
93 GET_FACTORY_ID: "GET_FACTORY_ID",
94 GET_TESTBED_ID: "GET_TESTBED_ID",
95 GET_TESTBED_VERSION: "GET_TESTBED_VERSION",
99 TESTBED_ID: "TESTBED_ID",
100 TESTBED_VERSION: "TESTBED_VERSION",
103 def log_msg(server, params):
105 instr = int(params[0])
106 instr_txt = instruction_text[instr]
107 server.log_debug("%s - msg: %s [%s]" % (server.__class__.__name__,
108 instr_txt, ", ".join(map(str, params[1:]))))
110 # don't die for logging
113 def log_reply(server, reply):
115 res = reply.split("|")
117 code_txt = instruction_text[code]
119 txt = base64.b64decode(res[1])
122 server.log_debug("%s - reply: %s %s" % (server.__class__.__name__,
125 # don't die for logging
126 server.log_debug("%s - reply: %s" % (server.__class__.__name__,
130 def to_server_log_level(log_level):
133 if log_level == DC.DEBUG_LEVEL
134 else server.ERROR_LEVEL
137 def get_access_config_params(access_config):
138 root_dir = access_config.get_attribute_value(DC.ROOT_DIRECTORY)
139 log_level = access_config.get_attribute_value(DC.LOG_LEVEL)
140 log_level = to_server_log_level(log_level)
141 user = host = port = agent = key = None
142 communication = access_config.get_attribute_value(DC.DEPLOYMENT_COMMUNICATION)
143 environment_setup = (
144 access_config.get_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP)
145 if access_config.has_attribute(DC.DEPLOYMENT_ENVIRONMENT_SETUP)
148 if communication == DC.ACCESS_SSH:
149 user = access_config.get_attribute_value(DC.DEPLOYMENT_USER)
150 host = access_config.get_attribute_value(DC.DEPLOYMENT_HOST)
151 port = access_config.get_attribute_value(DC.DEPLOYMENT_PORT)
152 agent = access_config.get_attribute_value(DC.USE_AGENT)
153 key = access_config.get_attribute_value(DC.DEPLOYMENT_KEY)
154 return (root_dir, log_level, user, host, port, key, agent, environment_setup)
156 class AccessConfiguration(AttributesMap):
157 def __init__(self, params = None):
158 super(AccessConfiguration, self).__init__()
160 from nepi.core.metadata import Metadata
162 for _,attr_info in Metadata.DEPLOYMENT_ATTRIBUTES:
163 self.add_attribute(**attr_info)
166 for attr_name, attr_value in params.iteritems():
167 parser = Attribute.type_parsers[self.get_attribute_type(attr_name)]
168 attr_value = parser(attr_value)
169 self.set_attribute_value(attr_name, attr_value)
171 class TempDir(object):
173 self.path = tempfile.mkdtemp()
176 shutil.rmtree(self.path)
178 class PermDir(object):
179 def __init__(self, path):
182 def create_controller(xml, access_config = None):
183 mode = None if not access_config \
184 else access_config.get_attribute_value(DC.DEPLOYMENT_MODE)
185 launch = True if not access_config \
186 else not access_config.get_attribute_value(DC.RECOVER)
187 if not mode or mode == DC.MODE_SINGLE_PROCESS:
189 raise ValueError, "Unsupported instantiation mode: %s with lanch=False" % (mode,)
191 from nepi.core.execute import ExperimentController
193 if not access_config or not access_config.has_attribute(DC.ROOT_DIRECTORY):
196 root_dir = PermDir(access_config.get_attribute_value(DC.ROOT_DIRECTORY))
197 controller = ExperimentController(xml, root_dir.path)
199 # inject reference to temporary dir, so that it gets cleaned
200 # up at destruction time.
201 controller._tempdir = root_dir
204 elif mode == DC.MODE_DAEMON:
205 (root_dir, log_level, user, host, port, key, agent, environment_setup) = \
206 get_access_config_params(access_config)
207 return ExperimentControllerProxy(root_dir, log_level,
208 experiment_xml = xml, host = host, port = port, user = user, ident_key = key,
209 agent = agent, launch = launch,
210 environment_setup = environment_setup)
211 raise RuntimeError("Unsupported access configuration '%s'" % mode)
213 def create_testbed_controller(testbed_id, testbed_version, access_config):
214 mode = None if not access_config \
215 else access_config.get_attribute_value(DC.DEPLOYMENT_MODE)
216 launch = True if not access_config \
217 else not access_config.get_attribute_value(DC.RECOVER)
218 if not mode or mode == DC.MODE_SINGLE_PROCESS:
220 raise ValueError, "Unsupported instantiation mode: %s with lanch=False" % (mode,)
221 return _build_testbed_controller(testbed_id, testbed_version)
222 elif mode == DC.MODE_DAEMON:
223 (root_dir, log_level, user, host, port, key, agent, environment_setup) = \
224 get_access_config_params(access_config)
225 return TestbedControllerProxy(root_dir, log_level, testbed_id = testbed_id,
226 testbed_version = testbed_version, host = host, port = port, ident_key = key,
227 user = user, agent = agent, launch = launch,
228 environment_setup = environment_setup)
229 raise RuntimeError("Unsupported access configuration '%s'" % mode)
231 def _build_testbed_controller(testbed_id, testbed_version):
232 mod_name = "nepi.testbeds.%s" % (testbed_id.lower())
233 if not mod_name in sys.modules:
235 module = sys.modules[mod_name]
236 return module.TestbedController(testbed_version)
238 # Just a namespace class
242 def pickled_data(sdata):
243 return cPickle.loads(base64.b64decode(sdata))
246 def base64_data(sdata):
247 return base64.b64decode(sdata)
251 return None if sdata == "None" else int(sdata)
255 return sdata == 'True'
259 def pickled_data(data):
260 return base64.b64encode(cPickle.dumps(data))
263 def base64_data(data):
264 return base64.b64encode(data)
268 return "None" if data is None else int(data)
272 return str(bool(data))
274 # import into Marshalling all the decoders
278 for typname, typ in vars(Decoders).iteritems()
279 if not typname.startswith('_')
282 _TYPE_ENCODERS = dict([
283 # id(type) -> (<encoding_function>, <formatting_string>)
284 (typname, (getattr(Encoders,typname),"%s"))
285 for typname in vars(Decoders)
286 if not typname.startswith('_')
287 and hasattr(Encoders,typname)
291 _TYPE_ENCODERS["float"] = (float, "%r")
292 _TYPE_ENCODERS["int"] = (int, "%d")
293 _TYPE_ENCODERS["long"] = (int, "%d")
294 _TYPE_ENCODERS["str"] = (str, "%s")
295 _TYPE_ENCODERS["unicode"] = (str, "%s")
298 _TYPE_ENCODERS[None] = (str, "%s")
303 Decorator that converts the given function into one that takes
304 a single "params" list, with each parameter marshalled according
305 to the given factory callable (type constructors are accepted).
307 The first argument (self) is left untouched.
311 @Marshalling.args(int,int,str,base64_data)
312 def somefunc(self, someint, otherint, somestr, someb64):
317 def rv(self, params):
318 return f(self, *[ ctor(val)
319 for ctor,val in zip(types, params[1:]) ])
323 # Derive type encoders by looking up types in _TYPE_ENCODERS
324 # make_proxy will use it to encode arguments in command strings
326 TYPE_ENCODERS = Marshalling._TYPE_ENCODERS
328 if typ.__name__ in TYPE_ENCODERS:
329 argencoders.append(TYPE_ENCODERS[typ.__name__])
332 argencoders.append(TYPE_ENCODERS[None])
334 rv._argencoders = tuple(argencoders)
336 rv._retval = getattr(f, '_retval', None)
341 def retval(typ=Decoders.base64_data):
343 Decorator that converts the given function into one that
344 returns a properly encoded return string, given that the undecorated
345 function returns suitable input for the encoding function.
347 The optional typ argument specifies a type.
348 For the default of base64_data, return values should be strings.
349 The return value of the encoding method should be a string always.
353 @Marshalling.args(int,int,str,base64_data)
354 @Marshalling.retval(str)
355 def somefunc(self, someint, otherint, somestr, someb64):
358 encode, fmt = Marshalling._TYPE_ENCODERS.get(
360 Marshalling._TYPE_ENCODERS[None])
365 def rv(self, *p, **kw):
366 data = f(self, *p, **kw)
372 rv._argtypes = getattr(f, '_argtypes', None)
373 rv._argencoders = getattr(f, '_argencoders', None)
380 Decorator that converts the given function into one that
381 always return an encoded empty string.
383 Useful for null-returning functions.
388 def rv(self, *p, **kw):
393 rv._argtypes = getattr(f, '_argtypes', None)
394 rv._argencoders = getattr(f, '_argencoders', None)
398 def handles(whichcommand):
400 Associates the method with a given command code for servers.
401 It should always be the topmost decorator.
404 f._handles_command = whichcommand
408 class BaseServer(server.Server):
409 def reply_action(self, msg):
411 result = base64.b64encode("Invalid command line")
412 reply = "%d|%s" % (ERROR, result)
414 params = msg.split("|")
415 instruction = int(params[0])
416 log_msg(self, params)
418 for mname,meth in vars(self.__class__).iteritems():
419 if not mname.startswith('_'):
420 cmd = getattr(meth, '_handles_command', None)
421 if cmd == instruction:
422 meth = getattr(self, mname)
426 error = "Invalid instruction %s" % instruction
427 self.log_error(error)
428 result = base64.b64encode(error)
429 reply = "%d|%s" % (ERROR, result)
431 error = self.log_error()
432 result = base64.b64encode(error)
433 reply = "%d|%s" % (ERROR, result)
434 log_reply(self, reply)
437 class TestbedControllerServer(BaseServer):
438 def __init__(self, root_dir, log_level, testbed_id, testbed_version):
439 super(TestbedControllerServer, self).__init__(root_dir, log_level)
440 self._testbed_id = testbed_id
441 self._testbed_version = testbed_version
444 def post_daemonize(self):
445 self._testbed = _build_testbed_controller(self._testbed_id,
446 self._testbed_version)
448 @Marshalling.handles(GUIDS)
450 @Marshalling.retval( Marshalling.pickled_data )
452 return self._testbed.guids
454 @Marshalling.handles(TESTBED_ID)
456 @Marshalling.retval()
457 def testbed_id(self):
458 return str(self._testbed.testbed_id)
460 @Marshalling.handles(TESTBED_VERSION)
462 @Marshalling.retval()
463 def testbed_version(self):
464 return str(self._testbed.testbed_version)
466 @Marshalling.handles(CREATE)
467 @Marshalling.args(int, str)
469 def defer_create(self, guid, factory_id):
470 self._testbed.defer_create(guid, factory_id)
472 @Marshalling.handles(TRACE)
473 @Marshalling.args(int, str, Marshalling.base64_data)
474 @Marshalling.retval()
475 def trace(self, guid, trace_id, attribute):
476 return self._testbed.trace(guid, trace_id, attribute)
478 @Marshalling.handles(START)
482 self._testbed.start()
484 @Marshalling.handles(STOP)
490 @Marshalling.handles(SHUTDOWN)
494 self._testbed.shutdown()
496 @Marshalling.handles(CONFIGURE)
497 @Marshalling.args(Marshalling.base64_data, Marshalling.pickled_data)
499 def defer_configure(self, name, value):
500 self._testbed.defer_configure(name, value)
502 @Marshalling.handles(CREATE_SET)
503 @Marshalling.args(int, Marshalling.base64_data, Marshalling.pickled_data)
505 def defer_create_set(self, guid, name, value):
506 self._testbed.defer_create_set(guid, name, value)
508 @Marshalling.handles(FACTORY_SET)
509 @Marshalling.args(Marshalling.base64_data, Marshalling.pickled_data)
511 def defer_factory_set(self, name, value):
512 self._testbed.defer_factory_set(name, value)
514 @Marshalling.handles(CONNECT)
515 @Marshalling.args(int, str, int, str)
517 def defer_connect(self, guid1, connector_type_name1, guid2, connector_type_name2):
518 self._testbed.defer_connect(guid1, connector_type_name1, guid2,
519 connector_type_name2)
521 @Marshalling.handles(CROSS_CONNECT)
522 @Marshalling.args(int, str, int, int, str, str, str)
524 def defer_cross_connect(self,
525 guid, connector_type_name,
526 cross_guid, cross_testbed_guid,
527 cross_testbed_id, cross_factory_id,
528 cross_connector_type_name):
529 self._testbed.defer_cross_connect(guid, connector_type_name, cross_guid,
530 cross_testbed_guid, cross_testbed_id, cross_factory_id,
531 cross_connector_type_name)
533 @Marshalling.handles(ADD_TRACE)
534 @Marshalling.args(int, str)
536 def defer_add_trace(self, guid, trace_id):
537 self._testbed.defer_add_trace(guid, trace_id)
539 @Marshalling.handles(ADD_ADDRESS)
540 @Marshalling.args(int, str, int, str)
542 def defer_add_address(self, guid, address, netprefix, broadcast):
543 self._testbed.defer_add_address(guid, address, netprefix,
546 @Marshalling.handles(ADD_ROUTE)
547 @Marshalling.args(int, str, int, str)
549 def defer_add_route(self, guid, destination, netprefix, nexthop):
550 self._testbed.defer_add_route(guid, destination, netprefix, nexthop)
552 @Marshalling.handles(DO_SETUP)
556 self._testbed.do_setup()
558 @Marshalling.handles(DO_CREATE)
562 self._testbed.do_create()
564 @Marshalling.handles(DO_CONNECT_INIT)
567 def do_connect_init(self):
568 self._testbed.do_connect_init()
570 @Marshalling.handles(DO_CONNECT_COMPL)
573 def do_connect_compl(self):
574 self._testbed.do_connect_compl()
576 @Marshalling.handles(DO_CONFIGURE)
579 def do_configure(self):
580 self._testbed.do_configure()
582 @Marshalling.handles(DO_PRECONFIGURE)
585 def do_preconfigure(self):
586 self._testbed.do_preconfigure()
588 @Marshalling.handles(DO_PRESTART)
591 def do_prestart(self):
592 self._testbed.do_prestart()
594 @Marshalling.handles(DO_CROSS_CONNECT_INIT)
595 @Marshalling.args( Marshalling.Decoders.pickled_data )
597 def do_cross_connect_init(self, cross_data):
598 self._testbed.do_cross_connect_init(cross_data)
600 @Marshalling.handles(DO_CROSS_CONNECT_COMPL)
601 @Marshalling.args( Marshalling.Decoders.pickled_data )
603 def do_cross_connect_compl(self, cross_data):
604 self._testbed.do_cross_connect_compl(cross_data)
606 @Marshalling.handles(GET)
607 @Marshalling.args(int, Marshalling.base64_data, str)
608 @Marshalling.retval( Marshalling.pickled_data )
609 def get(self, guid, name, time):
610 return self._testbed.get(guid, name, time)
612 @Marshalling.handles(SET)
613 @Marshalling.args(int, Marshalling.base64_data, Marshalling.pickled_data, str)
615 def set(self, guid, name, value, time):
616 self._testbed.set(guid, name, value, time)
618 @Marshalling.handles(GET_ADDRESS)
619 @Marshalling.args(int, int, Marshalling.base64_data)
620 @Marshalling.retval()
621 def get_address(self, guid, index, attribute):
622 return str(self._testbed.get_address(guid, index, attribute))
624 @Marshalling.handles(GET_ROUTE)
625 @Marshalling.args(int, int, Marshalling.base64_data)
626 @Marshalling.retval()
627 def get_route(self, guid, index, attribute):
628 return str(self._testbed.get_route(guid, index, attribute))
630 @Marshalling.handles(ACTION)
631 @Marshalling.args(str, int, Marshalling.base64_data)
633 def action(self, time, guid, command):
634 self._testbed.action(time, guid, command)
636 @Marshalling.handles(STATUS)
637 @Marshalling.args(Marshalling.nullint)
638 @Marshalling.retval(int)
639 def status(self, guid):
640 return self._testbed.status(guid)
642 @Marshalling.handles(GET_ATTRIBUTE_LIST)
643 @Marshalling.args(int)
644 @Marshalling.retval( Marshalling.pickled_data )
645 def get_attribute_list(self, guid):
646 return self._testbed.get_attribute_list(guid)
648 @Marshalling.handles(GET_FACTORY_ID)
649 @Marshalling.args(int)
650 @Marshalling.retval()
651 def get_factory_id(self, guid):
652 return self._testbed.get_factory_id(guid)
654 class ExperimentControllerServer(BaseServer):
655 def __init__(self, root_dir, log_level, experiment_xml):
656 super(ExperimentControllerServer, self).__init__(root_dir, log_level)
657 self._experiment_xml = experiment_xml
658 self._controller = None
660 def post_daemonize(self):
661 from nepi.core.execute import ExperimentController
662 self._controller = ExperimentController(self._experiment_xml,
663 root_dir = self._root_dir)
665 @Marshalling.handles(GUIDS)
667 @Marshalling.retval( Marshalling.pickled_data )
669 return self._controller.guids
671 @Marshalling.handles(XML)
673 @Marshalling.retval()
674 def experiment_xml(self):
675 return self._controller.experiment_xml
677 @Marshalling.handles(TRACE)
678 @Marshalling.args(int, str, Marshalling.base64_data)
679 @Marshalling.retval()
680 def trace(self, guid, trace_id, attribute):
681 return str(self._controller.trace(guid, trace_id, attribute))
683 @Marshalling.handles(FINISHED)
684 @Marshalling.args(int)
685 @Marshalling.retval(Marshalling.bool)
686 def is_finished(self, guid):
687 return self._controller.is_finished(guid)
689 @Marshalling.handles(GET)
690 @Marshalling.args(int, Marshalling.base64_data, str)
691 @Marshalling.retval( Marshalling.pickled_data )
692 def get(self, guid, name, time):
693 return self._controller.get(guid, name, time)
695 @Marshalling.handles(SET)
696 @Marshalling.args(int, Marshalling.base64_data, Marshalling.pickled_data, str)
698 def set(self, guid, name, value, time):
699 self._controller.set(guid, name, value, time)
701 @Marshalling.handles(START)
705 self._controller.start()
707 @Marshalling.handles(STOP)
711 self._controller.stop()
713 @Marshalling.handles(RECOVER)
717 self._controller.recover()
719 @Marshalling.handles(SHUTDOWN)
723 self._controller.shutdown()
725 @Marshalling.handles(GET_TESTBED_ID)
726 @Marshalling.args(int)
727 @Marshalling.retval()
728 def get_testbed_id(self, guid):
729 return self._controller.get_testbed_id(guid)
731 @Marshalling.handles(GET_FACTORY_ID)
732 @Marshalling.args(int)
733 @Marshalling.retval()
734 def get_factory_id(self, guid):
735 return self._controller.get_factory_id(guid)
737 @Marshalling.handles(GET_TESTBED_VERSION)
738 @Marshalling.args(int)
739 @Marshalling.retval()
740 def get_testbed_version(self, guid):
741 return self._controller.get_testbed_version(guid)
743 class BaseProxy(object):
745 _ServerClassModule = "nepi.util.proxy"
749 launch = True, host = None,
750 port = None, user = None, ident_key = None, agent = None,
751 environment_setup = ""):
756 "from %(classmodule)s import %(classname)s;"
757 "s = %(classname)s%(ctor_args)r;"
760 classname = self._ServerClass.__name__,
761 classmodule = self._ServerClassModule,
762 ctor_args = ctor_args
764 proc = server.popen_ssh_subprocess(python_code, host = host,
765 port = port, user = user, agent = agent,
766 ident_key = ident_key,
767 environment_setup = environment_setup,
770 err = proc.stderr.read()
771 raise RuntimeError, "Server could not be executed: %s" % (err,)
774 s = self._ServerClass(*ctor_args)
777 # connect client to server
778 self._client = server.Client(root_dir, host = host, port = port,
779 user = user, agent = agent,
780 environment_setup = environment_setup)
783 def _make_message(argtypes, argencoders, command, methname, classname, *args):
784 if len(argtypes) != len(argencoders):
785 raise ValueError, "Invalid arguments for _make_message: "\
786 "in stub method %s of class %s "\
787 "argtypes and argencoders must match in size" % (
788 methname, classname )
789 if len(argtypes) != len(args):
790 raise ValueError, "Invalid arguments for _make_message: "\
791 "in stub method %s of class %s "\
792 "expected %d arguments, got %d" % (
794 len(argtypes), len(args))
797 for argnum, (typ, (encode, fmt), val) in enumerate(zip(argtypes, argencoders, args)):
799 buf.append(fmt % encode(val))
802 raise TypeError, "Argument %d of stub method %s of class %s "\
803 "requires a value of type %s, but got %s - nested error: %s" % (
804 argnum, methname, classname,
805 getattr(typ, '__name__', typ), type(val),
806 traceback.format_exc()
809 return "%d|%s" % (command, '|'.join(buf))
812 def _parse_reply(rvtype, methname, classname, reply):
814 raise RuntimeError, "Invalid reply: %r "\
815 "for stub method %s of class %s" % (
821 result = reply.split("|")
822 code = int(result[0])
826 raise TypeError, "Return value of stub method %s of class %s "\
827 "cannot be parsed: must be of type %s, got %r - nested error: %s" % (
829 getattr(rvtype, '__name__', rvtype), reply,
830 traceback.format_exc()
833 text = base64.b64decode(text)
834 raise RuntimeError(text)
843 raise TypeError, "Return value of stub method %s of class %s "\
844 "cannot be parsed: must be of type %s - nested error: %s" % (
846 getattr(rvtype, '__name__', rvtype),
847 traceback.format_exc()
850 raise RuntimeError, "Invalid reply: %r "\
851 "for stub method %s of class %s - unknown code" % (
857 def _make_stubs(server_class, template_class):
859 Returns a dictionary method_name -> method
864 class SomeProxy(BaseProxy):
867 locals().update( BaseProxy._make_stubs(
872 ServerClass is the corresponding Server class, as
873 specified in the _ServerClass class method (_make_stubs
874 is static and can't access the method), and TemplateClass
875 is the ultimate implementation class behind the server,
876 from which argument names and defaults are taken, to
877 maintain meaningful interfaces.
884 func_template_path = os.path.join(
885 os.path.dirname(__file__),
887 func_template_file = open(func_template_path, "r")
888 func_template = func_template_file.read()
889 func_template_file.close()
891 for methname in vars(template_class):
892 if hasattr(server_class, methname) and not methname.startswith('_'):
893 template_meth = getattr(template_class, methname)
894 server_meth = getattr(server_class, methname)
896 command = getattr(server_meth, '_handles_command', None)
897 argtypes = getattr(server_meth, '_argtypes', None)
898 argencoders = getattr(server_meth, '_argencoders', None)
899 rvtype = getattr(server_meth, '_retval', None)
902 if hasattr(template_meth, 'fget'):
904 template_meth = template_meth.fget
907 if command is not None and argtypes is not None and argencoders is not None:
908 # We have an interface method...
909 code = template_meth.func_code
910 argnames = code.co_varnames[:code.co_argcount]
911 argdefaults = ( (NONE,) * (len(argnames) - len(template_meth.func_defaults or ()))
912 + (template_meth.func_defaults or ()) )
915 BaseProxy = BaseProxy,
917 argencoders = argencoders,
922 func_text = func_template % dict(
924 args = '%s' % (','.join(argnames[1:])),
926 argname if argdef is NONE
927 else "%s=%r" % (argname, argdef)
928 for argname, argdef in zip(argnames[1:], argdefaults[1:])
932 classname = server_class.__name__
940 exec func_text in func_globals, context
943 rv[methname] = property(context[methname])
945 rv[methname] = context[methname]
949 class TestbedControllerProxy(BaseProxy):
951 _ServerClass = TestbedControllerServer
953 def __init__(self, root_dir, log_level, testbed_id = None,
954 testbed_version = None, launch = True, host = None,
955 port = None, user = None, ident_key = None, agent = None,
956 environment_setup = ""):
957 if launch and (testbed_id == None or testbed_version == None):
958 raise RuntimeError("To launch a TesbedControllerServer a "
959 "testbed_id and testbed_version are required")
960 super(TestbedControllerProxy,self).__init__(
961 ctor_args = (root_dir, log_level, testbed_id, testbed_version),
963 launch = launch, host = host, port = port, user = user,
964 ident_key = ident_key, agent = agent,
965 environment_setup = environment_setup)
967 locals().update( BaseProxy._make_stubs(
968 server_class = TestbedControllerServer,
969 template_class = nepi.core.execute.TestbedController,
972 # Shutdown stops the serverside...
973 def shutdown(self, _stub = shutdown):
975 self._client.send_stop()
976 self._client.read_reply() # wait for it
980 class ExperimentControllerProxy(BaseProxy):
981 _ServerClass = ExperimentControllerServer
983 def __init__(self, root_dir, log_level, experiment_xml = None,
984 launch = True, host = None, port = None, user = None,
985 ident_key = None, agent = None, environment_setup = ""):
986 if launch and experiment_xml is None:
987 raise RuntimeError("To launch a ExperimentControllerServer a \
988 xml description of the experiment is required")
989 super(ExperimentControllerProxy,self).__init__(
990 ctor_args = (root_dir, log_level, experiment_xml),
992 launch = launch, host = host, port = port, user = user,
993 ident_key = ident_key, agent = agent,
994 environment_setup = environment_setup)
996 locals().update( BaseProxy._make_stubs(
997 server_class = ExperimentControllerServer,
998 template_class = nepi.core.execute.ExperimentController,
1002 # Shutdown stops the serverside...
1003 def shutdown(self, _stub = shutdown):
1005 self._client.send_stop()
1006 self._client.read_reply() # wait for it