Adding ONOS Segment Routing CLI files to new repo
diff --git a/cli/sdndb.py b/cli/sdndb.py
new file mode 100755
index 0000000..9af9dc9
--- /dev/null
+++ b/cli/sdndb.py
@@ -0,0 +1,978 @@
+#
+# 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 storeclient
+import json
+import utif
+
+# TODO:
+# don't use the python type of the string, use the 
+#  schema's type description
+#
+# associate the complex type with the schema so that
+#  during leaf generation, the complex schema type
+#  can be used to display the value
+#
+
+
+def string_type(value):
+    if type(value) == str or type(value) == unicode:
+        return True
+
+
+def integer_type(value):
+    if type(value) == int or type(value) == long:
+        return True
+
+
+def numeric_type(value):
+    if (integer_type(value) or 
+      type(value) == float or type(value) == complex):
+        return True
+
+
+def atomic_type(value):
+    if (string_type(value) or
+     numeric_type(value) or
+     type(value) == bool):
+        return True
+
+
+def path_adder(prefix, nextfix):
+    if prefix == '':
+        return nextfix
+    return '%s/%s' % (prefix, nextfix)
+
+
+class SDNDB():
+    
+    # Notes:
+    #
+    # The item order of the dictionary-like structures in the schema
+    # is actually not order-ed.  The columns of these items, then
+    # can't be derived from the schema.
+    #
+
+    def __init__(self, modi, sdnsh, pp):
+        self.modi = modi
+        self.sdnsh = sdnsh              # access to rest apu, needs help
+        self.pp = pp                    # access to formats, needs help
+
+        self.known_types = [
+                            'INTEGER',
+                            'STRING',
+                            'BOOLEAN',     # True/False
+                            'BINARY',      # bit array
+                            'LEAF',
+                            'LEAF_LIST',
+                            'LIST',
+                            'CONTAINER',
+                            'REFERENCE',
+                           ]
+
+        self.known_data_sources = [
+                                    'sdnplatform-module-data-source',
+                                    'switch-data-source',
+                                    'controller-data-source',
+                                    'topology-data-source',
+                                    'config'
+                                  ]
+        self.controller = 'localhost'
+        self.sdndb_port = 8082
+        self.schema_request()
+        self.int64max = 2**63 - 1
+        self.int64min = -2**63
+
+
+    def mm(self, v):
+        """
+        Quick converter for those values which are some
+        variation of int_max for a 64 bit java signed integer,
+        and int_min for a 64 bit java signed integer
+        """
+        if v == self.int64max:
+            return '' # '2^63'
+        if v == self.int64min:
+            return '' # '-2^63'
+        return v
+
+
+    # isolate references to outside entities, ie:
+    # sdnsh and pp references need to be collected here
+    # in preparation for better times.
+    def controller(self):
+        return self.sdnsh.controller
+
+
+    def schema_request(self):
+        self.sdndb_port = 8082
+        url = ('http://%s:%s/api/v1/schema/controller' %
+                (self.controller, self.sdndb_port))
+        self.schema = {}
+        try:
+            print url
+            self.schema = self.sdnsh.store.rest_json_request(url)
+        except Exception, e:
+            print 'BIG TROUBLE IN SDNDB', e
+            return
+        print self.schema.keys()
+
+        # for types: /api/v1/module/controller
+
+        self.crack_type(self.schema)
+
+
+    def data_rest_request(self, item):
+        url = ('http://%s:%s/api/v1/data/controller/%s' %
+               (self.controller, self.sdndb_port, item))
+        try:
+            rest_result = self.sdnsh.store.rest_simple_request(url)
+        except Exception, e:
+            print 'URL', url
+            print 'Exception: ', item, e
+            return
+
+        result = json.loads(rest_result)
+        # print result
+        # print self.format_table(result, rest_item)
+        return result
+
+
+    def crack_field(self, model, field, field_desc):
+        print model, field
+        (name, type, base_type_name, base_typedef, module) = \
+            (None, None, None, None, None)
+        (attributes, child_nodes, data_sources, description) = \
+            (None, None, None, None)
+        (key_node_nanmes, validator, defaultValueString) = (None, None, None)
+        (leaf_type, leaf_schema_node, type_schema_node) = (None, None, None)
+        (list_schema_node, mandatory) = (None, None)
+        # three fields seem to identify type:
+        # 'nodeType', 'baseTypeName', 'baseTypedef'
+
+        for (attr, attr_value) in field_desc.items():   
+            if attr == 'name':
+                if attr_value != field:
+                    print 'Warning: schema %s "name" %s ' \
+                            'doesn\'t match field name %s' % \
+                            (model, attr_value, field)
+            elif attr == 'nodeType':
+                type = attr_value
+                if type not in self.known_types:
+                    print 'Warning: schema: %s:%s unknown type %s' % \
+                            (model, field, type)
+                else:
+                    print model, field, type
+            elif attr == 'dataSources':
+                data_sources = attr_value
+                for source in data_sources:
+                    if source not in self.known_data_sources:
+                        print 'Warning: schema: %s:%s unknown data source %s' % \
+                                (model, field, source)
+
+            elif attr == 'mandatory':
+                mandatory = attr_value
+            elif attr == 'childNodes':
+                child_nodes = attr_value
+            elif attr == 'leafType':
+                leaf_type = attr_value
+            elif attr == 'typeSchemaNode':
+                type_schema_node = attr_value
+            elif attr == 'keyNodeNames':
+                key_node_names = attr_value
+            elif attr == 'listElementSchemaNode':
+                list_schema_node = attr_value
+            elif attr == 'leafSchemaNode':
+                leaf_schema_node = attr_value
+            elif attr == 'validator':
+                validator = attr_value
+                print model, field, 'VALIDATOR', validator
+            elif attr == 'defaultValueString':
+                defaultValueString = attr_value
+            elif attr == 'baseTypeName':
+                base_type_name = attr_value
+            elif attr == 'baseTypedef':
+                base_typedef = attr_value
+            elif attr == 'attributes':
+                attributes = attr_value
+            elif attr == 'description':
+                description = attr_value
+            elif attr == 'module':
+                module = attr_value
+            else:
+                print 'Warning: schema: %s:%s unknown attribute %s' % \
+                        (model, field, attr)
+                print "   --", attr, attr_value
+        
+
+    def crack_container(self, container):
+        for (model, model_details) in container.items():
+            print 'Model', model, model_details.keys()
+            type        = model_details['nodeType']
+            name        = model_details['name']
+            module      = model_details['module']
+
+
+            if type == 'LIST':
+                child_nodes = model_details['listElementSchemaNode']
+                child_nodes = child_nodes['childNodes']
+                print '-- ', name, type, module, child_nodes.keys()
+
+                for (field, field_value) in child_nodes.items():
+                    self.crack_field(name, field, field_value)
+                    #print field, field_value.items()
+            elif type == 'CONTAINER':
+                child_nodes = model_details['childNodes']
+
+                for (field, field_value) in child_nodes.items():
+                    self.crack_field(name, field, field_value)
+                    #print field, field_value.items()
+
+
+    def crack_type(self, item):
+        type = item.get('nodeType')
+        if type == None:
+            return
+
+        if type == 'CONTAINER':
+            # REST API envelope.
+            container = item.get('childNodes')
+            for (envelope_name, envelope_value) in container.items():
+                envelope_type = envelope_value.get('nodeType')
+                print 'ENVELOPE', container.keys(), envelope_type
+                if envelope_type == 'CONTAINER':
+                    self.crack_container(envelope_value['childNodes'])
+
+
+    def post_leaf_node_to_row(self, path, schema, results, row_dict, name = None):
+        leaf_type = schema.get('leafType')
+        if name == None:
+            name = schema.get('name')
+        if leaf_type == 'ENUMERATION':
+            type_node = schema.get('typeSchemaNode')
+            print 'LEAF ENUM', type_node, type_node != None
+            enum_result = results
+            if type_node:
+                if type_node.get('leafType'):
+                    enum_values = type_node.get('enumerationSpecifications')
+                    if enum_values:
+                        for name in enum_values:
+                            if name['value'] == enum_result:
+                                enum_result = name
+            print path, 'LEAF ENUM %s <- %s from %s' % (name, enum_result, results)
+            row_dict[name] = str(enum_result)
+        elif leaf_type == 'UNION':
+            row_dict[name] = str(results)
+        elif atomic_type(results):
+            print path, 'LEAF %s <- %s' % (name, results)
+            row_dict[name] = str(results)
+        else:
+            print path, 'LEAF MORE DETAILS', schema, type(results), results
+
+
+    def schema_to_results(self, path, schema, results, row_dict = None, indices = None):
+        """
+        Generator (iterator) for items in the results, associated with the
+        schema passed in.
+
+        'index' is a list of dictionary of items which are intended to be columns in the
+        table which must appear for every interior table.
+        """
+        node_type = schema.get('nodeType')
+        name = schema.get('name')
+        print path, name, 'TYPE', node_type
+
+        if row_dict == None:
+            row_dict = dict()
+        if indices == None:
+            indices = list()
+
+        if node_type in ['LEAF']:
+            self.post_leaf_node_to_row(path, schema, results, row_dict)
+        elif node_type == 'LIST':
+            row = {} if row_dict == None else dict(row_dict)
+
+            daughter = schema.get('listElementSchemaNode')
+            index = daughter.get('keyNodeNames')
+            # verify index in list_fields
+            list_items = daughter.get('childNodes')
+            print path, 'LIST', name, index, list_items.keys()
+            yield ('LIST-BEGIN', name, path, indices, row)
+            # spath = '%s/%s/%s' % (path, name, index_value)
+            # add_fields(depth+1, list_fields)
+            for (index_value, result) in results.items():
+                print '[]', '%s:%s' % (index, index_value)
+                new_row = dict(row)
+                new_row['|'.join(index)] = index_value
+                new_indices = list(indices) + [{name : index_value}]
+                spath = '%s/%s' % (path_adder(path, name), index_value)
+                for (item_name, item_value) in list_items.items():
+                    if item_name in result:
+                        for item in self.schema_to_results(spath,
+                                                           item_value, result[item_name],
+                                                           new_row, new_indices):
+                            yield item
+                print 'HERE', new_row
+                yield ('LIST-ITEM', name, path, indices, row, new_row)
+            yield ('LIST-END', name, path, indices + [{'|'.join(index) : None}], row)
+            return
+        elif node_type == 'LEAF_LIST':
+            #row = {} if row_dict == None else dict(row_dict)
+            row = {}
+            # verify index in list_fields
+            daughter = schema.get('leafSchemaNode')
+            last_index = indices[-1]
+            if len(last_index.keys()) == 1:
+                parent_name = last_index[last_index.keys()[0]]
+            print path, 'LEAF-LIST', parent_name, indices, daughter.keys(), last_index
+            yield ('LIST-BEGIN', parent_name, path, indices, row)
+            # spath = '%s/%s/%s' % (path, name, index_value)
+            # add_fields(depth+1, list_fields)
+            new_row = dict(row)
+            item_schema = daughter.get('typeSchemaNode')
+            leaf_node_type = item_schema.get('nodeType')
+            if leaf_node_type != 'TYPE':
+                print 'LEAF-LIST without interior TYPE node: %s' % leaf_node_type
+            else:
+                leaf_type = item_schema.get('leafType')
+                print 'XXX', results, name
+                for item in results:
+                    new_indices = list(indices) + [{name : item}]
+                    self.post_leaf_node_to_row(path, item_schema, item, row, name)
+                    yield ('LIST-ITEM', parent_name, path, new_indices, row, new_row)
+
+            new_indices = list(indices) + [{name : None}]
+            print 'XYZ', name, new_indices
+            yield ('LIST-END', parent_name, path, new_indices, row)
+            return
+            
+        elif node_type == 'CONTAINER':
+            # should abstract name types be added?
+            child_nodes = schema.get('childNodes')
+            print path, 'CONTAINER', name, child_nodes.keys()
+            yield ('CONTAINER-BEGIN', name, path, indices, row_dict)
+            spath = path_adder(path, name)
+            # add_fields(spath, child_nodes)
+            base_dict = dict(row_dict)
+            for (child_name, child_value) in child_nodes.items():
+                print path, 'CONTAINER PART', child_name, child_name in results
+                if child_name in results:
+                    for item in self.schema_to_results(spath, child_value, results[child_name],
+                                                       row_dict, indices):
+                        yield item
+            print path, 'CONTAINER DONE', name, row_dict
+            yield ('CONTAINER-END', name, path, indices, base_dict, row_dict)
+        else:
+            print 'TYPE %s NEEDS HELP' % node_type
+            print schema
+            print results
+
+
+    def format_table(self, result, name):
+        #
+        # format a table: paint a table as if generating some
+        # table output format from a hierarchial object.
+        # 
+        # columns are the names of any path members down to the
+        # most interior entry.
+        # 
+        #
+
+        def add_fields(depth, fields):
+            for (field_name, field_details) in fields.items():
+                # if the column is a list, all the entries underneath need to be added.
+                node_type = field_details.get('nodeType')
+                type_schema = field_details.get('typeSchemaNode')
+                print depth, 'FIELD', field_name, node_type, type_schema
+                if node_type in ['LEAF']:
+                    if not field_name in columns:
+                        columns.append(field_name)
+                    else:
+                        print 'XXXX IN THERE', field_name
+                    if type_schema and field_name not in column_type:
+                        column_type[field_name] = type_schema
+                elif node_type == 'LIST':
+                    daughter = field_details.get('listElementSchemaNode')
+                    index = daughter.get('keyNodeNames')
+                    # verify index in list_fields
+                    list_fields = daughter.get('childNodes')
+                    print depth, 'LIST', field_name, index, list_fields.keys()
+                    add_fields(depth+1, list_fields)
+                elif node_type == 'CONTAINER':
+                    # should abstract name types be added?
+                    name = field_details.get('name')
+                    child_nodes = field_details.get('childNodes')
+                    print depth, 'CONTAINER', field_name, child_nodes.keys()
+                    add_fields(depth+1, child_nodes)
+
+        schema = self.schemas.get(name)
+        if schema == None:
+            print 'Missing Schema', name
+            print 'Known:', ','.join(self.schemas.keys())
+            return
+        print schema.keys()
+        description = schema.get('description')
+        daughter = schema.get
+        if schema.get('nodeType') == 'LIST':
+            daughter = schema.get('listElementSchemaNode')
+            index = daughter.get('keyNodeNames')
+            fields = daughter.get('childNodes')
+            columns = ['|'.join(index)]
+        elif schema.get('nodeType') == 'CONTAINER':
+            index = schema.get('keyNodeNames')
+            fields = schema.get('childNodes')
+            columns = [] # no index for CONTAINER
+        else:
+            print 'Schema %s, NodeType %s needs root' % (name, schema.get('nodeType'))
+        print name, schema.get('nodeType')
+        if description:
+            print 'Model %s "%s", key: %s, fields: %s' % (name, description, index, fields.keys())
+        else:
+            print 'Model %s, key %s, fields %s' % (name, index, fields.keys())
+        if index:
+            # verify the the index items are 
+            found_index = [x for x in index if x in fields]
+            print 'Verified index', found_index
+        column_type = {}
+        add_fields(1, fields)
+        print columns
+        print 'COLUMN TYPE', len(column_type)
+        for (column, schema_type) in column_type.items():
+            print 'COLUMN TYPE', column, schema_type
+
+        # second verse, same as the first.
+        def table_maker(depth, schema, results, a_row):
+
+            node_type = schema.get('nodeType')
+            name = schema.get('name')
+            print depth, name, 'TYPE', node_type
+
+            if node_type in ['LEAF']:
+                leaf_type = schema.get('leafType')
+                if leaf_type == 'ENUMERATION':
+                    type_mode = schema.get('typeSchemaNode')
+                    print 'LEAF ENUM', type_mode != None
+                    enum_result = results
+                    if type_node:
+                        if type_node.get('leafType'):
+                            enum_values = type_node.get('enumerationSpecifications')
+                            if enum_values:
+                                for name in enum_values:
+                                    if name['value'] == enum_result:
+                                        enum_result = name
+                    print depth, 'LEAF ENUM %s <- %s from %s' % (name, enum_result, results)
+                    a_row[name] = str(enum_result)
+                elif atomic_type(results):
+                    print depth, 'LEAF %s <- %s' % (name, results)
+                    a_row[name] = str(results)
+                else:
+                    print depth, 'LEAF MORE DETAILS', schema, type(results), results
+            elif node_type == 'LIST':
+                row = {} if a_row == None else dict(a_row)
+
+                daughter = schema.get('listElementSchemaNode')
+                index = daughter.get('keyNodeNames')
+                # verify index in list_fields
+                list_items = daughter.get('childNodes')
+                print depth, 'LIST', name, index, list_items.keys()
+                # add_fields(depth+1, list_fields)
+                for (index_value, result) in results.items():
+                    print '[]', '%s:%s' % (index, index_value)
+                    row['|'.join(index)] = index_value
+                    for (item_name, item_value) in list_items.items():
+                        if item_name in result:
+                            table_maker(depth+1, item_value, result[item_name], row)
+                    print 'AROW', row
+                    table.append(dict(row))
+            elif node_type == 'CONTAINER':
+                # should abstract name types be added?
+                child_nodes = schema.get('childNodes')
+                print depth, 'CONTAINER', name, child_nodes.keys()
+                # add_fields(depth+1, child_nodes)
+                for (child_name, child_value) in child_nodes.items():
+                    print depth, 'CONTAINER PART', child_name
+                    if child_name in results:
+                        table_maker(depth+1, child_value, results[child_name], a_row)
+
+        table = []
+        cols_width = {}
+
+        if type(result) == list and len(result) == 1:
+            print 'PRUNE LIST'
+            result = result[0]
+        table_maker(1, schema, result, {})
+
+        print '+++++++++++++'
+        print table
+        print '+++++++++++++'
+
+        for column in columns:
+            cols_width[column] = len(column)
+        for row in table:
+            for (item, value) in row.items():
+                if item not in cols_width:
+                    cols_width[item] = len(value)
+                elif len(value) > cols_width[item]:
+                    cols_width[item] = len(value)
+        
+        print 'COLUMNS->', columns
+        print 'COLS_WIDTH->', cols_width
+
+        # column header
+        line = ''
+        for column in columns:
+            if column in cols_width:
+                line += '%-*s ' % (cols_width[column], column)
+        print line
+        print '=' * len(line)
+
+        line = ''
+        for column in columns:
+            type_info = ' ' * cols_width[column]
+            if column in column_type:
+                ct = column_type[column]
+                print column, ct
+                if type(ct) == str or type(ct) == unicode:
+                    type_info = '%*s' % (cols_width[column], ct)
+                elif ct.get('leafType'):
+                    leaf_type = ct.get('leafType')
+                    if type(leaf_type) == unicode or type(leaf_type) == str:
+                        type_info = '%*s' % (cols_width[column], leaf_type)
+                    else:
+                        print 'CT LEAF_TYPE', ct
+                if ct.get('nodeType'):
+                    node_type = ct.get('nodeType')
+                    if type(node_type) == str or type(node_type) == unicode:
+                        if node_type != 'LEAF':
+                            type_info = '%*s' % (cols_width[column], node_type)
+                    else:
+                        type_info = '%*s' % (cols_width[column], node_type['name'])
+                if ct.get('name'):
+                    type_info = '%*s' % (cols_width[column], ct['name'])
+
+            line += type_info
+        print line
+
+        line = ''
+        for column in columns:
+            if column in cols_width:
+                line += '%s|' % ('-' * cols_width[column],)
+        print line
+        for row in table:
+            line = ''
+            for column in columns:
+                line += '%*s ' % (cols_width[column], row.get(column, ''))
+            print line
+
+        return table
+
+
+    def name_is_compound_key(self, name):
+        if name.find('|') != -1:
+            return True
+        return False
+
+
+    def table_body_sorter(self, table, sort_columns):
+        def sort_cmp(x,y):
+            for f in sort_columns:
+                if f in x:
+                    c = utif.trailing_integer_cmp(x.get(f), y.get(f))
+                    if c:
+                        return c
+            return 0
+        return sorted(table, cmp=sort_cmp)
+
+
+    def table_columns_width(self, table, columns):
+        """
+        Table is a list of dictionaries.
+
+        Columns is a list of column header names.
+        """
+        cols_width = {}
+        for column in columns:
+            cols_width[column] = len(column)
+        for row in table:
+            for (item, value) in row.items():
+                if item not in cols_width:
+                    cols_width[item] = len(value)
+                elif len(value) > cols_width[item]:
+                    cols_width[item] = len(value)
+        return cols_width
+
+
+    def table_header(self, cols_width, title = None, columns = None):
+        """
+        Print the table headers.
+        """
+        # column header
+        line = ''
+        for column in columns:
+            if self.name_is_compound_key(column):
+                continue
+            if column in cols_width:
+                line += '%-*s ' % (cols_width[column], column)
+
+        # table title
+        if title:
+            len_dash_left = (len(line) - len(title) - 2)
+            half_left = len_dash_left / 2
+            slop = ''
+            if len_dash_left & 1:
+                slop = ' '
+            yield  '=' * half_left + ' %s%s ' % (title, slop) + '=' * half_left
+
+        # finally print the column header
+        if line == '':
+            yield '--cols empty--'
+        else:
+            yield line
+
+        line = ''
+        for column in columns:
+            if self.name_is_compound_key(column):
+                continue
+            if column in cols_width:
+                line += '%s|' % ('-' * cols_width[column],)
+        yield line
+
+
+    def all_columns_except(self, table, except_columns = None):
+        all_columns = []
+        if except_columns == None:
+            except_columns = []
+        # now ensure all columns are represented
+        for row in table:
+            for field in row.keys():
+                if self.name_is_compound_key(field):
+                    continue
+                if field not in except_columns and field not in all_columns:
+                    all_columns.append(field)
+        return sorted(all_columns)
+
+        
+    def table_body(self, table, title = None, columns = None):
+        """
+        The input table is a list of dictionaries.  From the
+        name:value pairs, build a simple output formatter.
+
+        """
+
+        if columns == None:
+            columns = []
+        else: # use the columns passed in as a basis for sorting
+            table = self.table_body_sorter(table, columns)
+
+        # now ensure all columns are represented
+        columns += self.all_columns_except(table, columns)
+
+        cols_width = self.table_columns_width(table, columns)
+
+        # waiting for 'yield from'
+        # yield from table_header(cols_width, title, column
+        for item in self.table_header(cols_width, title, columns):
+            yield item
+
+        for row in table:
+            line = ''
+            for column in columns:
+                if not self.name_is_compound_key(column):
+                    line += '%-*s ' % (cols_width[column], row.get(column, ''))
+            yield line
+
+        return
+
+
+    def table_title_builder(self, name, indices_list):
+        """
+        Build a title, based on the table name, then
+        added to that are any name:value paris in the
+        indices_list, in order, whose value isn't None
+        (None currently means the index is from the name of
+        a 'CONTAINER', which doesn't require an index)
+        """
+        title = [name]
+        if indices_list:
+            for index_dict in indices_list:
+                # not using a comprehension here to 
+                # keep the text width small.
+                for (n,v) in index_dict.items():
+                    if v != None:
+                        title.append('%s:%s' % (n,v))
+        return ' '.join(title)
+
+
+    def table_index_columns(self, name, indices_list):
+        """
+        The 'index columns' are the columns which have been
+        used as 'keyNodeNames' for each of the 'LIST's.  These
+        are handy to move towards the 'left' side of the table
+        """
+        columns = []
+        if indices_list:
+            for index_dict in indices_list:
+                columns += index_dict.keys()
+        return columns
+
+
+    def schema_of_path(self, path):
+        """
+        Return the child tree based on a requested path.
+        """
+        if type(path) == str:
+            path = path.split('/')
+        
+        curr = self.schema
+        for element in path:
+            node_type = curr.get('nodeType')
+            if node_type == 'CONTAINER':
+                child_nodes = curr.get('childNodes')
+                next = child_nodes.get(element)
+            else:
+                print 'schema_of_path: need help for ', node_type
+                print 'FIND', node_type, path, curr.keys()
+                next = None
+            if next == None:
+                return None
+            curr = next
+        return curr
+
+
+    def display(self, path, style = 'table'):
+        schema = self.schema_of_path(path)
+        if schema == None:
+            print 'Unknown Item', path
+            return
+
+        result = self.data_rest_request(path)
+        if result == None:
+            print 'No result for %s' % path
+            return
+
+        # print result
+        # print self.format_table(result, rest_item)
+
+        print 'SCHEMA-2-RESULT', path
+        #print 'SCHEMA-2-RESULT RESULT', result
+
+        tables_names = []   # table names in order.
+        tables = {}         # dictionary of tables, indexed by name
+        titles = {}         # dictionary of titles, indexed by name
+        columns = {}        # dictionary of columns, indexed by name
+
+        # Apply the result to the schema.
+        # 'schema_to_results' is an iterator (generator), which
+        # returns tuples.
+        for row in self.schema_to_results('', schema, result):
+            print '^', row
+            # return tuple:
+            # (action, name, path, indices, row, new_row)
+            #    0      1     2      3       4     5
+            action = row[0]
+            name = row[1]
+            if action == 'LIST-BEGIN':
+                if name not in tables_names:
+                    tables_names.append(name)
+                    tables[name] = []
+                # ensure table is empty
+                # if name in tables:
+                    # tables[name] = []
+            elif action == 'LIST-ITEM':
+                # add the items to the table.
+                table_row = dict(row[5])
+                for index in row[3]:
+                    table_row.update(index) # indices
+                if name in tables:
+                    tables[name].append(table_row)
+                else:
+                    tables[name] = [table_row]
+            elif action == 'LIST-END':
+                # display the result
+                if name in tables:
+                    titles[name] = self.table_title_builder(name, row[3])
+                    columns[name] = self.table_index_columns(name, row[3])
+                    print 'INDEX', name, row[3], columns[name]
+
+        # validation --
+        for table in tables_names:
+            if not table in tables.keys():
+                print 'List of tables doesn''t match tables keys'
+                print tables_names, len(tables_names)
+                print tables.keys(), len(tables.keys())
+
+        separator = None
+        # select style.
+        if style == 'list':
+            prefix = '    '
+            for (table_name, table_details) in tables.items():
+                cols = 79
+                first_columns = columns[table_name]
+                last_columns = self.all_columns_except(table_details,
+                                                       first_columns)
+                if separator != None:
+                    print separator
+                for row in table_details:
+                    row_lines = 0
+                    line = table_name + ' '
+                    for item_name in first_columns + last_columns:
+                        item_value = row.get(item_name)
+                        if item_value == None:
+                            continue
+                        next_item = '%s: %s ' % (item_name, item_value)
+                        if len(line) + len(next_item) > cols:
+                            print line
+                            line = prefix
+                            row_lines += 1
+                        line += next_item
+                    if line != prefix:
+                        print line
+                    if row_lines:
+                        print ''
+                separator = ''
+
+        elif style == 'table':
+            # now print the tables.
+            for table_name in tables_names:
+                if separator != None:
+                    print separator
+                if len(table_name) > 1:
+                    title = titles[table_name]
+
+                if len(tables[table_name]) == 0:
+                    if len(table_name) > 1:
+                        print table_name, 'None.'
+                    else:
+                        print 'None.'
+                else:
+                    title = table_name
+                    for item in self.table_body(tables[table_name],
+                                                title,
+                                                columns[table_name]):
+                        print item
+                separator = ''
+        else:
+            print 'sdndb:display unknown style %s' % style
+
+    def schema_detailer_validators(self, type_schema_node):
+        """
+        Result is a dictionary of validator_name:...
+
+        To display these, use somethng like:
+        ' '.join(['%s:%s' % (n,v) for (n,v) in v_dict]
+        """
+        v_dict = {}
+
+        for validator in type_schema_node.get('typeValidator', []):
+            kind = validator.get('type')
+            if kind == 'RANGE_VALIDATOR':
+                kind = 'range'
+            elif kind == 'LENGTH_VALIDATOR':
+                kind = 'length'
+            elif kind == 'ENUMERATION_VALIDATOR':
+                kind = 'enum'
+            elif kind == 'PATTERN_VALIDATOR':
+                kind = 'pattern'
+            else:
+                print 'Validator Kind unknown:', kind
+                continue
+
+            if not kind in v_dict:
+                v_dict[kind] = []
+
+            if kind == 'range' or kind == 'length':
+                for range in validator.get('ranges', []):
+                    v_dict[kind].append('(%s:%s)' %
+                                        (self.mm(range['start']),
+                                         self.mm(range['end'])))
+            elif kind == 'pattern':
+                v_dict[kind].append(validator.get('pattern'))
+            elif kind == 'enum':
+                name_dict = validator.get('names')
+                v_dict[kind].append(','.join(['[%s:%s]' % (n,v) for (n,v) in name_dict.items()]))
+        return v_dict
+  
+
+    def schema_detailer(self, schema, depth = None):
+        if depth == None:
+            depth = 0
+        indent = '  ' * depth
+
+        name = schema.get('name')
+        node_type = schema.get('nodeType')
+        if node_type == 'LEAF':
+            if self.sdnsh.description:
+                print indent, 'LEAF', schema
+            leaf_type = schema.get('leafType')
+            mandatory = schema.get('mandatory')
+            type_schema_node = schema.get('typeSchemaNode', {})
+            v_dict = self.schema_detailer_validators(type_schema_node)
+            yield ('%s%s LEAF type: %s mandatory: %s %s' %
+                   (indent, name, leaf_type, mandatory,
+                    ' '.join(["%s:%s" % (n,','.join(v)) for (n,v) in v_dict.items()])))
+
+            if leaf_type == 'UNION':
+                nodes = type_schema_node.get('typeSchemaNodes')
+                for node in nodes:
+                    v_dict = self.schema_detailer_validators(node)
+                    yield ('  %s%s TYPE %s' % (indent, node.get('name'),
+                           ' '.join(["%s:%s" % (n,','.join(v)) for (n,v) in v_dict.items()])))
+
+        elif node_type == 'LEAF_LIST':
+            leaf_node = schema.get('leafSchemaNode')
+            mandatory = schema.get('mandatory')
+            base_type = leaf_node.get('leafType')
+            type_schema_node = leaf_node.get('typeSchemaNode', {})
+            v_dict = self.schema_detailer_validators(type_schema_node)
+
+            yield ('%s%s: LEAF-LIST mandatory %s LIST of %s %s' %
+                    (indent, name, mandatory, base_type,
+                     ' '.join(["%s:%s" % (n,','.join(v)) for (n,v) in v_dict.items()])))
+        elif node_type == 'LIST':
+            node = schema.get('listElementSchemaNode')
+            elements_key = ''
+            if node:
+                key = node.get('keyNodeNames')
+                if key:
+                    elements_key = ' of %s' % ', '.join(key)
+
+            child_nodes = node.get('childNodes', [])
+            yield '%s%s: LIST %s ITEMS <%s>' % (indent, name, elements_key,
+                                              ', '.join(child_nodes))
+            for (child, value) in child_nodes.items():
+                for item in self.schema_detailer(value, depth + 1):
+                    yield item
+        elif node_type == 'CONTAINER':
+            child_nodes = schema.get('childNodes', [])
+            yield '%s%s: CONTAINER ITEMS <%s>' % (indent, name,
+                                                  ', '.join(child_nodes.keys()))
+            for (child, value) in child_nodes.items():
+                for item in self.schema_detailer(value, depth + 1):
+                    yield item
+        else:
+            print 'unknown type', node_type
+
+
+    def schema_detail(self, path):
+        print 'schema_detail:', path
+        schema = self.schema_of_path(path)
+        for item in self.schema_detailer(schema):
+            yield item
+        
+        return