| Index: testing/legion/client_lib.py
 | 
| diff --git a/testing/legion/client_lib.py b/testing/legion/client_lib.py
 | 
| deleted file mode 100644
 | 
| index 4656cac6462fa4265aef590327ab67143f095849..0000000000000000000000000000000000000000
 | 
| --- a/testing/legion/client_lib.py
 | 
| +++ /dev/null
 | 
| @@ -1,211 +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 client library."""
 | 
| -
 | 
| -import argparse
 | 
| -import datetime
 | 
| -import logging
 | 
| -import os
 | 
| -import socket
 | 
| -import subprocess
 | 
| -import sys
 | 
| -import tempfile
 | 
| -import threading
 | 
| -import xmlrpclib
 | 
| -
 | 
| -#pylint: disable=relative-import
 | 
| -import common_lib
 | 
| -
 | 
| -ISOLATE_PY = os.path.join(common_lib.SWARMING_DIR, 'isolate.py')
 | 
| -SWARMING_PY = os.path.join(common_lib.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 = self._CreateOTP()
 | 
| -    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 _CreateOTP(self):
 | 
| -    """Creates the OTP."""
 | 
| -    host_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)
 | 
| -    return otp
 | 
| -
 | 
| -  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),
 | 
| -        ]
 | 
| -
 | 
| -    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),
 | 
| -        ])
 | 
| -
 | 
| -    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 = p.communicate()
 | 
| -    if p.returncode != 0:
 | 
| -      raise Error(stderr)
 | 
| -
 | 
| -  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()
 | 
| 
 |