#!/usr/bin/env python # -*- coding: utf-8 -*- # PYTHON_ARGCOMPLETE_OK ## # Copyright 2015 Telefonica Investigacion 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 import osm_openvim.openflow_conn as openflow_conn from osm_openvim.openflow_thread import change_db2of, FlowBadFormat def of_switches(args): try: c = ofconnector.get_of_switches() for s in c: print " %s %s" % (s[0], s[1]) return 0 except openflow_conn.OpenflowconnException as e: print ("OF get switch error {}".format(str(e))) return -1 def of_list(args): try: c = ofconnector.get_of_rules(not args.no_translate) 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 except openflow_conn.OpenflowconnException as e: print("OF get list error {}".format(str(e))) return -1 def of_clear(args): try: 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 c = ofconnector.clear_all_flows() return 0 except openflow_conn.OpenflowconnException as e: print ("OF error {}".format(str(e))) return -1 def of_port_list(args): try: c = ofconnector.obtain_port_correspondence() 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 len(c) except openflow_conn.OpenflowconnException as e: print("OF error {}".format(str(e))) return -1 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 try: ofconnector.new_flow(rule) error = "OK" except openflow_conn.OpenflowconnException as e: error = "ERROR: " + str(e) 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=,out=,...'" % 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 try: c = ofconnector.new_flow(rule) if args.print_id: print rule["name"] return 0 except openflow_conn.OpenflowconnException as e: print("OF error {}".format(str(e))) return -1 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 try: ofconnector.del_flow(args.name) return 0 except openflow_conn.OpenflowconnException as e: print("OF error {}".format(str(e))) return -1 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',"8080") 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=,out=,...") 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= can be used several times") add_parser.add_argument('--debug', '-d', action='store_true', help="show debug information") add_parser.add_argument('--print-id', action='store_true', help="print the flow id after added") 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 try: pkg = __import__("osm_openvim." + module) of_conn = getattr(pkg, module) 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)