| # |
| # 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'}}) |
| |