Initial openvim v0.4.6 upload
[osm/openvim.git] / openflow
diff --git a/openflow b/openflow
new file mode 100755 (executable)
index 0000000..7ea59c4
--- /dev/null
+++ b/openflow
@@ -0,0 +1,388 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# PYTHON_ARGCOMPLETE_OK
+
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openmano
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: nfvlabs@tid.es
+##
+
+'''
+This program is useful to interact directly with Openflow Controllers
+to clear rules, add and delete rules, list rules, etc.
+'''
+
+__author__="Gerardo Garcia, Alfonso Tierno, Pablo Montes"
+__date__ ="$09-oct-2014 09:09:48$"
+
+#import time
+import os
+import sys
+import argparse
+import argcomplete
+import imp
+import yaml
+import requests
+import logging
+from openflow_thread import change_db2of, FlowBadFormat
+
+def of_switches(args):
+    r,c = ofconnector.get_of_switches()
+    if r<0:
+        print c
+        return r
+    else: 
+        for s in c:
+            print " %s %s" % (s[0], s[1])
+    return 0
+
+def of_list(args):
+    r,c = ofconnector.get_of_rules(not args.no_translate)
+    if r<0:
+        print c
+        return r
+    if args.verbose > 0:
+        print yaml.safe_dump(c, indent=4, default_flow_style=False)
+        return 0
+
+    print "       switch           priority        name                             ingress_port    dst_mac       vlan_id  actions"
+    for name,rule in c.iteritems():
+        action_list=[]
+        for action in rule["actions"]:
+            action_list.append(action[0]+"="+str(action[1]))
+        if "vlan_id" in rule:
+            vlan=str(rule["vlan_id"])
+        else:
+            vlan="any"
+        print "%s  %s  %s  %s  %s  %s  %s" % \
+            (rule["switch"], str(rule["priority"]).ljust(6), name.ljust(40), rule["ingress_port"].ljust(8), \
+            rule.get("dst_mac","any").ljust(18), vlan.ljust(4), ",".join(action_list) )
+    return 0
+
+def of_clear(args):
+    if not args.force:
+        r = raw_input("Clear all Openflow rules (y/N)? ")
+        if  not (len(r)>0  and r[0].lower()=="y"):
+            return 0
+    r,c = ofconnector.clear_all_flows()
+    if r<0:
+        print c
+        return r
+    return 0
+
+def of_port_list(args):
+    r,c = ofconnector.obtain_port_correspondence()
+    if r<0:
+        print c
+        return r
+    yaml.safe_dump({"ports": c}, sys.stdout, indent=2, default_flow_style=False)
+
+#def of_dump(args):
+#    args.verbose = 3
+#    args.no_translate=False
+#    of_list(args)
+    return 0
+
+def of_reinstall(args):
+    try:
+        URLrequest = "http://%s:%s/openvim/networks/all/openflow" %(vim_host, vim_admin_port)
+        print URLrequest
+        openvim_response = requests.put(URLrequest)
+        print openvim_response.text
+        return 0
+    except requests.exceptions.RequestException as e:
+        print " Exception GET at '"+URLrequest+"' " + str(e)
+        return -1
+
+def of_install(args):
+    line_number=1
+    try:
+        f = open(args.file, "r")
+        text = f.read()
+        f.close()
+        lines=text.split("\n")
+        heads=lines[0].split()
+
+        for line in lines[1:]:
+            line_number += 1
+            rule={}
+            items= line.split()
+            if len(items)==0 or items[0][0]=="#": #empty line or commented
+                continue
+            for i in range(0,len(items)):
+                rule[ heads[i] ] = items[i]
+            if rule["vlan_id"] == "any":
+                del rule["vlan_id"]
+            if rule["dst_mac"] == "any":
+                del rule["dst_mac"]
+            if 'priority' in rule and (rule['priority']==None or rule['priority']=="None" ):
+                del rule['priority']
+            try:
+                change_db2of(rule)
+            except FlowBadFormat as e:
+                print "Format error at line %d:  %s" % (line_number, str(e))
+                continue
+            r,c = ofconnector.new_flow(rule)
+            if r<0:
+                error="ERROR: "+c
+            else:
+                error="OK"
+            print "%s  %s  %s  input=%s  dst_mac=%s  vlan_id=%s  %s" % \
+                    (rule["switch"], str(rule.get("priority")).ljust(6), rule["name"].ljust(20), rule["ingress_port"].ljust(3), \
+                     rule.get("dst_mac","any").ljust(18), rule.get("vlan_id","any").ljust(4), error )
+        return 0
+    except IOError as e:
+        print " Error opening file '" + args.file + "': " + e.args[1]
+        return -1
+    except yaml.YAMLError as exc:
+        error_pos = ""
+        if hasattr(exc, 'problem_mark'):
+            mark = exc.problem_mark
+            error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1)
+        print " Error yaml/json format error at " + error_pos
+        return -1
+
+def of_add(args):
+    if args.act==None and args.actions==None:
+        print "openflow add: error: one of the arguments --actions or [--setvlan,--stripvlan],--out is required"
+        return -1
+    elif args.act!=None and args.actions!=None:
+        print "openflow add: error: Use either --actions option or [--setvlan,--stripvlan],--out options; but not both"
+        return -1
+    
+    rule={"name":args.name, "priority":args.priority,
+          "ingress_port": args.inport
+        }
+    if args.matchvlan:
+        rule["vlan_id"] = args.matchvlan
+    if args.matchmac:
+        rule["dst_mac"] = args.matchmac
+
+    if args.actions:
+        rule["actions"] = args.actions
+        try:
+            change_db2of(rule)
+        except FlowBadFormat as e:
+            print "Format error at --actions: '%s' Expected 'vlan=<None/vlan_id>,out=<egress_port>,...'" % str(e)
+            return -1
+    elif args.act:
+        rule["actions"]=[]
+        error_msj = "openflow add: error: --setvlan,--stripvlan options must be followed by an --out option"
+        previous_option_vlan=False # indicates if the previous option was a set or strip vlan to avoid consecutive ones and to force an out options afterwards
+        for action in args.act:
+            if action==None or type(action)==int:
+                if previous_option_vlan: #consecutive vlan options
+                    print error_msj
+                    return -1
+                previous_option_vlan=True
+                rule["actions"].append( ("vlan", action) )
+            else:
+                previous_option_vlan=False
+                rule["actions"].append( ("out", action) ) 
+        if previous_option_vlan:
+            print error_msj
+            return -1
+    #print rule
+    #return
+
+    r,c = ofconnector.new_flow(rule)
+    if r<0:
+        print c
+        return -1
+    return 0
+
+def of_delete(args):
+    if not args.force:
+        r = raw_input("Clear rule %s (y/N)? " %(args.name))
+        if  not (len(r)>0  and r[0].lower()=="y"):
+            return 0
+    r,c = ofconnector.del_flow(args.name)
+    if r<0:
+        print c
+        return -1
+    return 0
+
+def config(args):
+    print "OPENVIM_HOST: %s" %(vim_host)
+    print "OPENVIM_ADMIN_PORT: %s" %(vim_admin_port)
+    print "OF_CONTROLLER_TYPE: %s" %(of_controller_type)
+    if of_controller_module or (of_controller_type!="floodlight" and of_controller_type!="opendaylight"):
+        print "OF_CONTROLLER_MODULE: %s" %(of_controller_module)
+    print "OF_CONTROLLER_USER: %s" %(of_controller_user)
+    print "OF_CONTROLLER_PASSWORD: %s" %(of_controller_password)
+    #print "OF_CONTROLLER_VERSION: %s" %(of_controller_version)
+    print "OF_CONTROLLER_IP: %s" %(of_controller_ip)
+    print "OF_CONTROLLER_PORT: %s" %(of_controller_port)
+    print "OF_CONTROLLER_DPID: %s" %(of_controller_dpid)
+    return
+
+version="0.8"
+global vim_host
+global vim_admin_port
+global of_controller_type
+global of_controller_user
+global of_controller_password
+global of_controller_ip
+global of_controller_port
+global of_controller_dpid
+global of_controller_module
+global ofconnector
+   
+if __name__=="__main__":
+    #print "test_ofconnector version", version, "Jul 2015"
+    #print "(c) Copyright Telefonica"
+    
+    vim_host = os.getenv('OPENVIM_HOST',"localhost")
+    vim_admin_port = os.getenv('OPENVIM_ADMIN_PORT',"8085")
+    of_controller_type = os.getenv('OF_CONTROLLER_TYPE',"floodlight")
+    of_controller_user = os.getenv('OF_CONTROLLER_USER',None)
+    of_controller_password = os.getenv('OF_CONTROLLER_PASSWORD',None)
+    #of_controller_version = os.getenv('OF_CONTROLLER_VERSION',"0.90")
+    of_controller_ip = os.getenv('OF_CONTROLLER_IP',"localhost")
+    of_controller_port = os.getenv('OF_CONTROLLER_PORT',"7070")
+    of_controller_dpid = os.getenv('OF_CONTROLLER_DPID','00:01:02:03:e4:05:e6:07')
+    of_controller_module = os.getenv('OF_CONTROLLER_MODULE',None)
+    
+    main_parser = argparse.ArgumentParser(description='User program to interact with Openflow controller')
+    main_parser.add_argument('--version', action='version', version='%(prog)s ' + version )
+    
+    #main_parser = argparse.ArgumentParser()
+    subparsers = main_parser.add_subparsers(help='commands')
+    
+    config_parser = subparsers.add_parser('config', help="prints configuration values")
+    config_parser.set_defaults(func=config)
+    
+    add_parser = subparsers.add_parser('add', help="adds an openflow rule")
+    add_parser.add_argument('--verbose', '-v', action='count')
+    add_parser.add_argument("name", action="store", help="name of the rule")
+    add_parser.add_argument("--inport", required=True, action="store", type=str, help="match rule: ingress-port")
+    add_parser.add_argument("--actions", action="store", type=str, help="action with the format: vlan=<None/vlan-id>,out=<egress-port>,...")
+    add_parser.add_argument("--priority", action="store", type=int, help="rule priority")
+    add_parser.add_argument("--matchmac", action="store", help="match rule: mac address")
+    add_parser.add_argument("--matchvlan", action="store", type=int, help="match rule: vlan id")
+    add_parser.add_argument("--stripvlan", action="append_const", dest="act", const=None, help="alternative to --actions. Use before --out to strip vlan")
+    add_parser.add_argument("--setvlan", action="append", dest="act", type=int, help="alternative to --actions. Use before --out to set vlan")
+    add_parser.add_argument("--out", action="append", dest="act", type=str, help="alternative to --actions. out=<egress-port> can be used several times")
+    add_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+    add_parser.set_defaults(func=of_add)
+    
+    delete_parser = subparsers.add_parser('delete', help="delete an openflow rule")
+    delete_parser.add_argument('--verbose', '-v', action='count')
+    delete_parser.add_argument("-f", "--force", action="store_true", help="force deletion without asking")
+    delete_parser.add_argument("name", action="store", help="name of the rule to be deleted")
+    delete_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+    delete_parser.set_defaults(func=of_delete)
+    
+    switches_parser = subparsers.add_parser('switches', help="list all switches controlled by the OFC")
+    switches_parser.add_argument('--verbose', '-v', action='count')
+    switches_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+    switches_parser.set_defaults(func=of_switches)
+
+    list_parser = subparsers.add_parser('list', help="list openflow rules")
+    list_parser.add_argument('--verbose', '-v', action='count')
+    list_parser.add_argument("--no-translate", "-n", action="store_true", help="Skip translation from openflow index to switch port name")
+    list_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+    list_parser.set_defaults(func=of_list)
+
+    #dump_parser = subparsers.add_parser('dump', help="dump openflow rules")
+    #dump_parser.set_defaults(func=of_dump)
+    
+    clear_parser = subparsers.add_parser('clear', help="clear all openflow rules")
+    clear_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
+    clear_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+    clear_parser.set_defaults(func=of_clear)
+
+    install_parser = subparsers.add_parser('install', help="install openflow rules from file")
+    install_parser.add_argument("file", action="store", help="file with rules generated using 'openflow list > rules.txt'")
+    install_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+    install_parser.set_defaults(func=of_install)
+
+    reinstall_parser = subparsers.add_parser('reinstall', help="reinstall openflow rules from VIM rules")
+    reinstall_parser.set_defaults(func=of_reinstall)
+    reinstall_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+
+    portlist_parser = subparsers.add_parser('port-list', help="list the physical to openflow port correspondence")
+    portlist_parser.set_defaults(func=of_port_list)
+    portlist_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+
+    argcomplete.autocomplete(main_parser)
+    
+    args = main_parser.parse_args()
+    module_info=None
+    try:
+        if args.func is not config:
+            params={ "of_ip":   of_controller_ip,
+                        "of_port": of_controller_port, 
+                        "of_dpid": of_controller_dpid,
+                        "of_user": of_controller_user,
+                        "of_password": of_controller_password,
+                }
+            if "debug" in args and args.debug:
+                streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
+                logging.basicConfig(format=streamformat, level= logging.DEBUG)
+                logger = logging.getLogger('vim')
+                logger.setLevel(logging.DEBUG)
+                params["of_debug"]="DEBUG"
+            else:
+                #logger = logging.getLogger('vim').addHandler(logging.NullHandler())
+                #logger.setLevel(logging.CRITICAL)
+                params["of_debug"]="CRITICAL"
+            
+            if of_controller_type=='opendaylight':
+                module = "ODL"
+            elif of_controller_module != None:
+                module = of_controller_module
+            else:
+                module = of_controller_type
+            module_info = imp.find_module(module)
+            
+            of_conn = imp.load_module("of_conn", *module_info)
+            try:
+                ofconnector = of_conn.OF_conn(params)
+            except Exception as e: 
+                print "Cannot open the Openflow controller '%s': %s" % (type(e).__name__, str(e))
+                result = -1
+                exit()
+        result = args.func(args)
+        if result == None:
+            result = 0
+            
+        #for some reason it fails if call exit inside try instance. Need to call exit at the end !?
+    except (IOError, ImportError) as e:
+        print "Cannot open openflow controller module '%s'; %s: %s" % (module, type(e).__name__, str(e))
+        result = -1
+    #except Exception as e:
+    #    print "Cannot open the Openflow controller '%s': %s" % (type(e).__name__, str(e))
+    #    result = -1
+    except requests.exceptions.ConnectionError as e:
+        print "Cannot connect to server; %s: %s" % (type(e).__name__, str(e))
+        result = -2
+    except (KeyboardInterrupt):
+        print 'Exiting openVIM'
+        result = -3
+    except (SystemExit):
+        result = -4
+    
+    #close open file
+    if module_info and module_info[0]:
+        file.close(module_info[0])
+    exit(result)
+    
+
+