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