1 """This program performs required BGP application peer operations."""
3 # Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
5 # This program and the accompanying materials are made available under the
6 # terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 # and is available at http://www.eclipse.org/legal/epl-v10.html
9 __author__ = "Radovan Sajben"
10 __copyright__ = "Copyright(c) 2015, Cisco Systems, Inc."
11 __license__ = "Eclipse Public License v1.0"
12 __email__ = "rsajben@cisco.com"
19 import xml.dom.minidom as md
22 def _build_url(odl_ip, port, uri):
23 """Compose URL from generic IP, port and URI fragment.
26 :param odl_ip: controller's ip address or hostname
28 :param port: controller's restconf port
30 :param uri: URI without /restconf/ to complete URL
33 :returns url: full restconf url corresponding to params
36 url = "http://" + str(odl_ip) + ":" + port + "/restconf/" + uri
40 def _build_data(xml_template, prefix_base, prefix_len, count, element="ipv4-routes"):
41 """Generate list of routes based on xml templates.
44 :xml_template: xml template for routes
46 :prefix_base: first prefix IP address
48 :prefix_len: prefix length in bits
50 :count: number of routes to be generated
52 :element: element to be returned
55 :returns xml_data: requested element as xml data
57 global total_build_data_time_counter
58 build_data_timestamp = time.time()
60 routes = md.parse(xml_template)
62 routes_node = routes.getElementsByTagName("ipv4-routes")[0]
63 route_node = routes.getElementsByTagName("ipv4-route")[0]
64 if element == routes_node.tagName:
65 routes_node.removeChild(route_node)
67 prefix_gap = 2 ** (32 - prefix_len)
69 for prefix_index in range(count):
70 new_route_node = route_node.cloneNode(True)
71 new_route_prefix = new_route_node.getElementsByTagName("prefix")[0]
73 prefix = prefix_base + prefix_index * prefix_gap
74 new_route_prefix.childNodes[0].nodeValue = str(prefix) + "/" + str(prefix_len)
76 routes_node.appendChild(new_route_node)
78 xml_data = routes_node.toxml()
79 elif element == route_node.tagName:
80 route_node.setAttribute("xmlns", routes_node.namespaceURI)
81 route_prefix = route_node.getElementsByTagName("prefix")[0]
82 route_prefix.childNodes[0].nodeValue = str(prefix_base) + "/" + str(prefix_len)
83 xml_data = route_node.toxml()
87 logger.debug("xml data generated:\n%s", xml_data)
88 total_build_data_time_counter += time.time() - build_data_timestamp
92 def send_request(operation, odl_ip, port, uri, auth, xml_data=None, expect_status_code=200):
93 """Send a http request.
96 :operation: GET, POST, PUT, DELETE
98 :param odl_ip: controller's ip address or hostname
100 :param port: controller's restconf port
102 :param uri: URI without /restconf/ to complete URL
104 :param auth: authentication credentials
106 :param xml_data: list of routes as xml data
109 :returns http response object
111 global total_response_time_counter
112 global total_number_of_responses_counter
114 ses = requests.Session()
116 url = _build_url(odl_ip, port, uri)
117 header = {"Content-Type": "application/xml"}
118 req = requests.Request(operation, url, headers=header, data=xml_data, auth=auth)
121 send_request_timestamp = time.time()
122 rsp = ses.send(prep, timeout=60)
123 total_response_time_counter += time.time() - send_request_timestamp
124 total_number_of_responses_counter += 1
125 except requests.exceptions.Timeout:
126 logger.error("No response from %s", odl_ip)
128 logger.debug("%s %s", rsp.request, rsp.request.url)
129 logger.debug("Request headers: %s:", rsp.request.headers)
130 logger.debug("Request body: %s", rsp.request.body)
131 logger.debug("Response: %s", rsp.text)
132 if rsp.status_code == expect_status_code:
133 logger.debug("%s %s", rsp.request, rsp.request.url)
134 logger.debug("Request headers: %s:", rsp.request.headers)
135 logger.debug("Request body: %s", rsp.request.body)
136 logger.debug("Response: %s", rsp.text)
137 logger.debug("%s %s", rsp, rsp.reason)
139 logger.error("%s %s", rsp.request, rsp.request.url)
140 logger.error("Request headers: %s:", rsp.request.headers)
141 logger.error("Request body: %s", rsp.request.body)
142 logger.error("Response: %s", rsp.text)
143 logger.error("%s %s", rsp, rsp.reason)
147 def get_prefixes(odl_ip, port, uri, auth, prefix_base=None, prefix_len=None,
148 count=None, xml_template=None):
149 """Send a http GET request for getting all prefixes.
152 :param odl_ip: controller's ip address or hostname
154 :param port: controller's restconf port
156 :param uri: URI without /restconf/ to complete URL
158 :param auth: authentication tupple as (user, password)
160 :param prefix_base: IP address of the first prefix
162 :prefix_len: length of the prefix in bites (specifies the increment as well)
164 :param count: number of prefixes to be processed
166 :param xml_template: xml template for building the xml data
172 logger.info("Get all prefixes from %s:%s/restconf/%s", odl_ip, port, uri)
173 rsp = send_request("GET", odl_ip, port, uri, auth)
176 s = s.replace("{", "")
177 s = s.replace("}", "")
178 s = s.replace("[", "")
179 s = s.replace("]", "")
182 for item in s.split(","):
184 prefixes += item + ","
186 prefixes = prefixes[:len(prefixes)-1]
187 logger.debug("prefix_list=%s", prefixes)
188 logger.info("prefix_count=%s", prefix_count)
191 def post_prefixes(odl_ip, port, uri, auth, prefix_base=None, prefix_len=None,
192 count=0, xml_template=None):
193 """Send a http POST request for creating a prefix list.
196 :param odl_ip: controller's ip address or hostname
198 :param port: controller's restconf port
200 :param uri: URI without /restconf/ to complete URL
202 :param auth: authentication tupple as (user, password)
204 :param prefix_base: IP address of the first prefix
206 :prefix_len: length of the prefix in bites (specifies the increment as well)
208 :param count: number of prefixes to be processed
210 :param xml_template: xml template for building the xml data (not used)
215 logger.info("Post %s prefix(es) in a single request (starting from %s/%s) into %s:%s/restconf/%s",
216 count, prefix_base, prefix_len, odl_ip, port, uri)
217 xml_data = _build_data(xml_template, prefix_base, prefix_len, count)
218 send_request("POST", odl_ip, port, uri, auth, xml_data=xml_data, expect_status_code=204)
221 def put_prefixes(odl_ip, port, uri, auth, prefix_base, prefix_len, count,
223 """Send a http PUT request for updating the prefix list.
226 :param odl_ip: controller's ip address or hostname
228 :param port: controller's restconf port
230 :param uri: URI without /restconf/ to complete URL
232 :param auth: authentication tupple as (user, password)
234 :param prefix_base: IP address of the first prefix
236 :prefix_len: length of the prefix in bites (specifies the increment as well)
238 :param count: number of prefixes to be processed
240 :param xml_template: xml template for building the xml data (not used)
245 uri_add_prefix = uri + _uri_suffix_ipv4_routes
246 logger.info("Put %s prefix(es) in a single request (starting from %s/%s) into %s:%s/restconf/%s",
247 count, prefix_base, prefix_len, odl_ip, port, uri_add_prefix)
248 xml_data = _build_data(xml_template, prefix_base, prefix_len, count)
249 send_request("PUT", odl_ip, port, uri_add_prefix, auth, xml_data=xml_data)
252 def add_prefixes(odl_ip, port, uri, auth, prefix_base, prefix_len, count,
254 """Send a consequent http POST request for adding prefixes.
257 :param odl_ip: controller's ip address or hostname
259 :param port: controller's restconf port
261 :param uri: URI without /restconf/ to complete URL
263 :param auth: authentication tupple as (user, password)
265 :param prefix_base: IP address of the first prefix
267 :prefix_len: length of the prefix in bites (specifies the increment as well)
269 :param count: number of prefixes to be processed
271 :param xml_template: xml template for building the xml data (not used)
276 logger.info("Add %s prefixes (starting from %s/%s) into %s:%s/restconf/%s",
277 count, prefix_base, prefix_len, odl_ip, port, uri)
278 uri_add_prefix = uri + _uri_suffix_ipv4_routes
279 prefix_gap = 2 ** (32 - prefix_len)
280 for prefix_index in range(count):
281 prefix = prefix_base + prefix_index * prefix_gap
282 logger.info("Adding prefix %s/%s to %s:%s/restconf/%s",
283 prefix, prefix_len, odl_ip, port, uri)
284 xml_data = _build_data(xml_template, prefix, prefix_len, 1, "ipv4-route")
285 send_request("POST", odl_ip, port, uri_add_prefix, auth,
286 xml_data=xml_data, expect_status_code=204)
289 def delete_prefixes(odl_ip, port, uri, auth, prefix_base, prefix_len, count,
291 """Send a http DELETE requests for deleting prefixes.
294 :param odl_ip: controller's ip address or hostname
296 :param port: controller's restconf port
298 :param uri: URI without /restconf/ to complete URL
300 :param auth: authentication tupple as (user, password)
302 :param prefix_base: IP address of the first prefix
304 :prefix_len: length of the prefix in bites (specifies the increment as well)
306 :param count: number of prefixes to be processed
308 :param xml_template: xml template for building the xml data (not used)
313 logger.info("Delete %s prefix(es) (starting from %s/%s) from %s:%s/restconf/%s",
314 count, prefix_base, prefix_len, odl_ip, port, uri)
315 uri_del_prefix = uri + _uri_suffix_ipv4_routes + _uri_suffix_ipv4_route
316 prefix_gap = 2 ** (32 - prefix_len)
317 for prefix_index in range(count):
318 prefix = prefix_base + prefix_index * prefix_gap
319 logger.info("Deleting prefix %s/%s from %s:%s/restconf/%s",
320 prefix, prefix_len, odl_ip, port, uri)
321 send_request("DELETE", odl_ip, port,
322 uri_del_prefix + str(prefix) + "%2F" + str(prefix_len), auth)
325 def delete_all_prefixes(odl_ip, port, uri, auth, prefix_base=None,
326 prefix_len=None, count=None, xml_template=None):
327 """Send a http DELETE request for deleting all prefixes.
330 :param odl_ip: controller's ip address or hostname
332 :param port: controller's restconf port
334 :param uri: URI without /restconf/ to complete URL
336 :param auth: authentication tupple as (user, password)
338 :param prefix_base: IP address of the first prefix (not used)
340 :prefix_len: length of the prefix in bites (not used)
342 :param count: number of prefixes to be processed (not used)
344 :param xml_template: xml template for building the xml data (not used)
349 logger.info("Delete all prefixes from %s:%s/restconf/%s", odl_ip, port, uri)
350 uri_del_all_prefixes = uri + _uri_suffix_ipv4_routes
351 send_request("DELETE", odl_ip, port, uri_del_all_prefixes, auth)
354 _commands = ["post", "put", "add", "delete", "delete-all", "get"]
355 _uri_suffix_ipv4_routes = "bgp-inet:ipv4-routes/"
356 _uri_suffix_ipv4_route = "bgp-inet:ipv4-route/" # followed by IP address like 1.1.1.1%2F32
358 if __name__ == "__main__":
359 parser = argparse.ArgumentParser(description="BGP application peer script")
360 parser.add_argument("--host", type=ipaddr.IPv4Address, default="127.0.0.1",
361 help="ODL controller IP address")
362 parser.add_argument("--port", default="8181",
363 help="ODL RESTCONF port")
364 parser.add_argument("--command", choices=_commands, metavar="command",
365 help="Command to be performed."
366 "post, put, add, delete, delete-all, get")
367 parser.add_argument("--prefix", type=ipaddr.IPv4Address, default="8.0.1.0",
368 help="First prefix IP address")
369 parser.add_argument("--prefixlen", type=int, help="Prefix length in bites",
371 parser.add_argument("--count", type=int, help="Number of prefixes",
373 parser.add_argument("--user", help="Restconf user name", default="admin")
374 parser.add_argument("--password", help="Restconf password", default="admin")
375 parser.add_argument("--uri", help="The uri part of requests",
376 default="config/bgp-rib:application-rib/example-app-rib/"
377 "tables/bgp-types:ipv4-address-family/"
378 "bgp-types:unicast-subsequent-address-family/")
379 parser.add_argument("--xml", help="File name of the xml data template",
380 default="ipv4-routes-template.xml")
381 parser.add_argument("--error", dest="loglevel", action="store_const",
382 const=logging.ERROR, default=logging.INFO,
383 help="Set log level to error (default is info)")
384 parser.add_argument("--warning", dest="loglevel", action="store_const",
385 const=logging.WARNING, default=logging.INFO,
386 help="Set log level to warning (default is info)")
387 parser.add_argument("--info", dest="loglevel", action="store_const",
388 const=logging.INFO, default=logging.INFO,
389 help="Set log level to info (default is info)")
390 parser.add_argument("--debug", dest="loglevel", action="store_const",
391 const=logging.DEBUG, default=logging.INFO,
392 help="Set log level to debug (default is info)")
393 parser.add_argument("--logfile", default="bgp_app_peer.log", help="Log file name")
395 args = parser.parse_args()
397 logger = logging.getLogger("logger")
398 log_formatter = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
399 console_handler = logging.StreamHandler()
400 file_handler = logging.FileHandler(args.logfile, mode="w")
401 console_handler.setFormatter(log_formatter)
402 file_handler.setFormatter(log_formatter)
403 logger.addHandler(console_handler)
404 logger.addHandler(file_handler)
405 logger.setLevel(args.loglevel)
407 auth = (args.user, args.password)
411 command = args.command
412 prefix_base = args.prefix
413 prefix_len = args.prefixlen
415 auth = (args.user, args.password)
417 xml_template = args.xml
419 test_start_time = time.time()
420 total_build_data_time_counter = 0
421 total_response_time_counter = 0
422 total_number_of_responses_counter = 0
424 if command == "post":
425 post_prefixes(odl_ip, port, uri, auth, prefix_base, prefix_len, count,
428 put_prefixes(odl_ip, port, uri, auth, prefix_base, prefix_len, count,
431 add_prefixes(odl_ip, port, uri, auth, prefix_base, prefix_len, count,
433 elif command == "delete":
434 delete_prefixes(odl_ip, port, uri, auth, prefix_base, prefix_len, count)
435 elif command == "delete-all":
436 delete_all_prefixes(odl_ip, port, uri, auth)
437 elif command == "get":
438 get_prefixes(odl_ip, port, uri, auth)
440 total_test_execution_time = time.time() - test_start_time
442 logger.info("Total test execution time: %.3fs", total_test_execution_time)
443 logger.info("Total build data time: %.3fs", total_build_data_time_counter)
444 logger.info("Total response time: %.3fs", total_response_time_counter)
445 logger.info("Total number of response(s): %s", total_number_of_responses_counter)