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