blob: 9af9dc9685054ad72bb5e74001d1ef04d1159c08 [file] [log] [blame]
Srikanth Vavilapalli1725e492014-12-01 17:50:52 -08001#
2# Copyright (c) 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#
18#
19
20import storeclient
21import json
22import utif
23
24# TODO:
25# don't use the python type of the string, use the
26# schema's type description
27#
28# associate the complex type with the schema so that
29# during leaf generation, the complex schema type
30# can be used to display the value
31#
32
33
34def string_type(value):
35 if type(value) == str or type(value) == unicode:
36 return True
37
38
39def integer_type(value):
40 if type(value) == int or type(value) == long:
41 return True
42
43
44def numeric_type(value):
45 if (integer_type(value) or
46 type(value) == float or type(value) == complex):
47 return True
48
49
50def atomic_type(value):
51 if (string_type(value) or
52 numeric_type(value) or
53 type(value) == bool):
54 return True
55
56
57def path_adder(prefix, nextfix):
58 if prefix == '':
59 return nextfix
60 return '%s/%s' % (prefix, nextfix)
61
62
63class SDNDB():
64
65 # Notes:
66 #
67 # The item order of the dictionary-like structures in the schema
68 # is actually not order-ed. The columns of these items, then
69 # can't be derived from the schema.
70 #
71
72 def __init__(self, modi, sdnsh, pp):
73 self.modi = modi
74 self.sdnsh = sdnsh # access to rest apu, needs help
75 self.pp = pp # access to formats, needs help
76
77 self.known_types = [
78 'INTEGER',
79 'STRING',
80 'BOOLEAN', # True/False
81 'BINARY', # bit array
82 'LEAF',
83 'LEAF_LIST',
84 'LIST',
85 'CONTAINER',
86 'REFERENCE',
87 ]
88
89 self.known_data_sources = [
90 'sdnplatform-module-data-source',
91 'switch-data-source',
92 'controller-data-source',
93 'topology-data-source',
94 'config'
95 ]
96 self.controller = 'localhost'
97 self.sdndb_port = 8082
98 self.schema_request()
99 self.int64max = 2**63 - 1
100 self.int64min = -2**63
101
102
103 def mm(self, v):
104 """
105 Quick converter for those values which are some
106 variation of int_max for a 64 bit java signed integer,
107 and int_min for a 64 bit java signed integer
108 """
109 if v == self.int64max:
110 return '' # '2^63'
111 if v == self.int64min:
112 return '' # '-2^63'
113 return v
114
115
116 # isolate references to outside entities, ie:
117 # sdnsh and pp references need to be collected here
118 # in preparation for better times.
119 def controller(self):
120 return self.sdnsh.controller
121
122
123 def schema_request(self):
124 self.sdndb_port = 8082
125 url = ('http://%s:%s/api/v1/schema/controller' %
126 (self.controller, self.sdndb_port))
127 self.schema = {}
128 try:
129 print url
130 self.schema = self.sdnsh.store.rest_json_request(url)
131 except Exception, e:
132 print 'BIG TROUBLE IN SDNDB', e
133 return
134 print self.schema.keys()
135
136 # for types: /api/v1/module/controller
137
138 self.crack_type(self.schema)
139
140
141 def data_rest_request(self, item):
142 url = ('http://%s:%s/api/v1/data/controller/%s' %
143 (self.controller, self.sdndb_port, item))
144 try:
145 rest_result = self.sdnsh.store.rest_simple_request(url)
146 except Exception, e:
147 print 'URL', url
148 print 'Exception: ', item, e
149 return
150
151 result = json.loads(rest_result)
152 # print result
153 # print self.format_table(result, rest_item)
154 return result
155
156
157 def crack_field(self, model, field, field_desc):
158 print model, field
159 (name, type, base_type_name, base_typedef, module) = \
160 (None, None, None, None, None)
161 (attributes, child_nodes, data_sources, description) = \
162 (None, None, None, None)
163 (key_node_nanmes, validator, defaultValueString) = (None, None, None)
164 (leaf_type, leaf_schema_node, type_schema_node) = (None, None, None)
165 (list_schema_node, mandatory) = (None, None)
166 # three fields seem to identify type:
167 # 'nodeType', 'baseTypeName', 'baseTypedef'
168
169 for (attr, attr_value) in field_desc.items():
170 if attr == 'name':
171 if attr_value != field:
172 print 'Warning: schema %s "name" %s ' \
173 'doesn\'t match field name %s' % \
174 (model, attr_value, field)
175 elif attr == 'nodeType':
176 type = attr_value
177 if type not in self.known_types:
178 print 'Warning: schema: %s:%s unknown type %s' % \
179 (model, field, type)
180 else:
181 print model, field, type
182 elif attr == 'dataSources':
183 data_sources = attr_value
184 for source in data_sources:
185 if source not in self.known_data_sources:
186 print 'Warning: schema: %s:%s unknown data source %s' % \
187 (model, field, source)
188
189 elif attr == 'mandatory':
190 mandatory = attr_value
191 elif attr == 'childNodes':
192 child_nodes = attr_value
193 elif attr == 'leafType':
194 leaf_type = attr_value
195 elif attr == 'typeSchemaNode':
196 type_schema_node = attr_value
197 elif attr == 'keyNodeNames':
198 key_node_names = attr_value
199 elif attr == 'listElementSchemaNode':
200 list_schema_node = attr_value
201 elif attr == 'leafSchemaNode':
202 leaf_schema_node = attr_value
203 elif attr == 'validator':
204 validator = attr_value
205 print model, field, 'VALIDATOR', validator
206 elif attr == 'defaultValueString':
207 defaultValueString = attr_value
208 elif attr == 'baseTypeName':
209 base_type_name = attr_value
210 elif attr == 'baseTypedef':
211 base_typedef = attr_value
212 elif attr == 'attributes':
213 attributes = attr_value
214 elif attr == 'description':
215 description = attr_value
216 elif attr == 'module':
217 module = attr_value
218 else:
219 print 'Warning: schema: %s:%s unknown attribute %s' % \
220 (model, field, attr)
221 print " --", attr, attr_value
222
223
224 def crack_container(self, container):
225 for (model, model_details) in container.items():
226 print 'Model', model, model_details.keys()
227 type = model_details['nodeType']
228 name = model_details['name']
229 module = model_details['module']
230
231
232 if type == 'LIST':
233 child_nodes = model_details['listElementSchemaNode']
234 child_nodes = child_nodes['childNodes']
235 print '-- ', name, type, module, child_nodes.keys()
236
237 for (field, field_value) in child_nodes.items():
238 self.crack_field(name, field, field_value)
239 #print field, field_value.items()
240 elif type == 'CONTAINER':
241 child_nodes = model_details['childNodes']
242
243 for (field, field_value) in child_nodes.items():
244 self.crack_field(name, field, field_value)
245 #print field, field_value.items()
246
247
248 def crack_type(self, item):
249 type = item.get('nodeType')
250 if type == None:
251 return
252
253 if type == 'CONTAINER':
254 # REST API envelope.
255 container = item.get('childNodes')
256 for (envelope_name, envelope_value) in container.items():
257 envelope_type = envelope_value.get('nodeType')
258 print 'ENVELOPE', container.keys(), envelope_type
259 if envelope_type == 'CONTAINER':
260 self.crack_container(envelope_value['childNodes'])
261
262
263 def post_leaf_node_to_row(self, path, schema, results, row_dict, name = None):
264 leaf_type = schema.get('leafType')
265 if name == None:
266 name = schema.get('name')
267 if leaf_type == 'ENUMERATION':
268 type_node = schema.get('typeSchemaNode')
269 print 'LEAF ENUM', type_node, type_node != None
270 enum_result = results
271 if type_node:
272 if type_node.get('leafType'):
273 enum_values = type_node.get('enumerationSpecifications')
274 if enum_values:
275 for name in enum_values:
276 if name['value'] == enum_result:
277 enum_result = name
278 print path, 'LEAF ENUM %s <- %s from %s' % (name, enum_result, results)
279 row_dict[name] = str(enum_result)
280 elif leaf_type == 'UNION':
281 row_dict[name] = str(results)
282 elif atomic_type(results):
283 print path, 'LEAF %s <- %s' % (name, results)
284 row_dict[name] = str(results)
285 else:
286 print path, 'LEAF MORE DETAILS', schema, type(results), results
287
288
289 def schema_to_results(self, path, schema, results, row_dict = None, indices = None):
290 """
291 Generator (iterator) for items in the results, associated with the
292 schema passed in.
293
294 'index' is a list of dictionary of items which are intended to be columns in the
295 table which must appear for every interior table.
296 """
297 node_type = schema.get('nodeType')
298 name = schema.get('name')
299 print path, name, 'TYPE', node_type
300
301 if row_dict == None:
302 row_dict = dict()
303 if indices == None:
304 indices = list()
305
306 if node_type in ['LEAF']:
307 self.post_leaf_node_to_row(path, schema, results, row_dict)
308 elif node_type == 'LIST':
309 row = {} if row_dict == None else dict(row_dict)
310
311 daughter = schema.get('listElementSchemaNode')
312 index = daughter.get('keyNodeNames')
313 # verify index in list_fields
314 list_items = daughter.get('childNodes')
315 print path, 'LIST', name, index, list_items.keys()
316 yield ('LIST-BEGIN', name, path, indices, row)
317 # spath = '%s/%s/%s' % (path, name, index_value)
318 # add_fields(depth+1, list_fields)
319 for (index_value, result) in results.items():
320 print '[]', '%s:%s' % (index, index_value)
321 new_row = dict(row)
322 new_row['|'.join(index)] = index_value
323 new_indices = list(indices) + [{name : index_value}]
324 spath = '%s/%s' % (path_adder(path, name), index_value)
325 for (item_name, item_value) in list_items.items():
326 if item_name in result:
327 for item in self.schema_to_results(spath,
328 item_value, result[item_name],
329 new_row, new_indices):
330 yield item
331 print 'HERE', new_row
332 yield ('LIST-ITEM', name, path, indices, row, new_row)
333 yield ('LIST-END', name, path, indices + [{'|'.join(index) : None}], row)
334 return
335 elif node_type == 'LEAF_LIST':
336 #row = {} if row_dict == None else dict(row_dict)
337 row = {}
338 # verify index in list_fields
339 daughter = schema.get('leafSchemaNode')
340 last_index = indices[-1]
341 if len(last_index.keys()) == 1:
342 parent_name = last_index[last_index.keys()[0]]
343 print path, 'LEAF-LIST', parent_name, indices, daughter.keys(), last_index
344 yield ('LIST-BEGIN', parent_name, path, indices, row)
345 # spath = '%s/%s/%s' % (path, name, index_value)
346 # add_fields(depth+1, list_fields)
347 new_row = dict(row)
348 item_schema = daughter.get('typeSchemaNode')
349 leaf_node_type = item_schema.get('nodeType')
350 if leaf_node_type != 'TYPE':
351 print 'LEAF-LIST without interior TYPE node: %s' % leaf_node_type
352 else:
353 leaf_type = item_schema.get('leafType')
354 print 'XXX', results, name
355 for item in results:
356 new_indices = list(indices) + [{name : item}]
357 self.post_leaf_node_to_row(path, item_schema, item, row, name)
358 yield ('LIST-ITEM', parent_name, path, new_indices, row, new_row)
359
360 new_indices = list(indices) + [{name : None}]
361 print 'XYZ', name, new_indices
362 yield ('LIST-END', parent_name, path, new_indices, row)
363 return
364
365 elif node_type == 'CONTAINER':
366 # should abstract name types be added?
367 child_nodes = schema.get('childNodes')
368 print path, 'CONTAINER', name, child_nodes.keys()
369 yield ('CONTAINER-BEGIN', name, path, indices, row_dict)
370 spath = path_adder(path, name)
371 # add_fields(spath, child_nodes)
372 base_dict = dict(row_dict)
373 for (child_name, child_value) in child_nodes.items():
374 print path, 'CONTAINER PART', child_name, child_name in results
375 if child_name in results:
376 for item in self.schema_to_results(spath, child_value, results[child_name],
377 row_dict, indices):
378 yield item
379 print path, 'CONTAINER DONE', name, row_dict
380 yield ('CONTAINER-END', name, path, indices, base_dict, row_dict)
381 else:
382 print 'TYPE %s NEEDS HELP' % node_type
383 print schema
384 print results
385
386
387 def format_table(self, result, name):
388 #
389 # format a table: paint a table as if generating some
390 # table output format from a hierarchial object.
391 #
392 # columns are the names of any path members down to the
393 # most interior entry.
394 #
395 #
396
397 def add_fields(depth, fields):
398 for (field_name, field_details) in fields.items():
399 # if the column is a list, all the entries underneath need to be added.
400 node_type = field_details.get('nodeType')
401 type_schema = field_details.get('typeSchemaNode')
402 print depth, 'FIELD', field_name, node_type, type_schema
403 if node_type in ['LEAF']:
404 if not field_name in columns:
405 columns.append(field_name)
406 else:
407 print 'XXXX IN THERE', field_name
408 if type_schema and field_name not in column_type:
409 column_type[field_name] = type_schema
410 elif node_type == 'LIST':
411 daughter = field_details.get('listElementSchemaNode')
412 index = daughter.get('keyNodeNames')
413 # verify index in list_fields
414 list_fields = daughter.get('childNodes')
415 print depth, 'LIST', field_name, index, list_fields.keys()
416 add_fields(depth+1, list_fields)
417 elif node_type == 'CONTAINER':
418 # should abstract name types be added?
419 name = field_details.get('name')
420 child_nodes = field_details.get('childNodes')
421 print depth, 'CONTAINER', field_name, child_nodes.keys()
422 add_fields(depth+1, child_nodes)
423
424 schema = self.schemas.get(name)
425 if schema == None:
426 print 'Missing Schema', name
427 print 'Known:', ','.join(self.schemas.keys())
428 return
429 print schema.keys()
430 description = schema.get('description')
431 daughter = schema.get
432 if schema.get('nodeType') == 'LIST':
433 daughter = schema.get('listElementSchemaNode')
434 index = daughter.get('keyNodeNames')
435 fields = daughter.get('childNodes')
436 columns = ['|'.join(index)]
437 elif schema.get('nodeType') == 'CONTAINER':
438 index = schema.get('keyNodeNames')
439 fields = schema.get('childNodes')
440 columns = [] # no index for CONTAINER
441 else:
442 print 'Schema %s, NodeType %s needs root' % (name, schema.get('nodeType'))
443 print name, schema.get('nodeType')
444 if description:
445 print 'Model %s "%s", key: %s, fields: %s' % (name, description, index, fields.keys())
446 else:
447 print 'Model %s, key %s, fields %s' % (name, index, fields.keys())
448 if index:
449 # verify the the index items are
450 found_index = [x for x in index if x in fields]
451 print 'Verified index', found_index
452 column_type = {}
453 add_fields(1, fields)
454 print columns
455 print 'COLUMN TYPE', len(column_type)
456 for (column, schema_type) in column_type.items():
457 print 'COLUMN TYPE', column, schema_type
458
459 # second verse, same as the first.
460 def table_maker(depth, schema, results, a_row):
461
462 node_type = schema.get('nodeType')
463 name = schema.get('name')
464 print depth, name, 'TYPE', node_type
465
466 if node_type in ['LEAF']:
467 leaf_type = schema.get('leafType')
468 if leaf_type == 'ENUMERATION':
469 type_mode = schema.get('typeSchemaNode')
470 print 'LEAF ENUM', type_mode != None
471 enum_result = results
472 if type_node:
473 if type_node.get('leafType'):
474 enum_values = type_node.get('enumerationSpecifications')
475 if enum_values:
476 for name in enum_values:
477 if name['value'] == enum_result:
478 enum_result = name
479 print depth, 'LEAF ENUM %s <- %s from %s' % (name, enum_result, results)
480 a_row[name] = str(enum_result)
481 elif atomic_type(results):
482 print depth, 'LEAF %s <- %s' % (name, results)
483 a_row[name] = str(results)
484 else:
485 print depth, 'LEAF MORE DETAILS', schema, type(results), results
486 elif node_type == 'LIST':
487 row = {} if a_row == None else dict(a_row)
488
489 daughter = schema.get('listElementSchemaNode')
490 index = daughter.get('keyNodeNames')
491 # verify index in list_fields
492 list_items = daughter.get('childNodes')
493 print depth, 'LIST', name, index, list_items.keys()
494 # add_fields(depth+1, list_fields)
495 for (index_value, result) in results.items():
496 print '[]', '%s:%s' % (index, index_value)
497 row['|'.join(index)] = index_value
498 for (item_name, item_value) in list_items.items():
499 if item_name in result:
500 table_maker(depth+1, item_value, result[item_name], row)
501 print 'AROW', row
502 table.append(dict(row))
503 elif node_type == 'CONTAINER':
504 # should abstract name types be added?
505 child_nodes = schema.get('childNodes')
506 print depth, 'CONTAINER', name, child_nodes.keys()
507 # add_fields(depth+1, child_nodes)
508 for (child_name, child_value) in child_nodes.items():
509 print depth, 'CONTAINER PART', child_name
510 if child_name in results:
511 table_maker(depth+1, child_value, results[child_name], a_row)
512
513 table = []
514 cols_width = {}
515
516 if type(result) == list and len(result) == 1:
517 print 'PRUNE LIST'
518 result = result[0]
519 table_maker(1, schema, result, {})
520
521 print '+++++++++++++'
522 print table
523 print '+++++++++++++'
524
525 for column in columns:
526 cols_width[column] = len(column)
527 for row in table:
528 for (item, value) in row.items():
529 if item not in cols_width:
530 cols_width[item] = len(value)
531 elif len(value) > cols_width[item]:
532 cols_width[item] = len(value)
533
534 print 'COLUMNS->', columns
535 print 'COLS_WIDTH->', cols_width
536
537 # column header
538 line = ''
539 for column in columns:
540 if column in cols_width:
541 line += '%-*s ' % (cols_width[column], column)
542 print line
543 print '=' * len(line)
544
545 line = ''
546 for column in columns:
547 type_info = ' ' * cols_width[column]
548 if column in column_type:
549 ct = column_type[column]
550 print column, ct
551 if type(ct) == str or type(ct) == unicode:
552 type_info = '%*s' % (cols_width[column], ct)
553 elif ct.get('leafType'):
554 leaf_type = ct.get('leafType')
555 if type(leaf_type) == unicode or type(leaf_type) == str:
556 type_info = '%*s' % (cols_width[column], leaf_type)
557 else:
558 print 'CT LEAF_TYPE', ct
559 if ct.get('nodeType'):
560 node_type = ct.get('nodeType')
561 if type(node_type) == str or type(node_type) == unicode:
562 if node_type != 'LEAF':
563 type_info = '%*s' % (cols_width[column], node_type)
564 else:
565 type_info = '%*s' % (cols_width[column], node_type['name'])
566 if ct.get('name'):
567 type_info = '%*s' % (cols_width[column], ct['name'])
568
569 line += type_info
570 print line
571
572 line = ''
573 for column in columns:
574 if column in cols_width:
575 line += '%s|' % ('-' * cols_width[column],)
576 print line
577 for row in table:
578 line = ''
579 for column in columns:
580 line += '%*s ' % (cols_width[column], row.get(column, ''))
581 print line
582
583 return table
584
585
586 def name_is_compound_key(self, name):
587 if name.find('|') != -1:
588 return True
589 return False
590
591
592 def table_body_sorter(self, table, sort_columns):
593 def sort_cmp(x,y):
594 for f in sort_columns:
595 if f in x:
596 c = utif.trailing_integer_cmp(x.get(f), y.get(f))
597 if c:
598 return c
599 return 0
600 return sorted(table, cmp=sort_cmp)
601
602
603 def table_columns_width(self, table, columns):
604 """
605 Table is a list of dictionaries.
606
607 Columns is a list of column header names.
608 """
609 cols_width = {}
610 for column in columns:
611 cols_width[column] = len(column)
612 for row in table:
613 for (item, value) in row.items():
614 if item not in cols_width:
615 cols_width[item] = len(value)
616 elif len(value) > cols_width[item]:
617 cols_width[item] = len(value)
618 return cols_width
619
620
621 def table_header(self, cols_width, title = None, columns = None):
622 """
623 Print the table headers.
624 """
625 # column header
626 line = ''
627 for column in columns:
628 if self.name_is_compound_key(column):
629 continue
630 if column in cols_width:
631 line += '%-*s ' % (cols_width[column], column)
632
633 # table title
634 if title:
635 len_dash_left = (len(line) - len(title) - 2)
636 half_left = len_dash_left / 2
637 slop = ''
638 if len_dash_left & 1:
639 slop = ' '
640 yield '=' * half_left + ' %s%s ' % (title, slop) + '=' * half_left
641
642 # finally print the column header
643 if line == '':
644 yield '--cols empty--'
645 else:
646 yield line
647
648 line = ''
649 for column in columns:
650 if self.name_is_compound_key(column):
651 continue
652 if column in cols_width:
653 line += '%s|' % ('-' * cols_width[column],)
654 yield line
655
656
657 def all_columns_except(self, table, except_columns = None):
658 all_columns = []
659 if except_columns == None:
660 except_columns = []
661 # now ensure all columns are represented
662 for row in table:
663 for field in row.keys():
664 if self.name_is_compound_key(field):
665 continue
666 if field not in except_columns and field not in all_columns:
667 all_columns.append(field)
668 return sorted(all_columns)
669
670
671 def table_body(self, table, title = None, columns = None):
672 """
673 The input table is a list of dictionaries. From the
674 name:value pairs, build a simple output formatter.
675
676 """
677
678 if columns == None:
679 columns = []
680 else: # use the columns passed in as a basis for sorting
681 table = self.table_body_sorter(table, columns)
682
683 # now ensure all columns are represented
684 columns += self.all_columns_except(table, columns)
685
686 cols_width = self.table_columns_width(table, columns)
687
688 # waiting for 'yield from'
689 # yield from table_header(cols_width, title, column
690 for item in self.table_header(cols_width, title, columns):
691 yield item
692
693 for row in table:
694 line = ''
695 for column in columns:
696 if not self.name_is_compound_key(column):
697 line += '%-*s ' % (cols_width[column], row.get(column, ''))
698 yield line
699
700 return
701
702
703 def table_title_builder(self, name, indices_list):
704 """
705 Build a title, based on the table name, then
706 added to that are any name:value paris in the
707 indices_list, in order, whose value isn't None
708 (None currently means the index is from the name of
709 a 'CONTAINER', which doesn't require an index)
710 """
711 title = [name]
712 if indices_list:
713 for index_dict in indices_list:
714 # not using a comprehension here to
715 # keep the text width small.
716 for (n,v) in index_dict.items():
717 if v != None:
718 title.append('%s:%s' % (n,v))
719 return ' '.join(title)
720
721
722 def table_index_columns(self, name, indices_list):
723 """
724 The 'index columns' are the columns which have been
725 used as 'keyNodeNames' for each of the 'LIST's. These
726 are handy to move towards the 'left' side of the table
727 """
728 columns = []
729 if indices_list:
730 for index_dict in indices_list:
731 columns += index_dict.keys()
732 return columns
733
734
735 def schema_of_path(self, path):
736 """
737 Return the child tree based on a requested path.
738 """
739 if type(path) == str:
740 path = path.split('/')
741
742 curr = self.schema
743 for element in path:
744 node_type = curr.get('nodeType')
745 if node_type == 'CONTAINER':
746 child_nodes = curr.get('childNodes')
747 next = child_nodes.get(element)
748 else:
749 print 'schema_of_path: need help for ', node_type
750 print 'FIND', node_type, path, curr.keys()
751 next = None
752 if next == None:
753 return None
754 curr = next
755 return curr
756
757
758 def display(self, path, style = 'table'):
759 schema = self.schema_of_path(path)
760 if schema == None:
761 print 'Unknown Item', path
762 return
763
764 result = self.data_rest_request(path)
765 if result == None:
766 print 'No result for %s' % path
767 return
768
769 # print result
770 # print self.format_table(result, rest_item)
771
772 print 'SCHEMA-2-RESULT', path
773 #print 'SCHEMA-2-RESULT RESULT', result
774
775 tables_names = [] # table names in order.
776 tables = {} # dictionary of tables, indexed by name
777 titles = {} # dictionary of titles, indexed by name
778 columns = {} # dictionary of columns, indexed by name
779
780 # Apply the result to the schema.
781 # 'schema_to_results' is an iterator (generator), which
782 # returns tuples.
783 for row in self.schema_to_results('', schema, result):
784 print '^', row
785 # return tuple:
786 # (action, name, path, indices, row, new_row)
787 # 0 1 2 3 4 5
788 action = row[0]
789 name = row[1]
790 if action == 'LIST-BEGIN':
791 if name not in tables_names:
792 tables_names.append(name)
793 tables[name] = []
794 # ensure table is empty
795 # if name in tables:
796 # tables[name] = []
797 elif action == 'LIST-ITEM':
798 # add the items to the table.
799 table_row = dict(row[5])
800 for index in row[3]:
801 table_row.update(index) # indices
802 if name in tables:
803 tables[name].append(table_row)
804 else:
805 tables[name] = [table_row]
806 elif action == 'LIST-END':
807 # display the result
808 if name in tables:
809 titles[name] = self.table_title_builder(name, row[3])
810 columns[name] = self.table_index_columns(name, row[3])
811 print 'INDEX', name, row[3], columns[name]
812
813 # validation --
814 for table in tables_names:
815 if not table in tables.keys():
816 print 'List of tables doesn''t match tables keys'
817 print tables_names, len(tables_names)
818 print tables.keys(), len(tables.keys())
819
820 separator = None
821 # select style.
822 if style == 'list':
823 prefix = ' '
824 for (table_name, table_details) in tables.items():
825 cols = 79
826 first_columns = columns[table_name]
827 last_columns = self.all_columns_except(table_details,
828 first_columns)
829 if separator != None:
830 print separator
831 for row in table_details:
832 row_lines = 0
833 line = table_name + ' '
834 for item_name in first_columns + last_columns:
835 item_value = row.get(item_name)
836 if item_value == None:
837 continue
838 next_item = '%s: %s ' % (item_name, item_value)
839 if len(line) + len(next_item) > cols:
840 print line
841 line = prefix
842 row_lines += 1
843 line += next_item
844 if line != prefix:
845 print line
846 if row_lines:
847 print ''
848 separator = ''
849
850 elif style == 'table':
851 # now print the tables.
852 for table_name in tables_names:
853 if separator != None:
854 print separator
855 if len(table_name) > 1:
856 title = titles[table_name]
857
858 if len(tables[table_name]) == 0:
859 if len(table_name) > 1:
860 print table_name, 'None.'
861 else:
862 print 'None.'
863 else:
864 title = table_name
865 for item in self.table_body(tables[table_name],
866 title,
867 columns[table_name]):
868 print item
869 separator = ''
870 else:
871 print 'sdndb:display unknown style %s' % style
872
873 def schema_detailer_validators(self, type_schema_node):
874 """
875 Result is a dictionary of validator_name:...
876
877 To display these, use somethng like:
878 ' '.join(['%s:%s' % (n,v) for (n,v) in v_dict]
879 """
880 v_dict = {}
881
882 for validator in type_schema_node.get('typeValidator', []):
883 kind = validator.get('type')
884 if kind == 'RANGE_VALIDATOR':
885 kind = 'range'
886 elif kind == 'LENGTH_VALIDATOR':
887 kind = 'length'
888 elif kind == 'ENUMERATION_VALIDATOR':
889 kind = 'enum'
890 elif kind == 'PATTERN_VALIDATOR':
891 kind = 'pattern'
892 else:
893 print 'Validator Kind unknown:', kind
894 continue
895
896 if not kind in v_dict:
897 v_dict[kind] = []
898
899 if kind == 'range' or kind == 'length':
900 for range in validator.get('ranges', []):
901 v_dict[kind].append('(%s:%s)' %
902 (self.mm(range['start']),
903 self.mm(range['end'])))
904 elif kind == 'pattern':
905 v_dict[kind].append(validator.get('pattern'))
906 elif kind == 'enum':
907 name_dict = validator.get('names')
908 v_dict[kind].append(','.join(['[%s:%s]' % (n,v) for (n,v) in name_dict.items()]))
909 return v_dict
910
911
912 def schema_detailer(self, schema, depth = None):
913 if depth == None:
914 depth = 0
915 indent = ' ' * depth
916
917 name = schema.get('name')
918 node_type = schema.get('nodeType')
919 if node_type == 'LEAF':
920 if self.sdnsh.description:
921 print indent, 'LEAF', schema
922 leaf_type = schema.get('leafType')
923 mandatory = schema.get('mandatory')
924 type_schema_node = schema.get('typeSchemaNode', {})
925 v_dict = self.schema_detailer_validators(type_schema_node)
926 yield ('%s%s LEAF type: %s mandatory: %s %s' %
927 (indent, name, leaf_type, mandatory,
928 ' '.join(["%s:%s" % (n,','.join(v)) for (n,v) in v_dict.items()])))
929
930 if leaf_type == 'UNION':
931 nodes = type_schema_node.get('typeSchemaNodes')
932 for node in nodes:
933 v_dict = self.schema_detailer_validators(node)
934 yield (' %s%s TYPE %s' % (indent, node.get('name'),
935 ' '.join(["%s:%s" % (n,','.join(v)) for (n,v) in v_dict.items()])))
936
937 elif node_type == 'LEAF_LIST':
938 leaf_node = schema.get('leafSchemaNode')
939 mandatory = schema.get('mandatory')
940 base_type = leaf_node.get('leafType')
941 type_schema_node = leaf_node.get('typeSchemaNode', {})
942 v_dict = self.schema_detailer_validators(type_schema_node)
943
944 yield ('%s%s: LEAF-LIST mandatory %s LIST of %s %s' %
945 (indent, name, mandatory, base_type,
946 ' '.join(["%s:%s" % (n,','.join(v)) for (n,v) in v_dict.items()])))
947 elif node_type == 'LIST':
948 node = schema.get('listElementSchemaNode')
949 elements_key = ''
950 if node:
951 key = node.get('keyNodeNames')
952 if key:
953 elements_key = ' of %s' % ', '.join(key)
954
955 child_nodes = node.get('childNodes', [])
956 yield '%s%s: LIST %s ITEMS <%s>' % (indent, name, elements_key,
957 ', '.join(child_nodes))
958 for (child, value) in child_nodes.items():
959 for item in self.schema_detailer(value, depth + 1):
960 yield item
961 elif node_type == 'CONTAINER':
962 child_nodes = schema.get('childNodes', [])
963 yield '%s%s: CONTAINER ITEMS <%s>' % (indent, name,
964 ', '.join(child_nodes.keys()))
965 for (child, value) in child_nodes.items():
966 for item in self.schema_detailer(value, depth + 1):
967 yield item
968 else:
969 print 'unknown type', node_type
970
971
972 def schema_detail(self, path):
973 print 'schema_detail:', path
974 schema = self.schema_of_path(path)
975 for item in self.schema_detailer(schema):
976 yield item
977
978 return