blob: 7ea14fb69b8e36366188c3067609946056b5ef3c [file] [log] [blame]
#
# 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
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():
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():
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