diff options
-rw-r--r-- | testing/legion/common_lib.py | 2 | ||||
-rw-r--r-- | testing/legion/examples/hello_world/host_test.isolate | 22 | ||||
-rwxr-xr-x | testing/legion/examples/hello_world/host_test.py | 77 | ||||
-rw-r--r-- | testing/legion/examples/hello_world/task_test.isolate (renamed from testing/legion/examples/hello_world/client_test.isolate) | 6 | ||||
-rwxr-xr-x | testing/legion/examples/hello_world/task_test.py (renamed from testing/legion/examples/hello_world/client_test.py) | 0 | ||||
-rw-r--r-- | testing/legion/examples/subprocess/subprocess_test.isolate | 2 | ||||
-rwxr-xr-x | testing/legion/examples/subprocess/subprocess_test.py | 52 | ||||
-rw-r--r-- | testing/legion/examples/subprocess/task.isolate (renamed from testing/legion/examples/subprocess/client.isolate) | 4 | ||||
-rw-r--r-- | testing/legion/host_controller.py | 70 | ||||
-rw-r--r-- | testing/legion/legion.isolate | 12 | ||||
-rw-r--r-- | testing/legion/rpc_methods.py (renamed from testing/legion/client_rpc_methods.py) | 2 | ||||
-rw-r--r-- | testing/legion/rpc_server.py (renamed from testing/legion/client_rpc_server.py) | 12 | ||||
-rwxr-xr-x | testing/legion/run_task.py (renamed from testing/legion/client_controller.py) | 22 | ||||
-rw-r--r-- | testing/legion/task_controller.py (renamed from testing/legion/client_lib.py) | 57 | ||||
-rw-r--r-- | testing/legion/task_registration_server.py (renamed from testing/legion/discovery_server.py) | 32 | ||||
-rw-r--r-- | testing/legion/test_controller.py | 69 |
16 files changed, 175 insertions, 266 deletions
diff --git a/testing/legion/common_lib.py b/testing/legion/common_lib.py index 2f527ea..c752e0f 100644 --- a/testing/legion/common_lib.py +++ b/testing/legion/common_lib.py @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Common library methods used by both host and client controllers.""" +"""Common library methods used by both coordinator and task machines.""" import argparse import logging diff --git a/testing/legion/examples/hello_world/host_test.isolate b/testing/legion/examples/hello_world/host_test.isolate deleted file mode 100644 index da4ee4e..0000000 --- a/testing/legion/examples/hello_world/host_test.isolate +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../legion.isolate', - 'client_test.isolate' - ], - 'conditions': [ - ['multi_machine == 1', { - 'variables': { - 'command': [ - 'host_test.py', - ], - 'files': [ - 'host_test.py', - ], - }, - }], - ] -} diff --git a/testing/legion/examples/hello_world/host_test.py b/testing/legion/examples/hello_world/host_test.py deleted file mode 100755 index 7a7875b..0000000 --- a/testing/legion/examples/hello_world/host_test.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""A simple host test module. - -This module runs on the host machine and is responsible for creating 2 -client machines, waiting for them, and running RPC calls on them. -""" - -# Map the legion directory so we can import the host controller. -import sys -sys.path.append('../../') - -import logging -import time - -import host_controller - - -class ExampleController(host_controller.HostController): - """A simple example controller for a test.""" - - def __init__(self): - super(ExampleController, self).__init__() - self.client1 = None - self.client2 = None - - def CreateClient(self): - """Create a client object and set the proper values.""" - client = self.NewClient( - isolate_file='client_test.isolate', - config_vars={'multi_machine': '1'}, - dimensions={'os': 'legion-linux'}, priority=200, - idle_timeout_secs=90, connection_timeout_secs=90, - verbosity=logging.INFO) - client.Create() - return client - - def SetUp(self): - """Create the client machines and wait until they connect. - - In this call the actual creation of the client machines is done in parallel - by the system. The WaitForConnect calls are performed in series but will - return as soon as the clients connect. - """ - self.client1 = self.CreateClient() - self.client2 = self.CreateClient() - self.client1.WaitForConnection() - self.client2.WaitForConnection() - - def Task(self): - """Main method to run the task code.""" - self.CallEcho(self.client1) - self.CallEcho(self.client2) - self.CallClientTest(self.client1) - self.CallClientTest(self.client2) - - def CallEcho(self, client): - """Call rpc.Echo on a client.""" - logging.info('Calling Echo on %s', client.name) - logging.info(client.rpc.Echo(client.name)) - - def CallClientTest(self, client): - """Call client_test.py name on a client.""" - logging.info('Calling Subprocess to run "./client_test.py %s"', client.name) - proc = client.rpc.subprocess.Popen(['./client_test.py', client.name]) - client.rpc.subprocess.Wait(proc) - retcode = client.rpc.subprocess.GetReturncode(proc) - stdout = client.rpc.subprocess.ReadStdout(proc) - stderr = client.rpc.subprocess.ReadStderr(proc) - logging.info('retcode: %s, stdout: %s, stderr: %s', retcode, stdout, stderr) - - -if __name__ == '__main__': - ExampleController().RunController() diff --git a/testing/legion/examples/hello_world/client_test.isolate b/testing/legion/examples/hello_world/task_test.isolate index 7135ef2..1322f31 100644 --- a/testing/legion/examples/hello_world/client_test.isolate +++ b/testing/legion/examples/hello_world/task_test.isolate @@ -11,11 +11,11 @@ 'variables': { 'command': [ 'python', - '../../client_controller.py', + '../../run_task.py', ], 'files': [ - 'client_test.py', - 'client_test.isolate' + 'task_test.isolate', + 'task_test.py', ], }, }], diff --git a/testing/legion/examples/hello_world/client_test.py b/testing/legion/examples/hello_world/task_test.py index 0333f7b..0333f7b 100755 --- a/testing/legion/examples/hello_world/client_test.py +++ b/testing/legion/examples/hello_world/task_test.py diff --git a/testing/legion/examples/subprocess/subprocess_test.isolate b/testing/legion/examples/subprocess/subprocess_test.isolate index 5b20167..6b4561f 100644 --- a/testing/legion/examples/subprocess/subprocess_test.isolate +++ b/testing/legion/examples/subprocess/subprocess_test.isolate @@ -5,7 +5,7 @@ { 'includes': [ '../../legion.isolate', - 'client.isolate' + 'task.isolate' ], 'conditions': [ ['multi_machine == 1', { diff --git a/testing/legion/examples/subprocess/subprocess_test.py b/testing/legion/examples/subprocess/subprocess_test.py index 6d8ce87..28e3fb8 100755 --- a/testing/legion/examples/subprocess/subprocess_test.py +++ b/testing/legion/examples/subprocess/subprocess_test.py @@ -13,29 +13,29 @@ import logging import time import xmlrpclib -import host_controller +import test_controller -class ExampleController(host_controller.HostController): +class ExampleTestController(test_controller.TestController): """An example controller using the remote subprocess functions.""" def __init__(self): - super(ExampleController, self).__init__() - self.client = None + super(ExampleTestController, self).__init__() + self.task = None def SetUp(self): - """Creates the client machine and waits until it connects.""" - self.client = self.NewClient( - isolate_file='client.isolate', + """Creates the task machine and waits until it connects.""" + self.task = self.CreateNewTask( + isolate_file='task.isolate', config_vars={'multi_machine': '1'}, dimensions={'os': 'legion-linux'}, idle_timeout_secs=90, connection_timeout_secs=90, verbosity=logging.DEBUG) - self.client.Create() - self.client.WaitForConnection() + self.task.Create() + self.task.WaitForConnection() - def Task(self): - """Main method to run the task code.""" + def RunTest(self): + """Main method to run the test code.""" self.TestLs() self.TestTerminate() self.TestMultipleProcesses() @@ -43,37 +43,37 @@ class ExampleController(host_controller.HostController): def TestMultipleProcesses(self): start = time.time() - sleep20 = self.client.rpc.subprocess.Popen(['sleep', '20']) - sleep10 = self.client.rpc.subprocess.Popen(['sleep', '10']) + sleep20 = self.task.rpc.subprocess.Popen(['sleep', '20']) + sleep10 = self.task.rpc.subprocess.Popen(['sleep', '10']) - self.client.rpc.subprocess.Wait(sleep10) + self.task.rpc.subprocess.Wait(sleep10) elapsed = time.time() - start assert elapsed >= 10 and elapsed < 11 - self.client.rpc.subprocess.Wait(sleep20) + self.task.rpc.subprocess.Wait(sleep20) elapsed = time.time() - start assert elapsed >= 20 - self.client.rpc.subprocess.Delete(sleep20) - self.client.rpc.subprocess.Delete(sleep10) + self.task.rpc.subprocess.Delete(sleep20) + self.task.rpc.subprocess.Delete(sleep10) def TestTerminate(self): start = time.time() - proc = self.client.rpc.subprocess.Popen(['sleep', '20']) - self.client.rpc.subprocess.Terminate(proc) # Implicitly deleted + proc = self.task.rpc.subprocess.Popen(['sleep', '20']) + self.task.rpc.subprocess.Terminate(proc) # Implicitly deleted try: - self.client.rpc.subprocess.Wait(proc) + self.task.rpc.subprocess.Wait(proc) except xmlrpclib.Fault: pass assert time.time() - start < 20 def TestLs(self): - proc = self.client.rpc.subprocess.Popen(['ls']) - self.client.rpc.subprocess.Wait(proc) - assert self.client.rpc.subprocess.GetReturncode(proc) == 0 - assert 'client.isolate' in self.client.rpc.subprocess.ReadStdout(proc) - self.client.rpc.subprocess.Delete(proc) + proc = self.task.rpc.subprocess.Popen(['ls']) + self.task.rpc.subprocess.Wait(proc) + assert self.task.rpc.subprocess.GetReturncode(proc) == 0 + assert 'task.isolate' in self.task.rpc.subprocess.ReadStdout(proc) + self.task.rpc.subprocess.Delete(proc) if __name__ == '__main__': - ExampleController().RunController() + ExampleTestController().RunController() diff --git a/testing/legion/examples/subprocess/client.isolate b/testing/legion/examples/subprocess/task.isolate index 611562c..534275df 100644 --- a/testing/legion/examples/subprocess/client.isolate +++ b/testing/legion/examples/subprocess/task.isolate @@ -11,10 +11,10 @@ 'variables': { 'command': [ 'python', - '../../client_controller.py', + '../../run_task.py', ], 'files': [ - 'client.isolate' + 'task.isolate' ], }, }], diff --git a/testing/legion/host_controller.py b/testing/legion/host_controller.py deleted file mode 100644 index dadcba4..0000000 --- a/testing/legion/host_controller.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Defines the host controller base library. - -This module is the basis on which host controllers are built and executed. -""" - -import logging -import sys - -#pylint: disable=relative-import -import client_lib -import common_lib -import discovery_server - - -class HostController(object): - """The base host controller class.""" - - def __init__(self): - self._discovery_server = discovery_server.DiscoveryServer() - - def SetUp(self): - """Setup method used by the subclass.""" - pass - - def Task(self): - """Main task method used by the subclass.""" - pass - - def TearDown(self): - """Teardown method used by the subclass.""" - pass - - def NewClient(self, *args, **kwargs): - controller = client_lib.ClientController(*args, **kwargs) - self._discovery_server.RegisterClientCallback( - controller.otp, controller.OnConnect) - return controller - - def RunController(self): - """Main entry point for the controller.""" - print ' '.join(sys.argv) - common_lib.InitLogging() - self._discovery_server.Start() - - error = None - tb = None - try: - self.SetUp() - self.Task() - except Exception as e: - # Defer raising exceptions until after TearDown and _TearDown are called. - error = e - tb = sys.exc_info()[-1] - try: - self.TearDown() - except Exception as e: - # Defer raising exceptions until after _TearDown is called. - # Note that an error raised here will obscure any errors raised - # previously. - error = e - tb = sys.exc_info()[-1] - - self._discovery_server.Shutdown() - client_lib.ClientController.ReleaseAllControllers() - if error: - raise error, None, tb #pylint: disable=raising-bad-type diff --git a/testing/legion/legion.isolate b/testing/legion/legion.isolate index 463764d..774b27a 100644 --- a/testing/legion/legion.isolate +++ b/testing/legion/legion.isolate @@ -6,14 +6,14 @@ 'variables': { 'files': [ '__init__.py', - 'client_controller.py', - 'client_lib.py', - 'client_rpc_methods.py', - 'client_rpc_server.py', 'common_lib.py', - 'discovery_server.py', - 'host_controller.py', 'legion.isolate', + 'rpc_methods.py', + 'rpc_server.py', + 'run_task.py', + 'task_controller.py', + 'task_registration_server.py', + 'test_controller.py', '../../tools/swarming_client/', ], }, diff --git a/testing/legion/client_rpc_methods.py b/testing/legion/rpc_methods.py index e43a7d8..7f17e23 100644 --- a/testing/legion/client_rpc_methods.py +++ b/testing/legion/rpc_methods.py @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Defines the client RPC methods.""" +"""Defines the task RPC methods.""" import os import sys diff --git a/testing/legion/client_rpc_server.py b/testing/legion/rpc_server.py index 7a5f565..43b4317 100644 --- a/testing/legion/client_rpc_server.py +++ b/testing/legion/rpc_server.py @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""The client RPC server code. +"""The task RPC server code. This server is an XML-RPC server which serves code from -client_rpc_methods.RPCMethods. +rpc_methods.RPCMethods. This server will run until shutdown is called on the server object. This can be achieved in 2 ways: @@ -22,8 +22,8 @@ import SimpleXMLRPCServer import SocketServer #pylint: disable=relative-import -import client_rpc_methods import common_lib +import rpc_methods class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): @@ -33,10 +33,10 @@ class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): """ def do_POST(self): - """Verifies the client is authorized to perform RPCs.""" + """Verifies the task is authorized to perform RPCs.""" if self.client_address[0] != self.server.authorized_address: logging.error('Received unauthorized RPC request from %s', - self.client_address[0]) + self.task_address[0]) self.send_response(403) response = 'Forbidden' self.send_header('Content-type', 'text/plain') @@ -60,7 +60,7 @@ class RPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, self.authorized_address = authorized_address self.idle_timeout_secs = idle_timeout_secs - self.register_instance(client_rpc_methods.RPCMethods(self)) + self.register_instance(rpc_methods.RPCMethods(self)) self._shutdown_requested_event = threading.Event() self._rpc_received_event = threading.Event() diff --git a/testing/legion/client_controller.py b/testing/legion/run_task.py index dd80c29..6a55073 100755 --- a/testing/legion/client_controller.py +++ b/testing/legion/run_task.py @@ -3,11 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""The main client_controller code. - -This code is the main entry point for the client machines and handles -registering with the host server and running the local RPC server. -""" +"""The main task entrypoint.""" import argparse import logging @@ -16,32 +12,32 @@ import sys import time #pylint: disable=relative-import -import client_rpc_server import common_lib +import rpc_server def main(): print ' '.join(sys.argv) common_lib.InitLogging() - logging.info('Client controller starting') + logging.info('Task starting') parser = argparse.ArgumentParser() parser.add_argument('--otp', help='One time token used to authenticate with the host') - parser.add_argument('--host', - help='The ip address of the host') + parser.add_argument('--controller', + help='The ip address of the controller machine') parser.add_argument('--idle-timeout', type=int, default=common_lib.DEFAULT_TIMEOUT_SECS, help='The idle timeout for the rpc server in seconds') args, _ = parser.parse_known_args() logging.info( - 'Registering with discovery server at %s using OTP %s', args.host, - args.otp) - server = common_lib.ConnectToServer(args.host).RegisterClient( + 'Registering with registration server at %s using OTP "%s"', + args.controller, args.otp) + server = common_lib.ConnectToServer(args.controller).RegisterTask( args.otp, common_lib.MY_IP) - server = client_rpc_server.RPCServer(args.host, args.idle_timeout) + server = rpc_server.RPCServer(args.controller, args.idle_timeout) server.serve_forever() return 0 diff --git a/testing/legion/client_lib.py b/testing/legion/task_controller.py index 4656cac..e0812b4 100644 --- a/testing/legion/client_lib.py +++ b/testing/legion/task_controller.py @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Defines the client library.""" +"""Defines the task controller library.""" import argparse import datetime @@ -30,11 +30,24 @@ class ConnectionTimeoutError(Error): pass -class ClientController(object): - """Creates, configures, and controls a client machine.""" +class TaskController(object): + """Provisions, configures, and controls a task machine. - _client_count = 0 - _controllers = [] + This class is an abstraction of a physical task machine. It provides an + end to end API for controlling a task machine. Operations on the task machine + are performed using the instance's "rpc" property. A simple end to end + scenario is as follows: + + task = TaskController(...) + task.Create() + task.WaitForConnection() + proc = task.rpc.subprocess.Popen(['ls']) + print task.rpc.subprocess.GetStdout(proc) + task.Release() + """ + + _task_count = 0 + _tasks = [] def __init__(self, isolate_file, config_vars, dimensions, priority=100, idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS, @@ -42,10 +55,10 @@ class ClientController(object): verbosity='ERROR', name=None): assert isinstance(config_vars, dict) assert isinstance(dimensions, dict) - type(self)._controllers.append(self) - type(self)._client_count += 1 + type(self)._tasks.append(self) + type(self)._task_count += 1 self.verbosity = verbosity - self._name = name or 'Client%d' % type(self)._client_count + self._name = name or 'Task%d' % type(self)._task_count self._priority = priority self._isolate_file = isolate_file self._isolated_file = isolate_file + 'd' @@ -61,14 +74,14 @@ class ClientController(object): parser = argparse.ArgumentParser() parser.add_argument('--isolate-server') parser.add_argument('--swarming-server') - parser.add_argument('--client-connection-timeout-secs', + parser.add_argument('--task-connection-timeout-secs', default=common_lib.DEFAULT_TIMEOUT_SECS) args, _ = parser.parse_known_args() self._isolate_server = args.isolate_server self._swarming_server = args.swarming_server self._connection_timeout_secs = (connection_timeout_secs or - args.client_connection_timeout_secs) + args.task_connection_timeout_secs) @property def name(self): @@ -107,31 +120,31 @@ class ClientController(object): self._verbosity = level #pylint: disable=attribute-defined-outside-init @classmethod - def ReleaseAllControllers(cls): - for controller in cls._controllers: - controller.Release() + def ReleaseAllTasks(cls): + for task in cls._tasks: + task.Release() def _CreateOTP(self): """Creates the OTP.""" - host_name = socket.gethostname() + controller_name = socket.gethostname() test_name = os.path.basename(sys.argv[0]) creation_time = datetime.datetime.utcnow() - otp = 'client:%s-host:%s-test:%s-creation:%s' % ( - self._name, host_name, test_name, creation_time) + otp = 'task:%s controller:%s test:%s creation:%s' % ( + self._name, controller_name, test_name, creation_time) return otp def Create(self): - """Creates the client machine.""" + """Creates the task machine.""" logging.info('Creating %s', self.name) self._connect_event.clear() self._ExecuteIsolate() self._ExecuteSwarming() def WaitForConnection(self): - """Waits for the client machine to connect. + """Waits for the task machine to connect. Raises: - ConnectionTimeoutError if the client doesn't connect in time. + ConnectionTimeoutError if the task doesn't connect in time. """ logging.info('Waiting for %s to connect with a timeout of %d seconds', self._name, self._connection_timeout_secs) @@ -140,7 +153,7 @@ class ClientController(object): raise ConnectionTimeoutError('%s failed to connect' % self.name) def Release(self): - """Quits the client's RPC server so it can release the machine.""" + """Quits the task's RPC server so it can release the machine.""" if self._rpc is not None and self._connected: logging.info('Releasing %s', self._name) try: @@ -186,7 +199,7 @@ class ClientController(object): cmd.extend([ '--', - '--host', common_lib.MY_IP, + '--controller', common_lib.MY_IP, '--otp', self._otp, '--verbosity', self._verbosity, '--idle-timeout', str(self._idle_timeout_secs), @@ -203,7 +216,7 @@ class ClientController(object): raise Error(stderr) def OnConnect(self, ip_address): - """Receives client ip address on connection.""" + """Receives task ip address on connection.""" self._ip_address = ip_address self._connected = True self._rpc = common_lib.ConnectToServer(self._ip_address) diff --git a/testing/legion/discovery_server.py b/testing/legion/task_registration_server.py index 94786ce..52ba727 100644 --- a/testing/legion/discovery_server.py +++ b/testing/legion/task_registration_server.py @@ -2,11 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""The discovery server used to register clients. +"""The registration server used to register tasks. -The discovery server is started by the host controller and allows the clients -to register themselves when they start. Authentication of the client controllers -is based on an OTP passed to the client controller binary on startup. +The registration server is started by the test controller and allows the tasks +to register themselves when they start. Authentication of the tasks controllers +is based on an OTP passed to the run_task binary on startup. """ import logging @@ -18,38 +18,38 @@ import SimpleXMLRPCServer import common_lib -class DiscoveryServer(object): +class TaskRegistrationServer(object): """Discovery server run on the host.""" def __init__(self): - self._expected_clients = {} + self._expected_tasks = {} self._rpc_server = None self._thread = None - def _RegisterClientRPC(self, otp, ip): - """The RPC used by a client to register with the discovery server.""" - assert otp in self._expected_clients - cb = self._expected_clients.pop(otp) + def _RegisterTaskRPC(self, otp, ip): + """The RPC used by a task to register with the registration server.""" + assert otp in self._expected_tasks + cb = self._expected_tasks.pop(otp) cb(ip) - def RegisterClientCallback(self, otp, callback): + def RegisterTaskCallback(self, otp, callback): """Registers a callback associated with an OTP.""" assert callable(callback) - self._expected_clients[otp] = callback + self._expected_tasks[otp] = callback def Start(self): - """Starts the discovery server.""" - logging.debug('Starting discovery server') + """Starts the registration server.""" + logging.debug('Starting task registration server') self._rpc_server = SimpleXMLRPCServer.SimpleXMLRPCServer( (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT), allow_none=True, logRequests=False) self._rpc_server.register_function( - self._RegisterClientRPC, 'RegisterClient') + self._RegisterTaskRPC, 'RegisterTask') self._thread = threading.Thread(target=self._rpc_server.serve_forever) self._thread.start() def Shutdown(self): """Shuts the discovery server down.""" if self._thread and self._thread.is_alive(): - logging.debug('Shutting down discovery server') + logging.debug('Shutting down task registration server') self._rpc_server.shutdown() diff --git a/testing/legion/test_controller.py b/testing/legion/test_controller.py new file mode 100644 index 0000000..2703fad --- /dev/null +++ b/testing/legion/test_controller.py @@ -0,0 +1,69 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Defines the test controller base library. + +This module is the basis on which test controllers are built and executed. +""" + +import logging +import sys + +#pylint: disable=relative-import +import common_lib +import task_controller +import task_registration_server + + +class TestController(object): + """The base test controller class.""" + + def __init__(self): + self._registration_server = ( + task_registration_server.TaskRegistrationServer()) + + def SetUp(self): + """Setup method used by the subclass.""" + pass + + def RunTest(self): + """Main test method used by the subclass.""" + raise NotImplementedError() + + def TearDown(self): + """Teardown method used by the subclass.""" + pass + + def CreateNewTask(self, *args, **kwargs): + task = task_controller.TaskController(*args, **kwargs) + self._registration_server.RegisterTaskCallback( + task.otp, task.OnConnect) + return task + + def RunController(self): + """Main entry point for the controller.""" + print ' '.join(sys.argv) + common_lib.InitLogging() + self._registration_server.Start() + + error = None + tb = None + try: + self.SetUp() + self.RunTest() + except Exception as e: + # Defer raising exceptions until after TearDown is called. + error = e + tb = sys.exc_info()[-1] + try: + self.TearDown() + except Exception as e: + if not tb: + error = e + tb = sys.exc_info()[-1] + + self._registration_server.Shutdown() + task_controller.TaskController.ReleaseAllTasks() + if error: + raise error, None, tb #pylint: disable=raising-bad-type |