blob: 485dbf4a0430e8ffc3bbbdeddd103704e2532bbf [file] [log] [blame]
Srikanth Vavilapalli1725e492014-12-01 17:50:52 -08001#
2# Copyright (c) 2012,2013 Big Switch Networks, Inc.
3#
4# Licensed under the Eclipse Public License, Version 1.0 (the
5# "License"); you may not use this file except in compliance with the
6# License. You may obtain a copy of the License at
7#
8# http://www.eclipse.org/legal/epl-v10.html
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied. See the License for the specific language governing
14# permissions and limitations under the License.
15#
16
17# sdnsh - The Controller Shell
18
19import urllib
20import urllib2
21
22import base64
23import struct
24import time
25import re
26from datetime import datetime, timedelta, tzinfo
27from calendar import timegm
28from timesince import timesince, timesince_sec
29import doctest
30import traceback
31import utif
32import command
33import json
34
35
36# Timezone constants
37class Utc(tzinfo):
38 def utcoffset(self, dt):
39 return timedelta(0)
40 def tzname(self, dt):
41 return "UTC"
42 def dst(self, dt):
43 return timedelta(0)
44UTC = Utc()
45
46
47class Pst(tzinfo):
48 def utcoffset(self, dt):
49 return timedelta(0,0,0,0,0,-8)
50 def tzname(self, dt):
51 return "PST"
52 def dst(self, dt):
53 return timedelta(0,0,0,0,0,-7)
54PST = Pst()
55
56
57alias_dicts = {}
58
59
60def get_switches_names(object, data =None):
61 """
62 return the switches name (for ONOS)
63 """
64 switches_dpid_name ={}
65 url = "http://127.0.0.1:8000/rest/v1/switches"
66 result = urllib2.urlopen(url).read()
67 #result = command.sdnsh.store.rest_simple_request(query_url)
68 entries = result
69 entries = json.loads(result)
70 #eprint entries
71 for switch in entries:
72 #print switch
73 switches_dpid_name[switch.get("dpid")] = switch.get("stringAttributes").get('name')
74 #print switch.get("dpid")
75 #print switch.get("stringAttributes").get('name')
76 return switches_dpid_name
77
78
79def update_alias_dict(obj_type, dict):
80 """
81 Update alias dictionaries: for switch, host, etc.
82 """
83 global alias_dicts
84 alias_dicts[obj_type] = dict
85
86
87def convert_mac_in_base64_byte_array_to_hex_string(i, data=None):
88 # hmmm - tasty python
89 if i == "*" or i == "":
90 return "*"
91 return ":".join(["%0.2x" % x for x in struct.unpack('BBBBBB', base64.b64decode(i))])
92
93
94def convert_mac_in_decimal_to_hex_string(i, data=None):
95 if i == "*" or i == "":
96 return "*"
97 mac = hex(i)
98 # some python implementations append 'L' to hex() so we need to remove it
99 if mac[len(mac)-1] == 'L':
100 mac = mac[:len(mac)-1]
101 mac = ('0' * (14 - len(mac)) + mac[2:])
102 return ':'.join([mac[x:x+2] for x in xrange(0, len(mac), 2)])
103
104
105def convert_ip_in_integer_to_dotted_decimal(i, data=None):
106 if i == "*" or i == "":
107 return "*"
108 return '.'.join(reversed([ (str((i >> (8*x)) & 0xff)) for x in range(0,4)]))
109
110
111def convert_integer_to_bitmask(i, data=None):
112 if i == "*" or i == "":
113 return "*"
114 return bin(i)
115
116
117def convert_long_to_dpid(i, data=None):
118 if i == "*" or i == "":
119 return "*"
120 i = int(i)
121 return ':'.join(reversed([ "%-.2x" % ((i >> (8*x)) & 0xff) for x in range(0,8)]))
122
123
124def convert_signed_short_to_unsigned(i, data=None):
125 try:
126 i = int(i)
127 except:
128 return i
129
130 if i < 0:
131 return i + 2**16
132 else:
133 return i
134
135
136def convert_signed_short_for_vlan(i, data=None):
137 if i == -1:
138 return "-"
139 else:
140 return convert_signed_short_to_unsigned(i, data)
141
142
143def convert_to_string(i, data=None):
144 if type(i) == list:
145 return ', '.join(i)
146 elif type(i) == dict:
147 return ', '.join(["%s:%s" % x for x in i.dict()])
148 else:
149 return str(i)
150
151
152def print_hex(i, data=None):
153 if i == "" or not int(i):
154 return ""
155 else:
156 return hex(i)
157
158
159def timestamp_to_local_timestr(i, data=None):
160 if i == '':
161 return ''
162 return time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime(i/1000))
163
164
165def utc_timestr_to_timestamp(s):
166 return timegm(time.strptime(s, "%Y-%m-%d %H:%M:%S.%f")) * 1000
167
168
169def print_from_utc_timestr(s, data=None):
170 """Converts from a UTC time string like 2010-12-12 15:27:41.650000
171 to time string in local timezone like 2011-06-23 02:51:41 PDT
172
173 # Doctest only valid in Pacific Time ;-|
174 >>> print_from_utc_timestr('2010-12-12 15:27:41.650000')
175 '2010-12-12 07:27:41 PST'
176 >>> print_from_utc_timestr('2010-06-12 15:27:41.650000')
177 '2010-06-12 08:27:41 PDT'
178 >>> print_from_utc_timestr('')
179 ''
180 >>> print_from_utc_timestr(None)
181 ''
182 >>> print_from_utc_timestr('Not a valid timestamp')
183 ''
184 """
185 ret = ''
186 try:
187 ret = timestamp_to_local_timestr(utc_timestr_to_timestamp(s))
188 except:
189 pass
190 return ret
191
192
193def print_time_since_utc(i, data=None):
194 if i == None or i == '':
195 return ''
196 return timesince(datetime.fromtimestamp((i/1000)))
197
198
199def print_time_since_utc_timestr(s, data=None, now=None):
200 """Converts from a UTC time string like 2010-12-12 15:27:41.65000
201 to human readabel string like 'Last seen 4 minutes ago'
202
203 >>> now = datetime(*(time.strptime('2010-12-12 15:27:41.650000', "%Y-%m-%d %H:%M:%S.%f")[0:6]+(0, PST)))
204 >>> print_time_since_utc_timestr('2010-12-12 15:27:41.650000', now=now)
205 '8 hours'
206 >>> print_time_since_utc_timestr('')
207 ''
208 >>> print_time_since_utc_timestr(None)
209 ''
210 >>> print_time_since_utc_timestr('Not a valid timestamp')
211 ''
212 """
213 ret = ''
214 if s == '':
215 return ''
216 try:
217 date_obj = datetime(*(time.strptime(s, "%Y-%m-%d %H:%M:%S.%f")[0:6] + (0, UTC)))
218 ret = timesince(date_obj, now)
219 except Exception, e:
220 try:
221 date_obj = datetime(*(time.strptime(s, "%Y-%m-%d %H:%M:%S")[0:6] + (0, UTC)))
222 ret = timesince(date_obj, now)
223 except Exception, e:
224 try:
225 date_obj = datetime(*(time.strptime(s, "%Y-%m-%dT%H:%M:%S.%fZ")[0:6] + (0, UTC)))
226 ret = timesince(date_obj, now)
227 except Exception, e:
228 ret = "<fail>"
229 pass
230 return ret
231
232
233def print_timesince_msec_since(i, data=None):
234 if i == '':
235 return i
236 return timesince_sec(int(i)/1000)
237
238def print_enum_string_and_int(values, i, not_found_val = None):
239 if values and i in values:
240 return "%s(%s)" % (values[i], i)
241 else:
242 if not_found_val:
243 return not_found_val
244 else:
245 return i
246
247
248def print_enum_string_and_hex_int(values, i, not_found_val = None):
249 if values and i in values:
250 return "%s(%#x)" % (values[i], int(i))
251 else:
252 if not_found_val:
253 return not_found_val
254 else:
255 return i
256
257
258def print_mask_enum_string_and_int(values, bits):
259 if bits == "":
260 return bits
261 bits_copy = bits
262 enums = []
263 bit_test = 1
264 while bits_copy:
265 if bit_test & bits_copy:
266 if bit_test in values:
267 enums.append(values[bit_test])
268 bits_copy &= bits_copy-1
269 bit_test <<= 1
270 if enums:
271 return ",".join(enums) + "(%s)" % (hex(bits) if bits > 9 else bits)
272 else:
273 if bits > 9:
274 return hex(int(bits))
275 return "0"
276
277
278def print_physical_port(i, data=None, switch_key=None):
279 global alias_dicts
280
281 if not data:
282 return str(i)
283 name_dict = alias_dicts.get("portNames")
284 if not name_dict:
285 return str(i)
286 if switch_key:
287 key_string = data[switch_key] + '.' + "%d" % i
288 elif 'switch' in data:
289 key_string = data['switch'] + '.' + "%d" % i
290 elif 'Switch' in data:
291 key_string = data['Switch'] + '.' + "%d" % i
292 # return the physical name if it exists, otherwise i
293 return str(name_dict.get(key_string, i))
294
295
296def print_byte_unit(i, data=None, suffix=None):
297 """
298 Convert the value of 'i' into a byte rate value,
299 for example '10 GB'
300 """
301 try:
302 value = int(i)
303 except:
304 if i == '':
305 return 'Unknown'
306 return i
307
308 converter = (
309 (1024 * 1028 * 1024 * 1024 * 1024 , ' PB'),
310 (1024 * 1028 * 1024 * 1024 , ' TB'),
311 (1024 * 1028 * 1024 , ' GB'),
312 (1028 * 1024 , ' MB'),
313 (1024 , ' KB'),
314 )
315 if suffix == None:
316 suffix = ''
317
318 for idx, (boundary, name) in enumerate(converter):
319 value = i / boundary
320 if value:
321 return '%s%s%s' % (value, name, suffix)
322 else:
323 return '%s B%s' % (i, suffix)
324
325def print_byte_rate(i, data=None):
326 return print_byte_unit(i, data, 'ps')
327
328def print_bit_unit(i, data=None, suffix=None):
329 """
330 Convert the value of 'i' into a bit rate value,
331 for example '10 Gb'
332 """
333 try:
334 value = int(i)
335 except:
336 if i == '':
337 return 'Unknown'
338 return i
339
340 converter = (
341 (1000 * 1000 * 1000 * 1000 * 1000 , ' Pb'),
342 (1000 * 1000 * 1000 * 1000 , ' Tb'),
343 (1000 * 1000 * 1000 , ' Gb'),
344 (1000 * 1000 , ' Mb'),
345 (1000 , ' Kb'),
346 )
347 if suffix == None:
348 suffix = ''
349
350 for idx, (boundary, name) in enumerate(converter):
351 value = i / boundary
352 if value:
353 return '%s%s%s' % (value, name, suffix)
354 else:
355 return '%s b%s' % (i, suffix)
356
357def print_bit_rate(i, data=None):
358 return print_bit_unit(i, data, 'ps')
359
360
361def decode_openflow_port(i, data=None, switch_key=None):
362 if i == "*" or i == "":
363 return "*"
364 # If the first character of the port name is a plus sign ('+')
365 # this is intended to bypass any lookup -- its a way to indicate
366 # the field is already an openflow interface.
367 if isinstance(i, unicode) and i[0] == '+':
368 return i[1:]
369 i = convert_signed_short_to_unsigned(i)
370 i &= 0xffff
371 values = {
372 0xfff8 : "input",
373 0xfff9 : "table",
374 0xfffa : "normal",
375 0xfffb : "flood",
376 0xfffc : "all",
377 0xfffd : "controller",
378 0xfffe : "local",
379 0xffff : "none",
380 }
381 if i >= 0xff00:
382 if i in values:
383 return "%s (%s)" % (i, values[i])
384 else:
385 return "%s" % values[i]
386 phys_port = print_physical_port(i, data, switch_key)
387 if str(i) != phys_port:
388 return str(i) + ' (' + phys_port + ')'
389 return str(i)
390
391
392def decode_openflow_port_src_switch(i, data=None):
393 return decode_openflow_port(i, data, 'src-switch')
394
395
396def decode_openflow_port_source_switch(i, data=None):
397 return decode_openflow_port(i, data, 'Source-Switch')
398
399
400def decode_openflow_port_dst_switch(i, data=None):
401 return decode_openflow_port(i, data, 'dst-switch')
402
403
404def decode_openflow_port_dest_switch(i, data=None):
405 return decode_openflow_port(i, data, 'Dest-Switch')
406
407
408def decode_openflow_port_inputSwitch(i, data=None):
409 return decode_openflow_port(i, data, 'inputSwitch')
410
411
412def decode_openflow_port_dpid(i, data=None):
413 return decode_openflow_port(i, data, 'dpid')
414
415
416# well known cookie id's managed directly, other's can be registered
417cookie_app_ids = {
418 1: "lswitch",
419 2: "FL:forw",
420 3: "TUN:forw",
421 4: "VTA:forw",
422 10: "static",
423}
424
425flow_cookie_registry = { }
426
427def register_flow_cookie_decoder(name, app_id, encoder_proc, decoder_proc, conversion_dict_name):
428 """
429 Register a flow encoding/decoding strategy
430 """
431 # should be provide a return code?
432
433 cookie_app_ids[app_id] = name
434 flow_cookie_registry[int(app_id)] = {
435 'name' : name,
436 'encoder' : encoder_proc,
437 'decoder' : decoder_proc,
438 'cvt' : conversion_dict_name
439 }
440
441def callout_flow_encoders(context):
442 for (registrant, registry) in flow_cookie_registry.items():
443 dict_name = registry['cvt']
444 alias_dicts[dict_name] = registry['encoder'](context)
445
446
447def decode_flow_cookie(i, data=None):
448 cookie = i
449 new_cookie = None
450
451 # 12 bits app id - 20 bits flow hash - 32 bits user cookie
452 app_id = (cookie >> 52) & ((1 << 12) - 1)
453
454 flow_hash = (cookie >> 32) & ((1 << 20) - 1)
455 if app_id in cookie_app_ids:
456 new_cookie = cookie_app_ids[app_id]
457 if cookie_app_ids[app_id] == "static":
458 global alias_dicts
459 flow_map = alias_dicts.get("staticflow", [])
460 if flow_hash in flow_map:
461 new_cookie += "-%s" % flow_map[flow_hash]
462 else:
463 new_cookie += "-flow_hash: %s" % flow_hash
464 else:
465 if flow_hash != 0:
466 new_cookie += "-flow_hash: %s" % flow_hash
467 user_cookie = (cookie & ((1 << 32) - 1))
468
469 if user_cookie:
470 if app_id in flow_cookie_registry:
471 # Call registered function with the conversion dictionary
472 # and the cookie
473 conversion_dict_name = flow_cookie_registry[app_id]['cvt']
474 new_cookie = (flow_cookie_registry[app_id]['decoder'])(
475 alias_dicts.get(conversion_dict_name), cookie)
476 else:
477 new_cookie += ", cookie: %#x" % user_cookie
478 else:
479 # if the app_id isn't known, display all 64 bits of the cookie
480 new_cookie = "unknown %s, cookie %s" % (app_id, cookie)
481
482 if new_cookie:
483 return new_cookie
484
485 return '%#x' % cookie
486
487
488ether_type_to_name_dict = {
489 0x0800 : "ip",
490 0x0806 : "arp",
491 0x8035 : "rarp",
492 0x809B : "appletalk",
493 0x809B : "appletalk-aarp",
494 0x8100 : "802.1Q",
495 0x8137 : "ipx",
496 0x8138 : "novell",
497 0x86dd : "ipv6",
498 0x8847 : "mpls",
499 0x8848 : "mpls-mc",
500 0x88cc : "lldp",
501}
502
503
504ether_type_to_number_dict = dict([[v, n] for (n,v) in ether_type_to_name_dict.items()])
505
506
507def decode_ether_type(i, data=None):
508 if i == "*" or i == "":
509 return "*"
510 if type(i) != int and i.startswith("0x"):
511 i = int(i, 16) # i is in hex
512 i = convert_signed_short_to_unsigned(i)
513
514 try:
515 i &= 0xffff
516 except:
517 return '*cvt %s %s*' % (i, type(i))
518
519 return print_enum_string_and_hex_int(ether_type_to_name_dict, i)
520
521
522def decode_network_protocol(i, data=None):
523 if i == "*" or i == "":
524 return "*"
525 ether_type = "ip" # default
526 if data:
527 if 'ether-type' in data:
528 ether_type = decode_ether_type(data['ether-type'])
529 elif 'dataLayerType' in data:
530 ether_type = decode_ether_type(data['dataLayerType'])
531 ether_type = str(ether_type).split('(')[0]
532 net_proto_hash = {
533 "ip" : {
534 1 : "icmp",
535 2 : "igmp",
536 3 : "ggp",
537 4 : "ip-in-ip",
538 6 : "tcp",
539 17 : "udp",
540 50 : "esp",
541 51 : "ah"
542 },
543 "arp": {
544 1 : "arp-req",
545 2 : "arp-rep",
546 3 : "rarp-req",
547 4 : "rarp-rep",
548 5 : "drarp-req",
549 6 : "drarp-rep",
550 7 : "drarp-err",
551 8 : "inarp-req",
552 9 : "inarp-rep",
553 }
554 }
555 return print_enum_string_and_int(net_proto_hash.get(ether_type, None), i)
556
557tcp_decode_port_dict = {
558 22 : "ssh",
559 53 : "dns",
560 80 : 'http',
561 443 : 'https'
562}
563
564tcp_name_to_number_dict = dict([[v, n] for (n,v) in tcp_decode_port_dict.items()])
565
566# fill in values if we want!
567def decode_tcp_port(i):
568 return print_enum_string_and_int(tcp_decode_port_dict, i)
569
570
571udp_decode_port_dict = {
572 53 : "dns",
573}
574
575udp_name_to_number_dict = dict([[v, n] for (n,v) in udp_decode_port_dict.items()])
576
577def decode_udp_port(i):
578 values = {
579 }
580 return print_enum_string_and_int(values, i)
581
582
583icmp_type_code_hash = {
584 0 : { "name" : "echo-rep" },
585 3 : { "name" : "dest-unreach",
586 "codes" : { 0 : "net-unreach",
587 1 : "host-unreach",
588 2 : "proto-unreach",
589 3 : "port-unreach",
590 4 : "frag-needed",
591 5 : "src-rt-fail",
592 6 : "dest-net-unk",
593 7 : "dest-host-unk",
594 8 : "src-host-isol",
595 9 : "prohibit-dest-net",
596 10 : "prohibit-dest-host",
597 11 : "dest-net-unreach-for-tos",
598 12 : "dest-host-unreach-for-tos",
599 13 : "prohibit-comm" } },
600 4 : { "name" : "src-quench" },
601 5 : { "name" : "redirect",
602 "codes" : { 0 : "redir-for-net",
603 1 : "redir-for-host",
604 2 : "redir-for-net-for-tos",
605 3 : "redir-for-host-for-tos" } },
606 6 : { "name" : "alt-addr-for-host" },
607 8 : { "name" : "echo-req" },
608 9 : { "name" : "router-advert" },
609 10: { "name" : "router-sel" },
610 11: { "name" : "time-exceeded",
611 "codes" : { 0 : "ttl-exceeded",
612 1 : "frag-reassemble-exceeded" } },
613 12: { "name" : "param-problem",
614 "codes" : { 0 : "pointer-error",
615 1 : "missing-opt",
616 2 : "bad-length" } },
617 13: { "name" : "timestamp-request" },
618 14: { "name" : "timestamp-reply" },
619 15: { "name" : "info-request" },
620 16: { "name" : "info-reply" },
621 17: { "name" : "addr-mask-request" },
622 18: { "name" : "addr-mask-reply" },
623 30: { "name" : "traceroute" }
624}
625
626
627def decode_icmp_type(i, data=None):
628 if i in icmp_type_code_hash:
629 return "%s(%s)" % (icmp_type_code_hash[i]['name'], i)
630 else:
631 return i
632
633
634def decode_icmp_code(i, data=None):
635 if data and 'transportSource' in data:
636 icmp_type = decode_icmp_type(data['transportSource'], data)
637 elif data and 'src-port' in data:
638 icmp_type = decode_icmp_type(data['src-port'], data)
639 try:
640 return print_enum_string_and_int(icmp_type_code_hash[icmp_type]["codes"], i, "-")
641 except:
642 return "-"
643
644
645def decode_src_port(i, data=None):
646 i = convert_signed_short_to_unsigned(i)
647 if data:
648 if 'networkProtocol' in data:
649 net_proto = decode_network_protocol(data['networkProtocol'], data)
650 elif 'protocol' in data:
651 net_proto = decode_network_protocol(data['protocol'], data)
652 elif 'Protocol' in data:
653 net_proto = decode_network_protocol(data['Protocol'], data)
654 else:
655 return i
656 net_proto = str(net_proto).split('(')[0]
657 if net_proto == "icmp":
658 return decode_icmp_type(i)
659 elif net_proto == "tcp":
660 return decode_tcp_port(i)
661 elif net_proto == "udp":
662 return decode_udp_port(i)
663 return i
664
665
666def decode_dst_port(i, data=None):
667 i = convert_signed_short_to_unsigned(i)
668 if data:
669 if 'networkProtocol' in data:
670 net_proto = decode_network_protocol(data['networkProtocol'], data)
671 elif 'protocol' in data:
672 net_proto = decode_network_protocol(data['protocol'], data)
673 elif 'Protocol' in data:
674 net_proto = decode_network_protocol(data['Protocol'], data)
675 else:
676 return i
677 net_proto = str(net_proto).split('(')[0]
678 if net_proto == "icmp":
679 return decode_icmp_code(i, data)
680 elif net_proto == "tcp":
681 return decode_tcp_port(i)
682 elif net_proto == "udp":
683 return decode_udp_port(i)
684 return i
685
686
687def decode_actions(action_list, data=None):
688 TTL_DECREMENT_SUBTYPE = 18
689 SET_TUNNEL_DST_SUBTYPE = 2
690 NICIRA_VENDOR_ID=8992 #0x00002320
691 BSN_VENDOR_ID=6035143 #0x5c16c7
692 decoded_actions = []
693 for a in action_list:
694 decoded_action = a['type']
695 if decoded_action == "OUTPUT":
696 port = decode_openflow_port(a['port'], data)
697 decoded_action = "output=%s" % (port,)
698 elif decoded_action == "OPAQUE_ENQUEUE":
699 # LOOK! Not decoded to physical port since the configuration part of CLI does not do the translation either
700 decoded_action = "enqueue=%d:0x%02x" % (a['port'], a['queueId'])
701 elif decoded_action == "SET_VLAN_ID":
702 decoded_action = "set-vlan-id=%d" % a['virtualLanIdentifier']
703 elif decoded_action == "SET_VLAN_PCP":
704 decoded_action = "set-vlan-priority=0x%02x" % a['virtualLanPriorityCodePoint']
705 elif decoded_action == "STRIP_VLAN":
706 decoded_action = "strip-vlan"
707 elif decoded_action == "SET_DL_SRC":
708 decoded_action = "set-src-mac=%s" % a['dataLayerAddress']
709 elif decoded_action == "SET_DL_DST":
710 decoded_action = "set-dst-mac=%s" % a['dataLayerAddress']
711 elif decoded_action == "SET_NW_SRC":
712 decoded_action = "set-src-ip=%s" % decode_ipaddr(a['networkAddress'])
713 elif decoded_action == "SET_NW_DST":
714 decoded_action = "set-dst-ip=%s" % decode_ipaddr(a['networkAddress'])
715 elif decoded_action == "SET_NW_TOS":
716 decoded_action = "set-tos-bits=0x%02x" % a['networkTypeOfService']
717 elif decoded_action == "SET_TP_SRC":
718 decoded_action = "set-src-port=%d" % a['transportPort']
719 elif decoded_action == "SET_TP_DST":
720 decoded_action = "set-dst-port=%d" % a['transportPort']
721 elif decoded_action =="VENDOR":
722 if 'vendor' in a:
723 if a['vendor']==NICIRA_VENDOR_ID:
724 if 'vendorData' in a:
725 vendordata=a['vendorData'].decode('base64','strict')
726 vendordata=vendordata.encode('hex')
727 if vendordata.startswith('0012'):
728 decoded_action="nicira_vendor:dec TTL"
729 else:
730 decoded_action="nicira_vendor:unknown"
731 if a['vendor']==BSN_VENDOR_ID:
732 if 'vendorData' in a:
733 vendordata=a['vendorData'].decode('base64','strict')
734 vendordata=vendordata.encode('hex')
735 if vendordata.startswith('00000001'): #BSN_ACTION_MIRROR
736 decoded_action="bsn_vendor:MIRROR"
737 elif vendordata.startswith('00000002'): #SET_TUNNEL_DST_SUBTYPE
738 ip=vendordata[8:]
739 ipaddr=[]
740 for i in [6,4,2,0]:
741 ipint= ip[i:i+2]
742 ipint= str(int(ipint,16))
743 ipaddr.append(ipint)
744 ipstr='.'.join(ipaddr)
745 decoded_action="bsn_vendor:TUNNL:" + ipstr
746 else:
747 decoded_action="bsn_vendor:unknown"
748 decoded_actions.append(decoded_action)
749 if len(decoded_actions) == 0:
750 return "drop"
751 else:
752 return ",".join(decoded_actions)
753
754
755def decode_port_counter(i, data=None):
756 if i == -1:
757 return "n/a"
758 else:
759 return i
760
761
762def decode_macaddr(addr):
763 ret = 'invalid-mac-addr'
764 try:
765 macbytes = base64.b64decode(addr)
766 ret = ':'.join(["%02x" % ord(b) for b in macbytes])
767 except:
768 # ignore exception, the address is already set to invalid
769 pass
770 return ret
771
772
773def decode_ipaddr(addr):
774 ret = 'invalid-ip-addr'
775 try:
776 ipbytes = [0, 0, 0, 0]
777 for i in range(4):
778 ipbytes[3-i] = addr & 0x000000ff
779 addr = addr>>8
780 ret = '.'.join(["%d" % b for b in ipbytes])
781 except:
782 # ignore exception, the address is already set to invalid
783 pass
784 return ret
785
786
787def decode_port_config(i, data=None):
788 i = abs(i)
789 values = {
790 1 << 0 : "port-down",
791 1 << 1 : "no-stp",
792 1 << 2 : "no-recv",
793 1 << 3 : "no-recv-stp",
794 1 << 4 : "no-flood",
795 1 << 5 : "no-fwd",
796 1 << 6 : "no-pkt-in",
797 1 << 31 : "mirror",
798 }
799 if i == 0 or i == '':
800 return ''
801 return print_mask_enum_string_and_int(values, i)
802
803def decode_port_state(i, data=None):
804 if i == '':
805 return 'Unknown'
806
807 # The two bits at (3 << 8) are a mask for the STP value
808 # which are listed in the following dictionary. Note that these
809 # aren't simply bit masks, so we can't use print_mask_enum_string_and_int
810 # here. So instead we special case the formatting of this field. We could
811 # probably factor some of this logic into a utility function, but
812 # this works for now.
813 stp_values = {
814 0 << 8 : ": stp-listen",
815 1 << 8 : ": stp-learn-no-relay",
816 2 << 8 : ": stp-forward",
817 3 << 8 : ": stp-block-broadcast",
818 }
819 value = "link-down" if i == 1 else "link-up" + stp_values.get(i, '')
820 return "%s(%s)" % (value, hex(i) if i > 9 else i)
821
822
823def decode_port_up_down(i, data=None):
824 if i == '':
825 return 'Unknown'
826 return "down" if i == 1 else 'up'
827
828
829def decode_port_stp_state(i, data=None):
830 if i == '':
831 return ''
832 stp_values = {
833 0 << 8 : "listen",
834 1 << 8 : "learn-no-relay",
835 2 << 8 : "forward",
836 3 << 8 : "block-broadcast",
837 }
838 value = '' if i == 1 else stp_values.get(i)
839 return "%s(%s)" % (value, hex(i) if i > 9 else i)
840
841
842def decode_port_linkrate(i, data=None):
843 values = {
844 1 << 0 : "10mb-hd",
845 1 << 1 : "10mb-fd",
846 1 << 2 : "100mb-hd",
847 1 << 3 : "100mb-fd",
848 1 << 4 : "1gb-hd",
849 1 << 5 : "1gb-fd",
850 1 << 6 : "10gb-fd",
851 }
852 if type(i) != int:
853 return "Unknown"
854 if (int(i) & 0x7f) == 0:
855 return "Unknown"
856 return print_mask_enum_string_and_int(values, int(i) & 0x7f)
857
858
859def decode_port_features(i, data=None):
860 values = {
861 1 << 0 : "10mb-hd",
862 1 << 1 : "10mb-fd",
863 1 << 2 : "100mb-hd",
864 1 << 3 : "100mb-fd",
865 1 << 4 : "1gb-hd",
866 1 << 5 : "1gb-fd",
867 1 << 6 : "10gb-fd",
868 1 << 7 : "copper",
869 1 << 8 : "fiber",
870 1 << 9 : "autoneg",
871 1 << 10 : "pause",
872 1 << 11 : "pause-asym",
873 }
874 return print_mask_enum_string_and_int(values, i)
875
876
877def decode_switch_capabilities(i, data=None):
878 values = {
879 1 << 0 : "flow",
880 1 << 1 : "tbl",
881 1 << 2 : "port",
882 1 << 3 : "stp",
883 1 << 5 : "ip-reasm",
884 1 << 6 : "queue-stats",
885 1 << 7 : "match-ip-in-arp",
886 }
887 return print_mask_enum_string_and_int(values, i)
888
889
890def decode_switch_actions(i, data=None):
891 values = {
892 1 << 0 : "output",
893 1 << 1 : "vlan-vid",
894 1 << 2 : "vlan-pcp",
895 1 << 3 : "strip-vlan",
896 1 << 4 : "dl-src",
897 1 << 5 : "dl-dst",
898 1 << 6 : "nw-src",
899 1 << 7 : "nw-dst",
900 1 << 8 : "nw-tos",
901 1 << 9 : "tp-src",
902 1 << 10 : "tp-dst",
903 1 << 11 : "enqueue",
904 }
905 if (i == 0):
906 return "mirror"
907 return print_mask_enum_string_and_int(values, i)
908
909
910wildcards = {
911 1 << 0 : 'in port',
912 1 << 1 : 'vlan',
913 1 << 2 : 'pri',
914 1 << 3 : 'eth',
915 1 << 4 : 'tos',
916 1 << 5 : 'proto',
917 1 << 6 : 'src',
918 1 << 7 : 'dst',
919 1 << 8 : 'mpls',
920 1 << 9 : 'tc',
921}
922
923def realtime_flow_brief(i, data=None):
924 brief = []
925 wildcard = data['wildcards']
926 # wild = print_mask_enum_string_and_int(wildcards, wildcard)
927 try:
928 i = int(wildcard)
929 except:
930 return ''
931
932 if not wildcard & 0x1:
933 brief.append('rx=%s' % decode_openflow_port(data['inputPort'], data))
934 if (not wildcard & 0x2) and (data['dataLayerVirtualLan'] != -1):
935 brief.append('vlan:%s' % data['dataLayerVirtualLan'])
936 if (not wildcard & 0x4) and (data['dataLayerVirtualLanPriorityCodePoint'] != '*'):
937 brief.append('pri:%s' % data['dataLayerVirtualLanPriorityCodePoint'])
938 if (not wildcard & 0x8) and (data['dataLayerType'] != '*') :
939 brief.append('eth:%s' % data['dataLayerType'])
940 if not wildcard & 0x10:
941 brief.append('tos:%s' % data['networkTypeOfService'])
942 if not wildcard & 0x20:
943 brief.append('ip:%s' % data['networkProtocol'])
944 if data['networkSourceMaskLen']:
945 brief.append('src:%s' % utif.ip_and_neg_mask(data['networkSource'],
946 data['networkSourceMaskLen']))
947 if not wildcard & 0x40:
948 brief.append('sport:%s' % data['transportSource'])
949 if data['networkSourceMaskLen']:
950 brief.append('src:%s' % utif.ip_and_neg_mask(data['networkSource'],
951 data['networkSourceMaskLen']))
952 if not wildcard & 0x80:
953 brief.append('dport:%s' % data['transportDestination'])
954 # mpls not in OF1.0
955 #if not wildcard & 0x100:
956 #brief.append('mpls: ?')
957 #if not wildcard & 0x200:
958 #brief.append('mpls-tc: ?')
959
960 return ', '.join(brief)
961
962
963def formatter_to_alias_update(formatter, update):
964 """
965 Associate the items which need updating with the formatter.
966 the update parameter is a dict, allowing multiple updates
967 over several distinct fields
968
969 Update this procedure as new decoding procedures are
970 added which require some alias translation to be updated
971
972 @param formatter function which is called to format a field
973 @param update dictionry passed in, updated with alias types to update
974 """
975 name = formatter.__name__
976 if name in ['print_host_and_alias',
977 'print_all_host_attachment_points',
978 'print_devicemanager_attachment_points',
979 'replace_host_with_alias',
980 ]:
981 update['host'] = True
982 if name in ['print_host_attachment_point',
983 'print_vns_physical_interface_id',
984 'decode_openflow_port',
985 'decode_openflow_port_src_switch',
986 'decode_openflow_port_dst_switch',
987 'decode_openflow_port_source_switch',
988 'decode_openflow_port_inputSwitch',
989 'decode_openflow_port_dpid',
990 'realtime_flow_brief',
991 ]:
992 update['switch'] = True # port implies switch
993 update['port'] = True
994 if name in ['print_switch_and_alias',
995 'print_switch_and_alias_dpid_as_long',
996 'print_switches',
997 'replace_switch_with_alias',
998 ]:
999 update['switch'] = True
1000 if name in ['decode_flow_cookie'
1001 ]:
1002 update['flow'] = True
1003 if name in ['replace_controller_node_with_alias',
1004 ]:
1005 update['controller-node'] = True
1006 return update
1007
1008def print_switch_port_list(i, data=None):
1009 if i == None:
1010 return ''
1011 return ' '.join([replace_switch_with_alias(x['switch']) + \
1012 '/' + decode_openflow_port(x['port']) for x in i])
1013
1014def print_switch_port(i):
1015 if i == None:
1016 return ''
1017 return replace_switch_with_alias(x['switch']) + \
1018 '/' + decode_openflow_port(x['port'])
1019
1020def print_host_attachment_point(i, data=None):
1021 if i == None:
1022 return "Inactive"
1023 if len(i) == 1:
1024 if_name = decode_openflow_port(i[0]['ingress-port'],
1025 data['attachment-points'][0])
1026 dpid = str(i[0]['switch'])
1027 #print print_switch_and_alias(dpid, data)
1028 return print_switch_and_alias(i[0]['switch']) + '/' + if_name
1029 if len(i) == 0:
1030 return ""
1031 first = i[0]
1032 if 'prime' in first:
1033 if_name = decode_openflow_port(i[0]['ingress-port'],
1034 data['attachment-points'][0])
1035 return "%s/%s+[%s]" % (replace_switch_with_alias(i[0]['switch']),
1036 if_name, str(len(i)-1))
1037 return "multiple (" + str(len(i)) + ")"
1038
1039
1040def print_all_host_attachment_points(i, data=None):
1041 if i == None:
1042 return ''
1043 return ' '.join([replace_switch_with_alias(x['switch']) + \
1044 '/' + decode_openflow_port(x['ingress-port'],
1045 data['attachment-points'][0])
1046 for x in i])
1047
1048
1049def print_devicemanager_attachment_points(i, data=None):
1050 if i == None:
1051 return ''
1052 return ' '.join([replace_switch_with_alias(x['switch']) + \
1053 '/' + decode_openflow_port(x['port'],
1054 data['attachment-points'][0])
1055 for x in i])
1056
1057
1058def print_cluster_id(i, data=None):
1059 return str(i)
1060
1061
1062def print_switches(i, data=None):
1063 return ' '.join([replace_switch_with_alias(dpid) for dpid in i])
1064
1065
1066def print_single_tag(tag):
1067 fields = tag.split('|')
1068 return "%s.%s=%s" % (fields[0],fields[1],fields[2])
1069
1070def print_host_tags(i, data=None):
1071 if i == None or len(i) == 0:
1072 return ""
1073 tag = i
1074 if type(tag) == dict:
1075 tag = [i]
1076
1077 if len(tag) == 1:
1078 # 'tag' is a complete row from the store, specifically 'tag-mapping'
1079 # the 'tag' key holds a name which looks like: <namespace>|<name>|<value>
1080 # which is then split, and recombined for display
1081 return print_single_tag(tag[0]['tag'])
1082 if len(tag) == 0:
1083 return ""
1084 return "multiple (" + str(len(tag)) + ")"
1085
1086
1087def print_all_host_tags(i, data=None):
1088 if i == None:
1089 return ''
1090 if i == dict:
1091 return print_single_tag(i['tag'])
1092 return ' '.join([print_single_tag(x['tag']) for x in i])
1093
1094
1095def print_ip_addresses(i, data=None):
1096 if i != None:
1097 if len(i) == 1:
1098 return str(i[0]['ip-address'])
1099 if len(i) == 0:
1100 return ""
1101 # if there are multiple addressed, see if some of the addresses
1102 # are "less" important than others.
1103 less_interesting = 0
1104 more_interesting = None
1105 for ips in i:
1106 if ips['ip-address'] == '0.0.0.0':
1107 less_interesting += 1
1108 elif re.match(r'169.254.*', ips['ip-address']):
1109 less_interesting += 1
1110 else:
1111 more_interesting = ips['ip-address']
1112
1113 if len(i) == less_interesting + 1:
1114 return "%s+(%d)" % (more_interesting, less_interesting)
1115 s = sorted(i, key=lambda k: k['last-seen'])
1116 return "%s+(%d)" % (s[-1]['ip-address'], len(s) - 1)
1117 # return "multiple (" + str(len(i)) + ")"
1118 return "Unknown"
1119
1120
1121def print_all_ip_addresses(i, data=None):
1122 if i == None:
1123 return ''
1124 return ' '.join([x['ip-address'] for x in i])
1125
1126
1127def print_devicemanager_ip_addresses(i, data=None):
1128 if i == None:
1129 return ''
1130 return ' '.join([x['ip'] for x in i])
1131
1132
1133def print_join_list(i, data=None):
1134 return ', '.join(i)
1135
1136
1137def replace_with_alias(object_type, i, data=None):
1138 """
1139 use when we have aliases for other objects
1140 """
1141 global alias_dicts
1142 alias_dict = alias_dicts.get(object_type, {i : i})
1143 if not alias_dict:
1144 return i
1145 return alias_dict.get(i, i)
1146
1147
1148def print_switch_and_alias(i, data=None):
1149 """
1150 """
1151 dpid = str(i)
1152 alias = replace_switch_with_alias(i, data)
1153 if dpid == alias:
1154 dpid_name = get_switches_names(object, data)
1155 if i in dpid_name:
1156 alias = dpid_name[i]
1157 return dpid + ' (' + alias + ')'
1158 else:
1159 return dpid
1160 else:
1161 return dpid + ' (' + alias + ')'
1162
1163
1164def print_switch_and_alias_dpid_as_long(i, data=None):
1165 dpid = convert_long_to_dpid(i)
1166 return print_switch_and_alias(dpid)
1167
1168
1169def print_switch_interface_and_alias(i, data=None):
1170 """
1171 """
1172 switch_interface = str(i)
1173 return switch_interface
1174
1175
1176def print_host_and_alias(i, data=None):
1177 # The value of 'i' here ought to be the primary key
1178 # for the host, which is a compound key. Try hard to
1179 # not deal with the construction of the key.
1180 mac = str(i)
1181 # if the host address is eight hex values, chop off the prefix
1182 # nasty hack to deal with the use of a int64 to hold a host
1183 if len(mac) == 23:
1184 mac = mac[6:]
1185 i = mac
1186 name = replace_host_with_alias(mac, data)
1187 if data == None:
1188 data = {}
1189
1190 mac_in_data = data.get('mac')
1191 suffix = ''
1192 if name != mac:
1193 suffix = ' (%s)' % name
1194
1195 if mac_in_data:
1196 return mac_in_data + suffix
1197 return mac + suffix
1198
1199
1200def print_vlan_and_alias(i, data=None):
1201 vlan = int(i)
1202 # if the host address is eight hex values, chop off the prefix
1203 # nasty hack to deal with the use of a int64 to hold a host
1204 return vlan
1205
1206
1207def print_host_and_alias_mac_as_long(i, data=None):
1208 mac = convert_mac_in_decimal_to_hex_string(i)
1209 return print_host_and_alias(mac, data)
1210
1211def print_host_and_alias_mac_as_long_str(i, data=None):
1212 # i is long decimal as string, e.g. "1342"
1213 mac_decimal = int(i)
1214 mac = convert_mac_in_decimal_to_hex_string(mac_decimal)
1215 return print_host_and_alias(mac, data)
1216
1217
1218# host list may also contain other items (ie: not macs)
1219def print_host_list_and_alias(i, data=None):
1220 # use at most 60 columns
1221
1222 more = 0
1223 output = ""
1224 for host in i:
1225 mac = str(host)
1226 name = replace_host_with_alias(host, data)
1227 if name != mac:
1228 mac += ' (' + name + ')'
1229 if len(output) + len(mac) > 60:
1230 more += 1
1231 else:
1232 output += mac
1233 output += ' '
1234 if more:
1235 return "%s+(%s)" % (output, more)
1236 return output
1237
1238
1239def replace_switch_with_alias(i, data=None):
1240 return replace_with_alias("switch-config", i, data)
1241
1242def convert_inverse_netmask_handler(i, data=None):
1243 if not '.' in i:
1244 return i
1245 split_bytes = i.split('.')
1246 return "%s.%s.%s.%s" % (255-int(split_bytes[0]),
1247 255-int(split_bytes[1]),
1248 255-int(split_bytes[2]),
1249 255-int(split_bytes[3]))
1250
1251def replace_host_with_alias(i, data=None):
1252 return replace_with_alias("host-config", i, data)
1253
1254
1255def replace_controller_node_with_alias(i, data=None):
1256 return replace_with_alias("controller-node", i, data)
1257
1258
1259def print_vns_count_dict(i, data=None):
1260 output = ""
1261 more = 0
1262 for vns in i:
1263 # possibly vns:refs instead?
1264 #item = '%s:%s ' % (vns, i[vns])
1265 item = '%s ' % vns
1266 if len(output) > 40:
1267 more += 1
1268 else:
1269 output += item
1270 if more:
1271 return "%s +(%s)" % (output, more)
1272 return output
1273
1274
1275def print_vns_physical_interface_id(i, data=None):
1276 return i.split('|')[2]
1277
1278
1279def print_domain_name_servers(i, data=None):
1280 if len(i) == 0:
1281 return 'None'
1282 result = i[0]
1283 if len(i) > 1:
1284 result += ' +(%d)' % (len(i)-1)
1285 return result
1286
1287
1288def print_all_domain_name_servers(i, data=None):
1289 if i == None:
1290 return ''
1291 return ', '.join(i)
1292
1293
1294def print_clock_string(i, data = None):
1295 print i, data
1296
1297
1298def replace_boolean_with_enable_disable(i, data=None):
1299 return { True : 'enabled', False : 'disabled' } [i]
1300
1301
1302def sanitize_unicode(i, data=None):
1303 return unicode(i, errors='ignore')
1304
1305
1306def controller_node_me_entry(i, data=None):
1307 if data.get('me', '') == '':
1308 return 'Not current controller'
1309 return 'current controller'
1310
1311def controller_node_me(i, data=None):
1312 if 'me' in data:
1313 return '*'
1314 return ''
1315
1316
1317def print_ipv4addr(i, data=None):
1318 ipaddr = data['ip']
1319 mask = data['masklen']
1320 if (ipaddr != "*"):
1321 ipaddr_int = reduce(lambda a,b: a<<8 | b, map(int, ipaddr.split(".")))
1322 mask = (1 << mask) - 1
1323 ipaddr_int = ipaddr_int & ~mask
1324 ipaddr = ".".join(map(lambda n: str(ipaddr_int >>n & 0xFF), [24,16,8,0]))
1325 return ipaddr
1326