blob: 80cf624a826d4350b0d118231846bb6e19fe6cb1 [file] [log] [blame]
tiernof7aa8c42016-09-06 16:43:04 +02001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# PYTHON_ARGCOMPLETE_OK
4
5##
6# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
7# This file is part of openmano
8# All Rights Reserved.
9#
10# Licensed under the Apache License, Version 2.0 (the "License"); you may
11# not use this file except in compliance with the License. You may obtain
12# a copy of the License at
13#
14# http://www.apache.org/licenses/LICENSE-2.0
15#
16# Unless required by applicable law or agreed to in writing, software
17# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
19# License for the specific language governing permissions and limitations
20# under the License.
21#
22# For those usages not covered by the Apache License, Version 2.0 please
23# contact with: nfvlabs@tid.es
24##
25
26'''
27This program is useful to interact directly with Openflow Controllers
28to clear rules, add and delete rules, list rules, etc.
29'''
30
31__author__="Gerardo Garcia, Alfonso Tierno, Pablo Montes"
32__date__ ="$09-oct-2014 09:09:48$"
33
34#import time
35import os
36import sys
37import argparse
38import argcomplete
39import imp
40import yaml
41import requests
42import logging
43from openflow_thread import change_db2of, FlowBadFormat
44
45def of_switches(args):
46 r,c = ofconnector.get_of_switches()
47 if r<0:
48 print c
49 return r
50 else:
51 for s in c:
52 print " %s %s" % (s[0], s[1])
53 return 0
54
55def of_list(args):
56 r,c = ofconnector.get_of_rules(not args.no_translate)
57 if r<0:
58 print c
59 return r
60 if args.verbose > 0:
61 print yaml.safe_dump(c, indent=4, default_flow_style=False)
62 return 0
63
64 print " switch priority name ingress_port dst_mac vlan_id actions"
65 for name,rule in c.iteritems():
66 action_list=[]
67 for action in rule["actions"]:
68 action_list.append(action[0]+"="+str(action[1]))
69 if "vlan_id" in rule:
70 vlan=str(rule["vlan_id"])
71 else:
72 vlan="any"
73 print "%s %s %s %s %s %s %s" % \
74 (rule["switch"], str(rule["priority"]).ljust(6), name.ljust(40), rule["ingress_port"].ljust(8), \
75 rule.get("dst_mac","any").ljust(18), vlan.ljust(4), ",".join(action_list) )
76 return 0
77
78def of_clear(args):
79 if not args.force:
80 r = raw_input("Clear all Openflow rules (y/N)? ")
81 if not (len(r)>0 and r[0].lower()=="y"):
82 return 0
83 r,c = ofconnector.clear_all_flows()
84 if r<0:
85 print c
86 return r
87 return 0
88
89def of_port_list(args):
90 r,c = ofconnector.obtain_port_correspondence()
91 if r<0:
92 print c
93 return r
94 yaml.safe_dump({"ports": c}, sys.stdout, indent=2, default_flow_style=False)
95
96#def of_dump(args):
97# args.verbose = 3
98# args.no_translate=False
99# of_list(args)
100 return 0
101
102def of_reinstall(args):
103 try:
104 URLrequest = "http://%s:%s/openvim/networks/all/openflow" %(vim_host, vim_admin_port)
105 print URLrequest
106 openvim_response = requests.put(URLrequest)
107 print openvim_response.text
108 return 0
109 except requests.exceptions.RequestException as e:
110 print " Exception GET at '"+URLrequest+"' " + str(e)
111 return -1
112
113def of_install(args):
114 line_number=1
115 try:
116 f = open(args.file, "r")
117 text = f.read()
118 f.close()
119 lines=text.split("\n")
120 heads=lines[0].split()
121
122 for line in lines[1:]:
123 line_number += 1
124 rule={}
125 items= line.split()
126 if len(items)==0 or items[0][0]=="#": #empty line or commented
127 continue
128 for i in range(0,len(items)):
129 rule[ heads[i] ] = items[i]
130 if rule["vlan_id"] == "any":
131 del rule["vlan_id"]
132 if rule["dst_mac"] == "any":
133 del rule["dst_mac"]
134 if 'priority' in rule and (rule['priority']==None or rule['priority']=="None" ):
135 del rule['priority']
136 try:
137 change_db2of(rule)
138 except FlowBadFormat as e:
139 print "Format error at line %d: %s" % (line_number, str(e))
140 continue
141 r,c = ofconnector.new_flow(rule)
142 if r<0:
143 error="ERROR: "+c
144 else:
145 error="OK"
146 print "%s %s %s input=%s dst_mac=%s vlan_id=%s %s" % \
147 (rule["switch"], str(rule.get("priority")).ljust(6), rule["name"].ljust(20), rule["ingress_port"].ljust(3), \
148 rule.get("dst_mac","any").ljust(18), rule.get("vlan_id","any").ljust(4), error )
149 return 0
150 except IOError as e:
151 print " Error opening file '" + args.file + "': " + e.args[1]
152 return -1
153 except yaml.YAMLError as exc:
154 error_pos = ""
155 if hasattr(exc, 'problem_mark'):
156 mark = exc.problem_mark
157 error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1)
158 print " Error yaml/json format error at " + error_pos
159 return -1
160
161def of_add(args):
162 if args.act==None and args.actions==None:
163 print "openflow add: error: one of the arguments --actions or [--setvlan,--stripvlan],--out is required"
164 return -1
165 elif args.act!=None and args.actions!=None:
166 print "openflow add: error: Use either --actions option or [--setvlan,--stripvlan],--out options; but not both"
167 return -1
168
169 rule={"name":args.name, "priority":args.priority,
170 "ingress_port": args.inport
171 }
172 if args.matchvlan:
173 rule["vlan_id"] = args.matchvlan
174 if args.matchmac:
175 rule["dst_mac"] = args.matchmac
176
177 if args.actions:
178 rule["actions"] = args.actions
179 try:
180 change_db2of(rule)
181 except FlowBadFormat as e:
182 print "Format error at --actions: '%s' Expected 'vlan=<None/vlan_id>,out=<egress_port>,...'" % str(e)
183 return -1
184 elif args.act:
185 rule["actions"]=[]
186 error_msj = "openflow add: error: --setvlan,--stripvlan options must be followed by an --out option"
187 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
188 for action in args.act:
189 if action==None or type(action)==int:
190 if previous_option_vlan: #consecutive vlan options
191 print error_msj
192 return -1
193 previous_option_vlan=True
194 rule["actions"].append( ("vlan", action) )
195 else:
196 previous_option_vlan=False
197 rule["actions"].append( ("out", action) )
198 if previous_option_vlan:
199 print error_msj
200 return -1
201 #print rule
202 #return
203
204 r,c = ofconnector.new_flow(rule)
205 if r<0:
206 print c
207 return -1
tiernod03ce282016-12-02 14:40:59 +0100208 if args.print_id:
209 print rule["name"]
tiernof7aa8c42016-09-06 16:43:04 +0200210 return 0
211
212def of_delete(args):
213 if not args.force:
214 r = raw_input("Clear rule %s (y/N)? " %(args.name))
215 if not (len(r)>0 and r[0].lower()=="y"):
216 return 0
217 r,c = ofconnector.del_flow(args.name)
218 if r<0:
219 print c
220 return -1
221 return 0
222
223def config(args):
224 print "OPENVIM_HOST: %s" %(vim_host)
225 print "OPENVIM_ADMIN_PORT: %s" %(vim_admin_port)
226 print "OF_CONTROLLER_TYPE: %s" %(of_controller_type)
227 if of_controller_module or (of_controller_type!="floodlight" and of_controller_type!="opendaylight"):
228 print "OF_CONTROLLER_MODULE: %s" %(of_controller_module)
229 print "OF_CONTROLLER_USER: %s" %(of_controller_user)
230 print "OF_CONTROLLER_PASSWORD: %s" %(of_controller_password)
231 #print "OF_CONTROLLER_VERSION: %s" %(of_controller_version)
232 print "OF_CONTROLLER_IP: %s" %(of_controller_ip)
233 print "OF_CONTROLLER_PORT: %s" %(of_controller_port)
234 print "OF_CONTROLLER_DPID: %s" %(of_controller_dpid)
235 return
236
237version="0.8"
238global vim_host
239global vim_admin_port
240global of_controller_type
241global of_controller_user
242global of_controller_password
243global of_controller_ip
244global of_controller_port
245global of_controller_dpid
246global of_controller_module
247global ofconnector
248
249if __name__=="__main__":
250 #print "test_ofconnector version", version, "Jul 2015"
251 #print "(c) Copyright Telefonica"
252
253 vim_host = os.getenv('OPENVIM_HOST',"localhost")
254 vim_admin_port = os.getenv('OPENVIM_ADMIN_PORT',"8085")
255 of_controller_type = os.getenv('OF_CONTROLLER_TYPE',"floodlight")
256 of_controller_user = os.getenv('OF_CONTROLLER_USER',None)
257 of_controller_password = os.getenv('OF_CONTROLLER_PASSWORD',None)
258 #of_controller_version = os.getenv('OF_CONTROLLER_VERSION',"0.90")
259 of_controller_ip = os.getenv('OF_CONTROLLER_IP',"localhost")
260 of_controller_port = os.getenv('OF_CONTROLLER_PORT',"7070")
261 of_controller_dpid = os.getenv('OF_CONTROLLER_DPID','00:01:02:03:e4:05:e6:07')
262 of_controller_module = os.getenv('OF_CONTROLLER_MODULE',None)
263
264 main_parser = argparse.ArgumentParser(description='User program to interact with Openflow controller')
265 main_parser.add_argument('--version', action='version', version='%(prog)s ' + version )
266
267 #main_parser = argparse.ArgumentParser()
268 subparsers = main_parser.add_subparsers(help='commands')
269
270 config_parser = subparsers.add_parser('config', help="prints configuration values")
271 config_parser.set_defaults(func=config)
272
273 add_parser = subparsers.add_parser('add', help="adds an openflow rule")
274 add_parser.add_argument('--verbose', '-v', action='count')
275 add_parser.add_argument("name", action="store", help="name of the rule")
276 add_parser.add_argument("--inport", required=True, action="store", type=str, help="match rule: ingress-port")
277 add_parser.add_argument("--actions", action="store", type=str, help="action with the format: vlan=<None/vlan-id>,out=<egress-port>,...")
278 add_parser.add_argument("--priority", action="store", type=int, help="rule priority")
279 add_parser.add_argument("--matchmac", action="store", help="match rule: mac address")
280 add_parser.add_argument("--matchvlan", action="store", type=int, help="match rule: vlan id")
281 add_parser.add_argument("--stripvlan", action="append_const", dest="act", const=None, help="alternative to --actions. Use before --out to strip vlan")
282 add_parser.add_argument("--setvlan", action="append", dest="act", type=int, help="alternative to --actions. Use before --out to set vlan")
283 add_parser.add_argument("--out", action="append", dest="act", type=str, help="alternative to --actions. out=<egress-port> can be used several times")
284 add_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
tiernod03ce282016-12-02 14:40:59 +0100285 add_parser.add_argument('--print-id', action='store_true', help="print the flow id after added")
tiernof7aa8c42016-09-06 16:43:04 +0200286 add_parser.set_defaults(func=of_add)
287
288 delete_parser = subparsers.add_parser('delete', help="delete an openflow rule")
289 delete_parser.add_argument('--verbose', '-v', action='count')
290 delete_parser.add_argument("-f", "--force", action="store_true", help="force deletion without asking")
291 delete_parser.add_argument("name", action="store", help="name of the rule to be deleted")
292 delete_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
293 delete_parser.set_defaults(func=of_delete)
294
295 switches_parser = subparsers.add_parser('switches', help="list all switches controlled by the OFC")
296 switches_parser.add_argument('--verbose', '-v', action='count')
297 switches_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
298 switches_parser.set_defaults(func=of_switches)
299
300 list_parser = subparsers.add_parser('list', help="list openflow rules")
301 list_parser.add_argument('--verbose', '-v', action='count')
302 list_parser.add_argument("--no-translate", "-n", action="store_true", help="Skip translation from openflow index to switch port name")
303 list_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
304 list_parser.set_defaults(func=of_list)
305
306 #dump_parser = subparsers.add_parser('dump', help="dump openflow rules")
307 #dump_parser.set_defaults(func=of_dump)
308
309 clear_parser = subparsers.add_parser('clear', help="clear all openflow rules")
310 clear_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
311 clear_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
312 clear_parser.set_defaults(func=of_clear)
313
314 install_parser = subparsers.add_parser('install', help="install openflow rules from file")
315 install_parser.add_argument("file", action="store", help="file with rules generated using 'openflow list > rules.txt'")
316 install_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
317 install_parser.set_defaults(func=of_install)
318
319 reinstall_parser = subparsers.add_parser('reinstall', help="reinstall openflow rules from VIM rules")
320 reinstall_parser.set_defaults(func=of_reinstall)
321 reinstall_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
322
323 portlist_parser = subparsers.add_parser('port-list', help="list the physical to openflow port correspondence")
324 portlist_parser.set_defaults(func=of_port_list)
325 portlist_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
326
327 argcomplete.autocomplete(main_parser)
328
329 args = main_parser.parse_args()
330 module_info=None
331 try:
332 if args.func is not config:
333 params={ "of_ip": of_controller_ip,
334 "of_port": of_controller_port,
335 "of_dpid": of_controller_dpid,
336 "of_user": of_controller_user,
337 "of_password": of_controller_password,
338 }
339 if "debug" in args and args.debug:
340 streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
341 logging.basicConfig(format=streamformat, level= logging.DEBUG)
342 logger = logging.getLogger('vim')
343 logger.setLevel(logging.DEBUG)
344 params["of_debug"]="DEBUG"
345 else:
346 #logger = logging.getLogger('vim').addHandler(logging.NullHandler())
347 #logger.setLevel(logging.CRITICAL)
348 params["of_debug"]="CRITICAL"
349
350 if of_controller_type=='opendaylight':
351 module = "ODL"
352 elif of_controller_module != None:
353 module = of_controller_module
354 else:
355 module = of_controller_type
356 module_info = imp.find_module(module)
357
358 of_conn = imp.load_module("of_conn", *module_info)
359 try:
360 ofconnector = of_conn.OF_conn(params)
361 except Exception as e:
362 print "Cannot open the Openflow controller '%s': %s" % (type(e).__name__, str(e))
363 result = -1
364 exit()
365 result = args.func(args)
366 if result == None:
367 result = 0
368
369 #for some reason it fails if call exit inside try instance. Need to call exit at the end !?
370 except (IOError, ImportError) as e:
371 print "Cannot open openflow controller module '%s'; %s: %s" % (module, type(e).__name__, str(e))
372 result = -1
373 #except Exception as e:
374 # print "Cannot open the Openflow controller '%s': %s" % (type(e).__name__, str(e))
375 # result = -1
376 except requests.exceptions.ConnectionError as e:
377 print "Cannot connect to server; %s: %s" % (type(e).__name__, str(e))
378 result = -2
379 except (KeyboardInterrupt):
380 print 'Exiting openVIM'
381 result = -3
382 except (SystemExit):
383 result = -4
384
385 #close open file
386 if module_info and module_info[0]:
387 file.close(module_info[0])
388 exit(result)
389
390
391