# Copyright (C) 2008-2011 Dejan Muhamedagic # Copyright (C) 2013 Kristoffer Gronlund # See COPYING for license information. import re import time from . import command from . import completers as compl from . import config from . import utils from . import constants from . import userdir from . import xmlutil from . import ra from .cibconfig import mkset_obj, cib_factory from . import clidisplay from . import term from . import options from . import rsctest from . import schema from . import ui_cib from . import ui_cibstatus from . import ui_ra from . import ui_template from . import ui_history from . import ui_utils from . import ui_assist from .crm_gv import gv_types from .ui_node import get_resources_on_nodes, remove_redundant_attrs from . import log logger = log.setup_logger(__name__) logger_utils = log.LoggerUtils(logger) def _type_completions(): "completer for type: use in show" typelist = cib_factory.type_list() return ['type:%s' % (t) for t in typelist] def _tag_completions(): "completer for tag: use in show" return ['tag:%s' % (t) for t in cib_factory.tag_list()] # Tab completion helpers _id_list = compl.call(cib_factory.id_list) _id_xml_list = compl.join(_id_list, compl.choice(['xml'])) _id_show_list = compl.join(_id_list, compl.choice(['xml', 'changed']), compl.call(_type_completions), compl.call(_tag_completions)) _prim_id_list = compl.call(cib_factory.prim_id_list) _f_prim_free_id_list = compl.call(cib_factory.f_prim_free_id_list) _f_group_id_list = compl.call(cib_factory.f_group_id_list) _f_children_id_list = compl.call(cib_factory.f_children_id_list) _rsc_id_list = compl.call(cib_factory.rsc_id_list) _top_rsc_id_list = compl.call(cib_factory.top_rsc_id_list) _node_id_list = compl.call(cib_factory.node_id_list) _rsc_template_list = compl.call(cib_factory.rsc_template_list) _container_type = compl.choice(constants.container_type) def _group_completer(args): ''' completer for group resource ''' completing = args[-1] id_list = cib_factory.f_prim_free_id_list() if completing in id_list: return [completing] # complete resources id first if len(args) == 3: return [s for s in id_list if s not in args] # complete meta or params attributes key_words = ["meta", "params"] keyw = last_keyword(args, key_words) if keyw in key_words: return _advanced_completer(args) # otherwise, complete resources ids and some key words return [s for s in id_list if s not in args] + _advanced_completer(args) def _advanced_completer(args): ''' meta completers for group/ms/clone resource type ''' key_words = ["meta", "params"] completing = args[-1] resource_type = args[0] return_list = [] if completing.endswith('='): # TODO add some help messages return [] keyw = last_keyword(args, key_words) if keyw and keyw == "meta": if resource_type == "group": return_list = utils.filter_keys(constants.group_meta_attributes, args) if resource_type == "clone": return_list = utils.filter_keys(constants.clone_meta_attributes, args) if resource_type in ["ms", "master"]: return_list = utils.filter_keys(constants.ms_meta_attributes, args) return return_list + key_words def _list_resource(args): if len(args) > 3: if args[2] == "remove": res = cib_factory.f_prim_list_in_group(args[1]) if len(res) <= 1: return [] else: return res if args[2] == "add": return cib_factory.f_prim_free_id_list() def _list_resource_2(args): if len(args) > 5: return cib_factory.f_prim_list_in_group(args[1]) def _pick_position(args): if args[2] == "remove": return [] else: return ["after", "before"] def top_rsc_tmpl_id_list(args): return cib_factory.top_rsc_id_list() + cib_factory.rsc_template_list() def ra_classes_or_tmpl(args): return ui_ra.complete_class_provider_type(args) + \ ['@'+x for x in cib_factory.rsc_template_list()] def op_attr_list(args): schema_attr = [schema.get('attr', 'op', 'o') + '='] extra_attrs = [s + '=' for s in constants.op_extra_attrs] return schema_attr + extra_attrs def node_id_colon_list(args): return [s + ':' for s in _node_id_list(args)] def stonith_resource_list(args): return [x.obj_id for x in cib_factory.get_elems_on_type("type:primitive") if x.node.get("class") == "stonith"] def _load_2nd_completer(args): if args[1] == 'xml': return ['replace', 'update', 'push'] return [] # completion for primitives including help for parameters # (help also available for properties) def get_prim_token(words, n): for key in ("primitive", "rsc_template"): try: if key in words: return words[words.index(key) + n - 1] except IndexError: pass return '' def ra_agent_for_template(tmpl): '''@template -> ra.agent''' obj = cib_factory.find_resource(tmpl[1:]) if obj is None: return None return ra.get_ra(obj.node) def ra_agent_for_cpt(cpt): '''class:provider:type -> ra.agent''' agent = None ra_class, provider, rsc_type = ra.disambiguate_ra_type(cpt) if ra.ra_type_validate(cpt, ra_class, provider, rsc_type): agent = ra.RAInfo(ra_class, rsc_type, provider) return agent class CompletionHelp(object): ''' Print some help on whatever last word in the line. ''' timeout = 60 # don't print again and again laststamp = 0 lasttopic = '' @classmethod def help(cls, topic, helptxt, args): if cls.lasttopic == topic and \ time.time() - cls.laststamp < cls.timeout: return if helptxt: import readline cmdline = readline.get_line_buffer() print("\n%s" % helptxt, end='') if cmdline.split()[0] != args[0]: prompt = ' > ' else: if clidisplay.colors_enabled(): prompt = term.render(clidisplay.prompt_noreadline(constants.prompt)) else: prompt = constants.prompt print("\n%s%s" % (prompt, cmdline), end=' ') cls.laststamp = time.time() cls.lasttopic = topic def _prim_params_completer(agent, args): completing = args[-1] if completing == 'params': return ['params'] if completing.endswith('='): if len(completing) > 1 and options.interactive: topic = completing[:-1] CompletionHelp.help(topic, agent.meta_parameter(topic), args) return [] elif '=' in completing: return [] return utils.filter_keys(agent.params(completion=True), args) def _prim_meta_completer(agent, args): completing = args[-1] if completing == 'meta': return ['meta'] if '=' in completing: return [] return utils.filter_keys(constants.rsc_meta_attributes, args) def _prim_op_completer(agent, args): def concat_kv(k, v): return "{}={}".format(k, v) if args[-1] == 'op': return ['op'] actions = agent.actions() if not actions: return [] # list all actions, select one to complete if args[-2] == 'op': return actions.keys() # list all attributes of the action, select one to complete if args[-3] == 'op': res = [] op_name = args[-2] if op_name == 'monitor': for one_monitor in actions[op_name]: res += [concat_kv(k, v) for k, v in one_monitor.items()] else: res = [concat_kv(k, v) for k, v in actions[op_name].items()] return res args.pop() if '=' in args[-1]: res = [] # find latest action op_name = None for i, item in enumerate(reversed(args)): if item in actions: op_name = item break if not op_name: return [] # list all left attributes of the action, select one to complete actions_list_in_args = [arg.split('=')[0] for arg in args[len(args)-i:]] if op_name == 'monitor': for one_monitor in actions[op_name]: res += [concat_kv(k, v) for k, v in one_monitor.items() if k not in actions_list_in_args] else: res = [concat_kv(k, v) for k, v in actions[op_name].items() if k not in actions_list_in_args] return res return [] def last_keyword(words, keyw): '''returns the last occurance of an element in keyw in words''' for w in reversed(words): if w in keyw: return w return None def _property_completer(args): '''context-sensitive completer''' agent = ra.get_properties_meta() return _prim_params_completer(agent, args) def primitive_complete_complex(args): ''' This completer depends on the content of the line, i.e. on previous tokens, in particular on the type of the RA. ''' cmd = get_prim_token(args, 1) type_word = get_prim_token(args, 3) with_template = cmd == 'primitive' and type_word.startswith('@') if with_template: agent = ra_agent_for_template(type_word) else: agent = ra_agent_for_cpt(type_word) if agent is None: return [] completers_set = { "params": _prim_params_completer, "meta": _prim_meta_completer, "op": _prim_op_completer, } keywords = list(completers_set.keys()) if len(args) == 4: # return keywords last_keyw = last_keyword(args, keywords) if last_keyw is None: return [] complete_results = completers_set[last_keyw](agent, args) if len(args) > 4 and '=' in args[-1]: return complete_results + keywords return complete_results def container_helptxt(params, helptxt, topic): for item in reversed(params): if item in ["storage", "network", "docker", "rkt"]: return helptxt[item][topic] + "\n" if item == "port-mapping": return helptxt["network"][item][topic] + "\n" def _container_remove_exist_keywords(args, _keywords): for item in ["network", "primitive"]: if item in args: _keywords.remove(item) def _container_network_completer(args, _help, _keywords): key_words = ["network", "port-mapping"] completing = args[-1] token = args[-2] if completing.endswith("="): return [] if completing in key_words: return [completing] tmp = list(_help["network"].keys()) # port-mapping is element, not a network option tmp.remove("port-mapping") network_keys = utils.filter_keys(tmp, args) # bundle contain just one / element _container_remove_exist_keywords(args, _keywords) last_keyw = last_keyword(args, key_words) if last_keyw == "network": if token == "network": return network_keys else: # complete port-mapping or other parts return network_keys + ["port-mapping"] + _keywords if last_keyw == "port-mapping": mapping_required = ["id"] mapping_params = args[utils.rindex(args, "port-mapping"):] mapping_keys = utils.filter_keys(_help["network"]["port-mapping"].keys(), mapping_params) if token == "port-mapping": return mapping_keys # required options must be completed for s in mapping_required: if utils.any_startswith(mapping_params, s+'=') is None: return mapping_keys # complete port-mapping or other parts return mapping_keys + ["port-mapping"] + _keywords def _container_storage_completer(args, _help, _keywords): completing = args[-1] if completing.endswith("="): return [] if completing == "storage": return [completing] if args[-2] == "storage": return ["storage-mapping"] storage_required = ["id", "target-dir"] # get last storage part mapping_params = args[utils.rindex(args, "storage-mapping"):] storage_keys = utils.filter_keys(_help["storage"].keys(), mapping_params) # required options must be completed for s in storage_required: if utils.any_startswith(mapping_params, s+"=") is None: return storage_keys # bundle contain just one / element _container_remove_exist_keywords(args, _keywords) # complete storage or other parts return storage_keys + _keywords def _container_primitive_completer(args, _help, _keywords): completing = args[-1] if completing == "primitive": return [completing] _id_list = cib_factory.f_prim_free_id_list() if _id_list is None: return [] # bundle contain just one / element _container_remove_exist_keywords(args, _keywords) if args[-3] == "primitive" and args[-2] in _id_list: return _keywords return _id_list def _container_meta_completer(args, helptxt, _keywords): completing = args[-1] if completing.endswith("="): return [] if completing == "meta": return [completing] # bundle contain just one / element _container_remove_exist_keywords(args, _keywords) return utils.filter_keys(constants.bundle_meta_attributes, args) + _keywords def container_complete_complex(args): ''' Complete five parts: container options, network, storage, primitive and meta ''' container_options_required = ["image"] completing = args[-1] container_type = args[2] completers_set = { "network": _container_network_completer, "storage": _container_storage_completer, "primitive": _container_primitive_completer, "meta": _container_meta_completer } keywords = list(completers_set.keys()) last_keyw = last_keyword(args, keywords) # to show help messages if completing.endswith('='): if len(completing) > 1 and options.interactive: topic = completing[:-1] CompletionHelp.help(topic, container_helptxt(args, constants.container_helptxt, topic), args) return [] container_options = utils.filter_keys(constants.container_helptxt[container_type].keys(), args) # required options must be completed for s in container_options_required: if utils.any_startswith(args, s+'=') is None: return container_options if last_keyw is None: return container_options + keywords # to complete network, storage, primitive and meta return completers_set[last_keyw](args, constants.container_helptxt, keywords) class CibConfig(command.UI): ''' The configuration class ''' name = "configure" def __init__(self): command.UI.__init__(self) # for interactive use, we want to populate the CIB # immediately so that tab completion works def requires(self): if not cib_factory.initialize(): return False # see the configure ptest/simulate command has_ptest = utils.is_program('ptest') has_simulate = utils.is_program('crm_simulate') if not has_ptest: constants.simulate_programs["ptest"] = "crm_simulate" if not has_simulate: constants.simulate_programs["simulate"] = "ptest" if not (has_ptest or has_simulate): logger.warning("neither ptest nor crm_simulate exist, check your installation") constants.simulate_programs["ptest"] = "" constants.simulate_programs["simulate"] = "" return True @command.name('_test') @command.skill_level('administrator') def do_check_structure(self, context): cib_factory.ensure_cib_updated() return cib_factory.check_structure() @command.name('_regtest') @command.skill_level('administrator') def do_regression_testing(self, context, param): return cib_factory.regression_testing(param) @command.name('_objects') @command.skill_level('administrator') def do_showobjects(self, context): cib_factory.showobjects() @command.name('_keywords') @command.skill_level('administrator') def do_keywords(self, context): for k, v in sorted(iter(constants.keywords.items()), key=lambda v: v[0].lower()): print("%-16s %s" % (k, v)) @command.level(ui_ra.RA) def do_ra(self): pass @command.level(ui_cib.CibShadow) def do_cib(self): pass @command.level(ui_cibstatus.CibStatusUI) def do_cibstatus(self): pass @command.level(ui_template.Template) def do_template(self): pass @command.level(ui_history.History) def do_history(self): pass @command.level(ui_assist.Assist) def do_assist(self): pass @command.skill_level('administrator') @command.completers_repeating(_id_show_list) def do_show(self, context, *args): "usage: show [xml] [...]" from .utils import obscure osargs = [arg[8:] for arg in args if arg.startswith('obscure:')] if not osargs and config.core.obscure_pattern: # obscure_pattern could be # 1. "pattern1 pattern2 pattern3" # 2. "pattern1|pattern2|pattern3" # regrex here also filter out possible spaces osargs = re.split('\s*\|\s*|\s+', config.core.obscure_pattern.strip('|')) args = [arg for arg in args if not arg.startswith('obscure:')] cib_factory.ensure_cib_updated() with obscure(osargs): set_obj = mkset_obj(*args) return set_obj.show() @command.name("get_property") @command.alias("get-property") @command.skill_level('administrator') @command.completers_repeating(compl.call(ra.get_properties_list)) def do_get_property(self, context, *args): "usage: get-property [-t|--true [...]" properties = [a for a in args if a not in ('-t', '--true')] truth = any(a for a in args if a in ('-t', '--true')) if not properties: utils.multicolumn(ra.get_properties_list()) return def print_value(v): if truth: print(utils.canonical_boolean(v)) else: print(v) cib_factory.ensure_cib_updated() for p in properties: v = cib_factory.get_property_w_default(p) if v is not None: print_value(v) elif truth: print("false") else: context.fatal_error("%s: Property not set" % (p)) @command.skill_level('administrator') @command.completers_repeating(compl.null, _id_xml_list, _id_list) def do_filter(self, context, filterprog, *args): "usage: filter [xml] [...]" cib_factory.ensure_cib_updated() set_obj = mkset_obj(*args) return set_obj.filter(filterprog) @command.skill_level('administrator') @command.completers(_id_list) def do_set(self, context, path, value): """ usage: set path:: id.[op_type.][interval.]name """ path_errmsg = "Invalid path: \"{}\"; Valid path: \"id.[op_type.][interval.]name\"".format(path) path_list = path.split('.') if len(path_list) < 2 or len(path_list) > 4: context.fatal_error(path_errmsg) cib_factory.ensure_cib_updated() obj_id, *other_path_list = path_list rsc = cib_factory.find_object(obj_id) if not rsc: context.fatal_error("Object {} not found".format(obj_id)) # Use case for: set id.name value if len(other_path_list) == 1: obj_attr = other_path_list[0] nvpairs = rsc.node.xpath(".//nvpair[@name='{}']".format(obj_attr)) if not nvpairs: context.fatal_error("Attribute not found: {}".format(path)) if len(nvpairs) != 1: context.fatal_error("Expected 1 attribute named {}, found {}".format(obj_attr, len(nvpairs))) nvpairs[0].set("value", value) # Use case for: set id.op_type.name value if len(other_path_list) == 2: op_type, name = other_path_list op_res = rsc.node.xpath(".//operations/op[@name='{}']".format(op_type)) if not op_res: context.fatal_error("Operation \"{}\" not found for resource {}".format(op_type, obj_id)) if len(op_res) > 1: context.fatal_error("Should specify interval of {}".format(op_type)) op_res[0].set(name, value) # Use case for: set id.op_type.interval.name value if len(other_path_list) == 3: op_type, iv, name = other_path_list op_res = rsc.node.xpath(".//operations/op[@id='{}-{}-{}']".format(obj_id, op_type, iv)) if not op_res: context.fatal_error("Operation \"{}\" interval \"{}\" not found for resource {}".format(op_type, iv, obj_id)) op_res[0].set(name, value) rsc.set_updated() return True @command.skill_level('administrator') @command.completers(_f_group_id_list, compl.choice(['add', 'remove']), _list_resource, _pick_position, _list_resource_2) def do_modgroup(self, context, group_id, subcmd, prim_id, *args): """usage: modgroup add [after |before ] modgroup remove """ if subcmd not in ("add", "remove"): logger.error("modgroup subcommand %s unknown" % subcmd) return False after_before = None if args: if subcmd != 'add': context.fatal_error("Expected add (found %s)" % subcmd) if args[0] not in ("after", "before"): context.fatal_error("Expected after|before (found %s)" % args[0]) if len(args) != 2: context.fatal_error("Expected 'after|before ' (%d arguments given)" % len(args)) after_before = args[0] ref_member_id = args[1] cib_factory.ensure_cib_updated() g = cib_factory.find_object(group_id) if not g: context.fatal_error("group %s does not exist" % group_id) if not xmlutil.is_group(g.node): context.fatal_error("element %s is not a group" % group_id) children = xmlutil.get_rsc_children_ids(g.node) if after_before and ref_member_id not in children: context.fatal_error("%s is not member of %s" % (ref_member_id, group_id)) if subcmd == "remove" and prim_id not in children: context.fatal_error("%s is not member of %s" % (prim_id, group_id)) # done checking arguments # have a group and children if not after_before: after_before = "after" ref_member_id = children[-1] # just do the filter # (i wonder if this is a feature abuse?) if subcmd == "add": if after_before == "after": sed_s = r's/ %s( |$)/& %s /' % (ref_member_id, prim_id) else: sed_s = r's/ %s( |$)/ %s& /' % (ref_member_id, prim_id) else: sed_s = r's/ %s( |$)/ /' % prim_id l = (group_id,) set_obj = mkset_obj(*l) return set_obj.filter("sed -r '%s'" % sed_s) @command.skill_level('administrator') @command.completers_repeating(_id_xml_list, _id_list) def do_edit(self, context, *args): "usage: edit [xml] [...]" cib_factory.ensure_cib_updated() with logger_utils.buffer(): # keep error messages set_obj = mkset_obj(*args) return set_obj.edit() def _verify(self, set_obj_semantic, set_obj_all): rc1 = set_obj_all.verify() if config.core.check_frequency != "never": rc2 = set_obj_semantic.semantic_check(set_obj_all) else: rc2 = 0 return rc1 and rc2 <= 1 @command.skill_level('administrator') def do_verify(self, context): "usage: verify" cib_factory.ensure_cib_updated() set_obj_all = mkset_obj("xml") return self._verify(set_obj_all, set_obj_all) @command.name('validate-all') @command.alias('validate_all') @command.skill_level('administrator') @command.completers_repeating(_id_list) def do_validate_all(self, context, rsc): "usage: validate-all " cib_factory.ensure_cib_updated() from . import ra from . import cibconfig from . import cliformat obj = cib_factory.find_object(rsc) if not obj: context.error("Not found: %s" % (rsc)) if obj.obj_type != "primitive": context.error("Not a primitive: %s" % (rsc)) rnode = cibconfig.reduce_primitive(obj.node) if rnode is None: context.error("No resource template %s for %s" % (obj.node.get("template"), rsc)) params = [] for attrs in rnode.iterchildren("instance_attributes"): params.extend(cliformat.nvpairs2list(attrs)) if not all(nvp.get('name') is not None and nvp.get('value') is not None for nvp in params): context.error("Primitive too complex: %s" % (rsc)) params = dict([(nvp.get('name'), nvp.get('value')) for nvp in params]) agentname = xmlutil.mk_rsc_type(rnode) if not ra.can_validate_agent(agentname): context.error("%s: Cannot run validate-all for agent: %s" % (rsc, agentname)) rc, _ = ra.validate_agent(agentname, params, log=True) return rc == 0 @command.skill_level('administrator') @command.completers_repeating(_id_show_list) def do_save(self, context, *args): "usage: save [xml] [...] " if not args: context.fatal_error("Expected 1 argument (0 given)") cib_factory.ensure_cib_updated() filename = args[-1] setargs = args[:-1] set_obj = mkset_obj(*setargs) return set_obj.save_to_file(filename) @command.skill_level('administrator') @command.completers(compl.choice(['xml', 'replace', 'update', 'push']), _load_2nd_completer) def do_load(self, context, *args): "usage: load [xml] {replace|update|push} {|}" if len(args) < 2: context.fatal_error("Expected 2 arguments (0 given)") if args[0] == "xml": if len(args) != 3: context.fatal_error("Expected 3 arguments (%d given)" % len(args)) url = args[2] method = args[1] xml = True else: if len(args) != 2: context.fatal_error("Expected 2 arguments (%d given)" % len(args)) url = args[1] method = args[0] xml = False if method not in ("replace", "update", "push"): context.fatal_error("Unknown method %s" % method) cib_factory.ensure_cib_updated() if method == "replace": if options.interactive and cib_factory.has_cib_changed(): if not utils.ask("This operation will erase all changes. Do you want to proceed?"): return False cib_factory.erase() if xml: set_obj = mkset_obj("xml") else: set_obj = mkset_obj() return set_obj.import_file(method, url) @command.skill_level('administrator') @command.completers(compl.choice(list(gv_types.keys()) + ['exportsettings'])) def do_graph(self, context, *args): "usage: graph [ [ []]]" if args and args[0] == "exportsettings": return utils.save_graphviz_file(userdir.GRAPHVIZ_USER_FILE, constants.graph) cib_factory.ensure_cib_updated() set_obj = mkset_obj() rc = set_obj.query_graph(*args) if rc is None: context.fatal_error("Failed to create graph") return rc def _stop_if_running(self, rscs): rscstate = xmlutil.RscState() to_stop = [rsc for rsc in rscs if rscstate.is_running(rsc)] from .ui_resource import set_deep_meta_attr if len(to_stop) > 0: ok = all(set_deep_meta_attr(rsc, 'target-role', 'Stopped', commit=False) for rsc in to_stop) if not ok or not cib_factory.commit(): raise ValueError("Failed to stop one or more running resources: %s" % (', '.join(to_stop))) return len(to_stop) @command.skill_level('administrator') @command.completers_repeating(_id_list) @command.alias('rm') def do_delete(self, context, *args): "usage: delete [-f|--force] [...]" argl = list(args) arg_force = any((x in ('-f', '--force')) for x in argl) argl = [x for x in argl if x not in ('-f', '--force')] if arg_force or config.core.force: if self._stop_if_running(argl) > 0: utils.wait4dc(what="Stopping %s" % (", ".join(argl))) cib_factory.ensure_cib_updated() return cib_factory.delete(*argl) @command.name('default-timeouts') @command.alias('default_timeouts') @command.completers_repeating(_id_list) def do_default_timeouts(self, context, *args): "usage: default-timeouts [...]" cib_factory.ensure_cib_updated() return cib_factory.default_timeouts(*args) @command.skill_level('administrator') @command.completers(_id_list) def do_rename(self, context, old_id, new_id): "usage: rename " cib_factory.ensure_cib_updated() return cib_factory.rename(old_id, new_id) @command.skill_level('administrator') @command.completers(compl.choice(['nodes'])) def do_erase(self, context, nodes=None): "usage: erase [nodes]" if not options.regression_tests: logger.warning("`crm configure erase` is deprecated. The replacement could be `crm cluster remove [node]`") return True cib_factory.ensure_cib_updated() if nodes is None: return cib_factory.erase() if nodes != 'nodes': context.fatal_error("Expected 'nodes' (found '%s')" % (nodes)) return cib_factory.erase_nodes() @command.skill_level('administrator') def do_refresh(self, context): "usage: refresh" if options.interactive and cib_factory.has_cib_changed(): if not utils.ask("All changes will be dropped. Do you want to proceed?"): return cib_factory.refresh() @command.alias('simulate') @command.completers(compl.choice(['nograph'])) def do_ptest(self, context, *args): "usage: ptest [nograph] [v...] [scores] [utilization] [actions]" # use ptest/crm_simulate depending on which command was # used config.core.ptest = constants.simulate_programs[context.get_command_name()] if not config.core.ptest: return False set_obj = mkset_obj("xml") return ui_utils.ptestlike(set_obj.ptest, 'vv', context.get_command_name(), args) def _commit(self, force=False, replace=False): if not cib_factory.has_cib_changed(): logger.info("apparently there is nothing to commit") logger.info("try changing something first") return True rc1 = True if replace and not force: rc1 = cib_factory.is_current_cib_equal() rc2 = cib_factory.has_no_primitives() or \ self._verify(mkset_obj("xml", "changed"), mkset_obj("xml")) if rc1 and rc2: return cib_factory.commit(replace=replace) if force or config.core.force: logger.info("commit forced") return cib_factory.commit(force=True, replace=replace) if utils.ask("Do you still want to commit?"): return cib_factory.commit(force=True, replace=replace) return False @command.skill_level('administrator') @command.wait @command.completers(compl.choice(['force', 'replace']), compl.choice(['force', 'replace'])) def do_commit(self, context, arg0=None, arg1=None): "usage: commit [force] [replace]" force = "force" in [arg0, arg1] replace = "replace" in [arg0, arg1] if arg0 is not None and arg0 not in ("force", "replace"): logger_utils.syntax_err(('configure.commit', arg0)) return False if arg1 is not None and arg1 not in ("force", "replace"): logger_utils.syntax_err(('configure.commit', arg1)) return False return self._commit(force=force, replace=replace) @command.skill_level('administrator') @command.completers(compl.choice(['force'])) def do_upgrade(self, context, force=None): "usage: upgrade [force]" if force and force != "force": context.fatal_error("Expected 'force' or no argument") cib_factory.ensure_cib_updated() return cib_factory.upgrade_validate_with(force=config.core.force or force) @command.skill_level('administrator') def do_schema(self, context, schema_st=None): "usage: schema []" if not schema_st: print(cib_factory.get_schema()) return True return cib_factory.change_schema(schema_st) def __override_lower_level_attrs(self, *args): """ When setting up an attribute of a cluster, the same attribute may already exist in one of the nodes an/or any resource. The user should be informed about it and, if he wants, he will have an option to delete the already existing attribute. """ if not args: return nvpair = args[0].split('=', 1) if 2 != len(nvpair): return attr_name, attr_value = nvpair if "maintenance-mode" == attr_name: attr = "maintenance" conflicting_lower_level_attr = 'is-managed' # FIXME! the first argument is hardcoded objs = get_resources_on_nodes(cib_factory.node_id_list(), [ "primitive", "group", "clone"]) remove_redundant_attrs(objs, "meta_attributes", attr, conflicting_lower_level_attr) objs = get_resources_on_nodes(cib_factory.node_id_list(), [ "node" ]) remove_redundant_attrs(objs, "instance_attributes", attr, conflicting_lower_level_attr) def __conf_object(self, cmd, *args): "The configure object command." if cmd in list(constants.cib_cli_map.values()) and \ not cib_factory.is_elem_supported(cmd): logger.error("%s not supported by the RNG schema" % cmd) return False cib_factory.ensure_cib_updated() if not args: return cib_factory.create_object(cmd, *args) if args[0].startswith("id="): object_id = args[0][3:] else: object_id = args[0] params = (object_id,) + args[1:] return cib_factory.create_object(cmd, *params) @command.skill_level('administrator') @command.completers(_node_id_list, compl.choice(constants.node_attributes_keyw)) def do_node(self, context, *args): """usage: node [:] [attributes = [=...]] [utilization = [=...]]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.attr_id, ra_classes_or_tmpl, primitive_complete_complex) @command.alias('resource') def do_primitive(self, context, *args): """usage: primitive {[:[:]]|@