Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Unified Diff: testing/legion/client_lib.py

Issue 890773003: Adding the initial code for Omnibot multi-machine support (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Doing a little refactoring and changes. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « testing/legion/client_controller.py ('k') | testing/legion/client_rpc_methods.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: testing/legion/client_lib.py
diff --git a/testing/legion/client_lib.py b/testing/legion/client_lib.py
new file mode 100644
index 0000000000000000000000000000000000000000..da43ed2c36f08b3ac754e291ea00500e1572150a
--- /dev/null
+++ b/testing/legion/client_lib.py
@@ -0,0 +1,208 @@
+# 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 client library."""
+
+import argparse
+import logging
+import os
+import socket
+import subprocess
+import tempfile
+import threading
+import uuid
+import xmlrpclib
+import StringIO
+
+#pylint: disable=relative-import
+import common_lib
+
+THIS_DIR = os.path.dirname(os.path.abspath(__file__))
+SWARMING_DIR = os.path.join(THIS_DIR, '..', '..', 'tools/swarming_client')
+ISOLATE_PY = os.path.join(SWARMING_DIR, 'isolate.py')
+SWARMING_PY = os.path.join(SWARMING_DIR, 'swarming.py')
+
+
+class Error(Exception):
+ pass
+
+
+class ConnectionTimeoutError(Error):
+ pass
+
+
+class ClientController(object):
+ """Creates, configures, and controls a client machine."""
+
+ _client_count = 0
+ _controllers = []
+
+ def __init__(self, isolate_file, config_vars, dimensions, priority=100,
+ idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
+ connection_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
+ verbosity='ERROR', name=None):
+ assert isinstance(config_vars, dict)
+ assert isinstance(dimensions, dict)
+ type(self)._controllers.append(self)
+ type(self)._client_count += 1
+ self.verbosity = verbosity
+ self._name = name or 'Client%d' % type(self)._client_count
+ self._priority = priority
+ self._isolate_file = isolate_file
+ self._isolated_file = isolate_file + 'd'
+ self._idle_timeout_secs = idle_timeout_secs
+ self._config_vars = config_vars
+ self._dimensions = dimensions
+ self._connect_event = threading.Event()
+ self._connected = False
+ self._ip_address = None
+ self._otp = str(uuid.uuid1())
+ self._rpc = None
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--isolate-server')
+ parser.add_argument('--swarming-server')
+ parser.add_argument('--client-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)
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def otp(self):
+ return self._otp
+
+ @property
+ def connected(self):
+ return self._connected
+
+ @property
+ def connect_event(self):
+ return self._connect_event
+
+ @property
+ def rpc(self):
+ return self._rpc
+
+ @property
+ def verbosity(self):
+ return self._verbosity
+
+ @verbosity.setter
+ def verbosity(self, level):
+ """Sets the verbosity level as a string.
+
+ Either a string ('INFO', 'DEBUG', etc) or a logging level (logging.INFO,
+ logging.DEBUG, etc) is allowed.
+ """
+ assert isinstance(level, (str, int))
+ if isinstance(level, int):
+ level = logging.getLevelName(level)
+ self._verbosity = level #pylint: disable=attribute-defined-outside-init
+
+ @classmethod
+ def ReleaseAllControllers(cls):
+ for controller in cls._controllers:
+ controller.Release()
+
+ def Create(self):
+ """Creates the client 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.
+
+ Raises:
+ ConnectionTimeoutError if the client doesn't connect in time.
+ """
+ logging.info('Waiting for %s to connect with a timeout of %d seconds',
+ self._name, self._connection_timeout_secs)
+ self._connect_event.wait(self._connection_timeout_secs)
+ if not self._connect_event.is_set():
+ raise ConnectionTimeoutError('%s failed to connect' % self.name)
+
+ def Release(self):
+ """Quits the client'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:
+ self._rpc.Quit()
+ except (socket.error, xmlrpclib.Fault):
+ logging.error('Unable to connect to %s to call Quit', self.name)
+ self._rpc = None
+ self._connected = False
+
+ def _ExecuteIsolate(self):
+ """Executes isolate.py."""
+ cmd = [
+ 'python',
+ ISOLATE_PY,
+ 'archive',
+ '--isolate', self._isolate_file,
+ '--isolated', self._isolated_file,
+ ]
+
+ if self._isolate_server:
+ cmd.extend(['--isolate-server', self._isolate_server])
+ for key, value in self._config_vars.iteritems():
+ cmd.extend(['--config-var', key, value])
+
+ self._ExecuteProcess(cmd)
+
+ def _ExecuteSwarming(self):
+ """Executes swarming.py."""
+ cmd = [
+ 'python',
+ SWARMING_PY,
+ 'trigger',
+ self._isolated_file,
+ '--priority', str(self._priority)
M-A Ruel 2015/02/03 01:48:40 I generally prefer to keep comma even on trailing
Mike Meade 2015/02/03 17:47:57 Done.
+ ]
+
+ if self._isolate_server:
+ cmd.extend(['--isolate-server', self._isolate_server])
+ if self._swarming_server:
+ cmd.extend(['--swarming', self._swarming_server])
+ for key, value in self._dimensions.iteritems():
+ cmd.extend(['--dimension', key, value])
+
+ cmd.extend([
+ '--',
+ '--host', common_lib.MY_IP,
+ '--otp', self._otp,
+ '--verbosity', self._verbosity,
+ '--idle-timeout', str(self._idle_timeout_secs)
M-A Ruel 2015/02/03 01:48:40 same
Mike Meade 2015/02/03 17:47:57 Done.
+ ])
+
+ self._ExecuteProcess(cmd)
+
+ def _ExecuteProcess(self, cmd):
+ """Executes a process, waits for it to complete, and checks for success."""
+ logging.debug('Running %s', ' '.join(cmd))
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stderr = StringIO.StringIO()
+ while p.poll() is None:
+ _, _stderr = p.communicate()
+ stderr.write(_stderr)
+ if p.returncode != 0:
+ stderr.seek(0)
+ raise Error(stderr.read())
+
+ def OnConnect(self, ip_address):
+ """Receives client ip address on connection."""
+ self._ip_address = ip_address
+ self._connected = True
+ self._rpc = common_lib.ConnectToServer(self._ip_address)
+ logging.info('%s connected from %s', self._name, ip_address)
+ self._connect_event.set()
« no previous file with comments | « testing/legion/client_controller.py ('k') | testing/legion/client_rpc_methods.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698