def complete_tag_mapping(obj_type,
                         field,
                         data,
                         completions,
                         prefix=None,
                         other=None,
                         mode=None,
                         parent_field=None,
                         parent_id=None,
                         scoped=None):
    """
    Translate the completion results from complete_object_field into
    tag values of syntax <namespace.name>=<value
    """
    if not mi.obj_type_exists(obj_type):
        raise error.CommandDescriptionError("Unknown obj-type: %s" % obj_type)

    # since the prefix can contrict the search, and its not clear
    # what the prefix applies to, collect all the possible values,
    # compute wht the item would look like then match the prefix.
    collection = collect_object_fields(obj_type, field, data, mode,
                                       completions, '', other, parent_field,
                                       parent_id, scoped)
    if prefix != "":
        collection = [x for x in collection if x.startswith(prefix)]
    completions.update(dict([[x, "tag selection"] for x in collection]))
def validate_enum(typedef, value):
    # FIXME: Sort of a hack. The base enum class doesn't have a values
    # field, so there's nothing to check against. We really just use it
    # as a base type to indicate the validation function (i.e. this function)
    # to use.
    name = typedef.get('name')
    if name == 'enum':
        return

    enum_values = typedef.get('values')
    if not enum_values:
        raise error.CommandDescriptionError('Unspecified enum values')

    if not isinstance(enum_values, collections.Mapping):
        # If it's not a dictionary then it should be an array
        # or tuple where the (string) elements are both the key
        # and the value for the enum items. So here we convert
        # to a dictionary so we can use the same logic for both
        # cases below.
        if command._is_string(enum_values):
            enum_values = (enum_values,)
        if isinstance(enum_values, collections.Sequence):
            enum_values = dict((v,v) for v in enum_values)
        else:
            raise error.CommandDescriptionError(
                    'Enum values must be either a string, dict, tuple, or list')

    prefix_matches = []
    lower_value = value.lower()
    for enum_value, return_value in enum_values.items():
        lower_enum_value = enum_value.lower()
        if lower_enum_value == lower_value:
            return (return_value, enum_value)
        if lower_enum_value.startswith(lower_value):
            prefix_matches.append((return_value, enum_value))

    if len(prefix_matches) == 0:
        command._raise_argument_validation_exception(typedef, value,
                'unexpected value for enum', enum_values.keys())

    return prefix_matches
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(field, data, prefix, completions, no_command,
                          path = None, other_path = None, scoped = None):
    """
    Complete selections from an external object (unlreated to this
    object stack's details), only returning unique keys, either
    aliases for the path, or primary keys.

    This ought to be improved, objects_starting_with() in
    the bigcli.py, is primarily intended to be use within bigcli.py
    """
    if debug.description():
        print "complete_alias_choice:", path, other_path, field, data, prefix, scoped

    if path == None:
        raise error.CommandDescriptionError("path requrired")

    filter = data if scoped else {}
    # XXX scoped <- need data from submode stack.
    bigdb_path = other_path if other_path else path
    bigsh.bigdb.completions(completions, bigdb_path, field, prefix, filter)
def complete_object_field(field, data, completions,
                          path = None,
                          other_path = None,
                          mode = None,
                          parent_field = None,
                          parent_id = None,
                          prefix = None,
                          no_command = None,
                          scoped = None):
    """
    Populate 'completions' with  the values of the primary key for
    the particular path
    """
    if debug.description():
        print "complete_object_field: ", path, other_path, mode, field, data, scoped

    if path == None:
        top = bigsh.mode_stack[-1]
        path = top.get('path')
        if path == None:
            raise error.CommandDescriptionError("path required")
        
    filter = {}
    if scoped:
        filter = dict(data) # completion version of data
        bigsh.bigdb.add_mode_stack_paths(filter)

    if no_command:
        bigsh.bigdb.completions(completions, path, field, prefix, filter)
    else:
        # Include complete-config-field as a second completion,
        # it will remove items which are already configured from the
        # other choices which currently exist.
        deletions = {}
        bigsh.bigdb.completions(deletions, path, field, prefix, filter)
        if debug.description():
            print 'complete_object_field: removing ', deletions
        for delete in deletions:
            if delete in completions:
                del completions[delete]
def complete_object_field(obj_type,
                          field,
                          data,
                          completions,
                          mode=None,
                          prefix=None,
                          other=None,
                          parent_field=None,
                          parent_id=None,
                          scoped=None):
    """
    Populate 'completions' with  the values of the primary key for
    the particular obj_type
    """
    if sdnsh.description:  # description debugging
        print "complete_object_field: ", obj_type, mode, field, data, scoped, other

    if not mi.obj_type_exists(obj_type):
        raise error.CommandDescriptionError("Unknown obj-type: %s" % obj_type)
    result = collect_object_fields(obj_type, field, data, mode, completions,
                                   prefix, other, parent_field, parent_id,
                                   scoped)
    completions.update(
        dict([[x, "%s selection" % pretty(obj_type)] for x in result]))
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]))