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