From 5e515a6a2f30af005e94586cd37199bdefec67e1 Mon Sep 17 00:00:00 2001 From: Marcus Williams Date: Mon, 9 Nov 2015 11:18:17 -0800 Subject: [PATCH] Add ovsdbconfigblaster.py Start of ovsdb southbound performance suite. Change-Id: I12f6360f343c05d399789b97bbe6a66d7f477469 Signed-off-by: Marcus Williams --- .../ovsdbconfigblaster.py | 317 ++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 tools/odl-ovsdb-performance-tests/ovsdbconfigblaster.py diff --git a/tools/odl-ovsdb-performance-tests/ovsdbconfigblaster.py b/tools/odl-ovsdb-performance-tests/ovsdbconfigblaster.py new file mode 100644 index 0000000000..7065909ffa --- /dev/null +++ b/tools/odl-ovsdb-performance-tests/ovsdbconfigblaster.py @@ -0,0 +1,317 @@ +""" +Script to add bridges/ports/termination points to ovsdb config +""" +__author__ = 'Marcus Williams' +__copyright__ = "Copyright (c) 2015, Intel Corp Inc., Cisco Systems Inc. and others" +__credits__ = ["Jan Medved, Lori Jakab"] +__license__ = "New-style BSD" +__email__ = "marcus.williams@intel.com" +__version__ = "0.0.1" + +import argparse +import logging +import requests + + +class OvsdbConfigBlaster (object): + PUT_HEADERS = {'Content-Type': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=', + 'Accept': 'application/json'} + GET_HEADERS = {'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4='} + TIMEOUT = 10 + + def __init__(self, + controller_ip, + controller_port, + vswitch_ip, + vswitch_ovsdb_port, + vswitch_remote_ip, + vswitch_remote_ovsdb_port, + vswitch_port_type, + num_instances): + """ + Args: + :param controller_ip: The ODL host ip used to send RPCs + :param controller_port: The RESTCONF port on the ODL host + :param vswitch_ip: The ip of Open vSwitch to use + :param vswitch_ovsdb_port: The ovsdb port of Open vSwitch to use + :param vswitch_remote_ip: The ip of remote Open vSwitch to use + :param vswitch_remote_ovsdb_port: The ovsdb port of remote Open vSwitch to use + :param vswitch_port_type: Port type to create + :param num_instances: The number of instances (bridges, ports etc) to be added + """ + logging.basicConfig(level=logging.DEBUG) + self.session = requests.Session() + self.controller_ip = controller_ip + self.controller_port = controller_port + self.vswitch_dict = dict() + self.add_vswitch_to_dict( + vswitch_ip, + vswitch_remote_ip, + vswitch_ovsdb_port, 'ovs-1') + if vswitch_remote_ip: + self.add_vswitch_to_dict( + vswitch_remote_ip, + vswitch_ip, + vswitch_remote_ovsdb_port, 'ovs-2') + self.vswitch_port_type = vswitch_port_type + self.num_instances = num_instances + + self.connect_vswitch(self.vswitch_dict['ovs-1']) + if self.vswitch_dict.get('ovs-2'): + self.connect_vswitch(self.vswitch_dict['ovs-2']) + + @staticmethod + def return_ovsdb_url(vswitch_ip, vswitch_ovsdb_port, url_type="config"): + """ Return an ovsdb restconf url + Args: + :param vswitch_ip: The ip of Open vSwitch to use + :param vswitch_ovsdb_port: The ovsdb port of Open vSwitch to use + :param url_tyep: The type of url 'config' | 'oper' + """ + url_prefix = None + if url_type == "config": + url_prefix = 'restconf/config/' + elif url_type == "oper": + url_prefix = 'restconf/operational/' + ovsdb_url = url_prefix \ + + 'network-topology:' \ + 'network-topology/topology/' \ + 'ovsdb:1/node/ovsdb:%2F%2F' \ + + vswitch_ip\ + + ':' \ + + vswitch_ovsdb_port + return ovsdb_url + + def add_vswitch_to_dict(self, vswitch_ip, vswitch_remote_ip, vswitch_ovsdb_port, vswitch_name): + """ Add details of an Open vSwitch instance to self.vswitch_dict + Args: + :param vswitch_ip: The ip of Open vSwitch to use + :param vswitch_remote_ip: The ip of remote Open vSwitch to use + :param vswitch_ovsdb_port: The ovsdb port of Open vSwitch to use + :param vswitch_name: The name to label the added Open vSwitch instance + """ + urlprefix = 'http://' \ + + self.controller_ip + \ + ':' \ + + self.controller_port + \ + '/' + self.vswitch_dict.update({vswitch_name: { + 'name': vswitch_name, + 'ip': vswitch_ip, + 'remote-ip': vswitch_remote_ip, + 'ovsdb-port': vswitch_ovsdb_port, + 'node-id': 'ovsdb://%s:%s' + % (vswitch_ip, + vswitch_ovsdb_port), + 'post-url': urlprefix + + OvsdbConfigBlaster.return_ovsdb_url( + vswitch_ip, + vswitch_ovsdb_port), + 'get-config-url': urlprefix + + OvsdbConfigBlaster.return_ovsdb_url( + vswitch_ip, + vswitch_ovsdb_port), + 'get-oper-url': urlprefix + + OvsdbConfigBlaster.return_ovsdb_url( + vswitch_ip, + vswitch_ovsdb_port)}}) + + def connect_vswitch(self, vswitch_dict): + """ Connect ODL to an Open vSwitch instance using restconf + Args: + :param vswitch_dict: A dictionary detailing + an instance of Open vSwitch + """ + connect_ovs_body = { + u'network-topology:node': [ + { + u'node-id': unicode(vswitch_dict['node-id']), + u'connection-info': { + u'ovsdb:remote-port': unicode(vswitch_dict['ovsdb-port']), + u'ovsdb:remote-ip': unicode(vswitch_dict['ip']) + } + } + ] + } + self.send_rest(self.session, + vswitch_dict['post-url'], + connect_ovs_body) + + def add_bridge(self, num_instances, vswitch_name='ovs-1'): + """Add num_instances of bridge to ODL config + Args: + :param num_instances: Number of bridges to create + :param vswitch_name: A name describing + an instance of Open vSwitch + """ + + for i in range(num_instances): + bridge_name = unicode('br-' + str(i) + '-test') + add_bridge_body = { + u"network-topology:node": [ + { + u"node-id": u"%s/bridge/%s" + % (unicode(self.vswitch_dict[vswitch_name] + .get('node-id')), + unicode(bridge_name)), + u"ovsdb:bridge-name": unicode(bridge_name), + u"ovsdb:datapath-id": u"00:00:b2:bf:48:25:f2:4b", + u"ovsdb:protocol-entry": [ + { + u"protocol": + u"ovsdb:ovsdb-bridge-protocol-openflow-13" + }], + u"ovsdb:controller-entry": [ + { + u"target": u"tcp:%s:%s" % (self.controller_ip, + self.controller_port) + }], + u"ovsdb:managed-by": u"/network-topology:network-topology/" + u"network-topology:topology" + u"[network-topology:topology-id" + u"='ovsdb:1']/network-topology:node" + u"[network-topology:node-id=" + u"'%s']" + % unicode(self.vswitch_dict[vswitch_name] + .get('node-id')) + } + ] + } + self.send_rest(self.session, + self.vswitch_dict[vswitch_name] + .get('post-url') + + '%2Fbridge%2F' + + bridge_name, + add_bridge_body) + self.session.close() + + def add_port(self, port_type="ovsdb:interface-type-vxlan"): + """Add self.num_instances of port to ODL config + Args: + :param port_type: The type of port to create + default: 'ovsdb:interface-type-vxlan' + """ + bridge_name = 'br-0-test' + self.add_bridge(1, 'ovs-1') + self.add_bridge(1, 'ovs-2') + + for instance in range(self.num_instances): + for vswitch in self.vswitch_dict.itervalues(): + if port_type == "ovsdb:interface-type-vxlan": + port_prefix = "tp-" + ovsdb_rest_url = vswitch.get('post-url') \ + + '%2Fbridge%2F'\ + + bridge_name\ + + '/termination-point/' + body_name = 'tp-body' + else: + port_prefix = "port-" + ovsdb_rest_url = vswitch.get('post-url') \ + + '%2Fbridge%2F' \ + + bridge_name\ + + '/port/' + body_name = 'port-body' + port_name = port_prefix + str(instance) + '-test-' + vswitch.get('ip') + body = {'tp-body': { + u"network-topology:termination-point": [ + { + u"ovsdb:options": [ + { + u"ovsdb:option": u"remote_ip", + u"ovsdb:value": unicode(vswitch.get('remote-ip')) + } + ], + u"ovsdb:name": unicode(port_name), + u"ovsdb:interface-type": unicode(port_type), + u"tp-id": unicode(port_name), + u"vlan-tag": unicode(instance + 1), + u"trunks": [ + { + u"trunk": u"5" + } + ], + u"vlan-mode": u"access" + } + ] + }, + # TODO add port-body + 'port-body': {}} + self.send_rest(self.session, + ovsdb_rest_url + port_name, + body.get(body_name)) + + self.session.close() + + def send_rest(self, session, rest_url, json_body): + """Send an HTTP PUT to the Rest URL and return the status code + Args: + :param session: The HTTP session handle + :param json_body: the JSON body to be sent + Returns: + :return int: status_code - HTTP status code + """ + ret = session.put(rest_url, + json=json_body, + headers=self.PUT_HEADERS, + stream=False, + timeout=self.TIMEOUT) + + if ret.status_code is not 200: + raise ValueError(ret.text, + ret.status_code, + rest_url, + json_body) + return ret.status_code + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Add bridge/port/term-points' + ' to OpenDaylight') + + parser.add_argument('--mode', default='bridge', + help='Operating mode, can be "bridge", "port" or "term" \ + (default is "bridge")') + parser.add_argument('--controller', default='127.0.0.1', + help='IP of running ODL controller (default \ + is 127.0.0.1)') + parser.add_argument('--controllerport', default='8181', + help='Port of ODL RESTCONF \ + (default is 8181)') + parser.add_argument('--vswitch', default='127.0.0.1', + help='IP of Open vSwitch \ + (default is 127.0.0.1)') + parser.add_argument('--vswitchport', default='6640', + help='Port of Open vSwitch OVSDB server \ + (default is 6640)') + parser.add_argument('--vswitchremote', default=None, + help='IP of remote Open vSwitch \ + (default is none)') + parser.add_argument('--vswitchremoteport', default=None, + help='Port of remote Open vSwitch OVSDB server \ + (default is none)') + parser.add_argument('--vswitchporttype', default=None, + help='Port of remote Open vSwitch OVSDB server \ + (default is none)') + parser.add_argument('--instances', type=int, default=1, + help='Number of instances to add/get (default 1)') + + args = parser.parse_args() + + ovsdb_config_blaster = OvsdbConfigBlaster(args.controller, + args.controllerport, + args.vswitch, + args.vswitchport, + args.vswitchremote, + args.vswitchremoteport, + args.vswitchporttype, + args.instances) + + if args.mode == "bridge": + ovsdb_config_blaster.add_bridge(ovsdb_config_blaster.num_instances) + elif args.mode == "term": + ovsdb_config_blaster.add_port() + elif args.mode == "port" and args.vswitchporttype is not None: + ovsdb_config_blaster.add_port(args.vswitchporttype) + else: + print "Unsupported mode:", args.mode -- 2.36.6