Adding ONOS Segment Routing CLI files to new repo
diff --git a/cli/modi.py b/cli/modi.py
new file mode 100755
index 0000000..4bb156d
--- /dev/null
+++ b/cli/modi.py
@@ -0,0 +1,1212 @@
+#
+# Copyright (c) 2011,2012,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 sys
+# from midw import *
+
+#
+# MODel Information (modi)
+#
+
+class Modi():
+
+ def __init__(self, sdnsh, cli_model_info):
+ self.sdnsh = sdnsh
+
+ self.obj_type_info_dict = {}
+ self.obj_types = []
+ self.obj_keys = {}
+
+ # self.cli_model_info = CliModelInfo()
+ self.cli_model_info = cli_model_info
+
+ self.init_obj_type_info_dict(cli_model_info)
+ self.init_foreign_key_xref()
+ self.init_alias_obj_type_xref()
+
+ @staticmethod
+ def _line():
+ # pylint: disable=W0212
+ f = sys._getframe().f_back
+ return '[%s:%d]' % (f.f_code.co_filename, f.f_lineno)
+ #
+ # --------------------------------------------------------------------------------
+
+ def init_obj_type_info_dict(self, cli_model_info):
+ """
+ When obj_type_info_dict is indexed by the table/model name,
+ the value is a dictionary. That dictionary has several keys:
+
+ - 'cascade_delete' True/False, set to true when foreign key's in this
+ obj_type will be used o identify rows to delete based
+ on rows removed from the parent table.
+ - 'force_delete' True/False, set to true when foreign keys in this
+ obj_type are allowed to be null, but when they are not,
+ the rows identified by cascade_delete must be deleted
+ when rows are removed from the parent table
+ - 'source' set to 'user-config' for obj_types which user's configure
+ set to 'debug-only' to enable viewing object only in debug mode
+
+ 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:
+
+ Some of the key's are intended to be added to the dictionary
+ to further decorate details about the field. The objective is
+ to modify cli behavior, or override the model details. These
+ values, tho, ought to really come from the model description.
+
+ - 'verbose-name' identfies an additonal name for the field, inteneded
+ to be more descriptive
+
+ - 'hex' True/False, True for fields when a hex value can
+ be replaced with a decimal value
+ - 'edit' True/Falue, False when a field cannot be edited within
+ the nested config mode.
+
+ - 'validate' function to call to validate the field value
+
+ Various key's use '_' vs the typical '-' since they're
+ currently constructed via the django model description
+
+ - 'primary_key' identifes this field as the key for the table/model.
+ - 'has_rest_model' True/False
+ - 'json_serialize_string' True/False
+ - 'help_text' String providing more clues about the intent of this field
+ - 'type' various values, intended to be populated by tools/extract_model.py
+ allowing access of the type to the cli. the allows the
+ cli to find foreign_keys, and possibly determine
+ - 'max_length'
+
+ Other key decorations: sorting
+
+ 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.
+
+ @param cli_model_info instantiated class 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():
+ if not 'fields' in d:
+ print '%s: Missing "fields"' % k
+ continue
+ 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]
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def init_foreign_key_xref(self):
+ """
+ Walk through the obj_type_info_dict, looking for foreign keys.
+ Build a cross references, so that a <obj_type, field> can be
+ used to identify all the obj_types which reference that field.
+
+ To allow for both <obj_type, field> to be used to index
+ self.foreign_key_xref, two levels of dictionaries are built.
+ The first maps from obj_type to a dictionary of fields, and
+ the fields dictionary has values which are lists. Each of the
+ lists has two members: the <obj_type, field>, which will be
+ a foreign key to the original pair.
+
+ This allows identificaion of fields which have single foreign
+ key xref's, which can then be used to identify "sub-modes"
+ for particular obj_types.
+ """
+
+ self.foreign_key_xref = {}
+ for (obj_type, obj_type_dict) in self.obj_type_info_dict.items():
+ if not 'fields' in obj_type_dict:
+ print '%s: Missing "fields"' % obj_type
+ continue
+ for (fn, fd) in obj_type_dict['fields'].items():
+ if 'type' in fd:
+ if fd['type'] == 'ForeignKey':
+ ref_foreign_obj_type = fd['rel_obj_type']
+ ref_foreign_field = fd['rel_field_name']
+ if not ref_foreign_obj_type in self.foreign_key_xref:
+ self.foreign_key_xref[ref_foreign_obj_type] = {}
+ if not ref_foreign_field in self.foreign_key_xref[ref_foreign_obj_type]:
+ self.foreign_key_xref[ref_foreign_obj_type][ref_foreign_field] = []
+ self.foreign_key_xref[ref_foreign_obj_type][ref_foreign_field].append(
+ [obj_type, fn])
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def init_alias_obj_type_xref(self):
+ """
+ Alias obj types have a non-compound primary key, a foreign key, and
+ possibly 'DateTimeField' fields, but no other fields. These can
+ be identified by scanning the dictionary.
+
+ The alias_obj_type_xref dictionary is indexed by the obj_type referenced
+ by the foreign key of the original obj_type (its a cross ref)
+ """
+
+ self.alias_obj_type_xref = {}
+ self.alias_obj_types = []
+ for (obj_type, obj_type_dict) in self.obj_type_info_dict.items():
+ if not 'fields' in obj_type_dict:
+ print '%s: Missing "fields"' % obj_type
+ continue
+ foreign_key_obj_type = None
+ foreign_key_count = 0
+ other_types = False
+ for (fn, fd) in obj_type_dict['fields'].items():
+ # 'Idx' field is only for display counting of rows
+ if fn != 'Idx' and 'type' in fd:
+ if fd['type'] == 'ForeignKey':
+ foreign_key_count += 1
+ foreign_key_obj_type = fd['rel_obj_type']
+ elif 'primary_key' in fd:
+ if self.is_compound_key(obj_type, fn):
+ other_types = True
+ elif fd['type'] != 'DateTimeField':
+ other_types = True
+ if foreign_key_count == 1 and other_types == False:
+ self.alias_obj_types.append(obj_type)
+ if not foreign_key_obj_type in self.alias_obj_type_xref:
+ self.alias_obj_type_xref[foreign_key_obj_type] = []
+ self.alias_obj_type_xref[foreign_key_obj_type].append(
+ obj_type
+ )
+ #
+ # if the primariy key is a compound key, and the first item
+ # is also a foreign key here, then allow the first item to
+ # match up with the alias. currenly, only use the first item
+ # since the 'startswith' can use used to find the assocaited
+ # members
+ #
+ elif foreign_key_count >= 1:
+ key = self.obj_keys[obj_type]
+ compound_fields = self.compound_key_fields(obj_type, key)
+ if compound_fields:
+ first_field = compound_fields[0]
+ if first_field in self.obj_type_foreign_keys(obj_type):
+ #
+ # This is nasty -- assuming that the foreign_key's table
+ # name will then have an alias table associated with it.
+ # Perhaps some model field can help describe these
+ # associations, something like 'allow-alias: <obj_type>'
+ if first_field == 'vns':
+ pass
+ if not "%s-alias" % first_field in self.obj_type_info_dict:
+ # only build references to tables which exist.
+ pass
+ elif obj_type in self.alias_obj_type_xref:
+ self.alias_obj_type_xref[obj_type] += ["%s-alias" % first_field]
+ else:
+ self.alias_obj_type_xref[obj_type] = ["%s-alias" % first_field]
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def pk(self, obj_type):
+ """
+ Return the primary key name for the object
+
+ @param obj_type string, name of the object-type (ie: 'host', 'switch')
+ """
+
+ # Raise an exception when the name doesn't exist?
+ return self.obj_keys.get(obj_type, None)
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_exists(self, obj_type):
+ """
+ Return True if there's details about obj_type in obj_types_info_dict
+ """
+ return obj_type in self.obj_type_info_dict.keys()
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_has_model(self, obj_type):
+ """
+ Return True if the obj_type is serviced via the model rest api
+ (in other words, there's a db table which supports this model)
+ """
+ return obj_type in self.obj_types
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_has_url(self, obj_type):
+ """
+ Returns a url suffix describing a path which returns the
+ data associated with thie obj_type
+ """
+ if obj_type in self.obj_type_info_dict:
+ return self.obj_type_info_dict[obj_type].get('url', None)
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_foreign_key(self, obj_type, field):
+ """
+ Return True when the field within the obj_type is a foreign key.
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'].get(field, [])
+ if 'type' in field_info:
+ if field_info['type'] == 'ForeignKey':
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_integer_field(self, obj_type, field):
+ """
+ Return True when the type associated with the obj_type's field is an integer
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type].get('fields', [])[field]
+ if 'type' in field_info:
+ if field_info['type'] == 'IntegerField':
+ return True
+ return False
+
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_primary_key(self, obj_type, field):
+ """
+ Return true when the obj_type's field is a primary key
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type].get('fields',[])[field]
+ if 'primary_key' in field_info:
+ return field_info['primary_key'] # should be true if exists
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_field_editable(self, obj_type, field):
+ """
+ Return True if the field is editable. Default is True.
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type].get('fields',[])[field]
+ if 'edit' in field_info:
+ return field_info['edit'] # should be False if exists
+ return True
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_editable(self, obj_type, field):
+ """
+ Return true if the obj_type/field is available for editing
+ Excludes foreign keys, and primary keys (unless edit: True
+ is specifically enabled for the field)
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ return False
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'edit' in field_info and field_info['edit'] == True:
+ return True
+ if self.is_foreign_key(obj_type, field):
+ return False
+ if self.is_primary_key(obj_type, field):
+ return False
+ if not self.is_field_editable(obj_type, field):
+ return False
+ return True
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_disable_edit(self, obj_type, field):
+ """
+ Mark an obj_type's field as not being directly editable.
+ When the command descriptions are used for all obj_type's edit, the need
+ for 'disabling edit' ought to disappear
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ self.obj_type_info_dict[obj_type]['fields'] = {}
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ self.obj_type_info_dict[obj_type]['fields'][field] = {}
+ self.obj_type_info_dict[obj_type]['fields'][field]['edit'] = False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_marked_searchable(self, obj_type, field):
+ """
+ Return true if a field is searchable.
+
+ This ought to be true for any fields which is part of the
+ primary key construction.
+
+ This predicate, however, in intended to look for fields
+ which are identified by the model as being searchable,
+ even when the field doesn't appear in the primary key.
+
+ @param obj_type
+ @param field
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ return False
+
+ key = self.pk(obj_type)
+ if self.is_compound_key(obj_type, key):
+ if field in self.compound_key_fields(obj_type, key):
+ return True
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'searchable' in field_info:
+ return field_info['searchable'] == True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def get_obj_type_field_case_sensitive(self, obj_type, field):
+ """
+ Return true if a field is case sensitive.
+
+ @param obj_type
+ @param field
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return None
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return None
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ return None
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'case' in field_info:
+ return field_info['case']
+ return None
+
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def set_obj_type_field_case_sensitive(self, obj_type, field, case):
+ """
+ Set case sensitivity for a field in an obj_type
+
+ @param obj_type
+ @param field
+ @param case either 'upper', or 'lower'
+ """
+
+ if case not in ['upper', 'lower']:
+ print 'set_case_sensitive: obj_type %s field %s case %s ' \
+ 'case not upper/lower' % (obj_type, field, case)
+ return
+
+ if not obj_type in self.obj_type_info_dict:
+ self.obj_type_info_dict[obj_type] = {}
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ self.obj_type_info_dict[obj_type]['fields'] = {}
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ self.obj_type_info_dict[obj_type]['fields'][field] = {}
+ self.obj_type_info_dict[obj_type]['fields'][field]['case'] = case
+
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_hex_allowed(self, obj_type, field):
+ """
+ Return true if the obj_type/field allows hex instead of decimal
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'hex' in field_info:
+ return field_info['hex'] # likely False if exists
+ if self.is_integer_field(obj_type, field):
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_field_boolean(self, obj_type, field):
+ """
+ Return true if the obj_type/field is a boolean type
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'type' in field_info:
+ return field_info['type'] == 'BooleanField'
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+ # is_field_string
+ # Return true if the obj_type/field is a character (CharType) type
+ #
+ def is_field_string(self, obj_type, field):
+ """
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'type' in field_info:
+ return field_info['type'] == 'CharField'
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+ # is_null_allowed
+ # Return true if the obj_type/field is allowed to be null.
+ #
+ def is_null_allowed(self, obj_type, field):
+ """
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'null' in field_info:
+ return field_info['null']
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_fields(self, obj_type):
+ """
+ Return a list of field names (strings) for the obj_type,
+ includes all fields, including primary keys and foreign keys
+ """
+
+ if obj_type in self.obj_type_info_dict:
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return []
+ return self.obj_type_info_dict[obj_type]['fields'].keys()
+ return []
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_has_field(self, obj_type, field):
+ """
+ Return true if the obj_type has a field named 'field'
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return False
+
+ if obj_type in self.obj_type_info_dict:
+ fields_info = self.obj_type_info_dict[obj_type]['fields']
+ if field in fields_info:
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_config_fields(self, obj_type):
+ """
+ For an obj_type, return a list of fields which are possibly user configurable.
+
+ @param obj_type string, name of the object-type
+ """
+
+ if obj_type in self.obj_type_info_dict:
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return []
+ fields_info = self.obj_type_info_dict[obj_type]['fields']
+ return [x for x in fields_info if self.is_editable(obj_type, x)]
+ return []
+
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_show_this(self, obj_type):
+ """
+ Return a list of addition types to display for 'show this'
+
+ @param obj_type string, name of the object-type
+ """
+
+ if obj_type in self.obj_type_info_dict:
+ if 'show-this' in self.obj_type_info_dict[obj_type]:
+ result = self.obj_type_info_dict[obj_type]['show-this']
+ if type(result) == str or type(result) == 'unicode':
+ return [result]
+ return result
+ return []
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_cascade_delete_enabled(self, obj_type):
+ """
+ Cascade is enabled for an obj_type by setting 'cascade_enabled': True for
+ the primary key of an obj_type.
+
+ @param obj_type string, name of the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+
+ if 'cascade_delete' in self.obj_type_info_dict[obj_type]:
+ return self.obj_type_info_dict[obj_type]['cascade_delete']
+ return False
+
+ def is_force_delete_enabled(self, obj_type):
+ """
+ Force delete is enabled for an obj_type by setting 'force_delete': True
+ for the primary key of an obj_type.
+
+ @param obj_type string, name of the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return False
+
+ if 'force_delete' in self.obj_type_info_dict[obj_type]:
+ return self.obj_type_info_dict[obj_type]['force_delete']
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def cascade_delete_set_enable(self, obj_type):
+ """
+ Enable cascade_delete for an obj_type
+ """
+ if not obj_type in self.obj_type_info_dict:
+ self.obj_type_info_dict[obj_type] = {}
+ self.obj_type_info_dict[obj_type]['cascade_delete'] = True
+
+ def cascade_delete_enable_force(self, obj_type):
+ """
+ Force cascade_delete for an obj_type
+ """
+ if not obj_type in self.obj_type_info_dict:
+ self.obj_type_info_dict[obj_type] = {}
+ self.obj_type_info_dict[obj_type]['cascade_delete'] = True
+ self.obj_type_info_dict[obj_type]['force_delete'] = True
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def has_display_field(self, obj_type, field):
+ """
+ Determine if a particular obj_type has a particular field on the
+ display (ie: during a show command). Uses the 'field_orderings'
+ of the obj_type_info_dict.
+
+ Currently used to deterine whether an alias table needs to be
+ re-cached in preparation for the display of some obj_type
+
+ @param obj_type string, name of the object-type
+ """
+
+ if obj_type in self.obj_type_info_dict and \
+ 'field_orderings' in obj_type in self.obj_type_info_dict[obj_type]:
+ order = self.obj_type_info_dict[obj_type]['field_orderings']['default']
+ return field in order
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_obj_type_source_not_user_config(self, obj_type):
+ """
+ Return True if the obj_type is intended to be configured,
+ some tables are intended to be written by sdnplatform as a way
+ of presenting information; it makes no sense for the cli
+ to present these tables to the user as configurable.
+
+ keep in mind that this returns True only if 'source' exists,
+ and the source isn't get to user-config. This means that
+ for a table to be excluded, 'source' must be added, and
+ it must be set to something other than 'user-config'
+
+ @param obj_type string, name of the object-type
+ """
+
+ if obj_type in self.obj_type_info_dict and \
+ 'source' in self.obj_type_info_dict[obj_type]:
+ if self.obj_type_info_dict[obj_type]['source'] != 'user-config':
+ if self.sdnsh.debug and \
+ self.obj_type_info_dict[obj_type]['source'] == 'debug-only':
+ return False
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_obj_type_source_debug_only(self, obj_type):
+ """
+ Return True if the obj_type is marked to be viewed only in debug mode
+
+ @param obj_type string, name of the object-type
+ """
+
+ if obj_type in self.obj_type_info_dict and \
+ 'source' in self.obj_type_info_dict[obj_type]:
+ if self.obj_type_info_dict[obj_type]['source'] == 'debug-only':
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_source_set_debug_only(self, obj_type):
+ """
+ Set the source for the obj-type
+ """
+
+ if obj_type in self.obj_type_info_dict:
+ self.obj_type_info_dict[obj_type]['source'] = 'debug-only'
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def compound_key_text(self, obj_type, field):
+ """
+ Return a text string which describes the construction of a compound key.
+ The first character is a '#' when this returned field describes a
+ compound key. The second character is the separator for the field
+ itself (not the separator for the fields described by this text
+ field). The remainder is a concatenation of the fields names which
+ are used to construct this field.
+
+ Currently compound keys are identified through the help_text.
+ The help text isn't displayed to the user for primary since the value
+ of the key for compound key's must be constructed by the cli, and then
+ the user doesn't directly modify or search the compound value.
+
+ When the field is itself a foreign key, and no text string exists to
+ describe the construction of the compound key, its possible that the foreign
+ key's value is itself a compound key. Peek at the original foreign
+ key to see if it has a field identifying the layout of the compound key.
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_type_info_dict:
+ return None
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return None
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ return None
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'help_text' in field_info:
+ if field_info['help_text'][0] == '#':
+ return field_info['help_text']
+ if self.is_foreign_key(obj_type, field):
+ (fk_obj_type, fk_name) = self.foreign_key_references(obj_type, field)
+ field_info = self.obj_type_info_dict[fk_obj_type]['fields'][fk_name]
+ if field_info.get('help_text', ' ')[0] == '#':
+ return field_info['help_text']
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def is_compound_key(self, obj_type, field):
+ """
+ Return true if the obj_type/field is a compound key,
+
+ The first character of the compound key's text '#', the
+ second character identifies the separator characer for the
+ fields value. The fields are separated by '|' within the text.
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ text = self.compound_key_text(obj_type, field)
+ if text:
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+ # is_primitive_compound_key
+ #
+ def is_primitive_compound_key(self, obj_type, field):
+ """
+ Returns True for a primitive compound key.
+
+ Primitive means compound-keys which don't use CassandraSetting's
+ COMPOUND_KEY_FIELDS, rather the help text describes the fields which
+ are cobbled together to create a primary key.
+
+ For the cassandraSetting's COMPOUND_KEY_FIELDS, searches for
+ rows can be done by setting values of the fields of the
+ COMPOUND_KEY_FIELDS, for primitive_compound_keys,
+
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ if self.is_compound_key(obj_type, field):
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'type' in field_info:
+ if field_info['type'] != 'compound-key':
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+ # compound_key_separator
+ #
+ def compound_key_separator(self, obj_type, field):
+ """
+ Return the single character which is used to separate the values
+ of the different field parts for a field which is a compound key.
+
+ Return None when the
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ text = self.compound_key_text(obj_type, field)
+ if text:
+ return text[1]
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+ # compound_key_fields
+ #
+ def compound_key_fields(self, obj_type, field):
+ """
+ Return's a list of strings, where each is intended to be the
+ name of a field within the obj_typ (this is not validated)
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ text = self.compound_key_text(obj_type, field)
+ if text:
+ return text[2:].split('|')
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def deep_compound_key_fields(self, obj_type, field):
+ """
+ Similar to compound_key_fields(), but when any field is also a foreign
+ key, the references obj_type's field is checked, and if that field is
+ also a compound key, it is also expanded.
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+
+ def recurse_compound_key_fields(obj_type, field, parts):
+ if self.is_foreign_key(obj_type, field):
+ (fk_ot, fk_fn) = self.foreign_key_references(obj_type, field)
+ if self.is_compound_key(fk_ot, fk_fn):
+ recurse_compound_key_fields(fk_ot, fk_fn, parts)
+ elif self.is_compound_key(obj_type, field):
+ for cf in self.compound_key_fields(obj_type, field):
+ if (self.obj_type_has_field(obj_type, cf) and
+ self.is_foreign_key(obj_type, cf)):
+
+ (fk_ot, fk_fn) = self.foreign_key_references(obj_type, cf)
+ if self.is_compound_key(fk_ot, fk_fn):
+ recurse_compound_key_fields(fk_ot, fk_fn, parts)
+ else:
+ parts.append(cf)
+ else:
+ parts.append(cf)
+ return parts
+ return recurse_compound_key_fields(obj_type, field, [])
+
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def split_compound_into_dict(self, obj_type, key, target_dict, is_prefix = False):
+ """
+ To be used to convert a compound key in a row intended for display,
+ into separate component name:value pairs.
+
+ The original dict is from a row of a table. the 'key' parameter
+ identifies a compound key in the row, the procedure splits the
+ value of that key/field, and uses the compound_key_fields() to
+ determine what names ought to be associated with the field's
+ values, and add's these fields into the original dict.
+ """
+ def add_to_dict(a, b):
+ if a in target_dict and a != key:
+ if str(target_dict[a]) != b:
+ if self.is_foreign_key(obj_type, a):
+ target_dict[a] = b
+ else:
+ print self.sdnsh.error_msg("compound split dict has different value: "
+ "%s found %s expected %s" %
+ (a, target_dict[a], b))
+ else:
+ target_dict[a] = b
+
+ names = self.deep_compound_key_fields(obj_type, key)
+ separator = self.compound_key_separator(obj_type, key)
+ values = target_dict[key].split(separator)
+
+ if len(names) != len(values):
+ if not is_prefix:
+ print self.sdnsh.error_msg("%s: %s: compound length mismatch: %s %s" %
+ (obj_type, key, names, values))
+ min_len = len(names)
+ if len(values) < min_len:
+ min_len = len(values)
+ map(add_to_dict, names[:min_len], values[:min_len])
+ else:
+ map(add_to_dict, names, values)
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def foreign_key_references(self, obj_type, field):
+ """
+ For a field which is a foreign key, return the pair of
+ [obj_type, field] describing where the foreign key references
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return None
+ if not 'fields' in self.obj_type_info_dict[obj_type]:
+ return None
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ return None
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'type' in field_info:
+ if field_info['type'] == 'ForeignKey':
+ return [field_info['rel_obj_type'],
+ field_info['rel_field_name']]
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_foreign_keys(self, obj_type):
+ """
+ Return a list of foreign keys for this obj_type
+
+ @param field string, field within the object-type
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return []
+ return [x for x in self.obj_type_info_dict[obj_type]['fields']
+ if self.is_foreign_key(obj_type, x)]
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_show_title(self, obj_type):
+ """
+ Return a string, the display title for this table,
+ never return None, only displayable values
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return ''
+ if 'title' in self.obj_type_info_dict[obj_type]:
+ return self.obj_type_info_dict[obj_type]['title']
+ return obj_type
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_set_title(self, obj_type, title):
+ if not obj_type in self.obj_type_info_dict:
+ self.obj_type_info_dict[obj_type] = {}
+ self.obj_type_info_dict[obj_type]['title'] = title
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_set_show_this(self, obj_type, this_list):
+ if not obj_type in self.obj_type_info_dict:
+ self.obj_type_info_dict[obj_type] = {}
+ self.obj_type_info_dict[obj_type]['show-this'] = this_list
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def field_default_value(self, obj_type, field):
+ """
+ Return non null value of the default value of a field if its configured
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return None
+ if not field in self.obj_type_info_dict[obj_type]['fields']:
+ return None
+
+ field_info = self.obj_type_info_dict[obj_type]['fields'][field]
+ if 'default' in field_info:
+ return field_info['default']
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def field_current_obj_type_default_value(self, field):
+ """
+ Return non null value of the default value of a field if its configured
+
+ @param field string, field within the object-type
+ """
+ current_obj_type = self.sdnsh.get_current_mode_obj_type()
+
+ if current_obj_type:
+ if self.obj_type_has_field(current_obj_type, field):
+ default = self.field_default_value(current_obj_type, field)
+ if default == '':
+ return None
+ return default
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def field_validation(self, obj_type, field):
+ """
+ Return the field validation function
+
+ @param obj_type string, name of the object-type
+ @param field string, field within the object-type
+ """
+ if 'validate' in self.obj_type_info_dict[obj_type]['fields'][field]:
+ validate = self.obj_type_info_dict[obj_type]['fields'][field]['validate']
+ return getattr(self, validate, None)
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_prepare_row_update(self, obj_type):
+ """
+ Return the row update function is one is described in climodelinfo
+ These callouts are intended to do fixup for the primary key's
+ in a table where the field members are used to build the primary key
+
+ @param field string, field within the object-type
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return None
+
+ if 'update' in self.obj_type_info_dict[obj_type]:
+ update = self.obj_type_info_dict[obj_type]['update']
+ return getattr(self, update, None)
+ return None
+
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_show_sort(self, obj_type):
+ """
+ Return a sort-type for the obj-type during show. The value is extracted
+ from the obj_type_info_dict, and configured for the primary key for an
+ obj-type, for example vns-access-list-entry's 'id' field shows
+ 'sort' : 'integer' to describe that the table's items
+ are sorted by integer.
+
+ Currently the sort-by field is not described in the
+ cassandra model description
+
+ @param field string, field within the object-type
+ """
+
+ if not obj_type in self.obj_keys:
+ return None
+
+ key = self.obj_keys[obj_type]
+ if 'sort' in self.obj_type_info_dict[obj_type].get('fields', [])[key]:
+ return self.obj_type_info_dict[obj_type]['fields'][key]['sort']
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def alias_obj_type_field(self, alias_obj_type):
+ """
+ Return a single field name for an obj_type, usually a foreign key
+ in the model, which is the field associated with an alias.
+ """
+ foreign_keys = self.obj_type_foreign_keys(alias_obj_type)
+ if len(foreign_keys) == 1:
+ return foreign_keys[0]
+ return None
+
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def not_default_value(self, obj_type, field, value):
+ """
+ Return True when the value passed in is not the default value.
+ """
+ default_value = self.field_default_value(obj_type, field)
+
+ if (self.is_null_allowed(obj_type, field) and value != '') or \
+ (not self.is_null_allowed(obj_type, field) and default_value != None
+ and (default_value != value)):
+ return True
+ return False
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_related_config_obj_type(self, obj_type):
+ """
+ If an obj-type doesn't have a rest model, for example - host, the obj_type
+ may have a related config-table in the database, where additional configured
+ data for the discovered data can be found. Return the name of that table,
+ for host, its host-condig
+ """
+ if not obj_type in self.obj_type_info_dict:
+ return None
+
+ if self.obj_type_has_model(obj_type):
+ return obj_type
+
+ if 'config-obj-type' in self.obj_type_info_dict[obj_type]:
+ return self.obj_type_info_dict[obj_type]['config-obj-type']
+ return None
+
+ #
+ # --------------------------------------------------------------------------------
+
+ def obj_type_in_use_as_related_config_type(self, config_obj_type):
+ """
+ Return the obj_type is in use by the pased in config_obj_type
+ or None otherwise. Inverse of obj_type_related_config_obj_type
+ """
+ if config_obj_type == None:
+ return None
+
+ if not config_obj_type in self.obj_type_info_dict:
+ return None
+
+ for (ot, ov) in self.obj_type_info_dict.items():
+ if ov.get('config-obj-type') == config_obj_type:
+ return ot
+ return None