--- /dev/null
+"""
+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