blob: ac66a79ab939440015b3729f290bfc21cf8e2f1b [file] [log] [blame]
Srikanth Vavilapalli1725e492014-12-01 17:50:52 -08001#
2# Copyright (c) 2011,2012,2013 Big Switch Networks, Inc.
3#
4# Licensed under the Eclipse Public License, Version 1.0 (the
5# "License"); you may not use this file except in compliance with the
6# License. You may obtain a copy of the License at
7#
8# http://www.eclipse.org/legal/epl-v10.html
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied. See the License for the specific language governing
14# permissions and limitations under the License.
15#
16
17import os
18import re
19import modi
20import error
21import command
22import collections
23import utif
24
25from midw import *
26
27def check_rest_result(result, message=None):
28 if isinstance(result, collections.Mapping):
29 error_type = result.get('error_type')
30 if error_type:
31 raise error.CommandRestError(result, message)
32
33
34def pretty(text):
35 """
36 For object-type's, remove dashes, capitalize first character
37 """
38 return text.replace('-', ' ').capitalize()
39
40
41#
42# COMPLETION PROCS
43#
44# 'completions' is a dictionary, where the keys are the actual text
45# of the completion, while the value is the reason why this text
46# was added. The 'reason' provides the text for the two-column
47# help printed for the '?' character.
48#
49
50def collect_object_fields(obj_type, field, data, mode, completions,
51 prefix = None, other = None,
52 parent_field = None, parent_id = None, scoped = None):
53 """
54 Returns the list of possible completions for a particular obj-type.
55 """
56
57 data = dict(data)
58 if parent_field:
59 data[parent_field] = parent_id
60 if prefix:
61 data[field + '__startswith'] = prefix
62
63 key = mi.pk(obj_type)
64 if scoped:
65 obj_id = sdnsh.get_current_mode_obj()
66 if sdnsh.current_mode() != mode:
67 # XXX needs to be covered, shouldn't reach in like this
68 for x in sdnsh.mode_stack:
69 if x['mode_name'] == mode:
70 obj_id = x['obj']
71 obj_d = { key : obj_id }
72
73 if obj_type in mi.alias_obj_types:
74 # the submode ought to identify the foreign key
75 data[mi.alias_obj_type_field(obj_type)] = obj_id
76 else:
77 mi.split_compound_into_dict(obj_type, key, obj_d, is_prefix = True)
78 for (k,v) in obj_d.items():
79 if k != key and not k in data:
80 data[k] = v
81
82
83 # if this is one of the obj_type's associated with aliases, should
84 # the list of values be back-transformed into alias names?
85 # yes, because if the current value has an inverse alias, the existing
86 # inverse for the type implies that during a previous insert of this
87 # value, it was converted from its alias name to the current name.
88 #
89 # collect the complete collection of aliases, since its likely
90 # more than one back-to-alias conversion will be required, and query
91 # its value before obj_type in the hope that it was recently cached.
92 #
93 alias_obj_type = mi.obj_type_related_config_obj_type(obj_type)
94 if other and other in mi.alias_obj_type_xref:
95 alias_obj_type = mi.alias_obj_type_xref[other][0]
96 elif field != mi.pk(obj_type):
97 if mi.is_foreign_key(obj_type, field):
98 (alias_obj_type, fk_name) = mi.foreign_key_references(obj_type, field)
99 alias_obj_type = mi.obj_type_related_config_obj_type(alias_obj_type)
100 if alias_obj_type in mi.alias_obj_type_xref:
101 alias_obj_type = mi.alias_obj_type_xref[alias_obj_type][0]
102 else:
103 alias_obj_type = None
104 else:
105 if sdnsh.description: # description debugging
106 print 'collect_object_fields: no alias for %s ' \
107 'field %s not pk, and not fk' % (obj_type, field)
108 alias_obj_type = None
109 elif obj_type in mi.alias_obj_type_xref:
110 alias_obj_type = mi.alias_obj_type_xref[obj_type][0]
111 else:
112 alias_obj_type = None
113
114 alias_dict = {}
115 if alias_obj_type:
116 foreign_field = mi.alias_obj_type_field(alias_obj_type)
117 alias_dict = create_obj_type_dict(alias_obj_type, foreign_field)
118 alias_key = mi.pk(alias_obj_type)
119
120 # Remove any data fields which have values of None, these are fields
121 # which are getting reset.
122 for reset_fields in [x for x in data.keys() if data[x] == None]:
123 del data[reset_fields]
124
125 # collect complete obj_type
126 if not mi.obj_type_has_model(obj_type):
127 result = rest_to_model.get_model_from_url(obj_type, data)
128 else:
129 result = sdnsh.rest_query_objects(obj_type, data)
130 check_rest_result(result)
131 if sdnsh.description: # description debugging
132 print "collect_object_fields:", obj_type, field, data, result
133
134 is_compound = mi.is_compound_key(obj_type, key)
135 d = {}
136 for item in result:
137 if is_compound:
138 mi.split_compound_into_dict(obj_type, key, item)
139 value = item.get(field)
140 # XXX hack to correctly format tag completions
141 if obj_type == 'tag' and field == 'id':
142 value = '%s.%s=%s' % tuple(value.split('|'))
143 # remember to only add new items
144 if value:
145 if type(value) == list:
146 # Need a mechanism to select values from the list, field's not enough
147 for item in value:
148 if utif.quote_string(str(item)) not in completions:
149 if str(item) in alias_dict:
150 alias_item = alias_dict[str(item)][0][alias_key]
151 if alias_item.startswith(prefix):
152 item = alias_item
153 d[utif.quote_string(str(item))] = None
154 elif utif.quote_string(str(value)) not in completions:
155 if str(value) in alias_dict:
156 alias_value = alias_dict[str(value)][0][alias_key]
157 if alias_value.startswith(prefix):
158 value = alias_value
159 d[utif.quote_string(str(value))] = None
160
161 # if there's an alias for this object, and a prefix is included,
162 # then the alias'es which match also need to be directly included,
163 # since its not clear whether the prefix applies to the actual
164 # id or the alias. since alias_dict is already the complete
165 # collection of aliases for this obj-type, use it for matching names
166 if alias_obj_type and prefix and prefix != '':
167 alias_pk = mi.pk(alias_obj_type)
168 for (n,v) in alias_dict.items():
169 # 'n' here is the foreign key reference to this obj-type
170 for item in [x[alias_pk] for x in v if x[alias_pk].startswith(prefix)]:
171 if utif.quote_string(str(item)) not in completions:
172 d[utif.quote_string(str(item))] = None
173
174 return utif.add_delim(list(d), ' ')
175
176
177def complete_object_field(obj_type, field, data, completions,
178 mode = None,
179 prefix = None, other = None, parent_field = None, parent_id = None, scoped = None):
180 """
181 Populate 'completions' with the values of the primary key for
182 the particular obj_type
183 """
184 if sdnsh.description: # description debugging
185 print "complete_object_field: ", obj_type, mode, field, data, scoped, other
186
187 if not mi.obj_type_exists(obj_type):
188 raise error.CommandDescriptionError("Unknown obj-type: %s" % obj_type)
189 result = collect_object_fields(obj_type, field, data, mode, completions,
190 prefix, other, parent_field, parent_id, scoped)
191 completions.update(dict([[x, "%s selection" % pretty(obj_type)]
192 for x in result]))
193
194
195def complete_tag_mapping(obj_type, field, data, completions,
196 prefix = None, other = None, mode = None,
197 parent_field = None, parent_id = None, scoped = None):
198 """
199 Translate the completion results from complete_object_field into
200 tag values of syntax <namespace.name>=<value
201 """
202 if not mi.obj_type_exists(obj_type):
203 raise error.CommandDescriptionError("Unknown obj-type: %s" % obj_type)
204
205 # since the prefix can contrict the search, and its not clear
206 # what the prefix applies to, collect all the possible values,
207 # compute wht the item would look like then match the prefix.
208 collection = collect_object_fields(obj_type, field, data, mode, completions,
209 '', other, parent_field, parent_id, scoped)
210 if prefix != "":
211 collection = [x for x in collection if x.startswith(prefix)]
212 completions.update(dict([[x, "tag selection"] for x in collection]))
213
214
215def complete_from_another(other, obj_type, field, data, completions, no_command,
216 prefix = None,
217 parent_field = None, parent_id = None, scoped = None,explicit=None):
218 """
219 Completion function used when another obj_type is used to populate
220 values for the current obj_type
221
222 the 'other' field identifies the obj_type to use to collect choices from,
223 it can consist of two parts other|field. When field isn't described here,
224 it comes from the description parameter, however, the 'field' value there may
225 be in use to describe the value of the associated action.
226
227 """
228 if sdnsh.description: # description debugging
229 print "complete_from_another:", other, field, data, parent_field, parent_id, scoped
230
231 # complete_from_another is intended to include other fields, which
232 # shouldn't apply for a no command.
233 if no_command:
234 return
235
236 if other.find('|') >= 0:
237 parts = other.split('|')
238 other = parts[0]
239 field = parts[1]
240
241 if not mi.obj_type_exists(other):
242 raise error.CommandDescriptionError("Unknown obj-type/other: %s" % other)
243
244 id = mi.pk(other)
245 data = dict(data)
246 if parent_field and parent_id:
247 data[parent_field] = parent_id
248 if prefix:
249 data[field + '__startswith'] = prefix
250 key = mi.pk(other)
251 if scoped:
252 key = mi.pk(other)
253 if type(scoped) == str and scoped in data:
254 obj_d = { key : data[scoped] }
255 else:
256 obj_d = { key : sdnsh.get_current_mode_obj() }
257 mi.split_compound_into_dict(other, key, obj_d, is_prefix = True)
258 for (k,v) in obj_d.items():
259 if k != key and not k in data:
260 data[k] = v
261 if mi.is_primitive_compound_key(other, key):
262 # try to use the field values to populate the primary key...
263 value = ""
264 s = mi.compound_key_separator(other, key)
265 missing = None
266 for kf in mi.deep_compound_key_fields(other, key):
267 if kf in data:
268 value += data[kf] + s
269 else:
270 # the fields must appear in order
271 missing = kf
272 break
273 # For prefix extention to work here, the other field must have
274 # named the field, for example switch's interface completion,
275 # uses "other : 'port|number'"
276 post_prefix_match = False
277 if prefix:
278 post_prefix_match = True
279 if missing == field:
280 value += prefix
281 post_prefix_match = False
282 if mi.obj_type_has_model(other):
283 result = sdnsh.get_table_from_store(other, key, value)
284 else:
285 result = rest_to_model.get_model_from_url(other, { key : value } )
286
287 if post_prefix_match:
288 # try to match the missing field, more work ought to be done
289 # to identify whether the 'missing' field is the correect to match against
290 #
291 result = [x for x in result
292 if field in x and str(x[field]).startswith(prefix)]
293 elif mi.is_compound_key(other, key):
294 search = {}
295 if parent_id:
296 from_id = {mi.pk(obj_type) : parent_id}
297 mi.split_compound_into_dict(obj_type,
298 mi.pk(obj_type),
299 from_id,
300 is_prefix = True)
301 # the field name used to collapse the result is the last
302 # field in the compound key (id of 'other'), this may need
303 # improvement for other commands
304 for deep_field in mi.deep_compound_key_fields(other, key):
305 if deep_field in from_id:
306 search[deep_field] = from_id[deep_field]
307 if deep_field in data:
308 search[deep_field] = data[deep_field]
309 if scoped:
310 # move known compound fields from obj_d into search.
311 for deep_field in mi.deep_compound_key_fields(other, key):
312 if deep_field in obj_d:
313 search[deep_field] = obj_d[deep_field]
314 #
315 # possibly other search keys?
316 if prefix:
317 search[field + '__startswith'] = prefix
318 if explicit:
319 search.clear()
320 search[scoped]=data[scoped]
321 if prefix:
322 search[field + '__startswith'] = prefix
323 if mi.obj_type_has_model(other):
324 result = sdnsh.rest_query_objects(other, search)
325 else:
326 result = rest_to_model.get_model_from_url(other, search )
327 elif mi.obj_type_has_field(other, field) and mi.is_primary_key(other, field):
328 result = utif.add_delim(objects_starting_with(other, prefix), ' ')
329 completions.update(dict([[x, "%s selection" % pretty(other)]
330 for x in result]))
331 return
332 elif mi.obj_type_has_field(obj_type, field) and \
333 mi.is_foreign_key(obj_type, field):
334 # look up the values of the foreign key's from the other table
335 (fk_obj_type, fk_fn) = mi.foreign_key_references(obj_type, field)
336 result = sdnsh.get_table_from_store(fk_obj_type, fk_fn, prefix)
337 field = fk_fn
338 elif mi.obj_type_has_field(obj_type, field) and field == other:
339 # In this situation, this obj_type has a field, which seems to be named
340 # based on the other model's name, which seems to be requesting to
341 # search the other model.
342 field = mi.pk(other)
343 result += utif.add_delim(objects_starting_with(other, prefix), ' ')
344 completions.update(dict([[x, "%s selection" % pretty(other)]
345 for x in result]))
346 return
347 else:
348 if mi.obj_type_has_model(other):
349 result = sdnsh.rest_query_objects(other, data)
350 else:
351 result = rest_to_model.get_model_from_url(other, data)
352
353 check_rest_result(result)
354 if sdnsh.description: # description debugging
355 print "complete_from_another:", other, field, data, len(result)
356
357 d = {}
358 for item in result:
359 value = item.get(field)
360 # XXX hack to correctly format tag completions
361 if other == 'tag':
362 value = '%s.%s=%s' % tuple(value.split('|'))
363 # assume that 'values' are 'unique' within results
364 if value and utif.quote_string(value) not in completions:
365 d[utif.quote_string(str(value))] = None
366
367 if sdnsh.description: # description debugging
368 print "complete_from_another: final", other, field, data, d.keys()
369
370 result = utif.add_delim(list(d), ' ')
371 completions.update(dict([[x, "%s selection" % pretty(other)]
372 for x in result]))
373
374
375def complete_alias_choice(obj_type, field, data, prefix, completions, no_command,
376 other = None, scoped = None):
377 """
378 Complete selections from an external object (unlreated to this
379 object stack's details), only returning unique keys, either
380 aliases for the obj_type, or primary keys.
381
382 This ought to be improved, objects_starting_with() in
383 the cli.py, is primarily intended to be use within cli.py
384 """
385 if not mi.obj_type_exists(obj_type):
386 raise error.CommandDescriptionError("Unknown obj-type: %s" % obj_type)
387
388 if sdnsh.description: # description debugging
389 print "complete_alias_choice:", obj_type, field, other, data, prefix, scoped
390
391 if other and no_command == False:
392 parts = other.split('|')
393 obj_type = parts[0]
394 if len(parts) > 1:
395 # what to do with more parts?
396 field = parts[1]
397
398 if not mi.obj_type_has_field(obj_type, field):
399 raise error.CommandDescriptionError("Unknown field %s for obj-type: %s"
400 % (field, obj_type))
401
402
403 # quote string? alias choices ought to never have special characters
404 result = utif.add_delim(objects_starting_with(obj_type, prefix, field), ' ')
405 completions.update(dict([[x, "%s alias selection" % pretty(obj_type)]
406 for x in result]))
407
408
409def complete_config(prefix, data, completions, copy = False):
410 """
411 Complete selections for the 'copy' command.
412 """
413
414 configs = sdnsh.store.get_user_data_table('', "latest")
415
416 # exclude source if its in the data
417 source = data.get('source','')
418 src_dst = 'source' if source == '' else 'destination'
419
420 any = False
421 any_config = False
422
423 if copy:
424 if 'running-config'.startswith(prefix):
425 if source != 'running-config':
426 completions['running-config '] = 'running-config %s' % src_dst
427
428 for c in configs:
429 if ('config://' + c['name']).startswith(prefix):
430 if source != "config://" + c['name']:
431 completions["config://" + c['name'] + ' '] = \
432 'Saved Configuration %s' % src_dst
433 any_config = True
434
435 if source != '' and 'config://'.startswith(prefix):
436 completions['config://'] = 'config prefix %s' % src_dst
437
438 if copy:
439 for additions in ["http://", "file://", "ftp://", "tftp://", 'config://' ]:
440 if additions.startswith(prefix):
441 completions[additions] = 'other %s' % src_dst
442
443
444def complete_interface_list(prefix, data, completions):
445 """
446 Interface lists are comma separated interfaces or range
447 of interfaces.
448
449 The prefix here plays an important role in determining what
450 ought to appear nest.
451 """
452 if not 'switch' in data:
453 return
454
455 def switch_interfaces_startingwith(interfaces, intf, prefix, completions):
456 result = [prefix + x for x in interfaces.keys() if x.startswith(intf)]
457 completions.update(dict([[x, "known interface"] for x in result]))
458 return
459
460 def higher_interfaces(interfaces, intf, prefix, completions):
461 # depend on having an integer as the last component
462 last_digits = re.compile(r'(.*)(\d+)$')
463 match = last_digits.search(intf)
464 if match:
465 if_name = match.group(1)
466 first = int(match.group(2))
467 for i in interfaces:
468 match = last_digits.search(i)
469 if match and match.group(1) == if_name and int(match.group(2)) > first:
470 completions[prefix + match.group(2)] = 'inteface choice.'
471
472
473 ports = rest_to_model.get_model_from_url('interfaces', data)
474 interfaces = dict([[x['name'], x] for x in ports])
475 sic = sdnsh.get_table_from_store('switch-interface-config',
476 'switch', data['switch'])
477 interfaces.update(dict([[x['name'], x] for x in sic]))
478
479 # peek at the last character in the prefix:
480 # if it's a dash, then choose interfaces with the same prefix,
481 # if its a comma, then chose another interface
482
483 front_item = ''
484 if len(prefix) > 0:
485 if prefix[-1] == '-':
486 # complete more choices
487 previous = prefix[:-1]
488 if len(previous):
489 last_item = previous.split(',')[-1]
490 if last_item in interfaces:
491 higher_interfaces(interfaces, last_item, prefix, completions)
492 return
493
494 if prefix[-1] != ',':
495 if len(prefix) > 2:
496 parts = prefix.split(',')
497 last_item = parts[-1]
498 # see if the last_item of prefix is a known interface.
499 if last_item in interfaces:
500 completions[prefix + ','] = 'List of interfaces'
501 completions[prefix + '-'] = 'Range of interfaces'
502 completions[prefix + ' <cr>'] = 'Current interfaces selection'
503 return
504 # see if the last item is a range (intf in front, then a dash)
505 c = [y for y in [x for x in interfaces if last_item.startswith(x)]
506 if len(last_item) > len(y) and last_item[len(y)] == '-']
507 if len(c):
508 # found interface with a dash afterwards
509 # could actually check that everything after '-' is digits
510 completions[prefix + ','] = 'List of interfaces'
511 completions[prefix + ' <cr>'] = 'Current interfaces selection'
512 return
513
514 first_items = ''.join(['%s,' % x for x in parts[:-1]])
515 switch_interfaces_startingwith(interfaces,
516 last_item,
517 first_items,
518 completions)
519 return
520
521 # single token prefix
522 switch_interfaces_startingwith(interfaces, prefix, '', completions)
523 return
524 # last character is a comma
525 if len(prefix) == 1:
526 return # just a comma
527
528 # crack into parts, see if the last is a range, if so, then
529 # the choices are a comma or a <cr>
530 parts = prefix.split(',')
531 front_item = ','.join(parts[:-1]) + ','
532 prefix = parts[-1]
533 # fall through
534
535 switch_interfaces_startingwith(interfaces, prefix, front_item, completions)
536 return
537
538
539def complete_staticflow_actions(prefix, data, completions):
540 # peek at the last character in the prefix:
541 # if it's a comma, then choose all the possible actions
542 # if its a equal, then display the choices for this option
543
544 prefix_parts = []
545
546 actions = {
547 'output=' : 'Describe packet forwarding',
548 'enqueue=' : 'Enqueue packet',
549 'strip-vlan=' : 'Strip Vlan',
550 'set-vlan-id=' : 'Set Vlan',
551 'set-vlan-priority=' : 'Set Priority',
552 'set-src-mac=' : 'Set Src Mac',
553 'set-dst-mac=' : 'Set Dst Mac',
554 'set-tos-bits=' : 'Set TOS Bits',
555 'set-src-ip=' : 'Set IP Src',
556 'set-dst-ip=' : 'Set IP Dst',
557 'set-src-port=' : 'Set Src IP Port',
558 'set-dst-port=' : 'Set dst IP Port',
559 }
560
561 action_choices = {
562 ('output=', 'all') : 'Forward to all ports',
563 ('output=', 'controller') : 'Forward to controller',
564 ('output=', 'local') : 'Forward to local',
565 ('output=', 'ingress-port') : 'Forward to ingress port',
566 ('output=', 'normal') : 'Forward to ingress port',
567 ('output=', 'flood') : 'Forward, flood ports',
568 ('output=', ('<number>', '<number>')) : 'Forward, to a specific port',
569
570 ('enqueue=', ('<portNumber>.<queueID>', '<portNumber>.<queueID>')) : 'Enqueue to port, queue id',
571
572 ('set-vlan-id=',('<vlan number>','<vlan number>')) : 'Set vlan to <vlan number>',
573
574 ('set-vlan-priority=',('<vlan prio>','<vlan prio>')) : 'Set vlan priority to <prio>',
575
576 ('set-tos-bits=',('<number>',)) : 'Set TOS bits',
577 ('set-src-mac=',('<src-mac-address>',)) : 'Set src mac address',
578
579 ('set-dst-mac=',('<dst-mac-address>',)) : 'Set dst mac address',
580
581 ('set-src-ip=',('<src-ip-address>',)) : 'Set src mac address',
582
583 ('set-dst-ip=',('<src-ip-address>',)) : 'Set dst ip address',
584 }
585
586 for ps in prefix.split(','):
587 ps_parts = ps.split('=')
588 if len(ps_parts) == 1 and ps_parts[0] != '':
589 # possibly incomplete item before the '='
590 for choice in [x for x in actions.keys() if x.startswith(ps_parts[0])]:
591 completions[choice] = actions[choice]
592 return
593 elif len(ps_parts) == 2:
594 if len(ps_parts[0]) and len(ps_parts[1]):
595 prefix_parts.append((ps_parts[0], ps_parts[1]))
596 elif len(ps_parts[0]) and len(ps_parts[1]) == 0:
597 prefix_parts.append((ps_parts[0], ))
598
599 if prefix == '' or prefix.endswith(','):
600 completions.update(actions)
601 elif prefix.endswith('='):
602 last = prefix_parts[-1]
603 for ((match, next), desc) in action_choices.items():
604 if match[:-1] != last[0]:
605 continue
606 if type(next) == str:
607 completions[match + next] = desc
608 elif type(next) == tuple:
609 completions[(match + next[0], match + next[0])] = desc
610 # else? display error?
611 elif len(prefix_parts):
612 last = prefix_parts[-1]
613 if len(last) == 1:
614 pass
615 elif len(last) == 2:
616 # try to find the left item
617 for ((match, next), desc) in action_choices.items():
618 if match[:-1] != last[0]:
619 continue
620 if type(next) == str and next == last[1]:
621 eol = prefix + ' <cr>'
622 completions[(eol, eol)] = 'Complete Choice'
623 another = prefix + ','
624 completions[(another, another)] = 'Add another action'
625 elif type(next) == str and next.startswith(last[1]):
626 base_part = ''.join(prefix.rpartition(',')[:-1])
627 completions[base_part + last[0] + '=' + next] = 'Complete selection'
628 elif len(last[1]):
629 # hard to say what choices can be added here,
630 # there are some characters after '=', but none
631 # which match some prefix.
632 pass
633
634 # how to match the values?
635
636
637def complete_description_versions(prefix, completions):
638 for element in os.listdir(sdnsh.command_packages_path()):
639 if element == '__init__.py':
640 pass
641 elif element.startswith('version'):
642 # len('element') -> 7
643 version = "%2.2f" % (float(element[7:]) / 100)
644 if version[-2:] == '00':
645 version = version[:2] + '0'
646 if version.startswith(prefix):
647 completions[version] = 'VERSION'
648 if version == '2.0': # currently if 2.0 exists, so does 1.0
649 if '1.0'.startswith(prefix):
650 completions['1.0'] = 'VERSION'
651 else:
652 if element.startswith(prefix):
653 completions[element] = 'VERSION'
654
655
656def complete_log_names(prefix, data, completions):
657 """
658 Enumerate all the log file choices based on replies from the REST API.
659 """
660 controller = data.get('controller')
661 for ip_port in controller_ip_and_port(controller):
662 url = log_url(ip_and_port = ip_port)
663 log_names = command.sdnsh.rest_simple_request_to_dict(url)
664 for log in log_names:
665 log_name = log['log']
666 if log_name.startswith(prefix):
667 completions[log_name + ' '] = 'Log Selection'
668
669
670
671def init_completions(bs, modi):
672 global sdnsh, mi
673 sdnsh = bs
674 mi = modi
675
676 command.add_completion('complete-object-field', complete_object_field,
677 {'kwargs': {'obj_type' : '$obj-type',
678 'parent_field' : '$parent-field',
679 'parent_id' : '$current-mode-obj-id',
680 'field' : '$field',
681 'prefix' : '$text',
682 'data' : '$data',
683 'scoped' : '$scoped',
684 'other' : '$other',
685 'mode' : '$mode',
686 'completions' : '$completions'}})
687
688 command.add_completion('complete-tag-mapping', complete_tag_mapping,
689 {'kwargs': {'obj_type' : '$obj-type',
690 'parent_field' : '$parent-field',
691 'parent_id' : '$current-mode-obj-id',
692 'field' : '$field',
693 'prefix' : '$text',
694 'data' : '$data',
695 'scoped' : '$scoped',
696 'other' : '$other',
697 'mode' : '$mode',
698 'completions' : '$completions'}})
699
700 command.add_completion('complete-from-another', complete_from_another,
701 {'kwargs': {'other' : '$other',
702 'obj_type' : '$obj-type',
703 'parent_field' : '$parent-field',
704 'parent_id' : '$current-mode-obj-id',
705 'field' : '$field',
706 'prefix' : '$text',
707 'data' : '$data',
708 'scoped' : '$scoped',
709 'completions' : '$completions',
710 'no_command' : '$is-no-command',
711 'explicit' : '$explicit', }})
712
713 command.add_completion('complete-alias-choice', complete_alias_choice,
714 {'kwargs': {'obj_type' : '$obj-type',
715 'field' : '$field',
716 'other' : '$other',
717 'prefix' : '$text',
718 'data' : '$data',
719 'scoped' : '$scoped',
720 'completions' : '$completions',
721 'no_command' : '$is-no-command', }})
722
723 command.add_completion('complete-config', complete_config,
724 {'kwargs': {'prefix': '$text',
725 'data': '$data',
726 'completions': '$completions'}})
727
728 command.add_completion('complete-config-copy', complete_config,
729 {'kwargs': {'prefix': '$text',
730 'data': '$data',
731 'completions': '$completions',
732 'copy' : True }})
733
734 command.add_completion('complete-interface-list', complete_interface_list,
735 {'kwargs': {'prefix': '$text',
736 'data': '$data',
737 'completions': '$completions'}})
738
739 command.add_completion('complete-staticflow-actions', complete_staticflow_actions,
740 {'kwargs': {'prefix': '$text',
741 'data': '$data',
742 'completions': '$completions'}})
743
744 command.add_completion('description-versions', complete_description_versions,
745 {'kwargs': {'prefix': '$text',
746 'completions': '$completions'}})
747
748 command.add_completion('complete-log-names', complete_log_names,
749 {'kwargs': {'prefix' : '$text',
750 'data' : '$data',
751 'completions': '$completions'}})
752