Adding ONOS Segment Routing CLI files to new repo
diff --git a/cli/c_completions.py b/cli/c_completions.py
new file mode 100755
index 0000000..ac66a79
--- /dev/null
+++ b/cli/c_completions.py
@@ -0,0 +1,752 @@
+#
+# 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 os
+import re
+import modi
+import error
+import command
+import collections
+import utif
+
+from midw import *
+
+def check_rest_result(result, message=None):
+    if isinstance(result, collections.Mapping):
+        error_type = result.get('error_type')
+        if error_type:
+            raise error.CommandRestError(result, message)
+
+
+def pretty(text):
+    """
+    For object-type's, remove dashes, capitalize first character
+    """
+    return text.replace('-', ' ').capitalize()
+
+
+#
+# COMPLETION PROCS
+#
+# 'completions' is a dictionary, where the keys are the actual text
+# of the completion, while the value is the reason why this text 
+# was added.  The 'reason' provides the text for the two-column
+# help printed for the '?' character.
+#
+
+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_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_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 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 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_config(prefix, data, completions, copy = False):
+    """
+    Complete selections for the 'copy' command.
+    """
+
+    configs = sdnsh.store.get_user_data_table('', "latest")
+
+    # exclude source if its in the data
+    source = data.get('source','')
+    src_dst = 'source' if source == '' else 'destination'
+
+    any = False
+    any_config = False
+
+    if copy:
+        if 'running-config'.startswith(prefix):
+            if source != 'running-config':
+                completions['running-config '] = 'running-config %s' % src_dst
+
+    for c in configs:
+        if ('config://' + c['name']).startswith(prefix):
+            if source != "config://" + c['name']:
+                completions["config://" + c['name'] + ' '] = \
+                    'Saved Configuration %s' % src_dst
+            any_config = True
+
+    if source != '' and 'config://'.startswith(prefix):
+        completions['config://'] = 'config prefix %s' % src_dst
+
+    if copy:
+        for additions in ["http://", "file://", "ftp://", "tftp://", 'config://' ]:
+            if additions.startswith(prefix):
+                completions[additions] = 'other %s' % src_dst
+
+
+def complete_interface_list(prefix, data, completions):
+    """
+    Interface lists are comma separated interfaces or range
+    of interfaces.  
+
+    The prefix here plays an important role in determining what
+    ought to appear nest.
+    """
+    if not 'switch' in data:
+        return
+
+    def switch_interfaces_startingwith(interfaces, intf, prefix, completions):
+        result = [prefix + x for x in interfaces.keys() if x.startswith(intf)]
+        completions.update(dict([[x, "known interface"] for x in result]))
+        return
+
+    def higher_interfaces(interfaces, intf, prefix, completions):
+        # depend on having an integer as the last component
+        last_digits = re.compile(r'(.*)(\d+)$')
+        match = last_digits.search(intf)
+        if match: 
+            if_name = match.group(1)
+            first = int(match.group(2))
+            for i in interfaces:
+                match = last_digits.search(i)
+                if match and match.group(1) == if_name and int(match.group(2)) > first:
+                    completions[prefix + match.group(2)] = 'inteface choice.'
+
+ 
+    ports = rest_to_model.get_model_from_url('interfaces', data)
+    interfaces = dict([[x['name'], x] for x in ports])
+    sic = sdnsh.get_table_from_store('switch-interface-config',
+                                     'switch', data['switch'])
+    interfaces.update(dict([[x['name'], x] for x in sic]))
+
+    # peek at the last character in the prefix:
+    #  if it's a dash, then choose interfaces with the same prefix,
+    #  if its a comma, then chose another interface
+    
+    front_item = ''
+    if len(prefix) > 0:
+        if prefix[-1] == '-':
+            # complete more choices
+            previous = prefix[:-1]
+            if len(previous):
+                last_item = previous.split(',')[-1]
+                if last_item in interfaces:
+                    higher_interfaces(interfaces, last_item, prefix, completions)
+            return
+                
+        if prefix[-1] != ',':
+            if len(prefix) > 2:
+                parts = prefix.split(',')
+                last_item = parts[-1]
+                # see if the last_item of prefix is a known interface.
+                if last_item in interfaces:
+                    completions[prefix + ',']     = 'List of interfaces'
+                    completions[prefix + '-']     = 'Range of interfaces'
+                    completions[prefix + ' <cr>'] = 'Current interfaces selection'
+                    return
+                # see if the last item is a range (intf in front, then a dash)
+                c = [y for y in [x for x in interfaces if last_item.startswith(x)]
+                                if len(last_item) > len(y) and last_item[len(y)] == '-']
+                if len(c):
+                    # found interface with a dash afterwards
+                    # could actually check that everything after '-' is digits
+                    completions[prefix + ',']     = 'List of interfaces'
+                    completions[prefix + ' <cr>'] = 'Current interfaces selection'
+                    return
+
+                first_items = ''.join(['%s,' % x for x in parts[:-1]])
+                switch_interfaces_startingwith(interfaces,
+                                               last_item,
+                                               first_items,
+                                               completions)
+                return
+
+            # single token prefix
+            switch_interfaces_startingwith(interfaces, prefix, '', completions)
+            return
+        # last character is a comma
+        if len(prefix) == 1:
+            return # just a comma
+
+        # crack into parts, see if the last is a range, if so, then
+        # the choices are a comma or a <cr>
+        parts = prefix.split(',')
+        front_item = ','.join(parts[:-1]) + ','
+        prefix = parts[-1]
+        # fall through
+
+    switch_interfaces_startingwith(interfaces, prefix, front_item, completions)
+    return
+
+
+def complete_staticflow_actions(prefix, data, completions):
+    # peek at the last character in the prefix:
+    #  if it's a comma, then choose all the possible actions
+    #  if its a equal, then display the choices for this option
+    
+    prefix_parts = []
+
+    actions = {
+        'output='            : 'Describe packet forwarding',
+        'enqueue='           : 'Enqueue packet',
+        'strip-vlan='        : 'Strip Vlan',
+        'set-vlan-id='       : 'Set Vlan',
+        'set-vlan-priority=' : 'Set Priority',
+        'set-src-mac='       : 'Set Src Mac',
+        'set-dst-mac='       : 'Set Dst Mac',
+        'set-tos-bits='      : 'Set TOS Bits',
+        'set-src-ip='        : 'Set IP Src',
+        'set-dst-ip='        : 'Set IP Dst',
+        'set-src-port='      : 'Set Src IP Port',
+        'set-dst-port='      : 'Set dst IP Port',
+    }
+
+    action_choices = {
+        ('output=', 'all')          : 'Forward to all ports',
+        ('output=', 'controller')   : 'Forward to controller',
+        ('output=', 'local')        : 'Forward to local',
+        ('output=', 'ingress-port') : 'Forward to ingress port',
+        ('output=', 'normal')       : 'Forward to ingress port',
+        ('output=', 'flood')        : 'Forward, flood ports',
+        ('output=', ('<number>', '<number>'))  : 'Forward, to a specific port',
+
+        ('enqueue=', ('<portNumber>.<queueID>', '<portNumber>.<queueID>')) : 'Enqueue to port, queue id',
+
+        ('set-vlan-id=',('<vlan number>','<vlan number>')) : 'Set vlan to <vlan number>',
+        
+        ('set-vlan-priority=',('<vlan prio>','<vlan prio>')) : 'Set vlan priority to <prio>',
+
+        ('set-tos-bits=',('<number>',)) : 'Set TOS bits',
+        ('set-src-mac=',('<src-mac-address>',)) : 'Set src mac address',
+
+        ('set-dst-mac=',('<dst-mac-address>',)) : 'Set dst mac address',
+        
+        ('set-src-ip=',('<src-ip-address>',)) : 'Set src mac address',
+        
+        ('set-dst-ip=',('<src-ip-address>',)) : 'Set dst ip address',
+    }
+
+    for ps in prefix.split(','):
+        ps_parts = ps.split('=')
+        if len(ps_parts) == 1 and ps_parts[0] != '':
+            # possibly incomplete item before the '='
+            for choice in [x for x in actions.keys() if x.startswith(ps_parts[0])]:
+                completions[choice] = actions[choice]
+            return
+        elif len(ps_parts) == 2:
+            if len(ps_parts[0]) and len(ps_parts[1]):
+                prefix_parts.append((ps_parts[0], ps_parts[1]))
+            elif len(ps_parts[0]) and len(ps_parts[1]) == 0:
+                prefix_parts.append((ps_parts[0], ))
+
+    if prefix == '' or prefix.endswith(','):
+        completions.update(actions)
+    elif prefix.endswith('='):
+        last = prefix_parts[-1]
+        for ((match, next), desc) in action_choices.items():
+            if match[:-1] != last[0]:
+                continue
+            if type(next) == str:
+                completions[match + next] = desc
+            elif type(next) == tuple:
+                completions[(match + next[0], match + next[0])] = desc
+            # else?  display error?
+    elif len(prefix_parts):
+        last = prefix_parts[-1]
+        if len(last) == 1:
+            pass
+        elif len(last) == 2:
+            # try to find the left item
+            for ((match, next), desc) in action_choices.items():
+                if match[:-1] != last[0]:
+                    continue
+                if type(next) == str and next == last[1]:
+                    eol = prefix + ' <cr>'
+                    completions[(eol, eol)] = 'Complete Choice'
+                    another = prefix + ','
+                    completions[(another, another)] = 'Add another action'
+                elif type(next) == str and next.startswith(last[1]):
+                    base_part = ''.join(prefix.rpartition(',')[:-1])
+                    completions[base_part + last[0] + '=' + next] = 'Complete selection'
+                elif len(last[1]):
+                    # hard to say what choices can be added here,
+                    # there are some characters after '=', but none
+                    # which match some prefix.
+                    pass
+
+                # how to match the values?
+
+
+def complete_description_versions(prefix, completions):
+    for element in os.listdir(sdnsh.command_packages_path()):
+        if element == '__init__.py':
+            pass
+        elif element.startswith('version'):
+            # len('element') -> 7
+            version = "%2.2f" % (float(element[7:]) / 100)
+            if version[-2:] == '00':
+                version = version[:2] + '0'
+            if version.startswith(prefix):
+                completions[version] = 'VERSION'
+            if version == '2.0':    # currently if 2.0 exists, so does 1.0
+                if '1.0'.startswith(prefix):
+                    completions['1.0'] = 'VERSION'
+        else:
+            if element.startswith(prefix):
+                completions[element] = 'VERSION'
+
+
+def complete_log_names(prefix, data, completions):
+    """
+    Enumerate all the log file choices based on replies from the REST API.
+    """
+    controller = data.get('controller')
+    for ip_port in controller_ip_and_port(controller):
+        url = log_url(ip_and_port = ip_port)
+        log_names = command.sdnsh.rest_simple_request_to_dict(url)
+        for log in log_names:
+            log_name = log['log']
+            if log_name.startswith(prefix):
+                completions[log_name + ' '] = 'Log Selection'
+
+
+
+def init_completions(bs, modi):
+    global sdnsh, mi
+    sdnsh = bs
+    mi = modi
+
+    command.add_completion('complete-object-field', complete_object_field,
+                           {'kwargs': {'obj_type'     : '$obj-type',
+                                       'parent_field' : '$parent-field',
+                                       'parent_id'    : '$current-mode-obj-id',
+                                       'field'        : '$field',
+                                       'prefix'       : '$text',
+                                       'data'         : '$data',
+                                       'scoped'       : '$scoped',
+                                       'other'        : '$other',
+                                       'mode'         : '$mode',
+                                       'completions'  : '$completions'}})
+
+    command.add_completion('complete-tag-mapping', complete_tag_mapping,
+                           {'kwargs': {'obj_type'     : '$obj-type',
+                                       'parent_field' : '$parent-field',
+                                       'parent_id'    : '$current-mode-obj-id',
+                                       'field'        : '$field',
+                                       'prefix'       : '$text',
+                                       'data'         : '$data',
+                                       'scoped'       : '$scoped',
+                                       'other'        : '$other',
+                                       'mode'         : '$mode',
+                                       'completions'  : '$completions'}})
+
+    command.add_completion('complete-from-another', complete_from_another,
+                           {'kwargs': {'other'        : '$other',
+                                       'obj_type'     : '$obj-type',
+                                       'parent_field' : '$parent-field',
+                                       'parent_id'    : '$current-mode-obj-id',
+                                       'field'        : '$field',
+                                       'prefix'       : '$text',
+                                       'data'         : '$data',
+                                       'scoped'       : '$scoped',
+                                       'completions'  : '$completions',
+                                       'no_command'   : '$is-no-command',
+                                       'explicit'     : '$explicit', }})
+
+    command.add_completion('complete-alias-choice', complete_alias_choice,
+                           {'kwargs': {'obj_type'    : '$obj-type',
+                                       'field'       : '$field',
+                                       'other'       : '$other',
+                                       'prefix'      : '$text',
+                                       'data'        : '$data',
+                                       'scoped'      : '$scoped',
+                                       'completions' : '$completions',
+                                       'no_command'  : '$is-no-command', }})
+
+    command.add_completion('complete-config', complete_config,
+                           {'kwargs': {'prefix': '$text',
+                                       'data': '$data',
+                                       'completions': '$completions'}})
+
+    command.add_completion('complete-config-copy', complete_config,
+                           {'kwargs': {'prefix': '$text',
+                                       'data': '$data',
+                                       'completions': '$completions',
+                                       'copy' : True }})
+
+    command.add_completion('complete-interface-list', complete_interface_list,
+                           {'kwargs': {'prefix': '$text',
+                                       'data': '$data',
+                                       'completions': '$completions'}})
+
+    command.add_completion('complete-staticflow-actions', complete_staticflow_actions,
+                           {'kwargs': {'prefix': '$text',
+                                       'data': '$data',
+                                       'completions': '$completions'}})
+
+    command.add_completion('description-versions', complete_description_versions,
+                           {'kwargs': {'prefix': '$text',
+                                       'completions': '$completions'}})
+
+    command.add_completion('complete-log-names', complete_log_names,
+                           {'kwargs': {'prefix'     : '$text',
+                                       'data'       : '$data',
+                                       'completions': '$completions'}})
+