OLD | NEW |
---|---|
1 # Copyright (C) 2011 Google Inc. All rights reserved. | 1 # Copyright (C) 2011 Google Inc. All rights reserved. |
2 # | 2 # |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
69 self._mappings = {} | 69 self._mappings = {} |
70 self._pid_file = None | 70 self._pid_file = None |
71 self._start_cmd = None | 71 self._start_cmd = None |
72 | 72 |
73 # Subclasses may override these fields. | 73 # Subclasses may override these fields. |
74 self._env = None | 74 self._env = None |
75 self._stdout = self._executive.PIPE | 75 self._stdout = self._executive.PIPE |
76 self._stderr = self._executive.PIPE | 76 self._stderr = self._executive.PIPE |
77 self._process = None | 77 self._process = None |
78 self._pid = None | 78 self._pid = None |
79 self._error_log = None | |
79 | 80 |
80 def start(self): | 81 def start(self): |
81 """Starts the server. It is an error to start an already started server. | 82 """Starts the server. It is an error to start an already started server. |
82 | 83 |
83 This method also stops any stale servers started by a previous instance. """ | 84 This method also stops any stale servers started by a previous instance. """ |
84 assert not self._pid, '%s server is already running' % self._name | 85 assert not self._pid, '%s server is already running' % self._name |
85 | 86 |
86 # Stop any stale servers left over from previous instances. | 87 # Stop any stale servers left over from previous instances. |
87 if self._filesystem.exists(self._pid_file): | 88 if self._filesystem.exists(self._pid_file): |
88 try: | 89 try: |
89 self._pid = int(self._filesystem.read_text_file(self._pid_file)) | 90 self._pid = int(self._filesystem.read_text_file(self._pid_file)) |
91 _log.debug('stale %s pid file, pid %d' % (self._name, self._pid) ) | |
90 self._stop_running_server() | 92 self._stop_running_server() |
91 except (ValueError, UnicodeDecodeError): | 93 except (ValueError, UnicodeDecodeError): |
92 # These could be raised if the pid file is corrupt. | 94 # These could be raised if the pid file is corrupt. |
93 self._remove_pid_file() | 95 self._remove_pid_file() |
94 self._pid = None | 96 self._pid = None |
95 | 97 |
96 self._remove_stale_logs() | 98 self._remove_stale_logs() |
97 self._prepare_config() | 99 self._prepare_config() |
98 self._check_that_all_ports_are_available() | 100 self._check_that_all_ports_are_available() |
99 | 101 |
100 self._pid = self._spawn_process() | 102 self._pid = self._spawn_process() |
101 | 103 |
102 if self._wait_for_action(self._is_server_running_on_all_ports): | 104 if self._wait_for_action(self._is_server_running_on_all_ports): |
103 _log.debug("%s successfully started (pid = %d)" % (self._name, self. _pid)) | 105 _log.debug("%s successfully started (pid = %d)" % (self._name, self. _pid)) |
104 else: | 106 else: |
107 self._dump_logs() | |
105 self._stop_running_server() | 108 self._stop_running_server() |
106 raise ServerError('Failed to start %s server' % self._name) | 109 raise ServerError('Failed to start %s server' % self._name) |
107 | 110 |
108 def stop(self): | 111 def stop(self): |
109 """Stops the server. Stopping a server that isn't started is harmless."" " | 112 """Stops the server. Stopping a server that isn't started is harmless."" " |
110 actual_pid = None | 113 actual_pid = None |
111 try: | 114 try: |
112 if self._filesystem.exists(self._pid_file): | 115 if self._filesystem.exists(self._pid_file): |
113 try: | 116 try: |
114 actual_pid = int(self._filesystem.read_text_file(self._pid_f ile)) | 117 actual_pid = int(self._filesystem.read_text_file(self._pid_f ile)) |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
164 self._filesystem.write_text_file(self._pid_file, str(pid)) | 167 self._filesystem.write_text_file(self._pid_file, str(pid)) |
165 return pid | 168 return pid |
166 | 169 |
167 def _stop_running_server(self): | 170 def _stop_running_server(self): |
168 self._wait_for_action(self._check_and_kill) | 171 self._wait_for_action(self._check_and_kill) |
169 if self._filesystem.exists(self._pid_file): | 172 if self._filesystem.exists(self._pid_file): |
170 self._filesystem.remove(self._pid_file) | 173 self._filesystem.remove(self._pid_file) |
171 | 174 |
172 def _check_and_kill(self): | 175 def _check_and_kill(self): |
173 if self._executive.check_running_pid(self._pid): | 176 if self._executive.check_running_pid(self._pid): |
177 _log.debug('pid %d is running, killing it' % self._pid) | |
174 host = self._port_obj.host | 178 host = self._port_obj.host |
175 self._executive.kill_process(self._pid) | 179 self._executive.kill_process(self._pid) |
176 return False | 180 return False |
181 else: | |
182 _log.debug('pid %d is not running' % self._pid) | |
183 | |
177 return True | 184 return True |
178 | 185 |
179 def _remove_pid_file(self): | 186 def _remove_pid_file(self): |
180 if self._filesystem.exists(self._pid_file): | 187 if self._filesystem.exists(self._pid_file): |
181 self._filesystem.remove(self._pid_file) | 188 self._filesystem.remove(self._pid_file) |
182 | 189 |
183 def _remove_log_files(self, folder, starts_with): | 190 def _remove_log_files(self, folder, starts_with): |
184 files = self._filesystem.listdir(folder) | 191 files = self._filesystem.listdir(folder) |
185 for file in files: | 192 for file in files: |
186 if file.startswith(starts_with): | 193 if file.startswith(starts_with): |
187 full_path = self._filesystem.join(folder, file) | 194 full_path = self._filesystem.join(folder, file) |
188 self._filesystem.remove(full_path) | 195 self._filesystem.remove(full_path) |
189 | 196 |
197 def _dump_logs(self): | |
eseidel
2014/07/23 23:29:23
I didn't know what "dump" meant until I read it.
Dirk Pranke
2014/07/23 23:51:00
I thought dump was fairly conventional, but maybe
| |
198 _log.error('%s dumping logs' % self._name) | |
199 if self._process: | |
200 _log.error('%s returncode %s' % (self._name, str(self._process.retur ncode))) | |
201 if self._process.stderr: | |
202 stderr_text = self._process.stderr.read() | |
203 if stderr_text: | |
204 _log.error('%s stderr:' % self._name) | |
205 for line in stderr_text.splitlines(): | |
206 _log.error(' %s' % line) | |
207 else: | |
208 _log.error('%s no stderr' % self._name) | |
209 else: | |
210 _log.error('%s no stderr handle' % self._name) | |
211 else: | |
212 _log.error('%s no process' % self._name) | |
213 if self._error_log: | |
214 error_log_text = self._filesystem.read_text_file(self._error_log) | |
215 if error_log_text: | |
216 _log.error('%s error log (%s) contents:' % (self._name, self._er ror_log)) | |
217 for line in error_log_text.splitlines(): | |
218 _log.error(' %s' % line) | |
219 else: | |
220 _log.error('%s error log empty' % self._name) | |
221 _log.error('') | |
222 else: | |
223 _log.error('%s no error log' % self._name) | |
224 | |
190 def _wait_for_action(self, action, wait_secs=20.0, sleep_secs=1.0): | 225 def _wait_for_action(self, action, wait_secs=20.0, sleep_secs=1.0): |
191 """Repeat the action for wait_sec or until it succeeds, sleeping for sle ep_secs | 226 """Repeat the action for wait_sec or until it succeeds, sleeping for sle ep_secs |
192 in between each attempt. Returns whether it succeeded.""" | 227 in between each attempt. Returns whether it succeeded.""" |
193 start_time = time.time() | 228 start_time = time.time() |
194 while time.time() - start_time < wait_secs: | 229 while time.time() - start_time < wait_secs: |
195 if action(): | 230 if action(): |
196 return True | 231 return True |
197 _log.debug("Waiting for action: %s" % action) | 232 _log.debug("Waiting for action: %s" % action) |
198 time.sleep(sleep_secs) | 233 time.sleep(sleep_secs) |
199 | 234 |
200 return False | 235 return False |
201 | 236 |
202 def _is_server_running_on_all_ports(self): | 237 def _is_server_running_on_all_ports(self): |
203 """Returns whether the server is running on all the desired ports.""" | 238 """Returns whether the server is running on all the desired ports.""" |
204 if not self._executive.check_running_pid(self._pid): | 239 if not self._executive.check_running_pid(self._pid): |
205 _log.debug("Server isn't running at all") | 240 _log.error("Server isn't running at all") |
241 self._dump_logs() | |
206 raise ServerError("Server exited") | 242 raise ServerError("Server exited") |
207 | 243 |
208 for mapping in self._mappings: | 244 for mapping in self._mappings: |
209 s = socket.socket() | 245 s = socket.socket() |
210 port = mapping['port'] | 246 port = mapping['port'] |
211 try: | 247 try: |
212 s.connect(('localhost', port)) | 248 s.connect(('localhost', port)) |
213 _log.debug("Server running on %d" % port) | 249 _log.debug("Server running on %d" % port) |
214 except IOError, e: | 250 except IOError, e: |
215 if e.errno not in (errno.ECONNREFUSED, errno.ECONNRESET): | 251 if e.errno not in (errno.ECONNREFUSED, errno.ECONNRESET): |
216 raise | 252 raise |
217 _log.debug("Server NOT running on %d: %s" % (port, e)) | 253 _log.debug("Server NOT running on %d: %s" % (port, e)) |
218 return False | 254 return False |
219 finally: | 255 finally: |
220 s.close() | 256 s.close() |
221 return True | 257 return True |
222 | 258 |
223 def _check_that_all_ports_are_available(self): | 259 def _check_that_all_ports_are_available(self): |
224 for mapping in self._mappings: | 260 for mapping in self._mappings: |
225 s = socket.socket() | 261 s = socket.socket() |
226 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | 262 # s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
eseidel
2014/07/23 23:29:23
Intentional? Comment?
Dirk Pranke
2014/07/23 23:51:00
Definitely intentional; see the comments in patchs
| |
227 port = mapping['port'] | 263 port = mapping['port'] |
228 try: | 264 try: |
229 s.bind(('localhost', port)) | 265 s.bind(('localhost', port)) |
230 except IOError, e: | 266 except IOError, e: |
231 if e.errno in (errno.EALREADY, errno.EADDRINUSE): | 267 if e.errno in (errno.EALREADY, errno.EADDRINUSE): |
232 raise ServerError('Port %d is already in use.' % port) | 268 raise ServerError('Port %d is already in use.' % port) |
233 elif sys.platform == 'win32' and e.errno in (errno.WSAEACCES,): # pylint: disable=E1101 | 269 elif sys.platform == 'win32' and e.errno in (errno.WSAEACCES,): # pylint: disable=E1101 |
234 raise ServerError('Port %d is already in use.' % port) | 270 raise ServerError('Port %d is already in use.' % port) |
235 else: | 271 else: |
236 raise | 272 raise |
237 finally: | 273 finally: |
238 s.close() | 274 s.close() |
275 _log.debug('all ports are available') | |
OLD | NEW |