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

Side by Side 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: Fixing some silly subprocess code. Created 5 years, 10 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 unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Defines the client library."""
6
7 import argparse
8 import logging
9 import os
10 import socket
11 import subprocess
12 import tempfile
13 import threading
14 import uuid
15 import xmlrpclib
16
17 #pylint: disable=relative-import
18 import common_lib
19
20 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
21 SWARMING_DIR = os.path.join(THIS_DIR, '..', '..', 'tools/swarming_client')
22 ISOLATE_PY = os.path.join(SWARMING_DIR, 'isolate.py')
23 SWARMING_PY = os.path.join(SWARMING_DIR, 'swarming.py')
24
25
26 class Error(Exception):
27 pass
28
29
30 class ConnectionTimeoutError(Error):
31 pass
32
33
34 class ClientController(object):
35 """Creates, configures, and controls a client machine."""
36
37 _client_count = 0
38 _controllers = []
39
40 def __init__(self, isolate_file, config_vars, dimensions, priority=100,
41 idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
42 connection_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
43 verbosity='ERROR', name=None):
44 assert isinstance(config_vars, dict)
45 assert isinstance(dimensions, dict)
46 type(self)._controllers.append(self)
47 type(self)._client_count += 1
48 self.verbosity = verbosity
49 self._name = name or 'Client%d' % type(self)._client_count
50 self._priority = priority
51 self._isolate_file = isolate_file
52 self._isolated_file = isolate_file + 'd'
53 self._idle_timeout_secs = idle_timeout_secs
54 self._config_vars = config_vars
55 self._dimensions = dimensions
56 self._connect_event = threading.Event()
57 self._connected = False
58 self._ip_address = None
59 self._otp = str(uuid.uuid1())
60 self._rpc = None
61
62 parser = argparse.ArgumentParser()
63 parser.add_argument('--isolate-server')
64 parser.add_argument('--swarming-server')
65 parser.add_argument('--client-connection-timeout-secs',
66 default=common_lib.DEFAULT_TIMEOUT_SECS)
67 args, _ = parser.parse_known_args()
68
69 self._isolate_server = args.isolate_server
70 self._swarming_server = args.swarming_server
71 self._connection_timeout_secs = (connection_timeout_secs or
72 args.client_connection_timeout_secs)
73
74 @property
75 def name(self):
76 return self._name
77
78 @property
79 def otp(self):
80 return self._otp
81
82 @property
83 def connected(self):
84 return self._connected
85
86 @property
87 def connect_event(self):
88 return self._connect_event
89
90 @property
91 def rpc(self):
92 return self._rpc
93
94 @property
95 def verbosity(self):
96 return self._verbosity
97
98 @verbosity.setter
99 def verbosity(self, level):
100 """Sets the verbosity level as a string.
101
102 Either a string ('INFO', 'DEBUG', etc) or a logging level (logging.INFO,
103 logging.DEBUG, etc) is allowed.
104 """
105 assert isinstance(level, (str, int))
106 if isinstance(level, int):
107 level = logging.getLevelName(level)
108 self._verbosity = level #pylint: disable=attribute-defined-outside-init
109
110 @classmethod
111 def ReleaseAllControllers(cls):
112 for controller in cls._controllers:
113 controller.Release()
114
115 def Create(self):
116 """Creates the client machine."""
117 logging.info('Creating %s', self.name)
118 self._connect_event.clear()
119 self._ExecuteIsolate()
120 self._ExecuteSwarming()
121
122 def WaitForConnection(self):
123 """Waits for the client machine to connect.
124
125 Raises:
126 ConnectionTimeoutError if the client doesn't connect in time.
127 """
128 logging.info('Waiting for %s to connect with a timeout of %d seconds',
129 self._name, self._connection_timeout_secs)
130 self._connect_event.wait(self._connection_timeout_secs)
131 if not self._connect_event.is_set():
132 raise ConnectionTimeoutError('%s failed to connect' % self.name)
133
134 def Release(self):
135 """Quits the client's RPC server so it can release the machine."""
136 if self._rpc is not None and self._connected:
137 logging.info('Releasing %s', self._name)
138 try:
139 self._rpc.Quit()
140 except (socket.error, xmlrpclib.Fault):
141 logging.error('Unable to connect to %s to call Quit', self.name)
142 self._rpc = None
143 self._connected = False
144
145 def _ExecuteIsolate(self):
146 """Executes isolate.py."""
147 cmd = [
148 'python',
149 ISOLATE_PY,
150 'archive',
151 '--isolate', self._isolate_file,
152 '--isolated', self._isolated_file,
153 ]
154
155 if self._isolate_server:
156 cmd.extend(['--isolate-server', self._isolate_server])
157 for key, value in self._config_vars.iteritems():
158 cmd.extend(['--config-var', key, value])
159
160 self._ExecuteProcess(cmd)
161
162 def _ExecuteSwarming(self):
163 """Executes swarming.py."""
164 cmd = [
165 'python',
166 SWARMING_PY,
167 'trigger',
168 self._isolated_file,
169 '--priority', str(self._priority)
170 ]
171
172 if self._isolate_server:
173 cmd.extend(['--isolate-server', self._isolate_server])
174 if self._swarming_server:
175 cmd.extend(['--swarming', self._swarming_server])
176 for key, value in self._dimensions.iteritems():
177 cmd.extend(['--dimension', key, value])
178
179 cmd.extend([
180 '--',
181 '--host', common_lib.MY_IP,
182 '--otp', self._otp,
183 '--verbosity', self._verbosity,
184 '--idle-timeout', str(self._idle_timeout_secs)
185 ])
186
187 self._ExecuteProcess(cmd)
188
189 def _ExecuteProcess(self, cmd):
190 """Executes a process, waits for it to complete, and checks for success."""
191 logging.debug('Running %s', ' '.join(cmd))
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
193 _, stderr = p.communicate()
194 if p.returncode != 0:
195 stderr.seek(0)
M-A Ruel 2015/02/03 01:48:41 Remove, just: raise Error(stderr) It's not a fi
Mike Meade 2015/02/03 17:47:57 Done.
196 raise Error(stderr.read())
197
198 def OnConnect(self, ip_address):
199 """Receives client ip address on connection."""
200 self._ip_address = ip_address
201 self._connected = True
202 self._rpc = common_lib.ConnectToServer(self._ip_address)
203 logging.info('%s connected from %s', self._name, ip_address)
204 self._connect_event.set()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698