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

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: 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 import logging
19 import threading
20 import time
21 import xmlrpclib
22 import SimpleXMLRPCServer
23 import SocketServer
24
25 #pylint: disable=relative-import
26 import client_rpc_methods
27
28 SERVER_ADDRESS = ''
29 SERVER_PORT = 31710
30 IDLE_TIMEOUT = 30 * 60 # 30 minutes
31
32
33 class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
34 """A custom handler class that provides simple authentication.
35
36 Only requests received from specified addresses are fulfilled. All other
37 requests result in a 403 Forbidden result.
38 """
39
40 def Report403(self):
41 """Report a 403 Forbidden error."""
42 self.send_response(403)
43 response = 'Forbidden'
44 self.send_header("Content-type", "text/plain")
45 self.send_header("Content-length", str(len(response)))
46 self.end_headers()
47 self.wfile.write(response)
48
49 def do_POST(self):
50 """Custom do_POST that verifies the client is authorized to perform RPCs."""
51 if self.client_address[0] not in self.server.authorized_addresses:
52 logging.error('Received unauthorized RPC request from %s',
53 self.client_address[0])
54 self.Report403()
55 return
56 self.server.RPCReceived()
57 return SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self)
58
59
60 class RPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer,
61 SocketServer.ThreadingMixIn):
62 """Custom RPC server that supports authorized client addresses."""
63
64 def __init__(self, address=None, port=None):
65 """ctor.
66
67 Args:
68 address: The address to serve on ('', 'localhost', etc).
69 port: The port to serve on.
70 """
71 address = address or SERVER_ADDRESS
72 port = port or SERVER_PORT
73 SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(
74 self, (address, port), allow_none=True, logRequests=False,
75 requestHandler=RequestHandler)
76 self.authorized_addresses = []
77 self._idle_timeout = IDLE_TIMEOUT
78 self.register_instance(client_rpc_methods.RPCMethods(self))
79 self._StartIdleTimeoutThread()
80
81 def _StartIdleTimeoutThread(self):
82 """Create and start the idle timeout monitor thread."""
83 self.shutdown_requested_event = threading.Event()
84 self.rpc_received_event = threading.Event()
85 self.idle_thread = threading.Thread(target=self.CheckForIdleQuit)
86
87 def AddAuthorizedAddress(self, ip_address):
88 """Add an authorized caller address to the server.
89
90 Args:
91 ip_address: The IP address of the caller.
92 """
93 self.authorized_addresses.append(ip_address)
94
95 def SetIdleTimeout(self, timeout):
96 """Sets the idle timeout for the server.
97
98 Args:
99 timeout: The server timeout in seconds.
100 """
101 logging.debug('Idle timeout set to %s', timeout)
102 self._idle_timeout = timeout
103
104 @staticmethod
105 def Connect(client, port=None):
106 """Connect to an RPC server.
107
108 Args:
109 client: The client address which the RPC server is running on.
110 port: The port the client RPC server is running on.
111
112 Returns:
113 An XML-RPC connection to the client RPC server.
114 """
115 port = port or SERVER_PORT
116 server = 'http://%s:%d' % (client, port)
117 logging.debug('Connecting to RPC server at %s', server)
118 return xmlrpclib.Server(server)
119
120 def shutdown(self):
121 """Shutdown the server.
122
123 This overloaded method sets the shutdown_requested_event to allow the
124 idle timeout thread to quit.
125 """
126 self.shutdown_requested_event.set()
127 SimpleXMLRPCServer.SimpleXMLRPCServer.shutdown(self)
128 logging.info('Server shutdown complete')
129
130 def serve_forever(self, poll_interval=0.5):
131 """Serve forever.
132
133 This overloaded method starts the idle timeout thread before calling
134 serve_forever. This ensures the idle timer thread doesn't get started
135 without the server running.
136
137 Args:
138 poll_interval: The interval to poll for shutdown.
139 """
140 logging.info('RPC server starting')
141 self.idle_thread.start()
142 SimpleXMLRPCServer.SimpleXMLRPCServer.serve_forever(self, poll_interval)
143
144 def _dispatch(self, method, params):
145 """Dispatch the call to the correct method with the provided params.
146
147 This overloaded method adds logging to help trace connection and
148 call problems.
149
150 Args:
151 method: The method name to call.
152 params: A tuple of parameters to pass.
153
154 Returns:
155 The result of the parent class' _dispatch method.
156 """
157 logging.debug('Calling %s%s', method, params)
158 return SimpleXMLRPCServer.SimpleXMLRPCServer._dispatch(self, method, params)
159
160 def RPCReceived(self):
161 """Sets the rpc_received_event whenever an RPC is received.
162
163 Setting this event effectively resets the idle timeout thread.
164 """
165 self.rpc_received_event.set()
166
167 def CheckForIdleQuit(self):
168 """Check for, and exit, if the server is idle for too long.
169
170 This method must be run in a separate thread to avoid a deadlock when
171 calling server.shutdown.
172 """
173 timeout = time.time() + self._idle_timeout
174 while time.time() < timeout:
175 if self.shutdown_requested_event.is_set():
176 # An external source called shutdown()
177 return
178 elif self.rpc_received_event.is_set():
179 # An RPC was received, reset the timeout
180 logging.debug('Resetting the idle timeout')
181 timeout = time.time() + self._idle_timeout
182 self.rpc_received_event.clear()
183 time.sleep(1)
184 # We timed out, kill the server
185 logging.warning('Shutting down the server due to the idle timeout')
186 self.shutdown()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698