Bump pre-commit black to 22.1.0
[integration/test.git] / tools / odl-ovsdb-performance-tests / ovsdbconfigblaster.py
1 """
2 Script to add bridges/ports/termination points to ovsdb config
3 """
4 import argparse
5 import logging
6 import requests
7
8 __author__ = "Marcus Williams"
9 __copyright__ = "Copyright (c) 2015, Intel Corp Inc., Cisco Systems Inc. and others"
10 __credits__ = ["Jan Medved, Lori Jakab"]
11 __license__ = "New-style BSD"
12 __email__ = "marcus.williams@intel.com"
13 __version__ = "0.0.1"
14
15
16 class OvsdbConfigBlaster(object):
17     PUT_HEADERS = {
18         "Content-Type": "application/json",
19         "Authorization": "Basic YWRtaW46YWRtaW4=",
20         "Accept": "application/json",
21     }
22     GET_HEADERS = {
23         "Accept": "application/json",
24         "Authorization": "Basic YWRtaW46YWRtaW4=",
25     }
26     DELETE_HEADERS = {
27         "Accept": "application/json",
28         "Authorization": "Basic YWRtaW46YWRtaW4=",
29     }
30     TIMEOUT = 10
31
32     def __init__(
33         self,
34         controller_ip,
35         controller_port,
36         vswitch_ip,
37         vswitch_ovsdb_port,
38         vswitch_remote_ip,
39         vswitch_remote_ovsdb_port,
40         vswitch_port_type,
41         vswitch_lst_del_br,
42         delete_ports,
43         num_instances,
44     ):
45         """
46         Args:
47             :param controller_ip: The ODL host ip used to send RPCs
48             :param controller_port: The RESTCONF port on the ODL host
49             :param vswitch_ip: The ip of OpenvSwitch to use
50             :param vswitch_ovsdb_port: The ovsdb port of OpenvSwitch to use
51             :param vswitch_remote_ip: The ip of remote OpenvSwitch to use
52             :param vswitch_remote_ovsdb_port: The ovsdb port of remote OpenvSwitch to use
53             :param vswitch_port_type: Port type to create
54             :param vswitch_lst_del_br: string containing a list of ovs switches on which BR'S should be deleted.
55             :param num_instances: The number of instances (bridges, ports etc)to be added
56             :param delete_ports: The number of ports to be deleted from a bridge
57         """
58         logging.basicConfig(level=logging.DEBUG)
59         self.session = requests.Session()
60         self.controller_ip = controller_ip
61         self.controller_port = controller_port
62         self.vswitch_dict = dict()
63         self.add_vswitch_to_dict(
64             vswitch_ip, vswitch_remote_ip, vswitch_ovsdb_port, "ovs-1"
65         )
66         if vswitch_remote_ip:
67             self.add_vswitch_to_dict(
68                 vswitch_remote_ip, vswitch_ip, vswitch_remote_ovsdb_port, "ovs-2"
69             )
70         self.vswitch_port_type = vswitch_port_type
71         self.vswitch_lst_del_br = vswitch_lst_del_br
72         self.num_instances = num_instances
73         self.delete_ports = delete_ports
74         self.connect_vswitch(self.vswitch_dict["ovs-1"])
75         if self.vswitch_dict.get("ovs-2"):
76             self.connect_vswitch(self.vswitch_dict["ovs-2"])
77
78     @staticmethod
79     def return_ovsdb_url(vswitch_ip, vswitch_ovsdb_port, url_type="config"):
80         """Return an ovsdb restconf url
81         Args:
82             :param vswitch_ip: The ip of Open vSwitch to use
83             :param vswitch_ovsdb_port: The ovsdb port of Open vSwitch to use
84             :param url_tyep: The type of url 'config' | 'oper'
85         """
86         url_prefix = None
87         if url_type == "config":
88             url_prefix = "restconf/config/"
89         elif url_type == "oper":
90             url_prefix = "restconf/operational/"
91         ovsdb_url = (
92             url_prefix + "network-topology:"
93             "network-topology/topology/"
94             "ovsdb:1/node/ovsdb:%2F%2F" + vswitch_ip + ":" + vswitch_ovsdb_port
95         )
96         return ovsdb_url
97
98     def add_vswitch_to_dict(
99         self, vswitch_ip, vswitch_remote_ip, vswitch_ovsdb_port, vswitch_name
100     ):
101         """Add details of an Open vSwitch instance to self.vswitch_dict
102         Args:
103             :param vswitch_ip: The ip of Open vSwitch to use
104             :param vswitch_remote_ip: The ip of remote Open vSwitch to use
105             :param vswitch_ovsdb_port: The ovsdb port of Open vSwitch to use
106             :param vswitch_name: The name to label the added Open vSwitch instance
107         """
108         urlprefix = "http://" + self.controller_ip + ":" + self.controller_port + "/"
109         self.vswitch_dict.update(
110             {
111                 vswitch_name: {
112                     "name": vswitch_name,
113                     "ip": vswitch_ip,
114                     "remote-ip": vswitch_remote_ip,
115                     "ovsdb-port": vswitch_ovsdb_port,
116                     "node-id": "ovsdb://%s:%s" % (vswitch_ip, vswitch_ovsdb_port),
117                     "post-url": urlprefix
118                     + OvsdbConfigBlaster.return_ovsdb_url(
119                         vswitch_ip, vswitch_ovsdb_port
120                     ),
121                     "get-config-url": urlprefix
122                     + OvsdbConfigBlaster.return_ovsdb_url(
123                         vswitch_ip, vswitch_ovsdb_port
124                     ),
125                     "get-oper-url": urlprefix
126                     + OvsdbConfigBlaster.return_ovsdb_url(
127                         vswitch_ip, vswitch_ovsdb_port
128                     ),
129                 }
130             }
131         )
132
133     def connect_vswitch(self, vswitch_dict):
134         """Connect ODL to an Open vSwitch instance using restconf
135         Args:
136             :param vswitch_dict: A dictionary detailing
137                                  an instance of Open vSwitch
138         """
139         connect_ovs_body = {
140             "network-topology:node": [
141                 {
142                     "node-id": unicode(vswitch_dict["node-id"]),
143                     "connection-info": {
144                         "ovsdb:remote-port": unicode(vswitch_dict["ovsdb-port"]),
145                         "ovsdb:remote-ip": unicode(vswitch_dict["ip"]),
146                     },
147                 }
148             ]
149         }
150         self.send_rest(self.session, vswitch_dict["post-url"], connect_ovs_body)
151
152     def add_bridge(self, num_instances, vswitch_name="ovs-1"):
153         """Add num_instances of bridge to ODL config
154         Args:
155             :param num_instances: Number of bridges to create
156             :param vswitch_name: A name describing
157                                  an instance of Open vSwitch
158         """
159
160         for i in range(num_instances):
161             bridge_name = unicode("br-" + str(i) + "-test")
162             add_bridge_body = {
163                 "network-topology:node": [
164                     {
165                         "node-id": "%s/bridge/%s"
166                         % (
167                             unicode(self.vswitch_dict[vswitch_name].get("node-id")),
168                             unicode(bridge_name),
169                         ),
170                         "ovsdb:bridge-name": unicode(bridge_name),
171                         "ovsdb:datapath-id": "00:00:b2:bf:48:25:f2:4b",
172                         "ovsdb:protocol-entry": [
173                             {"protocol": "ovsdb:ovsdb-bridge-protocol-openflow-13"}
174                         ],
175                         "ovsdb:controller-entry": [
176                             {
177                                 "target": "tcp:%s:%s"
178                                 % (self.controller_ip, self.controller_port)
179                             }
180                         ],
181                         "ovsdb:managed-by": "/network-topology:network-topology/"
182                         "network-topology:topology"
183                         "[network-topology:topology-id"
184                         "='ovsdb:1']/network-topology:node"
185                         "[network-topology:node-id="
186                         "'%s']"
187                         % unicode(self.vswitch_dict[vswitch_name].get("node-id")),
188                     }
189                 ]
190             }
191             self.send_rest(
192                 self.session,
193                 self.vswitch_dict[vswitch_name].get("post-url")
194                 + "%2Fbridge%2F"
195                 + bridge_name,
196                 add_bridge_body,
197             )
198         self.session.close()
199
200     def add_port(self, port_type="ovsdb:interface-type-vxlan"):
201         """Add self.num_instances of port to ODL config
202         Args:
203             :param port_type: The type of port to create
204                                 default: 'ovsdb:interface-type-vxlan'
205         """
206         bridge_name = "br-0-test"
207         self.add_bridge(1, "ovs-1")
208         #        self.add_bridge(1, 'ovs-2')
209
210         for instance in range(self.num_instances):
211             for vswitch in self.vswitch_dict.itervalues():
212                 if port_type == "ovsdb:interface-type-vxlan":
213                     port_prefix = "tp-"
214                     ovsdb_rest_url = (
215                         vswitch.get("post-url")
216                         + "%2Fbridge%2F"
217                         + bridge_name
218                         + "/termination-point/"
219                     )
220                     body_name = "tp-body"
221                 else:
222                     port_prefix = "port-"
223                     ovsdb_rest_url = (
224                         vswitch.get("post-url")
225                         + "%2Fbridge%2F"
226                         + bridge_name
227                         + "/port/"
228                     )
229                     body_name = "port-body"
230                 port_name = port_prefix + str(instance) + "-test-" + vswitch.get("ip")
231                 body = {
232                     "tp-body": {
233                         "network-topology:termination-point": [
234                             {
235                                 "ovsdb:options": [
236                                     {
237                                         "ovsdb:option": "remote_ip",
238                                         "ovsdb:value": unicode(
239                                             vswitch.get("remote-ip")
240                                         ),
241                                     }
242                                 ],
243                                 "ovsdb:name": unicode(port_name),
244                                 "ovsdb:interface-type": unicode(port_type),
245                                 "tp-id": unicode(port_name),
246                                 "vlan-tag": unicode(instance + 1),
247                                 "trunks": [{"trunk": "5"}],
248                                 "vlan-mode": "access",
249                             }
250                         ]
251                     },
252                     # TODO add port-body
253                     "port-body": {},
254                 }
255                 self.send_rest(
256                     self.session, ovsdb_rest_url + port_name, body.get(body_name)
257                 )
258
259         self.session.close()
260
261     def delete_bridge(self, vswitch_lst_del_br, num_bridges):
262         """Delete num_instances of bridge in ODL config
263         Args:
264             :param num_bridges: Number of bridges to delete
265             :param vswitch_lst_del_br: A list containing instances of Open vSwitch on which bridges should be deleted.
266         """
267         for vswitch_names in vswitch_lst_del_br:
268             for br_num in range(num_bridges):
269                 bridge_name = unicode("br-" + str(br_num) + "-test")
270                 self.send_rest_del(
271                     self.session,
272                     self.vswitch_dict[vswitch_names].get("post-url")
273                     + "%2Fbridge%2F"
274                     + bridge_name,
275                 )
276             self.session.close()
277
278     def delete_port(self, num_ports):
279         """Delete ports from ODL config
280         Args:
281            :param num_ports: Number of ports to delete
282         """
283         for port in range(num_ports):
284             bridge_name = "br-0-test"
285             for vswitch in self.vswitch_dict.itervalues():
286                 port_prefix = "tp-"
287                 ovsdb_rest_url = (
288                     vswitch.get("post-url")
289                     + "%2Fbridge%2F"
290                     + bridge_name
291                     + "/termination-point/"
292                 )
293                 port_name = port_prefix + str(port) + "-test-" + vswitch.get("ip")
294                 self.send_rest_del(self.session, ovsdb_rest_url + port_name)
295                 self.session.close()
296
297     def send_rest_del(self, session, rest_url):
298         """Send an HTTP DELETE to the Rest URL and return the status code
299         Args:
300             :param session: The HTTP session handle
301             :return int: status_code - HTTP status code
302         """
303         ret = session.delete(
304             rest_url, headers=self.DELETE_HEADERS, stream=False, timeout=self.TIMEOUT
305         )
306
307         if ret.status_code is not 200:
308             raise ValueError(ret.text, ret.status_code, rest_url)
309         return ret.status_code
310
311     def send_rest(self, session, rest_url, json_body):
312         """Send an HTTP PUT to the Rest URL and return the status code
313         Args:
314             :param session: The HTTP session handle
315             :param json_body: the JSON body to be sent
316         Returns:
317             :return int: status_code - HTTP status code
318         """
319         ret = session.put(
320             rest_url,
321             json=json_body,
322             headers=self.PUT_HEADERS,
323             stream=False,
324             timeout=self.TIMEOUT,
325         )
326
327         if ret.status_code is not 200:
328             raise ValueError(ret.text, ret.status_code, rest_url, json_body)
329         return ret.status_code
330
331
332 if __name__ == "__main__":
333     parser = argparse.ArgumentParser(
334         description="Add:delete bridge/port/term-points to OpenDaylight"
335     )
336
337     parser.add_argument(
338         "--mode",
339         default="None",
340         help='Operating mode, can be "bridge", "port" or "term" \
341                             (default is "bridge")',
342     )
343     parser.add_argument(
344         "--controller",
345         default="127.0.0.1",
346         help="IP of running ODL controller \
347                              (default is 127.0.0.1)",
348     )
349     parser.add_argument(
350         "--controllerport",
351         default="8181",
352         help="Port of ODL RESTCONF \
353                             (default is 8181)",
354     )
355     parser.add_argument(
356         "--vswitch",
357         default="127.0.0.1",
358         help="IP of Open vSwitch \
359                             (default is 127.0.0.1)",
360     )
361     parser.add_argument(
362         "--vswitchport",
363         default="6640",
364         help="Port of Open vSwitch OVSDB server \
365                             (default is 6640)",
366     )
367     parser.add_argument(
368         "--vswitchremote",
369         default=None,
370         help="IP of remote Open vSwitch \
371                             (default is none)",
372     )
373     parser.add_argument(
374         "--vswitchremoteport",
375         default=None,
376         help="Port of remote Open vSwitch OVSDB server \
377                             (default is none)",
378     )
379     parser.add_argument(
380         "--vswitchporttype",
381         default=None,
382         help="Port of remote Open vSwitch OVSDB server \
383                             (default is none)",
384     )
385     parser.add_argument(
386         "--deletebridges",
387         nargs="*",
388         type=str,
389         default=None,
390         help="A list of switches on which to delete bridges, "
391         'uses instances for number of bridges. \
392                               Example: "ovs-1 ovs2" \
393                             (default is none)',
394     )
395     parser.add_argument(
396         "--deleteports",
397         type=int,
398         default=1,
399         help="delete ports of remote open vswitch ovsdb server (default 1)",
400     )
401     parser.add_argument(
402         "--instances",
403         type=int,
404         default=1,
405         help="Number of instances to add/get (default 1)",
406     )
407
408     args = parser.parse_args()
409
410     ovsdb_config_blaster = OvsdbConfigBlaster(
411         args.controller,
412         args.controllerport,
413         args.vswitch,
414         args.vswitchport,
415         args.vswitchremote,
416         args.vswitchremoteport,
417         args.vswitchporttype,
418         args.deletebridges,
419         args.deleteports,
420         args.instances,
421     )
422     if args.mode == "bridge":
423         if args.deletebridges is not None:
424             ovsdb_config_blaster.delete_bridge(
425                 ovsdb_config_blaster.vswitch_lst_del_br,
426                 ovsdb_config_blaster.num_instances,
427             )
428         else:
429             ovsdb_config_blaster.add_bridge(ovsdb_config_blaster.num_instances)
430     elif args.mode == "term":
431         if args.deleteports is not None:
432             ovsdb_config_blaster.delete_port(ovsdb_config_blaster.delete_ports)
433         else:
434             ovsdb_config_blaster.add_port()
435     else:
436         print(
437             "please use: python ovsdbconfigblaster.py --help " "\nUnsupported mode: ",
438             args.mode,
439         )