#!/usr/bin/python """ Spin-up script for Opendaylight GBP and GBPSFC sample concept demonstrations. This script has to be run from the environment on which OVS switches and Docker containers are located. This script works together with infrastructure_config.py configuration script where the entire topology for specific scenario is configured. This file defines to which already existing switch docker containers have to be connected. OVS switch have to be created on the same hosting environment as this script is executed from. Local switch name should match one of the names specified in configuration file so that docker containers are created and connected to it. Updated: 22.10.2015 """ import socket import os import sys import ipaddr from subprocess import call from subprocess import check_output from infrastructure_config import switches from infrastructure_config import hosts from infrastructure_config import defaultContainerImage def add_controller(sw, ip): """Connects OVS switch to a controller. The switch is specified by it's name and the controller by it's IP address. Args: :param sw: name of a switch on which controller is set :param ip: IP address of controller NOTE: :Required controller should listen on TCP port 6653. """ try: socket.inet_aton(ip) except socket.error: print "Error: %s is not a valid IPv4 address of controller!" % ip os.exit(2) call(['sudo', 'ovs-vsctl', 'set-controller', sw, 'tcp:%s:6653' % ip]) def add_manager(ip): """Sets OVSDB manager for OVS instance. Args: :param ip: IP address of specified manager NOTE: :Required manager should listen on TCP port 6640. """ try: socket.inet_aton(ip) except socket.error: print "Error: %s is not a valid IPv4 address of manager!" % ip os.exit(2) cmd = ['sudo', 'ovs-vsctl', 'set-manager', 'tcp:%s:6640' % ip] call(cmd) def add_switch(name, dpid=None): """Adds switch to OVS instance and sets it's DataPath ID if specified. Args: :param ip: name of new switch :param dpid: DataPath ID of new switch """ call(['sudo', 'ovs-vsctl', 'add-br', name]) # Add bridge if dpid: if len(dpid) < 16: # DPID must be 16-bytes in later versions of OVS filler = '0000000000000000' # prepending zeros to match 16-byt length, e.g. 123 -> 0000000000000123 dpid = filler[:len(filler) - len(dpid)] + dpid elif len(dpid) > 16: print 'DPID: %s is too long' % dpid sys.exit(3) call(['sudo', 'ovs-vsctl', 'set', 'bridge', name, 'other-config:datapath-id=%s' % dpid]) def set_of_version(sw, version='OpenFlow13,OpenFlow12,OpenFlow10'): """Sets OpenFlow protocol versions on OVS switch Args: :param sw: name of switch :param sw: OpenFlow versions to support on switch """ call(['sudo', 'ovs-vsctl', 'set', 'bridge', sw, 'protocols={}'.format(version)]) def add_vxlan_tunnel(sw): """Adds VXLAN tunnel to OVS switch. Args: :param sw: name of switch NOTE: :Remote IP is read from flows. """ ifaceName = '{}-vxlan-0'.format(sw) cmd = ['sudo', 'ovs-vsctl', 'add-port', sw, ifaceName, '--', 'set', 'Interface', ifaceName, 'type=vxlan', 'options:remote_ip=flow', 'options:key=flow'] call(cmd) def add_gpe_tunnel(sw): """Adds GPE tunnel to OVS switch. Args: :param sw: name of switch :param dpid: DataPath ID of new switches NOTE: :Remote IP is read from flows. """ ifaceName = '{}-vxlangpe-0'.format(sw) cmd = ['sudo', 'ovs-vsctl', 'add-port', sw, ifaceName, '--', 'set', 'Interface', ifaceName, 'type=vxlan', 'options:remote_ip=flow', 'options:dst_port=6633', 'options:nshc1=flow', 'options:nshc2=flow', 'options:nshc3=flow', 'options:nshc4=flow', 'options:nsp=flow', 'options:nsi=flow', 'options:key=flow'] call(cmd) def launch_container(host, containerImage): # TODO use Docker.py """Runs docker container in background. Args: :param host: container host name :param dpid: DataPath ID of new switch Returns: :returns string: container ID NOTE: :No networking set. """ containerID = check_output(['docker', 'run', '-d', '--net=none', '--name=%s' % host['name'], '-h', host['name'], '-t', '-i', '--privileged=True', containerImage, '/bin/bash']) return containerID[:-1] # Remove extraneous \n from output of above def connect_container_to_switch(sw, host, containerID): """Connects docker to OVS switch. Args: :param sw: name of switch :param host: host object to process. Here is an example of such as object {'name': 'h35_2', 'mac': '00:00:00:00:35:02', 'ip': '10.0.35.2/24', 'switch': 'sw1'} Note: 'switch' - name of OVS switch to which the host will be connected :param containerID: ID of docker container """ hostIP = host['ip'] mac = host['mac'] nw = ipaddr.IPv4Network(hostIP) broadcast = "{}".format(nw.broadcast) router = "{}".format(nw.network + 1) ovswork_path = os.path.dirname(os.path.realpath(__file__)) + '/ovswork.sh' cmd = [ovswork_path, sw, containerID, hostIP, broadcast, router, mac, host['name']] if ('vlan') in host: cmd.append(host['vlan']) call(cmd) def launch(switches, hosts, odl_ip='127.0.0.1'): """Connects hosts to switches. Arguments are tied to underlying configuration file. Processing runs for switch, that is present on local environment and for hosts configured to be connected to the switch. Args: :param switches: switches to connect to Example of switch object {'name': 'sw1', 'dpid': '1'} :param hosts: hosts to connect Example of host object {'name': 'h35_2', 'mac': '00:00:00:00:35:02', 'ip': '10.0.35.2/24', 'switch': 'sw1'} Note: 'switch' - name of OVS switch to which the host will be connected :param odl_ip: IP address of ODL, acting as both - manager and controller. Default value is '127.0.0.1' """ for sw in switches: add_manager(odl_ip) ports = 0 first_host = True for host in hosts: if host['switch'] == sw['name']: if first_host: add_switch(sw['name'], sw['dpid']) set_of_version(sw['name']) add_controller(sw['name'], odl_ip) add_gpe_tunnel(sw['name']) add_vxlan_tunnel(sw['name']) first_host = False containerImage = defaultContainerImage # from Config if ('container_image') in host: # from Config containerImage = host['container_image'] containerID = launch_container(host, containerImage) ports += 1 connect_container_to_switch( sw['name'], host, containerID) host['port-name'] = 'vethl-' + host['name'] print "Created container: %s with IP: %s. Connect using docker attach %s," \ "disconnect with 'ctrl-p-q'." % (host['name'], host['ip'], host['name']) if __name__ == "__main__": if len(sys.argv) < 2 or len(sys.argv) > 3: print "Please, specify IP of ODL and switch index in arguments." print "usage: ./infrastructure_launch.py ODL_IP SWITCH_INDEX" sys.exit(2) controller = sys.argv[1] try: socket.inet_aton(controller) except socket.error: print "Error: %s is not a valid IPv4 address!" % controller sys.exit(2) sw_index = int(sys.argv[2]) print sw_index print switches[sw_index] if sw_index not in range(0, len(switches) + 1): print len(switches) + 1 print "Error: %s is not a valid switch index!" % sw_index sys.exit(2) sw_type = switches[sw_index]['type'] sw_name = switches[sw_index]['name'] if sw_type == 'gbp': print "*****************************" print "Configuring %s as a GBP node." % sw_name print "*****************************" print launch([switches[sw_index]], hosts, controller) print "*****************************" print "OVS status:" print "-----------" print call(['sudo', 'ovs-vsctl', 'show']) print print "Docker containers:" print "------------------" call(['docker', 'ps']) print "*****************************" elif sw_type == 'sff': print "*****************************" print "Configuring %s as an SFF." % sw_name print "*****************************" call(['sudo', 'ovs-vsctl', 'set-manager', 'tcp:%s:6640' % controller]) print elif sw_type == 'sf': print "*****************************" print "Configuring %s as an SF." % sw_name print "*****************************" call(['%s/sf-config.sh' % os.path.dirname(os.path.realpath(__file__)), '%s' % sw_name])