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

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