]>
Commit | Line | Data |
---|---|---|
e0edde6f | 1 | # Copyright (c) 2011, 2012 Nicira, Inc. |
0be6140a AA |
2 | # |
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | # you may not use this file except in compliance with the License. | |
5 | # You may obtain a copy of the License at: | |
6 | # | |
7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
8 | # | |
9 | # Unless required by applicable law or agreed to in writing, software | |
10 | # distributed under the License is distributed on an "AS IS" BASIS, | |
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | # See the License for the specific language governing permissions and | |
13 | # limitations under the License. | |
14 | ||
15 | """ | |
16 | rpcserver is an XML RPC server that allows RPC client to initiate tests | |
17 | """ | |
18 | ||
8d25d9a2 | 19 | import sys |
8d25d9a2 | 20 | |
6c7050b5 | 21 | import exceptions |
22 | ||
0c4d144a | 23 | import xmlrpc.client |
6c7050b5 | 24 | |
25 | import tcp | |
26 | ||
0be6140a | 27 | from twisted.internet import reactor |
0be6140a | 28 | from twisted.internet.error import CannotListenError |
8d25d9a2 | 29 | from twisted.web import server |
6c7050b5 | 30 | from twisted.web import xmlrpc |
8d25d9a2 | 31 | |
8d25d9a2 | 32 | import udp |
6c7050b5 | 33 | |
0be6140a | 34 | import util |
6c7050b5 | 35 | |
8d25d9a2 | 36 | import vswitch |
0be6140a AA |
37 | |
38 | ||
39 | class TestArena(xmlrpc.XMLRPC): | |
40 | """ | |
8d25d9a2 | 41 | This class contains all the functions that ovs-test client will call |
0be6140a AA |
42 | remotely. The caller is responsible to use designated handleIds |
43 | for designated methods (e.g. do not mix UDP and TCP handles). | |
44 | """ | |
45 | ||
46 | def __init__(self): | |
8d25d9a2 | 47 | xmlrpc.XMLRPC.__init__(self, allowNone=True) |
0be6140a AA |
48 | self.handle_id = 1 |
49 | self.handle_map = {} | |
8d25d9a2 AA |
50 | self.bridges = set() |
51 | self.pbridges = set() | |
52 | self.ports = set() | |
53 | self.request = None | |
0be6140a AA |
54 | |
55 | def __acquire_handle(self, value): | |
56 | """ | |
57 | Allocates new handle and assigns value object to it | |
58 | """ | |
59 | handle = self.handle_id | |
60 | self.handle_map[handle] = value | |
61 | self.handle_id += 1 | |
62 | return handle | |
63 | ||
64 | def __get_handle_resources(self, handle): | |
65 | """ | |
66 | Return resources that were assigned to handle | |
67 | """ | |
68 | return self.handle_map[handle] | |
69 | ||
70 | def __delete_handle(self, handle): | |
71 | """ | |
72 | Releases handle from handle_map | |
73 | """ | |
74 | del self.handle_map[handle] | |
75 | ||
8d25d9a2 AA |
76 | def cleanup(self): |
77 | """ | |
78 | Delete all remaining bridges and ports if ovs-test client did not had | |
79 | a chance to remove them. It is necessary to call this function if | |
80 | ovs-test server is abruptly terminated when doing the tests. | |
81 | """ | |
82 | for port in self.ports: | |
83 | # Remove ports that were added to existing bridges | |
84 | vswitch.ovs_vsctl_del_port_from_bridge(port) | |
85 | ||
86 | for bridge in self.bridges: | |
87 | # Remove bridges that were added for L3 tests | |
88 | vswitch.ovs_vsctl_del_bridge(bridge) | |
89 | ||
90 | for pbridge in self.pbridges: | |
91 | # Remove bridges that were added for VLAN tests | |
92 | vswitch.ovs_vsctl_del_pbridge(pbridge[0], pbridge[1]) | |
93 | ||
94 | def render(self, request): | |
95 | """ | |
96 | This method overrides the original XMLRPC.render method so that it | |
97 | would be possible to get the XML RPC client IP address from the | |
98 | request object. | |
99 | """ | |
100 | self.request = request | |
101 | return xmlrpc.XMLRPC.render(self, request) | |
102 | ||
103 | def xmlrpc_get_my_address(self): | |
104 | """ | |
105 | Returns the RPC client's IP address. | |
106 | """ | |
107 | return self.request.getClientIP() | |
108 | ||
109 | def xmlrpc_get_my_address_from(self, his_ip, his_port): | |
110 | """ | |
111 | Returns the ovs-test server IP address that the other ovs-test server | |
112 | with the given ip will see. | |
113 | """ | |
0c4d144a | 114 | server1 = xmlrpc.client.Server("http://%s:%u/" % (his_ip, his_port)) |
8d25d9a2 | 115 | return server1.get_my_address() |
0be6140a AA |
116 | |
117 | def xmlrpc_create_udp_listener(self, port): | |
118 | """ | |
119 | Creates a UDP listener that will receive packets from UDP sender | |
120 | """ | |
121 | try: | |
122 | listener = udp.UdpListener() | |
123 | reactor.listenUDP(port, listener) | |
124 | handle_id = self.__acquire_handle(listener) | |
125 | except CannotListenError: | |
126 | return -1 | |
127 | return handle_id | |
128 | ||
129 | def xmlrpc_create_udp_sender(self, host, count, size, duration): | |
130 | """ | |
131 | Send UDP datagrams to UDP listener | |
132 | """ | |
133 | sender = udp.UdpSender(tuple(host), count, size, duration) | |
134 | reactor.listenUDP(0, sender) | |
135 | handle_id = self.__acquire_handle(sender) | |
136 | return handle_id | |
137 | ||
138 | def xmlrpc_get_udp_listener_results(self, handle): | |
139 | """ | |
140 | Returns number of datagrams that were received | |
141 | """ | |
142 | listener = self.__get_handle_resources(handle) | |
143 | return listener.getResults() | |
144 | ||
145 | def xmlrpc_get_udp_sender_results(self, handle): | |
146 | """ | |
147 | Returns number of datagrams that were sent | |
148 | """ | |
149 | sender = self.__get_handle_resources(handle) | |
150 | return sender.getResults() | |
151 | ||
152 | def xmlrpc_close_udp_listener(self, handle): | |
153 | """ | |
154 | Releases UdpListener and all its resources | |
155 | """ | |
156 | listener = self.__get_handle_resources(handle) | |
157 | listener.transport.stopListening() | |
158 | self.__delete_handle(handle) | |
159 | return 0 | |
160 | ||
161 | def xmlrpc_close_udp_sender(self, handle): | |
162 | """ | |
163 | Releases UdpSender and all its resources | |
164 | """ | |
165 | sender = self.__get_handle_resources(handle) | |
166 | sender.transport.stopListening() | |
167 | self.__delete_handle(handle) | |
168 | return 0 | |
169 | ||
170 | def xmlrpc_create_tcp_listener(self, port): | |
171 | """ | |
172 | Creates a TcpListener that will accept connection from TcpSender | |
173 | """ | |
174 | try: | |
175 | listener = tcp.TcpListenerFactory() | |
176 | port = reactor.listenTCP(port, listener) | |
177 | handle_id = self.__acquire_handle((listener, port)) | |
178 | return handle_id | |
179 | except CannotListenError: | |
180 | return -1 | |
181 | ||
182 | def xmlrpc_create_tcp_sender(self, his_ip, his_port, duration): | |
183 | """ | |
184 | Creates a TcpSender that will connect to TcpListener | |
185 | """ | |
186 | sender = tcp.TcpSenderFactory(duration) | |
187 | connector = reactor.connectTCP(his_ip, his_port, sender) | |
188 | handle_id = self.__acquire_handle((sender, connector)) | |
189 | return handle_id | |
190 | ||
191 | def xmlrpc_get_tcp_listener_results(self, handle): | |
192 | """ | |
193 | Returns number of bytes received | |
194 | """ | |
195 | (listener, _) = self.__get_handle_resources(handle) | |
196 | return listener.getResults() | |
197 | ||
198 | def xmlrpc_get_tcp_sender_results(self, handle): | |
199 | """ | |
200 | Returns number of bytes sent | |
201 | """ | |
202 | (sender, _) = self.__get_handle_resources(handle) | |
203 | return sender.getResults() | |
204 | ||
205 | def xmlrpc_close_tcp_listener(self, handle): | |
206 | """ | |
207 | Releases TcpListener and all its resources | |
208 | """ | |
209 | try: | |
210 | (_, port) = self.__get_handle_resources(handle) | |
211 | port.loseConnection() | |
212 | self.__delete_handle(handle) | |
213 | except exceptions.KeyError: | |
214 | return -1 | |
215 | return 0 | |
216 | ||
217 | def xmlrpc_close_tcp_sender(self, handle): | |
218 | """ | |
219 | Releases TcpSender and all its resources | |
220 | """ | |
221 | try: | |
222 | (_, connector) = self.__get_handle_resources(handle) | |
223 | connector.disconnect() | |
224 | self.__delete_handle(handle) | |
225 | except exceptions.KeyError: | |
226 | return -1 | |
227 | return 0 | |
228 | ||
8d25d9a2 AA |
229 | def xmlrpc_create_test_bridge(self, bridge, iface): |
230 | """ | |
231 | This function creates a physical bridge from iface. It moves the | |
232 | IP configuration from the physical interface to the bridge. | |
233 | """ | |
234 | ret = vswitch.ovs_vsctl_add_bridge(bridge) | |
235 | if ret == 0: | |
236 | self.pbridges.add((bridge, iface)) | |
237 | util.interface_up(bridge) | |
238 | (ip_addr, mask) = util.interface_get_ip(iface) | |
239 | util.interface_assign_ip(bridge, ip_addr, mask) | |
0b2c7e69 | 240 | util.interface_up(bridge) |
8d25d9a2 | 241 | util.move_routes(iface, bridge) |
0b2c7e69 | 242 | util.interface_remove_ip(iface, ip_addr, mask) |
8d25d9a2 AA |
243 | ret = vswitch.ovs_vsctl_add_port_to_bridge(bridge, iface) |
244 | if ret == 0: | |
245 | self.ports.add(iface) | |
246 | else: | |
247 | util.interface_assign_ip(iface, ip_addr, mask) | |
0b2c7e69 | 248 | util.interface_up(iface) |
8d25d9a2 AA |
249 | util.move_routes(bridge, iface) |
250 | vswitch.ovs_vsctl_del_bridge(bridge) | |
251 | ||
252 | return ret | |
253 | ||
254 | def xmlrpc_del_test_bridge(self, bridge, iface): | |
255 | """ | |
256 | This function deletes the test bridge and moves its IP configuration | |
257 | back to the physical interface. | |
258 | """ | |
259 | ret = vswitch.ovs_vsctl_del_pbridge(bridge, iface) | |
260 | self.pbridges.discard((bridge, iface)) | |
261 | return ret | |
262 | ||
263 | def xmlrpc_get_iface_from_bridge(self, brname): | |
264 | """ | |
265 | Tries to figure out physical interface from bridge. | |
266 | """ | |
267 | return vswitch.ovs_get_physical_interface(brname) | |
268 | ||
269 | def xmlrpc_create_bridge(self, brname): | |
270 | """ | |
271 | Creates an OVS bridge. | |
272 | """ | |
273 | ret = vswitch.ovs_vsctl_add_bridge(brname) | |
274 | if ret == 0: | |
275 | self.bridges.add(brname) | |
276 | return ret | |
277 | ||
278 | def xmlrpc_del_bridge(self, brname): | |
279 | """ | |
280 | Deletes an OVS bridge. | |
281 | """ | |
282 | ret = vswitch.ovs_vsctl_del_bridge(brname) | |
283 | if ret == 0: | |
284 | self.bridges.discard(brname) | |
285 | return ret | |
286 | ||
287 | def xmlrpc_is_ovs_bridge(self, bridge): | |
288 | """ | |
289 | This function verifies whether given interface is an ovs bridge. | |
290 | """ | |
291 | return vswitch.ovs_vsctl_is_ovs_bridge(bridge) | |
292 | ||
293 | def xmlrpc_add_port_to_bridge(self, bridge, port): | |
294 | """ | |
295 | Adds a port to the OVS bridge. | |
296 | """ | |
297 | ret = vswitch.ovs_vsctl_add_port_to_bridge(bridge, port) | |
298 | if ret == 0: | |
299 | self.ports.add(port) | |
300 | return ret | |
301 | ||
302 | def xmlrpc_del_port_from_bridge(self, port): | |
303 | """ | |
304 | Removes a port from OVS bridge. | |
305 | """ | |
306 | ret = vswitch.ovs_vsctl_del_port_from_bridge(port) | |
307 | if ret == 0: | |
308 | self.ports.discard(port) | |
309 | return ret | |
310 | ||
311 | def xmlrpc_ovs_vsctl_set(self, table, record, column, key, value): | |
312 | """ | |
313 | This function allows to alter OVS database. | |
314 | """ | |
315 | return vswitch.ovs_vsctl_set(table, record, column, key, value) | |
316 | ||
317 | def xmlrpc_interface_up(self, iface): | |
318 | """ | |
319 | This function brings up given interface. | |
320 | """ | |
321 | return util.interface_up(iface) | |
322 | ||
323 | def xmlrpc_interface_assign_ip(self, iface, ip_address, mask): | |
324 | """ | |
325 | This function allows to assing ip address to the given interface. | |
326 | """ | |
327 | return util.interface_assign_ip(iface, ip_address, mask) | |
0be6140a | 328 | |
0b2c7e69 BP |
329 | def xmlrpc_interface_remove_ip(self, iface, ip_address, mask): |
330 | """ | |
331 | This function allows to assing ip address to the given interface. | |
332 | """ | |
333 | return util.interface_remove_ip(iface, ip_address, mask) | |
334 | ||
0be6140a AA |
335 | def xmlrpc_get_interface(self, address): |
336 | """ | |
337 | Finds first interface that has given address | |
338 | """ | |
339 | return util.get_interface(address) | |
340 | ||
341 | def xmlrpc_get_interface_mtu(self, iface): | |
342 | """ | |
343 | Returns MTU of the given interface | |
344 | """ | |
345 | return util.get_interface_mtu(iface) | |
346 | ||
347 | def xmlrpc_uname(self): | |
348 | """ | |
349 | Return information about running kernel | |
350 | """ | |
351 | return util.uname() | |
352 | ||
353 | def xmlrpc_get_driver(self, iface): | |
354 | """ | |
355 | Returns driver version | |
356 | """ | |
357 | return util.get_driver(iface) | |
358 | ||
2d8bdd8f AA |
359 | def xmlrpc_get_interface_from_routing_decision(self, ip): |
360 | """ | |
361 | Returns driver version | |
362 | """ | |
363 | return util.get_interface_from_routing_decision(ip) | |
364 | ||
0be6140a AA |
365 | |
366 | def start_rpc_server(port): | |
8d25d9a2 AA |
367 | """ |
368 | This function creates a RPC server and adds it to the Twisted Reactor. | |
369 | """ | |
370 | rpc_server = TestArena() | |
371 | reactor.listenTCP(port, server.Site(rpc_server)) | |
372 | try: | |
8ea171ab | 373 | print("Starting RPC server\n") |
8d25d9a2 | 374 | sys.stdout.flush() |
eda26d40 RB |
375 | # If this server was started from ovs-test client then we must flush |
376 | # STDOUT so that client would know that server is ready to accept | |
377 | # XML RPC connections. | |
8d25d9a2 AA |
378 | reactor.run() |
379 | finally: | |
380 | rpc_server.cleanup() |