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

Side by Side Diff: testing/legion/client_rpc_server.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: Addressing initial comments 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 """The client RPC server code.
5
6 This server is an XML-RPC server which serves code from
7 client_rpc_methods.RPCMethods.
8
9 This server will run until shutdown is called on the server object. This can
10 be achieved in 2 ways:
11
12 - Calling the Quit RPC method defined in RPCMethods
13 - Not receiving any calls within the RPC_IDLE_TIMEOUT time.
14
15 The second state is used in a case where the host controller has crashed and
16 cannot send a Quit command.
17 """
18
19 import logging
20 import threading
21 import time
22 import xmlrpclib
23 import SimpleXMLRPCServer
24 import SocketServer
25
26 #pylint: disable=relative-import
27 import client_rpc_methods
28
29 SERVER_ADDRESS = ''
30 SERVER_PORT = 31710
31 IDLE_TIMEOUT = 30 * 60 # 30 minutes
32
33
34 class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
35 """A custom handler class that provides simple authentication.
Marc-Antoine Ruel (Google) 2015/01/30 21:58:39 """Restricts access to only specified IP address.
Mike Meade 2015/02/03 01:18:08 Done.
36
37 Only requests received from specified addresses are fulfilled. All other
38 requests result in a 403 Forbidden result.
39 """
40
41 def Report403(self):
42 """Report a 403 Forbidden error."""
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 Not necessary.
Mike Meade 2015/02/03 01:18:08 Done.
43 self.send_response(403)
44 response = 'Forbidden'
45 self.send_header("Content-type", "text/plain")
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 Did you copy-paste this from somewhere? It looks l
Mike Meade 2015/02/03 01:18:08 Done.
46 self.send_header("Content-length", str(len(response)))
47 self.end_headers()
48 self.wfile.write(response)
49
50 def do_POST(self):
51 """Custom do_POST that verifies the client is authorized to perform RPCs."""
Marc-Antoine Ruel (Google) 2015/01/30 21:58:39 """Verifies the client is ..."""
Mike Meade 2015/02/03 01:18:08 Done.
52 if self.client_address[0] not in self.server.authorized_addresses:
53 logging.error('Received unauthorized RPC request from %s',
54 self.client_address[0])
55 self.Report403()
56 return
57 self.server.RPCReceived()
58 return SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self)
59
60
61 class RPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer,
62 SocketServer.ThreadingMixIn):
63 """Custom RPC server that supports authorized client addresses."""
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 """Restricts all endpoints to only specified IP ad
Mike Meade 2015/02/03 01:18:09 Done.
64
65 def __init__(self, address=None, port=None):
Marc-Antoine Ruel (Google) 2015/01/30 21:58:39 def __init__(self, *args, **kwargs):
Mike Meade 2015/02/03 01:18:08 I was able to completely get rid of all args.
66 """ctor.
Marc-Antoine Ruel (Google) 2015/01/30 21:58:39 Remove all docstring.
Mike Meade 2015/02/03 01:18:08 Done.
67
68 Args:
69 address: The address to serve on ('', 'localhost', etc).
70 port: The port to serve on.
71 """
72 address = address or SERVER_ADDRESS
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 I don't like that.
Mike Meade 2015/02/03 01:18:08 Done.
73 port = port or SERVER_PORT
74 SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(
75 self, (address, port), allow_none=True, logRequests=False,
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 super(RPCServer, self).__init__( *args, allow_
Mike Meade 2015/02/03 01:18:09 Yes its an old style class.
76 requestHandler=RequestHandler)
77 self.authorized_addresses = []
78 self._idle_timeout = IDLE_TIMEOUT
79 self.register_instance(client_rpc_methods.RPCMethods(self))
80 self._StartIdleTimeoutThread()
81
82 def _StartIdleTimeoutThread(self):
83 """Create and start the idle timeout monitor thread."""
84 self.shutdown_requested_event = threading.Event()
85 self.rpc_received_event = threading.Event()
86 self.idle_thread = threading.Thread(target=self.CheckForIdleQuit)
87
88 def AddAuthorizedAddress(self, ip_address):
89 """Add an authorized caller address to the server.
90
91 Args:
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 Bof
Mike Meade 2015/02/03 01:18:09 Done.
92 ip_address: The IP address of the caller.
93 """
94 self.authorized_addresses.append(ip_address)
95
96 def SetIdleTimeout(self, timeout):
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 s/timeout/timeout_secs/ then you can strip the co
Mike Meade 2015/02/03 01:18:09 Done.
97 """Sets the idle timeout for the server.
98
99 Args:
100 timeout: The server timeout in seconds.
101 """
102 logging.debug('Idle timeout set to %s', timeout)
103 self._idle_timeout = timeout
104
105 @staticmethod
106 def Connect(client, port=None):
Marc-Antoine Ruel (Google) 2015/01/30 21:58:40 I'd prefer this to not be a static method, it's co
Mike Meade 2015/02/03 01:18:08 Done.
107 """Connect to an RPC server.
108
109 Args:
110 client: The client address which the RPC server is running on.
111 port: The port the client RPC server is running on.
112
113 Returns:
114 An XML-RPC connection to the client RPC server.
115 """
116 port = port or SERVER_PORT
117 server = 'http://%s:%d' % (client, port)
118 logging.debug('Connecting to RPC server at %s', server)
119 return xmlrpclib.Server(server)
120
121 def shutdown(self):
122 """Shutdown the server.
123
124 This overloaded method sets the shutdown_requested_event to allow the
125 idle timeout thread to quit.
126 """
127 self.shutdown_requested_event.set()
128 SimpleXMLRPCServer.SimpleXMLRPCServer.shutdown(self)
129 logging.info('Server shutdown complete')
130
131 def serve_forever(self, poll_interval=0.5):
132 """Serve forever.
133
134 This overloaded method starts the idle timeout thread before calling
135 serve_forever. This ensures the idle timer thread doesn't get started
136 without the server running.
137
138 Args:
139 poll_interval: The interval to poll for shutdown.
140 """
141 logging.info('RPC server starting')
142 self.idle_thread.start()
143 SimpleXMLRPCServer.SimpleXMLRPCServer.serve_forever(self, poll_interval)
144
145 def _dispatch(self, method, params):
146 """Dispatch the call to the correct method with the provided params.
147
148 This overloaded method adds logging to help trace connection and
149 call problems.
150
151 Args:
152 method: The method name to call.
153 params: A tuple of parameters to pass.
154
155 Returns:
156 The result of the parent class' _dispatch method.
157 """
158 logging.debug('Calling %s%s', method, params)
159 return SimpleXMLRPCServer.SimpleXMLRPCServer._dispatch(self, method, params)
160
161 def RPCReceived(self):
162 """Sets the rpc_received_event whenever an RPC is received.
163
164 Setting this event effectively resets the idle timeout thread.
165 """
166 self.rpc_received_event.set()
167
168 def CheckForIdleQuit(self):
169 """Check for, and exit, if the server is idle for too long.
170
171 This method must be run in a separate thread to avoid a deadlock when
172 calling server.shutdown.
173 """
174 timeout = time.time() + self._idle_timeout
175 while time.time() < timeout:
176 if self.shutdown_requested_event.is_set():
177 # An external source called shutdown()
178 return
179 elif self.rpc_received_event.is_set():
180 # An RPC was received, reset the timeout
181 logging.debug('Resetting the idle timeout')
182 timeout = time.time() + self._idle_timeout
183 self.rpc_received_event.clear()
184 time.sleep(1)
185 # We timed out, kill the server
186 logging.warning('Shutting down the server due to the idle timeout')
187 self.shutdown()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698