blob: 316ac0fc8ec35c3fe14345f4b16c3f0ac894898b [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
mirabal9f657102017-04-10 20:05:40 +020043import osm_openvim.openflow_conn as openflow_conn
44from osm_openvim.openflow_thread import change_db2of, FlowBadFormat
tiernof7aa8c42016-09-06 16:43:04 +020045
mirabal6c600652017-03-16 17:22:57 +010046
tiernof7aa8c42016-09-06 16:43:04 +020047def of_switches(args):
mirabal6c600652017-03-16 17:22:57 +010048 try:
49 c = ofconnector.get_of_switches()
50
tiernof7aa8c42016-09-06 16:43:04 +020051 for s in c:
52 print " %s %s" % (s[0], s[1])
mirabal6c600652017-03-16 17:22:57 +010053 return 0
54 except openflow_conn.OpenflowconnException as e:
55 print ("OF get switch error {}".format(str(e)))
56 return -1
57
tiernof7aa8c42016-09-06 16:43:04 +020058
59def of_list(args):
mirabal6c600652017-03-16 17:22:57 +010060 try:
61 c = ofconnector.get_of_rules(not args.no_translate)
62
63 if args.verbose > 0:
64 print yaml.safe_dump(c, indent=4, default_flow_style=False)
65 return 0
66
67 print " switch priority name ingress_port " \
68 "dst_mac vlan_id actions"
69 for name, rule in c.iteritems():
70 action_list = []
71 for action in rule["actions"]:
72 action_list.append(action[0] + "=" + str(action[1]))
73 if "vlan_id" in rule:
74 vlan = str(rule["vlan_id"])
75 else:
76 vlan = "any"
77 print "%s %s %s %s %s %s %s" % \
78 (rule["switch"], str(rule["priority"]).ljust(6), name.ljust(40), rule["ingress_port"].ljust(8),
79 rule.get("dst_mac", "any").ljust(18), vlan.ljust(4), ",".join(action_list))
tiernof7aa8c42016-09-06 16:43:04 +020080 return 0
81
mirabal6c600652017-03-16 17:22:57 +010082 except openflow_conn.OpenflowconnException as e:
83 print("OF get list error {}".format(str(e)))
84 return -1
85
tiernof7aa8c42016-09-06 16:43:04 +020086
87def of_clear(args):
mirabal6c600652017-03-16 17:22:57 +010088 try:
89 if not args.force:
90 r = raw_input("Clear all Openflow rules (y/N)? ")
91 if not (len(r) > 0 and r[0].lower() == "y"):
92 return 0
93 c = ofconnector.clear_all_flows()
94 return 0
95 except openflow_conn.OpenflowconnException as e:
96 print ("OF error {}".format(str(e)))
97 return -1
98
tiernof7aa8c42016-09-06 16:43:04 +020099
100def of_port_list(args):
mirabal6c600652017-03-16 17:22:57 +0100101 try:
102 c = ofconnector.obtain_port_correspondence()
103 yaml.safe_dump({"ports": c}, sys.stdout, indent=2, default_flow_style=False)
104 # def of_dump(args):
105 # args.verbose = 3
106 # args.no_translate=False
107 # of_list(args)
108 return len(c)
109 except openflow_conn.OpenflowconnException as e:
110 print("OF error {}".format(str(e)))
111 return -1
tiernof7aa8c42016-09-06 16:43:04 +0200112
tiernof7aa8c42016-09-06 16:43:04 +0200113
114def of_reinstall(args):
115 try:
116 URLrequest = "http://%s:%s/openvim/networks/all/openflow" %(vim_host, vim_admin_port)
117 print URLrequest
118 openvim_response = requests.put(URLrequest)
119 print openvim_response.text
120 return 0
121 except requests.exceptions.RequestException as e:
122 print " Exception GET at '"+URLrequest+"' " + str(e)
123 return -1
124
mirabal6c600652017-03-16 17:22:57 +0100125
tiernof7aa8c42016-09-06 16:43:04 +0200126def of_install(args):
127 line_number=1
128 try:
129 f = open(args.file, "r")
130 text = f.read()
131 f.close()
132 lines=text.split("\n")
133 heads=lines[0].split()
134
135 for line in lines[1:]:
136 line_number += 1
137 rule={}
138 items= line.split()
139 if len(items)==0 or items[0][0]=="#": #empty line or commented
140 continue
141 for i in range(0,len(items)):
142 rule[ heads[i] ] = items[i]
143 if rule["vlan_id"] == "any":
144 del rule["vlan_id"]
145 if rule["dst_mac"] == "any":
146 del rule["dst_mac"]
147 if 'priority' in rule and (rule['priority']==None or rule['priority']=="None" ):
148 del rule['priority']
149 try:
150 change_db2of(rule)
151 except FlowBadFormat as e:
152 print "Format error at line %d: %s" % (line_number, str(e))
153 continue
mirabal6c600652017-03-16 17:22:57 +0100154 try:
155 ofconnector.new_flow(rule)
156 error = "OK"
157 except openflow_conn.OpenflowconnException as e:
158 error = "ERROR: " + str(e)
159 print "%s %s %s input=%s dst_mac=%s vlan_id=%s %s" % (rule["switch"],
160 str(rule.get("priority")).ljust(6),
161 rule["name"].ljust(20),
162 rule["ingress_port"].ljust(3),
163 rule.get("dst_mac", "any").ljust(18),
164 rule.get("vlan_id", "any").ljust(4), error)
tiernof7aa8c42016-09-06 16:43:04 +0200165 return 0
166 except IOError as e:
167 print " Error opening file '" + args.file + "': " + e.args[1]
168 return -1
169 except yaml.YAMLError as exc:
170 error_pos = ""
171 if hasattr(exc, 'problem_mark'):
172 mark = exc.problem_mark
173 error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1)
174 print " Error yaml/json format error at " + error_pos
175 return -1
176
mirabal6c600652017-03-16 17:22:57 +0100177
tiernof7aa8c42016-09-06 16:43:04 +0200178def of_add(args):
179 if args.act==None and args.actions==None:
180 print "openflow add: error: one of the arguments --actions or [--setvlan,--stripvlan],--out is required"
181 return -1
182 elif args.act!=None and args.actions!=None:
183 print "openflow add: error: Use either --actions option or [--setvlan,--stripvlan],--out options; but not both"
184 return -1
185
186 rule={"name":args.name, "priority":args.priority,
187 "ingress_port": args.inport
188 }
189 if args.matchvlan:
190 rule["vlan_id"] = args.matchvlan
191 if args.matchmac:
192 rule["dst_mac"] = args.matchmac
193
194 if args.actions:
195 rule["actions"] = args.actions
196 try:
197 change_db2of(rule)
198 except FlowBadFormat as e:
199 print "Format error at --actions: '%s' Expected 'vlan=<None/vlan_id>,out=<egress_port>,...'" % str(e)
200 return -1
201 elif args.act:
202 rule["actions"]=[]
203 error_msj = "openflow add: error: --setvlan,--stripvlan options must be followed by an --out option"
204 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
205 for action in args.act:
206 if action==None or type(action)==int:
207 if previous_option_vlan: #consecutive vlan options
208 print error_msj
209 return -1
210 previous_option_vlan=True
211 rule["actions"].append( ("vlan", action) )
212 else:
213 previous_option_vlan=False
214 rule["actions"].append( ("out", action) )
215 if previous_option_vlan:
216 print error_msj
217 return -1
218 #print rule
219 #return
220
mirabal6c600652017-03-16 17:22:57 +0100221 try:
222 c = ofconnector.new_flow(rule)
223 if args.print_id:
224 print rule["name"]
225 return 0
226
227 except openflow_conn.OpenflowconnException as e:
228 print("OF error {}".format(str(e)))
tiernof7aa8c42016-09-06 16:43:04 +0200229 return -1
mirabal6c600652017-03-16 17:22:57 +0100230
tiernof7aa8c42016-09-06 16:43:04 +0200231
232def of_delete(args):
233 if not args.force:
234 r = raw_input("Clear rule %s (y/N)? " %(args.name))
mirabal6c600652017-03-16 17:22:57 +0100235 if not (len(r) >0 and r[0].lower() == "y"):
tiernof7aa8c42016-09-06 16:43:04 +0200236 return 0
mirabal6c600652017-03-16 17:22:57 +0100237 try:
238 ofconnector.del_flow(args.name)
239 return 0
240 except openflow_conn.OpenflowconnException as e:
241 print("OF error {}".format(str(e)))
tiernof7aa8c42016-09-06 16:43:04 +0200242 return -1
mirabal6c600652017-03-16 17:22:57 +0100243
tiernof7aa8c42016-09-06 16:43:04 +0200244
245def config(args):
246 print "OPENVIM_HOST: %s" %(vim_host)
247 print "OPENVIM_ADMIN_PORT: %s" %(vim_admin_port)
248 print "OF_CONTROLLER_TYPE: %s" %(of_controller_type)
249 if of_controller_module or (of_controller_type!="floodlight" and of_controller_type!="opendaylight"):
250 print "OF_CONTROLLER_MODULE: %s" %(of_controller_module)
251 print "OF_CONTROLLER_USER: %s" %(of_controller_user)
252 print "OF_CONTROLLER_PASSWORD: %s" %(of_controller_password)
253 #print "OF_CONTROLLER_VERSION: %s" %(of_controller_version)
254 print "OF_CONTROLLER_IP: %s" %(of_controller_ip)
255 print "OF_CONTROLLER_PORT: %s" %(of_controller_port)
256 print "OF_CONTROLLER_DPID: %s" %(of_controller_dpid)
257 return
258
259version="0.8"
260global vim_host
261global vim_admin_port
262global of_controller_type
263global of_controller_user
264global of_controller_password
265global of_controller_ip
266global of_controller_port
267global of_controller_dpid
268global of_controller_module
269global ofconnector
270
271if __name__=="__main__":
272 #print "test_ofconnector version", version, "Jul 2015"
273 #print "(c) Copyright Telefonica"
274
275 vim_host = os.getenv('OPENVIM_HOST',"localhost")
276 vim_admin_port = os.getenv('OPENVIM_ADMIN_PORT',"8085")
277 of_controller_type = os.getenv('OF_CONTROLLER_TYPE',"floodlight")
278 of_controller_user = os.getenv('OF_CONTROLLER_USER',None)
279 of_controller_password = os.getenv('OF_CONTROLLER_PASSWORD',None)
280 #of_controller_version = os.getenv('OF_CONTROLLER_VERSION',"0.90")
281 of_controller_ip = os.getenv('OF_CONTROLLER_IP',"localhost")
282 of_controller_port = os.getenv('OF_CONTROLLER_PORT',"7070")
283 of_controller_dpid = os.getenv('OF_CONTROLLER_DPID','00:01:02:03:e4:05:e6:07')
284 of_controller_module = os.getenv('OF_CONTROLLER_MODULE',None)
285
286 main_parser = argparse.ArgumentParser(description='User program to interact with Openflow controller')
287 main_parser.add_argument('--version', action='version', version='%(prog)s ' + version )
288
289 #main_parser = argparse.ArgumentParser()
290 subparsers = main_parser.add_subparsers(help='commands')
291
292 config_parser = subparsers.add_parser('config', help="prints configuration values")
293 config_parser.set_defaults(func=config)
294
295 add_parser = subparsers.add_parser('add', help="adds an openflow rule")
296 add_parser.add_argument('--verbose', '-v', action='count')
297 add_parser.add_argument("name", action="store", help="name of the rule")
298 add_parser.add_argument("--inport", required=True, action="store", type=str, help="match rule: ingress-port")
299 add_parser.add_argument("--actions", action="store", type=str, help="action with the format: vlan=<None/vlan-id>,out=<egress-port>,...")
300 add_parser.add_argument("--priority", action="store", type=int, help="rule priority")
301 add_parser.add_argument("--matchmac", action="store", help="match rule: mac address")
302 add_parser.add_argument("--matchvlan", action="store", type=int, help="match rule: vlan id")
303 add_parser.add_argument("--stripvlan", action="append_const", dest="act", const=None, help="alternative to --actions. Use before --out to strip vlan")
304 add_parser.add_argument("--setvlan", action="append", dest="act", type=int, help="alternative to --actions. Use before --out to set vlan")
305 add_parser.add_argument("--out", action="append", dest="act", type=str, help="alternative to --actions. out=<egress-port> can be used several times")
306 add_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
tiernod03ce282016-12-02 14:40:59 +0100307 add_parser.add_argument('--print-id', action='store_true', help="print the flow id after added")
tiernof7aa8c42016-09-06 16:43:04 +0200308 add_parser.set_defaults(func=of_add)
309
310 delete_parser = subparsers.add_parser('delete', help="delete an openflow rule")
311 delete_parser.add_argument('--verbose', '-v', action='count')
312 delete_parser.add_argument("-f", "--force", action="store_true", help="force deletion without asking")
313 delete_parser.add_argument("name", action="store", help="name of the rule to be deleted")
314 delete_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
315 delete_parser.set_defaults(func=of_delete)
316
317 switches_parser = subparsers.add_parser('switches', help="list all switches controlled by the OFC")
318 switches_parser.add_argument('--verbose', '-v', action='count')
319 switches_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
320 switches_parser.set_defaults(func=of_switches)
321
322 list_parser = subparsers.add_parser('list', help="list openflow rules")
323 list_parser.add_argument('--verbose', '-v', action='count')
324 list_parser.add_argument("--no-translate", "-n", action="store_true", help="Skip translation from openflow index to switch port name")
325 list_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
326 list_parser.set_defaults(func=of_list)
327
328 #dump_parser = subparsers.add_parser('dump', help="dump openflow rules")
329 #dump_parser.set_defaults(func=of_dump)
330
331 clear_parser = subparsers.add_parser('clear', help="clear all openflow rules")
332 clear_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
333 clear_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
334 clear_parser.set_defaults(func=of_clear)
335
336 install_parser = subparsers.add_parser('install', help="install openflow rules from file")
337 install_parser.add_argument("file", action="store", help="file with rules generated using 'openflow list > rules.txt'")
338 install_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
339 install_parser.set_defaults(func=of_install)
340
341 reinstall_parser = subparsers.add_parser('reinstall', help="reinstall openflow rules from VIM rules")
342 reinstall_parser.set_defaults(func=of_reinstall)
343 reinstall_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
344
345 portlist_parser = subparsers.add_parser('port-list', help="list the physical to openflow port correspondence")
346 portlist_parser.set_defaults(func=of_port_list)
347 portlist_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
348
349 argcomplete.autocomplete(main_parser)
350
351 args = main_parser.parse_args()
352 module_info=None
353 try:
354 if args.func is not config:
355 params={ "of_ip": of_controller_ip,
356 "of_port": of_controller_port,
357 "of_dpid": of_controller_dpid,
358 "of_user": of_controller_user,
359 "of_password": of_controller_password,
360 }
361 if "debug" in args and args.debug:
362 streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
363 logging.basicConfig(format=streamformat, level= logging.DEBUG)
364 logger = logging.getLogger('vim')
365 logger.setLevel(logging.DEBUG)
366 params["of_debug"]="DEBUG"
367 else:
368 #logger = logging.getLogger('vim').addHandler(logging.NullHandler())
369 #logger.setLevel(logging.CRITICAL)
370 params["of_debug"]="CRITICAL"
371
372 if of_controller_type=='opendaylight':
373 module = "ODL"
374 elif of_controller_module != None:
375 module = of_controller_module
376 else:
377 module = of_controller_type
mirabal72fcda72017-05-09 11:01:06 +0200378
tiernof7aa8c42016-09-06 16:43:04 +0200379 try:
mirabal72fcda72017-05-09 11:01:06 +0200380 pkg = __import__("osm_openvim." + module)
381 of_conn = getattr(pkg, module)
tiernof7aa8c42016-09-06 16:43:04 +0200382 ofconnector = of_conn.OF_conn(params)
383 except Exception as e:
384 print "Cannot open the Openflow controller '%s': %s" % (type(e).__name__, str(e))
385 result = -1
386 exit()
387 result = args.func(args)
388 if result == None:
389 result = 0
390
391 #for some reason it fails if call exit inside try instance. Need to call exit at the end !?
392 except (IOError, ImportError) as e:
393 print "Cannot open openflow controller module '%s'; %s: %s" % (module, type(e).__name__, str(e))
394 result = -1
395 #except Exception as e:
396 # print "Cannot open the Openflow controller '%s': %s" % (type(e).__name__, str(e))
397 # result = -1
398 except requests.exceptions.ConnectionError as e:
399 print "Cannot connect to server; %s: %s" % (type(e).__name__, str(e))
400 result = -2
401 except (KeyboardInterrupt):
402 print 'Exiting openVIM'
403 result = -3
404 except (SystemExit):
405 result = -4
406
407 #close open file
408 if module_info and module_info[0]:
409 file.close(module_info[0])
410 exit(result)
411
412
413