# Copyright (C) 2008 Dejan Muhamedagic # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # import os import time import copy import readline from cibconfig import CibFactory from cibstatus import CibStatus from levels import Levels from ra import * from vars import Vars from utils import * from xmlutil import * class CompletionHelp(object): ''' Print some help on whatever last word in the line. ''' timeout = 60 # don't print again and again def __init__(self): self.laststamp = 0 self.lastitem = '' def help(self,f,*args): words = readline.get_line_buffer().split() if not words: return key = words[-1] if key.endswith('='): key = key[0:-1] if self.lastitem == key and \ time.time() - self.laststamp < self.timeout: return help_s = f(key,*args) if help_s: print "\n%s" % help_s print "%s%s" % (vars.prompt,readline.get_line_buffer()), self.laststamp = time.time() self.lastitem = key def attr_cmds(idx,delimiter = False): if delimiter: return ' ' return ["delete","set","show"] def nodes_list(idx,delimiter = False): if delimiter: return ' ' return listnodes() def shadows_list(idx,delimiter = False): if delimiter: return ' ' return listshadows() def templates_list(idx,delimiter = False): if delimiter: return ' ' return listtemplates() def config_list(idx,delimiter = False): if delimiter: return ' ' return listconfigs() def config_list_method(idx,delimiter = False): if delimiter: return ' ' return listconfigs() + ["replace","update"] def shadows_live_list(idx,delimiter = False): if delimiter: return ' ' return listshadows() + ['live'] def rsc_list(idx,delimiter = False): if delimiter: return ' ' doc = resources_xml() if not doc: return [] nodes = get_interesting_nodes(doc,[]) return [x.getAttribute("id") for x in nodes if is_resource(x)] def null_list(idx,delimiter = False): if delimiter: return ' ' return [] def loop(idx,delimiter = False): "just a marker in a list" pass def id_xml_list(idx,delimiter = False): if delimiter: return ' ' return cib_factory.id_list() + ['xml','changed'] def id_list(idx,delimiter = False): if delimiter: return ' ' return cib_factory.id_list() def f_prim_id_list(idx,delimiter = False): if delimiter: return ' ' return cib_factory.f_prim_id_list() def f_children_id_list(idx,delimiter = False): if delimiter: return ' ' return cib_factory.f_children_id_list() def rsc_id_list(idx,delimiter = False): if delimiter: return ' ' return cib_factory.rsc_id_list() def node_id_list(idx,delimiter = False): if delimiter: return ' ' return cib_factory.node_id_list() def node_attr_keyw_list(idx,delimiter = False): if delimiter: return ' ' return vars.node_attributes_keyw def status_node_list(idx,delimiter = False): if delimiter: return ' ' return cib_status.status_node_list() def status_rsc_list(idx,delimiter = False): if delimiter: return ' ' return cib_status.status_rsc_list() def node_states_list(idx,delimiter = False): if delimiter: return ' ' return vars.node_states def ra_operations_list(idx,delimiter = False): if delimiter: return ' ' return vars.ra_operations def lrm_exit_codes_list(idx,delimiter = False): if delimiter: return ' ' return vars.lrm_exit_codes.keys() def lrm_status_codes_list(idx,delimiter = False): if delimiter: return ' ' return vars.lrm_status_codes.keys() def skills_list(idx,delimiter = False): if delimiter: return ' ' return user_prefs.skill_levels.keys() def ra_classes_list(idx,delimiter = False): if delimiter: return ':' return ra_classes() # # completion for primitives including help for parameters # (help also available for properties) # def get_primitive_type(words): try: idx = words.index("primitive") + 2 type_word = words[idx] except: type_word = '' return type_word def ra_type_list(toks,idx,delimiter): if idx == 2: if toks[0] == "ocf": dchar = ':' l = ra_providers_all() else: dchar = ' ' l = ra_types(toks[0]) elif idx == 3: dchar = ' ' if toks[0] == "ocf": l = ra_types(toks[0],toks[1]) else: l = ra_types(toks[0]) if delimiter: return dchar return l def prim_meta_attr_list(idx,delimiter = False): if delimiter: return '=' return vars.rsc_meta_attributes def op_attr_list(idx,delimiter = False): if delimiter: return '=' return vars.op_attributes def operations_list(): return vars.op_cli_names def prim_complete_meta(ra,delimiter = False): if delimiter: return '=' return prim_meta_attr_list(0,delimiter) def prim_complete_op(ra,delimiter): words = split_buffer() if (readline.get_line_buffer()[-1] == ' ' and words[-1] == "op") \ or (readline.get_line_buffer()[-1] != ' ' and words[-2] == "op"): dchar = ' ' l = operations_list() else: if readline.get_line_buffer()[-1] == '=': dchar = ' ' l = [] else: dchar = '=' l = op_attr_list() if delimiter: return dchar return l def prim_complete_params(ra,delimiter): if readline.get_line_buffer()[-1] == '=': dchar = ' ' l = [] else: dchar = '=' l = ra.completion_params() if delimiter: return dchar return l def prim_params_info(key,ra): return ra.meta_parameter(key) def meta_attr_info(key,ra): pass def op_attr_info(key,ra): pass def get_lastkeyw(words,keyw): revwords = copy.copy(words) revwords.reverse() for w in revwords: if w in keyw: return w def primitive_complete_complex(idx,delimiter = False): ''' This completer depends on the content of the line, i.e. on previous tokens, in particular on the type of the RA. ''' completers_set = { "params": (prim_complete_params, prim_params_info), "meta": (prim_complete_meta, meta_attr_info), "op": (prim_complete_op, op_attr_info), } # manage the resource type words = readline.get_line_buffer().split() type_word = get_primitive_type(words) toks = type_word.split(':') if toks[0] != "ocf": idx += 1 if idx in (2,3): return ra_type_list(toks,idx,delimiter) # create an ra object ra = None ra_class,provider,rsc_type = disambiguate_ra_type(type_word) if ra_type_validate(type_word,ra_class,provider,rsc_type): ra = RAInfo(ra_class,rsc_type,provider) keywords = completers_set.keys() if idx == 4: if delimiter: return ' ' return keywords lastkeyw = get_lastkeyw(words,keywords) if '=' in words[-1] and readline.get_line_buffer()[-1] != ' ': if not delimiter and lastkeyw and \ readline.get_line_buffer()[-1] == '=' and len(words[-1]) > 1: compl_help.help(completers_set[lastkeyw][1],ra) if delimiter: return ' ' return ['*'] else: if lastkeyw: return completers_set[lastkeyw][0](ra,delimiter) def property_complete(idx,delimiter = False): ''' This completer depends on the content of the line, i.e. on previous tokens. ''' ra = get_properties_meta() words = readline.get_line_buffer().split() if '=' in words[-1] and readline.get_line_buffer()[-1] != ' ': if not delimiter and \ readline.get_line_buffer()[-1] == '=' and len(words[-1]) > 1: compl_help.help(prim_params_info,ra) if delimiter: return ' ' return ['*'] else: return prim_complete_params(ra,delimiter) # # core completer stuff # def lookup_dynamic(fun_list,idx,f_idx,words): if not fun_list: return [] if fun_list[f_idx] == loop: f_idx -= 1 f = fun_list[f_idx] w = words[0] wordlist = f(idx) delimiter = f(idx,1) if len(wordlist) == 1 and wordlist[0] == '*': return lookup_dynamic(fun_list,idx+1,f_idx+1,words[1:]) elif len(words) == 1: return [x+delimiter for x in wordlist if x.startswith(w)] return lookup_dynamic(fun_list,idx+1,f_idx+1,words[1:]) def lookup_words(ctab,words): if not ctab: return [] if type(ctab) == type(()): return lookup_dynamic(ctab,0,0,words) if len(words) == 1: return [x+' ' for x in ctab if x.startswith(words[0])] elif words[0] in ctab.keys(): return lookup_words(ctab[words[0]],words[1:]) return [] def split_buffer(): p = readline.get_line_buffer() p = p.replace(':',' ').replace('=',' ') return p.split() def completer(txt,state): levels = Levels.getInstance() words = split_buffer() if readline.get_begidx() == readline.get_endidx(): words.append('') matched = lookup_words(levels.completion_tab,words) matched.append(None) return matched[state] def setup_readline(): readline.set_history_length(100) readline.parse_and_bind("tab: complete") readline.set_completer(completer) readline.set_completer_delims(\ readline.get_completer_delims().replace('-','').replace('/','').replace('=','')) try: readline.read_history_file(vars.hist_file) except: pass # # a dict of completer functions # (feel free to add more completers) # completer_lists = { "options" : { "skill-level" : (skills_list,), "editor" : None, "pager" : None, "user" : None, "output" : None, "colorscheme" : None, "check-frequency" : None, "check-mode" : None, "sort-elements" : None, "save" : None, "show" : None, }, "cib" : { "new" : None, "delete" : (shadows_list,), "reset" : (shadows_list,), "commit" : (shadows_list,), "use" : (shadows_live_list,), "diff" : None, "list" : None, "import" : None, "cibstatus" : None, }, "template" : { "new" : (null_list,templates_list,loop), "load" : (config_list,), "edit" : (config_list,), "delete" : (config_list,), "show" : (config_list,), "apply" : (config_list_method,config_list), "list" : None, }, "resource" : { "status" : (rsc_list,), "start" : (rsc_list,), "stop" : (rsc_list,), "restart" : (rsc_list,), "promote" : (rsc_list,), "demote" : (rsc_list,), "manage" : (rsc_list,), "unmanage" : (rsc_list,), "migrate" : (rsc_list,nodes_list), "unmigrate" : (rsc_list,), "param" : (rsc_list,attr_cmds), "meta" : (rsc_list,attr_cmds), "utilization" : (rsc_list,attr_cmds), "failcount" : (rsc_list,attr_cmds,nodes_list), "cleanup" : (rsc_list,nodes_list), "refresh" : (nodes_list,), "reprobe" : (nodes_list,), }, "node" : { "status" : (nodes_list,), "show" : (nodes_list,), "standby" : (nodes_list,), "online" : (nodes_list,), "fence" : (nodes_list,), "delete" : (nodes_list,), "clearstate" : (nodes_list,), "attribute" : (nodes_list,attr_cmds), "utilization" : (nodes_list,attr_cmds), "status-attr" : (nodes_list,attr_cmds), }, "ra" : { "classes" : None, "list" : None, "providers" : None, "meta" : None, }, "cibstatus" : { "show" : None, "save" : None, "load" : None, "origin" : None, "node" : (status_node_list,node_states_list), "op" : (ra_operations_list,status_rsc_list,lrm_exit_codes_list,lrm_status_codes_list,status_node_list), "run" : None, "simulate" : None, "quorum" : None, }, "configure" : { "erase" : None, "verify" : None, "refresh" : None, "ptest" : None, "commit" : None, "upgrade" : None, "show" : (id_xml_list,id_list,loop), "edit" : (id_xml_list,id_list,loop), "filter" : (null_list,id_xml_list,id_list,loop), "delete" : (id_list,loop), "default-timeouts" : (id_list,loop), "rename" : (id_list,id_list), "save" : None, "load" : None, "node" : (node_id_list,node_attr_keyw_list), "primitive" : (null_list,ra_classes_list,primitive_complete_complex,loop), "group" : (null_list,f_prim_id_list,loop), "clone" : (null_list,f_children_id_list), "ms" : (null_list,f_children_id_list), "location" : (null_list,rsc_id_list), "colocation" : (null_list,null_list,rsc_id_list,loop), "order" : (null_list,null_list,rsc_id_list,loop), "property" : (property_complete,loop), "rsc_defaults" : (prim_complete_meta,loop), "op_defaults" : (op_attr_list,loop), "xml" : None, "monitor" : None, "ra" : None, "cib" : None, "cibstatus" : None, "template" : None, "_test" : None, "_regtest" : None, "_objects" : None, }, } def get_completer_list(level,cmd): 'Return a list of completer functions.' try: return completer_lists[level][cmd] except: return None compl_help = CompletionHelp() user_prefs = UserPrefs.getInstance() vars = Vars.getInstance() cib_status = CibStatus.getInstance() cib_factory = CibFactory.getInstance() # vim:ts=4:sw=4:et: