Adding ONOS Segment Routing CLI files to new repo
diff --git a/cli/tools/expr_to_desc.py b/cli/tools/expr_to_desc.py
new file mode 100755
index 0000000..9867d49
--- /dev/null
+++ b/cli/tools/expr_to_desc.py
@@ -0,0 +1,803 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+#
+
+# Simple command description builder.
+#
+
+import os
+import copy
+import fileinput
+import traceback
+import argparse
+
+def parse(*line):
+ if args.p:
+ print line
+
+def debug(*line):
+ if args.d:
+ print line
+
+def verbose(*line):
+ if args.v:
+ print line
+
+def error(*line):
+ print line
+
+
+add_token = ('o', '+')
+front_token = ('o', '^')
+end_token = ('o', '$')
+
+def tokens(line):
+ """
+ Returns the tokenzied line, with a eol token at the end
+ The start-token must be prefixed before parsing
+ """
+ tokens = []
+ i = 0
+ end = len(line)
+ while i < end:
+ # break
+ while line[i] in ' \t\r\n':
+ i += 1
+ if i == end:
+ break
+ if i == end:
+ break
+ if line[i] in '[]{}<>()|':
+ tokens.append(('o', line[i])) # 'o' <= char, op
+ i += 1
+ continue
+ # span.
+ token = ''
+ run_i = i
+ while not line[run_i] in ' \t[]{}<>|\n':
+ run_i += 1
+ if run_i == end:
+ break
+ tokens.append(('t', line[i:run_i])) # 't' <= token
+ i = run_i
+ #
+ tokens.append(end_token) # 't' <= '$' for eol
+
+ return tokens
+
+priority_dict = {
+ '^' : 0, # hat, front of line
+ '$' : 1, # newline, end of line
+ ')' : 2,
+ '(' : 3,
+ '}' : 4,
+ '{' : 5,
+ ']' : 6,
+ '[' : 7,
+ '>' : 8,
+ '<' : 9,
+ '|' : 10,
+ '+' : 11,
+ }
+
+def priority(tok):
+ if not is_op(tok):
+ return -1 # lower than any with a valid priority, enables push
+ if not tok[1] in priority_dict:
+ return 100 # higher than any with a valid priority, enables push
+ return priority_dict[tok[1]]
+
+def is_op(tok):
+ return tok[0] == 'o'
+
+def is_token(tok):
+ return tok[0] == 't'
+
+def is_field(tok):
+ return tok[0] == 'field'
+
+def is_tree(tok):
+ return tok[0] == 'tree'
+
+def is_list(tok):
+ return type(tok) == list
+
+def op_of(tok):
+ return tok[1]
+
+def token_of(tok):
+ return tok[1]
+
+def field_list_of(tok):
+ return tok[1]
+
+balance = {
+ ']' : '[',
+ '}' : '{',
+ '>' : '<',
+ ')' : '(',
+ '$' : '^',
+}
+
+def partner(tok):
+ if is_op(tok):
+ if token_of(tok) in balance:
+ return balance[token_of(tok)]
+ return None
+
+
+stack = []
+
+def print_stack():
+ for s in stack:
+ print s
+
+def push(item):
+ parse("PUSH ", stack, "ADD ", item)
+ stack.append(item)
+
+def pop():
+ parse( "POP", stack )
+ p = stack[-1]
+ del stack[-1]
+ return p
+
+def reset_stack():
+ global stack
+ stack = []
+
+def peek(i = 1):
+ top = len(stack)
+ if top < i:
+ return ('o','^') # <= front of line
+ return stack[-i]
+
+def is_in_order(collect):
+ if is_op(collect[1]) and collect[1][1] == '+':
+ return True
+ return False
+
+def is_either(collect):
+ if is_op(collect[1]) and collect[1][1] == '|':
+ return True
+ return False
+
+def gather_field_list(collect, field_list):
+ if type(collect) == list:
+ for f in collect:
+ gather_field_list(f, field_list)
+ elif is_token(collect):
+ field_list.append(collect)
+ elif is_tree(collect):
+ gather_field_list(token_of(token_of(collect)), field_list)
+ else:
+ field_list.append(field_list)
+
+def gather_field(collect):
+ parse( "GATHER FIELD ", collect)
+ field_list = []
+ if is_token(collect):
+ field_list.append(collect)
+ elif is_tree(collect):
+ gather_field_list(token_of(token_of(collect)), field_list)
+ elif type(collect) == list:
+ gather_field_list(collect, field_list)
+ else:
+ field_list.append(collect)
+ return ('field', field_list)
+
+def tree_builder(collect, tok):
+ result = None
+ op = op_of(tok)
+ parse( "WHAT ", collect, tok)
+ if op == '}': # { stuff } ... select one from the args
+ # XXX early return
+ if len(collect) == 1:
+ result = ('CHOICE ALONE', collect)
+ elif is_either(collect):
+ result = ("CHOICE OF", [c for c in collect if not is_op(c)])
+ elif is_in_order(collect):
+ result = ("CHOICE ORDER", [c for c in collect if not is_op(c)])
+ elif is_tree(collect):
+ result = ('CHOICE TREE', collect)
+ elif is_field(collect):
+ return gather_field(collect)
+ elif is_token(collect):
+ return collect
+ elif is_token(collect):
+ return collect
+ else:
+ result = ("CHOICE TROUBLE", op, collect)
+ elif op == ']': # [ stuff ] ... stuff which is optional
+ if len(collect) == 1:
+ result = ('OPTION ALONE', collect)
+ elif is_either(collect):
+ result = ("OPTION OF", [c for c in collect if not is_op(c)])
+ elif is_in_order(collect):
+ result = ("OPTION ORDER", [c for c in collect if not is_op(c)])
+ elif is_tree(collect):
+ result = ('OPTION TREE', collect)
+ elif is_field(collect):
+ return ('tree', ('OPTION FIELD', collect))
+ elif is_token(collect):
+ result = ('OPTION TOKEN', collect)
+ else:
+ result = ("OPTION TROUBLE", op, collect)
+ elif op == ')': # ( stuff ) ... no semantic meaning,
+ # XXX early return
+ return collect
+ elif op == '>': # description of a field
+ gather = gather_field(collect)
+ parse("GATHERED: ",gather)
+ return gather
+ elif op == '|': # either of
+ result = ('EITHER', [c for c in collect if not is_op(c)])
+ elif op == '+': # sum of
+ result = ('ORDER', [c for c in collect if not is_op(c)])
+ elif op == '$': # eol, collect up any tree's left
+ # XXX syntax error?
+ print "STACK "
+ print_stack()
+ print "TOK ", tok
+ print "COLLECT", collect
+ exit()
+ else:
+ parse('return collect, later tok', tok)
+ return collect
+ parse( "BUILD ", op, type(result))
+ return ('tree', result)
+
+
+def single_token(tok):
+ (which, t) = tok
+ parse( "NEXT", which, t, peek())
+
+ if is_token(tok) and is_token(peek()):
+ # two tokens in a row, pretend the op is '+'
+ push(add_token)
+
+ # is this a <tree><tree> ?
+ if is_tree(peek()) and (is_tree(peek(2)) or is_field(peek(2))):
+ # collect together as many as possible
+ collect = [pop()]
+ while is_tree(peek()) or is_field(peek()):
+ collect.insert(0, pop())
+ push(tree_builder(collect, add_token))
+ # is this a <tree><tree> ?
+ elif (not is_op(peek())) and (not is_op(peek(2))):
+ # collect together as many as possible
+ collect = [pop()]
+ while not is_op(peek()):
+ collect.insert(0, pop())
+ push(tree_builder(collect, add_token))
+
+ if is_op(tok):
+ if not is_op(peek(1)): # item or token or field
+ parse( 'PRIO ', tok, priority(tok), peek(2), priority(peek(2)))
+ while priority(tok) < priority(peek(2)):
+ # collect as many from the same priority
+ last = pop()
+ parse( "-->", stack, tok, last)
+ # uniary op?
+ if is_op(peek()) and partner(tok) == op_of(peek()):
+ parse( "UNIARY ")
+ pop() # <= pop matching op
+ push(tree_builder(last, tok)) # <= token popped off
+ parse( "LEAVE", stack, tok)
+ return # don't push the uniary right op
+
+ collect = last
+ op = tok
+ if is_op(peek()):
+ op = peek()
+ parse( "BINARY ", op)
+ collect = [last]
+ while is_op(peek()) and \
+ not (partner(tok) == op_of(peek())) and \
+ priority(op) == priority(peek()):
+ parse( "WHY ", op_of(op), priority(op), op_of(peek()), priority(peek()))
+ collect.insert(0, pop()) # <= op
+ collect.insert(0, pop()) # <= token
+ if len(collect) == 1:
+ print "NOT BINARY", tok, op, peek
+ exit()
+ parse( "==> ", collect, tok)
+ parse( "__ ", stack)
+ push(tree_builder(collect, op))
+ parse( "SO FAR ", stack)
+ parse( "OP FAR ", tok)
+ push(tok)
+ parse( "LAST", stack, tok)
+
+
+#
+def single_line(tokens):
+ reset_stack()
+ for (which, t) in tokens:
+ single_token((which, t))
+
+def all_tokens(token_list):
+ for token in token_list:
+ parse( "ALL TOKEN? ", token)
+ if not is_token(token):
+ return False
+ return True
+
+
+class printer:
+ def __init__(self, form, is_optional, indent = 2):
+ self.form = form
+ self.is_optional = is_optional
+ self.need_optional = False
+ self.indent = indent
+
+ def __str__(self):
+ return "form %s need %s optional %s indent %s" % (
+ self.form, self.need_optional, self.is_optional, self.indent)
+
+ def indents(self, extra = 0):
+ return " " * (self.indent + extra)
+
+ def to_dict(self):
+ self.form = 'dict'
+
+ def is_dict(self):
+ if self.form == 'dict':
+ return True
+ return False
+
+ def to_tuple(self):
+ self.form = 'tuple'
+
+ def is_tuple(self):
+ if self.form == 'tuple':
+ return True
+ return False
+
+ def more_indent(self, incr = 1):
+ self.indent += incr
+
+ def less_indent(self, decr = 1):
+ self.indent -= decr
+
+ def nest(self, incr = 1, form = None, is_optional = None):
+ new = copy.deepcopy(self)
+ new.indent += 1
+ if form:
+ new.form = form
+ if is_optional:
+ new.is_optional = is_optional
+ return new
+
+class description:
+ def __init__(self):
+ self.desc = []
+
+ def raw_out(self, line):
+ self.desc.append(line)
+
+ def out(self, line, printer):
+ self.desc.append(printer.indents() + line)
+
+ def out_with_indent(self, line, printer):
+ self.desc.append(printer.indents(1) + line)
+
+ def result(self):
+ return '\n'.join(self.desc)
+
+desc = description()
+
+def single_recurse(tree):
+ """
+ look for nested trees whose leaf only has a single element
+ """
+ return False
+
+def maker_in_order(in_order, printer):
+ debug( "IN_ORDER ", is_tree(in_order), in_order )
+ if is_list(in_order):
+ was_dict = False
+ desc.out('# %d items in order' % len(in_order) , printer)
+ desc.out('# %s ' % printer , printer)
+ if printer.is_dict():
+ printer.to_tuple()
+ desc.out("(", printer)
+ printer.more_indent()
+ was_dict = True
+ do_optional = False
+ if printer.is_optional:
+ if not was_dict:
+ desc.out("(", printer)
+ printer.more_indent()
+ do_optional = True
+ save_need_optional = printer.need_optional
+ printer.need_optional = True
+ printer.is_optional = False
+
+ for (n, tree) in enumerate(in_order, 1):
+ debug( "IN_ORDER ITEM ", n, is_token(tree), tree)
+ desc.out('# item %d %s' % (n, printer) , printer)
+ maker_items(tree, printer)
+
+ if was_dict or do_optional:
+ printer.less_indent()
+ desc.out("),", printer)
+ if was_dict:
+ printer.to_dict()
+ if do_optional:
+ printer.is_optional = True
+ printer.need_optional = save_need_optional
+
+ elif is_tree(in_order):
+ was_dict = False
+ if printer.is_dict():
+ desc.out("(", printer) # )(
+ desc.out("#in_order2", printer)
+ traceback.print_stack()
+ printer.to_tuple()
+ printer.more_indent()
+ was_dict = True
+ debug( "IN_ORDER TREE ", token_of(in_order) )
+ maker_do_op(token_of(in_order), printer)
+ if was_dict:
+ printer.less_indent()
+ desc.out("),", printer)
+ printer.to_dict()
+ elif is_token(in_order):
+ maker_items(in_order, printer.nest(incr = 1))
+ elif is_field(in_order):
+ maker_field(field_list_of(in_order), printer.nest(incr = 1))
+ else:
+ error( "IN_ORDER STUCK" )
+
+def maker_field(field_list, printer):
+ was_tuple = False
+ if printer.is_tuple:
+ was_tuple = True
+ desc.out("{", printer)
+ printer.more_indent()
+ printer.to_tuple()
+
+ if printer.need_optional:
+ desc.out("'optional' : %s," % printer.is_optional, printer)
+
+ for field in field_list:
+ # Add more items here to provide more field decoration
+ printer.more_indent
+ value = token_of(field)
+ if value.find('=') == -1:
+ desc.out("'field' : '%s'," % value, printer)
+ else:
+ desc.out("'%s' : '%s'," % tuple(value.split('=')), printer)
+ printer.less_indent
+
+ if was_tuple:
+ printer.less_indent()
+ desc.out( "},", printer )
+ printer.to_dict()
+
+def maker_choice(tree_tuple, printer):
+ debug( 'MAKER_CHOICE', tree_tuple, printer.indent )
+
+ if is_tree(tree_tuple):
+ # XXX some tree's can be squashed.
+ debug( "MAKER_CHOICE ITEM ", tree_tuple )
+ maker_do_op(token_of(tree_tuple), printer)
+ return
+
+ # choice needs to print a dictionary.
+ was_tuple = False
+ if printer.is_tuple():
+ printer.to_dict()
+ was_tuple = True
+
+ desc.out('{', printer)
+ printer.more_indent()
+
+ if printer.is_optional:
+ desc.out("'optional': %s," % printer.is_optional, printer)
+ desc.out("'choices' : (", printer)
+ desc.out(" # maker_choice", printer)
+
+ if is_list(tree_tuple):
+ debug( "CHOICE LIST", len(tree_tuple), tree_tuple )
+ printer.more_indent()
+ for (n, item) in enumerate(tree_tuple, 1):
+ debug( "CHOICE LIst #%d" % n )
+ debug( " ITEM ", item )
+ maker_items(item, printer)
+ printer.less_indent()
+ elif is_tree(tree_tuple):
+ debug( "CHOICE TREE" )
+ (tree_which, tree) = tree_tuple[1]
+ # tree_which == 'tree'
+ maker_do_op(token_of(tree_tuple), printer.nest(form = 'tuple', incr = 1))
+ elif is_field(tree_tuple):
+ debug( "CHOICE FIELD", tree_tuple[1] )
+ printer.more_indent()
+ maker_field(field_list_of(tree_tuple), printer)
+ printer.less_indent()
+ else:
+ error( 'MAKER_CHOICE CONFUSED' )
+
+ desc.out(")", printer)
+ printer.less_indent()
+ desc.out('},', printer)
+
+ if was_tuple:
+ printer.to_tuple()
+
+
+def maker_do_op(op_tuple, printer):
+ debug( 'OP=> ', op_tuple )
+ (op, operands) = op_tuple
+ debug( 'OP ', op_tuple, op, operands )
+ if op == 'ORDER':
+ debug( "OP IN_ORDER ", operands )
+ maker_in_order(operands, printer)
+ elif op == 'EITHER':
+ # XXX wrong
+ maker_choice(operands, printer)
+ elif op.startswith('CHOICE'):
+ maker_choice(operands, printer)
+ elif op.startswith('OPTION'):
+ was_optional = printer.is_optional
+ printer.is_optional = True
+ debug( 'OP OPTIONAL', operands )
+ maker_items(operands, printer)
+ printer.is_optional = was_optional
+
+
+def maker_trees(trees, printer):
+ (tree_which, op) = trees
+ maker_do_op(op, printer)
+
+
+def maker_items(items, printer):
+ debug( "ITEMS-> ", type(items), items )
+ if type(items) == list:
+ for item in items:
+ if is_tree(item):
+ debug( "TREE ", item )
+ maker_do_op(item, printer)
+ elif is_field(item):
+ desc.out("{", printer)
+ maker_field(field_list_of(item), printer)
+ if printer.need_optional:
+ desc.out("'optional' : %s," % printer.is_optional, printer)
+ desc.out( "},", printer)
+ elif is_token(item):
+ desc.out("{", printer)
+ printer.more_indent()
+ desc.out("'token' : '%s'," % token_of(item), printer)
+ if printer.need_optional:
+ desc.out("'optional' : %s," % printer.is_optional, printer)
+ printer.less_indent()
+ desc.out("},", printer)
+ elif is_tree(items):
+ maker_do_op(token_of(items), printer)
+ elif is_field(items):
+ maker_field(field_list_of(items), printer)
+ elif is_token(items):
+ debug( 'ITEMS TOKEN', items )
+ desc.out( "{", printer)
+ printer.more_indent()
+ desc.out( "'token' : '%s'," % token_of(items), printer)
+ if printer.need_optional:
+ desc.out( "'optional' : %s," % printer.is_optional, printer)
+ printer.less_indent()
+ desc.out( "},", printer)
+ else:
+ error( "ITEMS> STUCK", items )
+
+
+#
+def maker_args(args, printer):
+ debug( "MAKER_ARGS ", args )
+ (pick, item) = args
+ if pick == 'EITHER':
+ debug( "MAKER_ARGS EITHER ", item )
+ if len(item) >= 1:
+ desc.raw_out( ' args : {')
+ if all_tokens(item):
+ for choice in item:
+ desc.raw_out(" '%s'," % token_of(choice, 3))
+ else:
+ maker_trees(item, printer)
+ desc.raw_out( ' },' )
+ else: # exactly one choice
+ desc.raw_out( ' args : { ' )
+ if all_token(item):
+ desc.raw_out(" '%s'," % token_of(item[0]))
+ else:
+ print maker_trees(item, printer)
+ desc.raw_out( ' },' )
+ elif pick.startswith('CHOICE'):
+ debug( "CHOICE", len(item) )
+ if len(item) == 1:
+ maker_args(item)
+ elif is_tree(item) and token_of(item)[0] == 'EITHER':
+ maker_choice(token_of(item), printer)
+ elif is_field(item) or is_token(item):
+ maker_choice(item, printer)
+ else:
+ error( "CHOICE HELP ", item )
+ elif pick.startswith('ORDER'):
+ # ought to choose the form of the printer based on item
+ desc.out("'args' : {", printer)
+ printer.less_indent()
+ maker_in_order(item, printer)
+ printer.less_indent()
+ desc.out('}', printer)
+ elif pick.startswith('OPTION'):
+ printer.is_optional = True
+ desc.raw_out( ' args : {')
+ maker_trees(item, printer)
+ desc.raw_out( ' },')
+ else:
+ error( "MAKER_PICKER HELP ", pick )
+
+saved_input = []
+
+#
+def maker(name_tuple, no_supported, result, printer):
+
+ name = token_of(name_tuple)
+ verbose( 'Name: %s no %s Result %s' % (name, no_supported, result) )
+
+ type = 'add-command-type'
+ mode = 'login'
+ new_mode = 'config-CHANGE'
+ obj_type = None
+ #
+ # command-name@command-type@command-mode@obj-type@new_mode
+ #
+ if name.find('@') >= 0:
+ name_parts = name.split('@')
+ name = name_parts[0]
+ type = name_parts[1]
+ if len(name_parts) > 2:
+ mode = name_parts[2]
+ if len(name_parts) > 3:
+ obj_type = name_parts[3]
+ if len(name_parts) > 4:
+ new_mode = name_parts[4]
+ # name-value pairs?
+
+ debug( "NAME ", (name) )
+ debug( 'command name ', name )
+ desc.raw_out( '%s_%s_COMMAND_DESCRIPTION = {' %
+ (args.n.upper(), name.replace("-","_").upper()))
+ desc.raw_out( " 'name' : '%s'," % name)
+ desc.raw_out( " 'mode' : '%s'," % mode)
+ if no_supported == False:
+ desc.raw_out( " 'no-supported' : False,")
+ desc.raw_out( " 'command-type' : '%s'," % type)
+ if obj_type:
+ desc.raw_out( " 'obj-type' : '%s'," % obj_type)
+ if type == 'config-submode':
+ desc.raw_out( " 'submode-name' : '%s'," % new_mode)
+ desc.raw_out( " 'parent-id' : None,")
+ desc.raw_out( " 'short-help' : 'add-short-command-help',")
+ if args.f:
+ desc.raw_out( " 'feature' : '%s'," % args.f)
+ # if the remaining length is two, ORDER should be popped.
+ desc.raw_out( " 'args' : ( ")
+ maker_items(result, printer)
+ desc.raw_out( " ),")
+ desc.raw_out( "}")
+ return
+
+ if len(order_list) == 2 and len(order) == 1:
+ build_choice = None
+ if is_tree(order_list[1]):
+ debug( "MAKER TREE.", token_of(order_list[1]) )
+ if token_of(order_list[1])[0].startswith('CHOICE'):
+ choice = token_of(order_list[1])
+ build_choice = order_list[1]
+ if build_choice:
+ desc.out("'args' : {", printer)
+ printer.more_indent()
+ maker_choice(build_choice, printer)
+ printer.less_indent()
+ desc.out( '},', printer)
+ else:
+ print "XXX", order_list
+ print "XXX", order_list[1]
+ desc.out("'args' : (", printer)
+ printer.more_indent()
+ printer.to_tuple()
+ maker_in_order(order_list[1], printer)
+ printer.less_indent()
+ desc.out( '),', printer)
+ elif len(order) > 1:
+ maker_args((order[0], order[1:]), printer)
+ else:
+ desc.raw_out('}')
+
+
+parser = argparse.ArgumentParser(prog='desc_maker')
+parser.add_argument('file')
+parser.add_argument('-p')
+parser.add_argument('-d')
+parser.add_argument('-v')
+parser.add_argument('-n', default = "NEW")
+parser.add_argument('-f')
+args = parser.parse_args()
+
+for line in fileinput.input(args.file):
+
+ line_tokens = tokens(line)
+ if len(line_tokens) < 2: # why 2? even blank lines get eol
+ saved_input.append( "# @ %s" % line)
+ continue
+ #
+ # remove '[' 'no' ']' if its there, don't forget the leading '^'
+ no_supported = False
+
+ if len(line_tokens) > 1:
+ if token_of(line_tokens[0]) == '#':
+ saved_input.append( "# @ %s" % line)
+ continue
+ elif token_of(line_tokens[0]) == '[':
+ if len(line_tokens) > 2:
+ if token_of(line_tokens[1]) == 'no':
+ if len(line_tokens) > 3:
+ if token_of(line_tokens[2]) == ']':
+ if len(line_tokens) > 4:
+ no_supported = True
+ name = line_tokens[3]
+ parse_tokens = line_tokens[4:]
+ else:
+ print 'Warning: name required after \[ no \']'
+ continue
+ else:
+ print 'Warning: only \'\[ no \]\' allowed as prefix'
+ continue
+ else:
+ print 'Warning: only \'\[ no \]\' allowed as prefix'
+ continue
+ else:
+ print 'Warning: only single \[ in line'
+ continue
+ else:
+ name = line_tokens[0]
+ parse_tokens = line_tokens[1:]
+
+ saved_input.append( "# @ %s" % line)
+ single_line([front_token] + parse_tokens)
+
+ # should look like ^ tree $
+ if len(stack) == 3 and not is_op(stack[1]):
+ debug( "OK------------------ -> ", token_of(stack[1]) )
+ a_printer = printer('tuple', is_optional = False)
+ desc.out('\n#\n# %s#\n' % line, a_printer)
+ maker(name, no_supported, stack[1], a_printer)
+ else:
+ #
+ # Could peek at the stack to get an idea of the nature of the syntax error
+ print "SYNTAX ERROR", name, stack
+
+#
+#
+
+print "#"
+print "# Command used as input for this run are listed below,"
+print "# Fish the command out by egreping '^# @' "
+print "#"
+
+print ''.join(saved_input)
+print desc.result()
diff --git a/cli/tools/extract_model.py b/cli/tools/extract_model.py
new file mode 100755
index 0000000..6838352
--- /dev/null
+++ b/cli/tools/extract_model.py
@@ -0,0 +1,105 @@
+#
+# Copyright (c) 2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+#
+
+#import time
+
+import datetime
+import django.core.management
+
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+django.core.management.setup_environ(settings)
+
+from django.db import models
+from django.db.models.fields import AutoField, BooleanField, IntegerField
+from django.db.models.fields.related import ForeignKey
+
+import sdncon.rest.views
+
+print "# Generated automatically from controller source"
+print "# via tools/extract_model.py date: %s" % datetime.datetime.now().ctime()
+
+# we iterate over rest_name_dict, limiting CLI access to those models
+# and model fields accessible via rest
+
+rest_map = {}
+for (model, model_info) in sdncon.rest.views.rest_model_info_dict.items():
+ rest_map[model] = {}
+ for (rest_field_name, rest_field_info) in model_info.rest_name_dict.items():
+ django_field_name = rest_field_info.name
+ rest_map[model][django_field_name] = rest_field_name
+
+model_info_dict = {}
+for (model, model_info) in sdncon.rest.views.rest_model_info_dict.items():
+ django_model_class = model_info.model_class
+ field_info_dict = {}
+ for (rest_field_name, rest_field_info) in model_info.rest_name_dict.items():
+ django_field_name = rest_field_info.name
+ django_field_info = rest_field_info.django_field_info
+ # now that we have the django info and our own rest info, create a field info to dump
+ json_serialize_string = type(django_field_info) not in (AutoField, BooleanField, IntegerField)
+ field_info = {}
+ field_info['json_serialize_string'] = json_serialize_string
+ if django_field_info.verbose_name != django_field_name:
+ # Check if this is a proxy class
+ if type(django_field_info.verbose_name) is str:
+ field_info['verbose-name'] = django_field_info.verbose_name
+ if django_field_info.primary_key == True:
+ field_info['primary_key'] = True
+ if django_field_info.help_text != "":
+ field_info['help_text'] = django_field_info.help_text
+ field_info['null'] = django_field_info.null
+ if type(django_field_info.default) in [int, bool, str]:
+ field_info['default'] = django_field_info.default
+ field_info['type'] = str(type(django_field_info)).split('.')[-1].replace("'>", "")
+ if field_info['type'] == 'AutoField':
+ # Re-label the cassandra compound key for easier consumption
+ if hasattr(django_model_class, 'CassandraSettings'):
+ cassandra_settings = django_model_class.CassandraSettings
+ if hasattr(cassandra_settings, 'COMPOUND_KEY_FIELDS'):
+ compound_key_fields = cassandra_settings.COMPOUND_KEY_FIELDS
+ rest_key_fields = [rest_map[model].get(x, x) for x in compound_key_fields]
+ field_info['compound_key_fields'] = rest_key_fields
+ field_info['type'] = 'compound-key'
+ field_info['help_text'] = '#|%s' % \
+ '|'.join(rest_key_fields)
+ if field_info['type'] == 'ForeignKey':
+ other_object = django_field_info.rel.to.Rest.NAME
+ if django_field_info.rel.field_name in rest_map[other_object]:
+ field_info['rel_field_name'] = \
+ rest_map[other_object][django_field_info.rel.field_name]
+ else:
+ field_info['rel_field_name'] = django_field_info.rel.field_name
+ field_info['rel_obj_type'] = django_field_info.rel.to.Rest.NAME
+ if field_info['type'] == 'CharField':
+ field_info['max_length'] = django_field_info.max_length
+ #if django_field_info.validators:
+ #field_info['validators'] = django_field_info.validators
+ #if isinstance(type, django.PositiveIntegerField):
+ #type_name = 'PositiveIntegerField'
+ #print type_name
+
+ field_info_dict[rest_field_name] = field_info
+ model_info_dict[model]={'fields':field_info_dict, 'has_rest_model': True}
+
+import pprint
+pp = pprint.PrettyPrinter(indent=2)
+print "model_info_dict = ",pp.pprint(model_info_dict)
diff --git a/cli/tools/rebuild_model.sh b/cli/tools/rebuild_model.sh
new file mode 100755
index 0000000..24e7683
--- /dev/null
+++ b/cli/tools/rebuild_model.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+
+if [ ! -d "tools/" ]; then
+ echo "You must execute this script from ./cli"
+ exit -1
+fi
+cd ../sdncon
+rm -f extract_model.py
+cp ../cli/tools/extract_model.py extract_model.py
+python extract_model.py > ../cli/model_info_list.py
+cd ../cli
diff --git a/cli/tools/validate.py b/cli/tools/validate.py
new file mode 100755
index 0000000..e710130
--- /dev/null
+++ b/cli/tools/validate.py
@@ -0,0 +1,664 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2010,2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+#
+
+import subprocess
+import os, atexit
+import sys, traceback # traceback.print_exc()
+from optparse import OptionParser
+from types import StringType
+import datetime
+import json
+import traceback
+import re
+import time
+import urllib2
+import httplib # provides error processing for isinstance
+import socket
+
+from prettyprint import PrettyPrinter
+from storeclient import StoreClient
+from climodelinfo import CliModelInfo
+from vendor import VendorDB
+
+class ParamException(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+TIMESTAMP_HELP = \
+ ('Enter an integer timestamp in milliseconds since the epoch or formatted\n' +
+ 'as one of the following options in your local timezone:\n'
+ '"YYYY-MM-DD HH:MM:SS" YYYY-MM-DDTHH:MM:SS\n' +
+ '"YYYY-MM-DD HH:MM:SS+TTT" YYYY-MM-DDTHH:MM:SS+TTT\n' +
+ 'YYYY-MM-DD MM-DD\n' +
+ 'HH:MM now')
+
+
+class Validate():
+
+ # initialize in init_obj_type_info
+ obj_type_info_dict = {} # see init_obj_type_info_dict
+ obj_types = []
+ obj_keys = {}
+
+ known_controllers = ["127.0.0.1:8000"]
+ controller = None
+ # LOOK! manage this separately until we can for sure talk to eth0 addr instead of 127.0.0.1
+ controller_for_prompt = "127.0.0.1:8000"
+ cluster = "default"
+
+ run = True
+
+ # Cached options for last completion
+ last_line = None
+ last_options = None
+
+ # Are we running in batch mode, i.e. "cat commands | cli.py"
+ batch = False
+
+ # used for parsing as an arg
+ local_name_pattern = "localhost://([A-Za-z0-9_:@\-\.\/]+$)"
+
+ # contained objects
+ pp = None
+ store = None
+ model_handler = None
+
+ #
+ # --------------------------------------------------------------------------------
+ # init_obj_type_info_dict
+ #
+ # Builds the init_obj_type_info_dict, which is a dictionary indexed by
+ # the current mode, which also results in a dictionary, then indexed
+ # by a either a 'field' or 'field_ordering' key.
+ #
+ # When init_obj_type_info_dict is indexed by the table/model name, the
+ # value is a dictionary. That dictionary has several keys:
+ # - 'fields'
+ # - 'field_orderings'
+ #
+ # The 'fields' indexed result returns a dictionary, each of which
+ # is indexed by the names of the fields within the table/model. further
+ # indicies describe details about that field within the table:
+ # - 'formatter' desscribes the prettyPrinter function for that field
+ # - 'primary_key' identifes this field as the key for the table/model.
+ # - 'verbose_name' identfies an additonal name for the field, inteneded
+ # to be more descriptive
+ # - 'has_rest_model' True/False
+ # - 'json_serialize_string' True/False
+ # - 'help_text' String providing more clues about the intent of this field
+ #
+ # The 'field_ordering' dictionary associated with the name of the table/model
+ # is used to order the output.
+ #
+ # Also populates the self.obj_keys[], which is a dictionary mapping the table/model
+ # name to the name of the storage key (table/model's column) for that table.
+ #
+ def init_obj_type_info_dict(self):
+ self.cli_model_info = CliModelInfo()
+ self.obj_type_info_dict = self.cli_model_info.get_complete_obj_type_info_dict()
+ self.obj_types = [k for (k,v) in self.obj_type_info_dict.items()
+ if 'has_rest_model' in v]
+ for (k,d) in self.obj_type_info_dict.items():
+ candidate_keys = [f for f in d['fields'].keys()
+ if d['fields'][f].get('primary_key', False)]
+ if len(candidate_keys) > 0:
+ self.obj_keys[k] = candidate_keys[0]
+
+
+ #
+ # --------------------------------------------------------------------------------
+ # make_unit_formatter
+ #
+ def make_unit_formatter(self, units):
+ return lambda x,y: '%s %s' % (x, units)
+
+ #
+ # --------------------------------------------------------------------------------
+ # init_obj_type_info_dict
+ #
+ # Builds the init_obj_type_info_dict, which is a dictionary indexed by
+ # the current mode, which also results in a dictionary, then indexed
+ # by a either a 'field' or 'field_ordering' key.
+ #
+ # When init_obj_type_info_dict is indexed by the table/model name, the
+ # value is a dictionary. That dictionary has several keys:
+ # - 'fields'
+ # - 'field_orderings'
+ #
+ # The 'fields' indexed result returns a dictionary, each of which
+ # is indexed by the names of the fields within the table/model. further
+ # indicies describe details about that field within the table:
+ # - 'formatter' desscribes the prettyPrinter function for that field
+ # - 'primary_key' identifes this field as the key for the table/model.
+ # - 'verbose_name' identfies an additonal name for the field, inteneded
+ # to be more descriptive
+ # - 'has_rest_model' True/False
+ # - 'json_serialize_string' True/False
+ # - 'help_text' String providing more clues about the intent of this field
+ #
+ # The 'field_ordering' dictionary associated with the name of the table/model
+ # is used to order the output.
+ #
+ # Also populates the self.obj_keys[], which is a dictionary mapping the table/model
+ # name to the name of the storage key (table/model's column) for that table.
+ #
+ def init_obj_type_info_dict(self):
+ self.cli_model_info = CliModelInfo()
+ self.obj_type_info_dict = self.cli_model_info.get_complete_obj_type_info_dict()
+ self.obj_types = [k for (k,v) in self.obj_type_info_dict.items()
+ if 'has_rest_model' in v]
+ for (k,d) in self.obj_type_info_dict.items():
+ candidate_keys = [f for f in d['fields'].keys()
+ if d['fields'][f].get('primary_key', False)]
+ if len(candidate_keys) > 0:
+ self.obj_keys[k] = candidate_keys[0]
+
+ #
+ # --------------------------------------------------------------------------------
+ # init
+ #
+ def init(self):
+
+ self.vendordb = VendorDB()
+ self.vendordb.init()
+
+ parser = OptionParser()
+ parser.add_option("-c", "--controller", dest="controller",
+ help="set default controller to CONTROLLER",
+ metavar="CONTROLLER", default=self.controller)
+ (options, args) = parser.parse_args()
+ self.controller = options.controller
+ if not self.controller:
+ self.controller = "127.0.0.1:8000"
+
+ if not sys.stdin.isatty():
+ self.batch = True
+
+ self.init_obj_type_info_dict()
+
+ self.pp = PrettyPrinter(self.obj_type_info_dict)
+ self.store = StoreClient()
+ self.store.set_controller(self.controller)
+
+
+ #
+ # --------------------------------------------------------------------------------
+ # parse_optional_parameters
+ #
+ def parse_optional_parameters(self, params, words):
+ parsed = {}
+ i = 0
+ while i < len(words):
+ word = words[i]
+ possible = [x for x in params if x.startswith(word)]
+ if len(possible) == 0:
+ raise ParamException('unknown option: %s' % word)
+ elif len(possible) > 1:
+ raise ParamException('ambiguous option: %s\n%s' %
+ (word, "\n".join(possible)))
+ else:
+ param_name = possible[0]
+ param = params[param_name]
+ if (param['type'] == 'flag'):
+ parsed[param_name] = True
+ else:
+ if i+1 < len(words):
+ argument = words[i+1]
+ if (param['type'] == 'string'):
+ parsed[param_name] = argument
+ elif (param['type'] == 'int'):
+ try:
+ parsed[param_name] = int(argument)
+ except ValueError:
+ raise ParamException('option %s requires ' +
+ 'integer argument'
+ % word)
+ elif (param['type'] == 'enum'):
+ arg_possible = [x
+ for x in param['values']
+ if x.startswith(argument)]
+ if (len(arg_possible) == 0):
+ raise ParamException('option %s value must be in (%s)' %
+ (word,", ".join(param['values'])))
+ elif (len(arg_possible) > 1):
+ raise ParamException('ambiguous option %s value:\n%s' %
+ (word, "\n".join(arg_possible)))
+ else:
+ parsed[param_name] = arg_possible[0]
+ i += 1
+ else:
+ raise ParamException('option %s requires an argument'
+ % word)
+ i += 1
+ return parsed
+
+ #
+ # --------------------------------------------------------------------------------
+ # rest_error_to_dict
+ # Turn an exception into an error dictionary, which can later be printed.
+ # using rest_error_dict_to_message().
+ #
+ def rest_error_to_dict(self, e, detail=None):
+ errors = None
+ # try to identifify validation requests
+ if isinstance(e, httplib.BadStatusLine):
+ errors = {'connection_error' : 'REST API server %s: '
+ 'server not running' %
+ self.controller}
+ return errors
+
+ elif isinstance(e, urllib2.HTTPError):
+ code = e.code
+ error_returned = e.readline()
+
+ if code == 404:
+ if detail:
+ errors = {'not_found_error' : 'Not Found: %s' % detail}
+ else:
+ errors = {'not_found_error' : 'Not Found: %s' % error_returned}
+ elif code == 500 or code == 403:
+ errors = {'connection_error' : 'REST API server %s unable to connect: '
+ 'Cassandra possibly not running' %
+ self.controller}
+ elif code == 400:
+ try:
+ errors = json.loads(error_returned)
+ except:
+ # if the error can't be converted into a dictionary, then imbed the complete
+ # errors as the value for a specific error key.
+ errors = {'error_result_error': "Can't convert returned error: %s" % error_returned}
+ pass
+ else:
+ errors = {'unknown_error': 'HttpError %s' % error_returned}
+ else:
+ errors = {'unknown_error': "Need error managmenet for error %s" % type(e)}
+
+ return errors
+
+ #
+ # --------------------------------------------------------------------------------
+ # rest_error_dict_to_message
+ # Turn an rest_error_dict returned from rest_error_to_dict
+ # into an error message which can ge printed. Code assumes multiple errors
+ # won't occur; if a 'field_error' exists, for example, a 'model_error' won't
+ # also be posted in the error
+ #
+ def rest_error_dict_to_message(self, rest_error_dict):
+ error_msg = ""
+ if 'field_errors' in rest_error_dict:
+ for (k,v) in rest_error_dict['field_errors'].items():
+ error_msg += "Syntax error: field %s: %s" % (k,v)
+ # traceback.print_stack(), to find out why the error occured
+ elif 'model_error' in rest_error_dict:
+ error_msg += "Error: %s" % rest_error_dict['model_error']
+ elif 'not_found_error' in rest_error_dict:
+ error_msg += "Error: %s" % rest_error_dict['not_found_error']
+ elif 'connection_error' in rest_error_dict:
+ error_msg += rest_error_dict['connection_error']
+ elif 'error_result_error' in rest_error_dict:
+ error_msg += rest_error_dict['error_result_error']
+ elif 'unknown_error' in rest_error_dict:
+ error_msg += rest_error_dict['unknown_error']
+ else:
+ error_msg = "REST API server on controller-node %s " % self.controller
+ error_msg += "had %s error:\n" % rest_error_dict['error_type']
+ error_msg += rest_error_dict['description']
+ return error_msg
+
+ #
+ # --------------------------------------------------------------------------------
+ # method_from_name
+ #
+ def method_from_name(self, name):
+ return getattr(self, "validate_" + name.replace("-","_"), None)
+
+ #
+ # --------------------------------------------------------------------------------
+ # validate_port
+ #
+ # - validate port foreign key (to switch)
+ #
+ def validate_port(self):
+ error = None
+ print "validate_port"
+
+ # collect known switches
+ switch_ids = None
+ switch_key = self.obj_keys["switch"]
+ try:
+ switch_table = self.store.get_table_from_store("switch")
+ switch_ids = [x[switch_key] for x in switch_table]
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect switch, no switch dpid validation for port table"
+ error = None
+
+ # collect known ports
+ port_table = None
+ port_key = self.obj_keys["port"]
+ try:
+ port_table = self.store.get_table_from_store("port")
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect ports"
+ return
+
+ for port in port_table:
+ if not port_key in port:
+ print "No port id in row"
+ else:
+ port_id = port[port_key]
+ if not 'switch' in port:
+ print 'port %s No switch in port (foreign key)' % port_id
+
+ #
+ # --------------------------------------------------------------------------------
+ # validate_switch
+ #
+ # - validate switch foreign key (to controller)
+ #
+ def validate_switch(self):
+ print "validate_switch"
+ error = None
+
+ # collect known controllers
+ controller_ids = None
+ controller_key = self.obj_keys["controller-node"]
+ try:
+ controller_table = self.store.get_table_from_store("controller-node")
+ controller_ids = [x[contoller_key] for x in controller_table if controller_key in controller_table]
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect controller, no controller validation for switches"
+ error = None
+ if len(controller_ids) == 0:
+ print "Unable to collect any controller ids"
+
+ # collect known ports
+ switch_table = None
+ switch_key = self.obj_keys["switch"]
+ try:
+ switch_table = self.store.get_table_from_store("switch")
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect switches"
+ return
+
+ if len(switch_table) == 0:
+ print "switch table empty"
+
+ for switch in switch_table:
+ if not switch_key in switch:
+ print "No switch id in row"
+ else:
+ switch_id = switch[switch_key]
+ if not 'switch' in switch:
+ print 'switch %s No controller foreign key' % switch_id
+ else:
+ controller = switch['controller']
+ if not controller in controller_ids:
+ print "switch %s missing controller (foreign key) %s " % (switch_id, controller)
+
+ #
+ # --------------------------------------------------------------------------------
+ # validate_host_vns_interface
+ #
+ # - validate host-vns-interface foreigb key (to vns-interface)
+ # - validate host-vns-interface foreigb key (to host)
+ # - crack the id into three fragments, and validate each of the
+ # fragments references the expected componont (ie: matches the foreign key)
+ #
+ def validate_host_vns_interface(self):
+ print "host_vns_interface"
+
+ error = None
+ # collect host's
+ host_ids = None
+ host_key = self.obj_keys["host"]
+ try:
+ host_table = self.store.get_table_from_store("host")
+ host_ids = [x[host_key] for x in host_table]
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect hosts, no host-vns-interface host validation"
+ error = None
+
+ # collect vns-interfaces
+ vns_interface_ids = None
+ vns_interface_key = self.obj_keys["vns-interface"]
+ try:
+ vns_interface_table = self.store.get_table_from_store("vns-interface")
+ vns_interface_ids = [x[vns_interface_key] for x in vns_interface_table]
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect vns-interfaces, no host-vns-interface validation for vns-interfaces"
+ error = None
+
+ # collect host-vns-interface
+ host_vns_interface_ids = None
+ host_vns_interface_key = self.obj_keys["host-vns-interface"]
+ try:
+ host_vns_interface_table = self.store.get_table_from_store("host-vns-interface")
+ host_vns_interface_ids = [x[host_vns_interface_key] for x in host_vns_interface_table]
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect host-vns-interface"
+ return
+
+ if len(host_vns_interface_table) == 0:
+ print "host_vns_interface_table empty"
+
+ host_vns_interface_id = self.obj_keys['host-vns-interface']
+ for host_vns_interface in host_vns_interface_table:
+ if not host_vns_interface_id in host_vns_interface:
+ print "host_vns_interface no primary key"
+ this_host_vns_interface_id = host_vns_interface[host_vns_interface_id]
+ if not 'host' in host_vns_interface:
+ print "host_vns_interface %s no host foreign key" % this_host_vns_interface_id
+ else:
+ host_foreign_key = host_vns_interface['host']
+ if not host_foreign_key in host_ids:
+ print "host_vns_interface %s foreign key %s references missing host" % \
+ (this_host_vns_interface_id, host_foreign_key)
+
+ if not 'interface' in host_vns_interface:
+ print "host_vns_interface %s no vns-interface foreign key %s" % \
+ (this_host_vns_interface_id, foreign_key)
+ else:
+ interface_foreign_key = host_vns_interface['interface']
+ if not interface_foreign_key in vns_interface_ids:
+ print "host_vns_interface %s foreign key %s" % \
+ (this_host_vns_interface, interface_foreign_key)
+
+ parts = this_host_vns_interface_id.split("|")
+ if len(parts) != 3:
+ print "host_vns_interface_id %d needs to be three fields split by '|'"
+ else:
+ if parts[0] != host_foreign_key:
+ print "host_vns_interface %s related host foreign key %s isn't part of id" % \
+ (this_host_vns_interface, host_foreign_key)
+ # the interface_foreign_key should have two parts.
+ interface_parts = interface_foreign_key.split('|')
+ if len(interface_parts) != 2:
+ print "host_vns_interface %s related vns-interface foreign key %s " \
+ "needs to be two words split by '|'" % \
+ (this_host_vns_interface_id, interface_foreign_key)
+ elif interface_parts[0] != parts[1]:
+ print "host_vns_interface %s related vns_interface foreign key %s " \
+ "doesn't match host id part %s" % \
+ (this_host_vns_interface, interface_part[0], parts[1])
+ elif interface_parts[1] != parts[2]:
+ print "host_vns_interface %s related vns_interface foreign key %s " \
+ "doesn't match interface long name part %s" % \
+ (this_host_vns_interface, interface_part[1], parts[2])
+
+ #
+ # --------------------------------------------------------------------------------
+ # validate_vns_interface
+ #
+ def validate_vns_interface(self):
+ print "vns-interface"
+
+ # --------------------------------------------------------------------------------
+ # validate_vns_interface_rule
+ #
+ # - id exists,
+ # - each row has a foreign key
+ # - each foreign key exists
+ # - the id, which is a concatenation of vns name and row id, has the correct foreign key
+ #
+ def validate_vns_interface_rule(self):
+ print "vns_interface_rule"
+
+ error = None
+ # collect known vns's
+ try:
+ vns_table = self.store.get_table_from_store("vns-definition")
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ pass
+ if error:
+ print "Unable to collect vns-definition"
+ return
+
+ # collect known switches
+ switch_ids = None
+ switch_key = self.obj_keys["switch"]
+ try:
+ switch_table = self.store.get_table_from_store("switch")
+ switch_ids = [x[switch_key] for x in switch_table]
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ print self.rest_error_dict_to_message(error)
+ pass
+
+ if error:
+ print "Unable to collect switch, no switch dpid validation for vns rules"
+ error = None
+
+ try:
+ vns_interface_rules_table = self.store.get_table_from_store("vns-interface-rule")
+ except Exception, e:
+ error = self.rest_error_to_dict(e)
+ pass
+ if error:
+ print "Unable to collect vns-interface-rule"
+ return
+ vns_id = self.obj_keys["vns-interface-rule"]
+
+ for rule in vns_interface_rules_table:
+ if not vns_id in rule:
+ print "rule has missing rule id"
+ rule_id = rule[vns_id]
+ parts = rule_id.split("|")
+ if len(parts) < 2:
+ print "rule %s has invalid id" % rule_id
+ vns_part = parts[0]
+ if not 'vns' in rule:
+ print "rule %s has missing vns foreign key" % rule_id
+ else:
+ if rule['vns'] != vns_part:
+ print "rule %s has inconsistent vns foreign key: %s" % (rule_id, rule['vns'])
+ if 'ports' in rule and not 'switch' in rule:
+ print "rule %s has a ports field populated but no switch" % rule_id
+ elif 'switch' in rule and not 'ports' in rule:
+ print "rule %s has a switch field populated but no switch" % rule_id
+ if 'switch' in rule and not rule['switch'] in switch_ids:
+ print "rule %s has an unknown switch dpid %s" % (rule_id, rule['switch'])
+
+
+ #
+ # --------------------------------------------------------------------------------
+ # validate
+ #
+ def validate(self):
+ print "store validation"
+
+ tables = self.obj_type_info_dict.keys()
+ for table in tables:
+ method = self.method_from_name(table)
+ if method:
+ method()
+
+
+#
+# --------------------------------------------------------------------------------
+# Initialization crazyness to make it work across platforms. Many platforms don't include
+# GNU readline (e.g. mac os x) and we need to compensate for this
+
+import sys
+try:
+ import readline
+except ImportError:
+ try:
+ import pyreadline as readline
+ except ImportError:
+ print "Can't find any readline equivalent - aborting."
+else:
+ import rlcompleter
+ if(sys.platform == 'darwin'):
+ # needed for Mac, please fix Apple
+ readline.parse_and_bind ("bind ^I rl_complete")
+ else:
+ readline.parse_and_bind("tab: complete")
+ readline.parse_and_bind("?: possible-completions")
+
+
+#
+# --------------------------------------------------------------------------------
+# Run the shell
+
+def main():
+ # Uncomment the next two lines to enable remote debugging with PyDev
+ #import pydevd
+ #pydevd.settrace()
+ validate = Validate()
+ validate.init()
+ validate.validate()
+
+if __name__ == '__main__':
+ main()