def complete_alias_choice(obj_type, field, data, prefix, completions, no_command, other=None, scoped=None): """ Complete selections from an external object (unlreated to this object stack's details), only returning unique keys, either aliases for the obj_type, or primary keys. This ought to be improved, objects_starting_with() in the cli.py, is primarily intended to be use within cli.py """ if not mi.obj_type_exists(obj_type): raise error.CommandDescriptionError("Unknown obj-type: %s" % obj_type) if sdnsh.description: # description debugging print "complete_alias_choice:", obj_type, field, other, data, prefix, scoped if other and no_command == False: parts = other.split("|") obj_type = parts[0] if len(parts) > 1: # what to do with more parts? field = parts[1] if not mi.obj_type_has_field(obj_type, field): raise error.CommandDescriptionError("Unknown field %s for obj-type: %s" % (field, obj_type)) # quote string? alias choices ought to never have special characters result = utif.add_delim(objects_starting_with(obj_type, prefix, field), " ") completions.update(dict([[x, "%s alias selection" % pretty(obj_type)] for x in result]))
def complete_alias_choice(obj_type, field, data, prefix, completions, no_command, other=None, scoped=None): """ Complete selections from an external object (unlreated to this object stack's details), only returning unique keys, either aliases for the obj_type, or primary keys. This ought to be improved, objects_starting_with() in the cli.py, is primarily intended to be use within cli.py """ if not mi.obj_type_exists(obj_type): raise error.CommandDescriptionError("Unknown obj-type: %s" % obj_type) if sdnsh.description: # description debugging print "complete_alias_choice:", obj_type, field, other, data, prefix, scoped if other and no_command == False: parts = other.split('|') obj_type = parts[0] if len(parts) > 1: # what to do with more parts? field = parts[1] if not mi.obj_type_has_field(obj_type, field): raise error.CommandDescriptionError( "Unknown field %s for obj-type: %s" % (field, obj_type)) # quote string? alias choices ought to never have special characters result = utif.add_delim(objects_starting_with(obj_type, prefix, field), ' ') completions.update( dict([[x, "%s alias selection" % pretty(obj_type)] for x in result]))
def collect_object_fields( obj_type, field, data, mode, completions, prefix=None, other=None, parent_field=None, parent_id=None, scoped=None ): """ Returns the list of possible completions for a particular obj-type. """ data = dict(data) if parent_field: data[parent_field] = parent_id if prefix: data[field + "__startswith"] = prefix key = mi.pk(obj_type) if scoped: obj_id = sdnsh.get_current_mode_obj() if sdnsh.current_mode() != mode: # XXX needs to be covered, shouldn't reach in like this for x in sdnsh.mode_stack: if x["mode_name"] == mode: obj_id = x["obj"] obj_d = {key: obj_id} if obj_type in mi.alias_obj_types: # the submode ought to identify the foreign key data[mi.alias_obj_type_field(obj_type)] = obj_id else: mi.split_compound_into_dict(obj_type, key, obj_d, is_prefix=True) for (k, v) in obj_d.items(): if k != key and not k in data: data[k] = v # if this is one of the obj_type's associated with aliases, should # the list of values be back-transformed into alias names? # yes, because if the current value has an inverse alias, the existing # inverse for the type implies that during a previous insert of this # value, it was converted from its alias name to the current name. # # collect the complete collection of aliases, since its likely # more than one back-to-alias conversion will be required, and query # its value before obj_type in the hope that it was recently cached. # alias_obj_type = mi.obj_type_related_config_obj_type(obj_type) if other and other in mi.alias_obj_type_xref: alias_obj_type = mi.alias_obj_type_xref[other][0] elif field != mi.pk(obj_type): if mi.is_foreign_key(obj_type, field): (alias_obj_type, fk_name) = mi.foreign_key_references(obj_type, field) alias_obj_type = mi.obj_type_related_config_obj_type(alias_obj_type) if alias_obj_type in mi.alias_obj_type_xref: alias_obj_type = mi.alias_obj_type_xref[alias_obj_type][0] else: alias_obj_type = None else: if sdnsh.description: # description debugging print "collect_object_fields: no alias for %s " "field %s not pk, and not fk" % (obj_type, field) alias_obj_type = None elif obj_type in mi.alias_obj_type_xref: alias_obj_type = mi.alias_obj_type_xref[obj_type][0] else: alias_obj_type = None alias_dict = {} if alias_obj_type: foreign_field = mi.alias_obj_type_field(alias_obj_type) alias_dict = create_obj_type_dict(alias_obj_type, foreign_field) alias_key = mi.pk(alias_obj_type) # Remove any data fields which have values of None, these are fields # which are getting reset. for reset_fields in [x for x in data.keys() if data[x] == None]: del data[reset_fields] # collect complete obj_type if not mi.obj_type_has_model(obj_type): result = rest_to_model.get_model_from_url(obj_type, data) else: result = sdnsh.rest_query_objects(obj_type, data) check_rest_result(result) if sdnsh.description: # description debugging print "collect_object_fields:", obj_type, field, data, result is_compound = mi.is_compound_key(obj_type, key) d = {} for item in result: if is_compound: mi.split_compound_into_dict(obj_type, key, item) value = item.get(field) # XXX hack to correctly format tag completions if obj_type == "tag" and field == "id": value = "%s.%s=%s" % tuple(value.split("|")) # remember to only add new items if value: if type(value) == list: # Need a mechanism to select values from the list, field's not enough for item in value: if utif.quote_string(str(item)) not in completions: if str(item) in alias_dict: alias_item = alias_dict[str(item)][0][alias_key] if alias_item.startswith(prefix): item = alias_item d[utif.quote_string(str(item))] = None elif utif.quote_string(str(value)) not in completions: if str(value) in alias_dict: alias_value = alias_dict[str(value)][0][alias_key] if alias_value.startswith(prefix): value = alias_value d[utif.quote_string(str(value))] = None # if there's an alias for this object, and a prefix is included, # then the alias'es which match also need to be directly included, # since its not clear whether the prefix applies to the actual # id or the alias. since alias_dict is already the complete # collection of aliases for this obj-type, use it for matching names if alias_obj_type and prefix and prefix != "": alias_pk = mi.pk(alias_obj_type) for (n, v) in alias_dict.items(): # 'n' here is the foreign key reference to this obj-type for item in [x[alias_pk] for x in v if x[alias_pk].startswith(prefix)]: if utif.quote_string(str(item)) not in completions: d[utif.quote_string(str(item))] = None return utif.add_delim(list(d), " ")
def complete_from_another( other, obj_type, field, data, completions, no_command, prefix=None, parent_field=None, parent_id=None, scoped=None, explicit=None, ): """ Completion function used when another obj_type is used to populate values for the current obj_type the 'other' field identifies the obj_type to use to collect choices from, it can consist of two parts other|field. When field isn't described here, it comes from the description parameter, however, the 'field' value there may be in use to describe the value of the associated action. """ if sdnsh.description: # description debugging print "complete_from_another:", other, field, data, parent_field, parent_id, scoped # complete_from_another is intended to include other fields, which # shouldn't apply for a no command. if no_command: return if other.find("|") >= 0: parts = other.split("|") other = parts[0] field = parts[1] if not mi.obj_type_exists(other): raise error.CommandDescriptionError("Unknown obj-type/other: %s" % other) id = mi.pk(other) data = dict(data) if parent_field and parent_id: data[parent_field] = parent_id if prefix: data[field + "__startswith"] = prefix key = mi.pk(other) if scoped: key = mi.pk(other) if type(scoped) == str and scoped in data: obj_d = {key: data[scoped]} else: obj_d = {key: sdnsh.get_current_mode_obj()} mi.split_compound_into_dict(other, key, obj_d, is_prefix=True) for (k, v) in obj_d.items(): if k != key and not k in data: data[k] = v if mi.is_primitive_compound_key(other, key): # try to use the field values to populate the primary key... value = "" s = mi.compound_key_separator(other, key) missing = None for kf in mi.deep_compound_key_fields(other, key): if kf in data: value += data[kf] + s else: # the fields must appear in order missing = kf break # For prefix extention to work here, the other field must have # named the field, for example switch's interface completion, # uses "other : 'port|number'" post_prefix_match = False if prefix: post_prefix_match = True if missing == field: value += prefix post_prefix_match = False if mi.obj_type_has_model(other): result = sdnsh.get_table_from_store(other, key, value) else: result = rest_to_model.get_model_from_url(other, {key: value}) if post_prefix_match: # try to match the missing field, more work ought to be done # to identify whether the 'missing' field is the correect to match against # result = [x for x in result if field in x and str(x[field]).startswith(prefix)] elif mi.is_compound_key(other, key): search = {} if parent_id: from_id = {mi.pk(obj_type): parent_id} mi.split_compound_into_dict(obj_type, mi.pk(obj_type), from_id, is_prefix=True) # the field name used to collapse the result is the last # field in the compound key (id of 'other'), this may need # improvement for other commands for deep_field in mi.deep_compound_key_fields(other, key): if deep_field in from_id: search[deep_field] = from_id[deep_field] if deep_field in data: search[deep_field] = data[deep_field] if scoped: # move known compound fields from obj_d into search. for deep_field in mi.deep_compound_key_fields(other, key): if deep_field in obj_d: search[deep_field] = obj_d[deep_field] # # possibly other search keys? if prefix: search[field + "__startswith"] = prefix if explicit: search.clear() search[scoped] = data[scoped] if prefix: search[field + "__startswith"] = prefix if mi.obj_type_has_model(other): result = sdnsh.rest_query_objects(other, search) else: result = rest_to_model.get_model_from_url(other, search) elif mi.obj_type_has_field(other, field) and mi.is_primary_key(other, field): result = utif.add_delim(objects_starting_with(other, prefix), " ") completions.update(dict([[x, "%s selection" % pretty(other)] for x in result])) return elif mi.obj_type_has_field(obj_type, field) and mi.is_foreign_key(obj_type, field): # look up the values of the foreign key's from the other table (fk_obj_type, fk_fn) = mi.foreign_key_references(obj_type, field) result = sdnsh.get_table_from_store(fk_obj_type, fk_fn, prefix) field = fk_fn elif mi.obj_type_has_field(obj_type, field) and field == other: # In this situation, this obj_type has a field, which seems to be named # based on the other model's name, which seems to be requesting to # search the other model. field = mi.pk(other) result += utif.add_delim(objects_starting_with(other, prefix), " ") completions.update(dict([[x, "%s selection" % pretty(other)] for x in result])) return else: if mi.obj_type_has_model(other): result = sdnsh.rest_query_objects(other, data) else: result = rest_to_model.get_model_from_url(other, data) check_rest_result(result) if sdnsh.description: # description debugging print "complete_from_another:", other, field, data, len(result) d = {} for item in result: value = item.get(field) # XXX hack to correctly format tag completions if other == "tag": value = "%s.%s=%s" % tuple(value.split("|")) # assume that 'values' are 'unique' within results if value and utif.quote_string(value) not in completions: d[utif.quote_string(str(value))] = None if sdnsh.description: # description debugging print "complete_from_another: final", other, field, data, d.keys() result = utif.add_delim(list(d), " ") completions.update(dict([[x, "%s selection" % pretty(other)] for x in result]))
def collect_object_fields(obj_type, field, data, mode, completions, prefix=None, other=None, parent_field=None, parent_id=None, scoped=None): """ Returns the list of possible completions for a particular obj-type. """ data = dict(data) if parent_field: data[parent_field] = parent_id if prefix: data[field + '__startswith'] = prefix key = mi.pk(obj_type) if scoped: obj_id = sdnsh.get_current_mode_obj() if sdnsh.current_mode() != mode: # XXX needs to be covered, shouldn't reach in like this for x in sdnsh.mode_stack: if x['mode_name'] == mode: obj_id = x['obj'] obj_d = {key: obj_id} if obj_type in mi.alias_obj_types: # the submode ought to identify the foreign key data[mi.alias_obj_type_field(obj_type)] = obj_id else: mi.split_compound_into_dict(obj_type, key, obj_d, is_prefix=True) for (k, v) in obj_d.items(): if k != key and not k in data: data[k] = v # if this is one of the obj_type's associated with aliases, should # the list of values be back-transformed into alias names? # yes, because if the current value has an inverse alias, the existing # inverse for the type implies that during a previous insert of this # value, it was converted from its alias name to the current name. # # collect the complete collection of aliases, since its likely # more than one back-to-alias conversion will be required, and query # its value before obj_type in the hope that it was recently cached. # alias_obj_type = mi.obj_type_related_config_obj_type(obj_type) if other and other in mi.alias_obj_type_xref: alias_obj_type = mi.alias_obj_type_xref[other][0] elif field != mi.pk(obj_type): if mi.is_foreign_key(obj_type, field): (alias_obj_type, fk_name) = mi.foreign_key_references(obj_type, field) alias_obj_type = mi.obj_type_related_config_obj_type( alias_obj_type) if alias_obj_type in mi.alias_obj_type_xref: alias_obj_type = mi.alias_obj_type_xref[alias_obj_type][0] else: alias_obj_type = None else: if sdnsh.description: # description debugging print 'collect_object_fields: no alias for %s ' \ 'field %s not pk, and not fk' % (obj_type, field) alias_obj_type = None elif obj_type in mi.alias_obj_type_xref: alias_obj_type = mi.alias_obj_type_xref[obj_type][0] else: alias_obj_type = None alias_dict = {} if alias_obj_type: foreign_field = mi.alias_obj_type_field(alias_obj_type) alias_dict = create_obj_type_dict(alias_obj_type, foreign_field) alias_key = mi.pk(alias_obj_type) # Remove any data fields which have values of None, these are fields # which are getting reset. for reset_fields in [x for x in data.keys() if data[x] == None]: del data[reset_fields] # collect complete obj_type if not mi.obj_type_has_model(obj_type): result = rest_to_model.get_model_from_url(obj_type, data) else: result = sdnsh.rest_query_objects(obj_type, data) check_rest_result(result) if sdnsh.description: # description debugging print "collect_object_fields:", obj_type, field, data, result is_compound = mi.is_compound_key(obj_type, key) d = {} for item in result: if is_compound: mi.split_compound_into_dict(obj_type, key, item) value = item.get(field) # XXX hack to correctly format tag completions if obj_type == 'tag' and field == 'id': value = '%s.%s=%s' % tuple(value.split('|')) # remember to only add new items if value: if type(value) == list: # Need a mechanism to select values from the list, field's not enough for item in value: if utif.quote_string(str(item)) not in completions: if str(item) in alias_dict: alias_item = alias_dict[str(item)][0][alias_key] if alias_item.startswith(prefix): item = alias_item d[utif.quote_string(str(item))] = None elif utif.quote_string(str(value)) not in completions: if str(value) in alias_dict: alias_value = alias_dict[str(value)][0][alias_key] if alias_value.startswith(prefix): value = alias_value d[utif.quote_string(str(value))] = None # if there's an alias for this object, and a prefix is included, # then the alias'es which match also need to be directly included, # since its not clear whether the prefix applies to the actual # id or the alias. since alias_dict is already the complete # collection of aliases for this obj-type, use it for matching names if alias_obj_type and prefix and prefix != '': alias_pk = mi.pk(alias_obj_type) for (n, v) in alias_dict.items(): # 'n' here is the foreign key reference to this obj-type for item in [ x[alias_pk] for x in v if x[alias_pk].startswith(prefix) ]: if utif.quote_string(str(item)) not in completions: d[utif.quote_string(str(item))] = None return utif.add_delim(list(d), ' ')
def complete_from_another(other, obj_type, field, data, completions, no_command, prefix=None, parent_field=None, parent_id=None, scoped=None, explicit=None): """ Completion function used when another obj_type is used to populate values for the current obj_type the 'other' field identifies the obj_type to use to collect choices from, it can consist of two parts other|field. When field isn't described here, it comes from the description parameter, however, the 'field' value there may be in use to describe the value of the associated action. """ if sdnsh.description: # description debugging print "complete_from_another:", other, field, data, parent_field, parent_id, scoped # complete_from_another is intended to include other fields, which # shouldn't apply for a no command. if no_command: return if other.find('|') >= 0: parts = other.split('|') other = parts[0] field = parts[1] if not mi.obj_type_exists(other): raise error.CommandDescriptionError("Unknown obj-type/other: %s" % other) id = mi.pk(other) data = dict(data) if parent_field and parent_id: data[parent_field] = parent_id if prefix: data[field + '__startswith'] = prefix key = mi.pk(other) if scoped: key = mi.pk(other) if type(scoped) == str and scoped in data: obj_d = {key: data[scoped]} else: obj_d = {key: sdnsh.get_current_mode_obj()} mi.split_compound_into_dict(other, key, obj_d, is_prefix=True) for (k, v) in obj_d.items(): if k != key and not k in data: data[k] = v if mi.is_primitive_compound_key(other, key): # try to use the field values to populate the primary key... value = "" s = mi.compound_key_separator(other, key) missing = None for kf in mi.deep_compound_key_fields(other, key): if kf in data: value += data[kf] + s else: # the fields must appear in order missing = kf break # For prefix extention to work here, the other field must have # named the field, for example switch's interface completion, # uses "other : 'port|number'" post_prefix_match = False if prefix: post_prefix_match = True if missing == field: value += prefix post_prefix_match = False if mi.obj_type_has_model(other): result = sdnsh.get_table_from_store(other, key, value) else: result = rest_to_model.get_model_from_url(other, {key: value}) if post_prefix_match: # try to match the missing field, more work ought to be done # to identify whether the 'missing' field is the correect to match against # result = [ x for x in result if field in x and str(x[field]).startswith(prefix) ] elif mi.is_compound_key(other, key): search = {} if parent_id: from_id = {mi.pk(obj_type): parent_id} mi.split_compound_into_dict(obj_type, mi.pk(obj_type), from_id, is_prefix=True) # the field name used to collapse the result is the last # field in the compound key (id of 'other'), this may need # improvement for other commands for deep_field in mi.deep_compound_key_fields(other, key): if deep_field in from_id: search[deep_field] = from_id[deep_field] if deep_field in data: search[deep_field] = data[deep_field] if scoped: # move known compound fields from obj_d into search. for deep_field in mi.deep_compound_key_fields(other, key): if deep_field in obj_d: search[deep_field] = obj_d[deep_field] # # possibly other search keys? if prefix: search[field + '__startswith'] = prefix if explicit: search.clear() search[scoped] = data[scoped] if prefix: search[field + '__startswith'] = prefix if mi.obj_type_has_model(other): result = sdnsh.rest_query_objects(other, search) else: result = rest_to_model.get_model_from_url(other, search) elif mi.obj_type_has_field(other, field) and mi.is_primary_key( other, field): result = utif.add_delim(objects_starting_with(other, prefix), ' ') completions.update( dict([[x, "%s selection" % pretty(other)] for x in result])) return elif mi.obj_type_has_field(obj_type, field) and \ mi.is_foreign_key(obj_type, field): # look up the values of the foreign key's from the other table (fk_obj_type, fk_fn) = mi.foreign_key_references(obj_type, field) result = sdnsh.get_table_from_store(fk_obj_type, fk_fn, prefix) field = fk_fn elif mi.obj_type_has_field(obj_type, field) and field == other: # In this situation, this obj_type has a field, which seems to be named # based on the other model's name, which seems to be requesting to # search the other model. field = mi.pk(other) result += utif.add_delim(objects_starting_with(other, prefix), ' ') completions.update( dict([[x, "%s selection" % pretty(other)] for x in result])) return else: if mi.obj_type_has_model(other): result = sdnsh.rest_query_objects(other, data) else: result = rest_to_model.get_model_from_url(other, data) check_rest_result(result) if sdnsh.description: # description debugging print "complete_from_another:", other, field, data, len(result) d = {} for item in result: value = item.get(field) # XXX hack to correctly format tag completions if other == 'tag': value = '%s.%s=%s' % tuple(value.split('|')) # assume that 'values' are 'unique' within results if value and utif.quote_string(value) not in completions: d[utif.quote_string(str(value))] = None if sdnsh.description: # description debugging print "complete_from_another: final", other, field, data, d.keys() result = utif.add_delim(list(d), ' ') completions.update( dict([[x, "%s selection" % pretty(other)] for x in result]))