class Site(command.UI): ''' The site class ''' name = "site" def requires(self): if not utils.is_program('crm_ticket'): no_prog_err('crm_ticket') return False return True @command.skill_level('administrator') @command.completers(compl.choice(_ticket_commands.keys())) def do_ticket(self, context, subcmd, ticket): "usage: ticket {grant|revoke|standby|activate|show|time|delete} <ticket>" base_cmd = "crm_ticket" if config.core.force: base_cmd += " --force" attr_cmd = _ticket_commands.get(subcmd) if not attr_cmd: context.fatal_error('Expected one of %s' % '|'.join(_ticket_commands.keys())) if not utils.is_name_sane(ticket): return False if subcmd not in ("show", "time"): return utils.ext_cmd(attr_cmd % (base_cmd, ticket)) == 0 rc, l = utils.stdout2list(attr_cmd % (base_cmd, ticket)) try: val = l[0] except IndexError: context.fatal_error("apparently nothing to show for ticket %s" % ticket) if subcmd == "show": _show(context, ticket, val) else: # time _time(context, ticket, val)
class RscMgmt(command.UI): ''' Resources management class ''' name = "resource" rsc_status_all = "crm_resource -L" rsc_status = "crm_resource -W -r '%s'" rsc_showxml = "crm_resource -q -r '%s'" rsc_setrole = "crm_resource --meta -r '%s' -p target-role -v '%s'" rsc_migrate = "crm_resource -M -r '%s' %s" rsc_unmigrate = "crm_resource -U -r '%s'" rsc_cleanup = "crm_resource -C -r '%s' -H '%s'" rsc_cleanup_all = "crm_resource -C -r '%s'" rsc_maintenance = "crm_resource -r '%s' --meta -p maintenance -v '%s'" rsc_param = { 'set': "crm_resource -r '%s' -p '%s' -v '%s'", 'delete': "crm_resource -r '%s' -d '%s'", 'show': "crm_resource -r '%s' -g '%s'", } rsc_meta = { 'set': "crm_resource --meta -r '%s' -p '%s' -v '%s'", 'delete': "crm_resource --meta -r '%s' -d '%s'", 'show': "crm_resource --meta -r '%s' -g '%s'", } rsc_failcount = { 'set': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -v '%s' -d 0", 'delete': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -D -d 0", 'show': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -G -d 0", } rsc_utilization = { 'set': "crm_resource -z -r '%s' -p '%s' -v '%s'", 'delete': "crm_resource -z -r '%s' -d '%s'", 'show': "crm_resource -z -r '%s' -g '%s'", } rsc_secret = { 'set': "cibsecret set '%s' '%s' '%s'", 'stash': "cibsecret stash '%s' '%s'", 'unstash': "cibsecret unstash '%s' '%s'", 'delete': "cibsecret delete '%s' '%s'", 'show': "cibsecret get '%s' '%s'", 'check': "cibsecret check '%s' '%s'", } rsc_refresh = "crm_resource -R" rsc_refresh_node = "crm_resource -R -H '%s'" rsc_reprobe = "crm_resource -P" rsc_reprobe_node = "crm_resource -P -H '%s'" def requires(self): for program in ('crm_resource', 'crm_attribute'): if not utils.is_program(program): no_prog_err(program) return False return True @command.alias('show', 'list') @command.completers(compl.resources) def do_status(self, context, rsc=None): "usage: status [<rsc>]" if rsc: if not utils.is_name_sane(rsc): return False return utils.ext_cmd(self.rsc_status % rsc) == 0 else: return utils.ext_cmd(self.rsc_status_all) == 0 @command.wait @command.completers(compl.resources) def do_start(self, context, rsc): "usage: start <rsc>" if not utils.is_name_sane(rsc): return False return set_deep_meta_attr("target-role", "Started", rsc) @command.wait @command.completers(compl.resources) def do_restart(self, context, rsc): "usage: restart <rsc>" if not utils.is_name_sane(rsc): return False common_info("ordering %s to stop" % rsc) if not self.do_stop(context, rsc): return False if not utils.wait4dc("stop", not options.batch): return False common_info("ordering %s to start" % rsc) return self.do_start(context, rsc) @command.wait @command.completers(compl.resources) def do_stop(self, context, rsc): "usage: stop <rsc>" if not utils.is_name_sane(rsc): return False return set_deep_meta_attr("target-role", "Stopped", rsc) @command.wait @command.completers(compl.resources) def do_promote(self, context, rsc): "usage: promote <rsc>" if not utils.is_name_sane(rsc): return False if not xmlutil.RscState().is_ms(rsc): common_err("%s is not a master-slave resource" % rsc) return False return utils.ext_cmd(self.rsc_setrole % (rsc, "Master")) == 0 def do_scores(self, context): "usage: scores" if utils.is_program('crm_simulate'): utils.ext_cmd('crm_simulate -sL') elif utils.is_program('ptest'): utils.ext_cmd('ptest -sL') else: context.fatal_error("Need crm_simulate or ptest in path to display scores") @command.wait @command.completers(compl.resources) def do_demote(self, context, rsc): "usage: demote <rsc>" if not utils.is_name_sane(rsc): return False if not xmlutil.RscState().is_ms(rsc): common_err("%s is not a master-slave resource" % rsc) return False return utils.ext_cmd(self.rsc_setrole % (rsc, "Slave")) == 0 @command.completers(compl.resources) def do_manage(self, context, rsc): "usage: manage <rsc>" if not utils.is_name_sane(rsc): return False return set_deep_meta_attr("is-managed", "true", rsc) @command.completers(compl.resources) def do_unmanage(self, context, rsc): "usage: unmanage <rsc>" if not utils.is_name_sane(rsc): return False return set_deep_meta_attr("is-managed", "false", rsc) @command.alias('move') @command.skill_level('administrator') @command.completers_repeating(compl.resources, compl.nodes, compl.choice(['lifetime', 'force'])) def do_migrate(self, context, *args): """usage: migrate <rsc> [<node>] [<lifetime>] [force]""" argl = list(args) rsc = argl[0] if not utils.is_name_sane(rsc): return False del argl[0] node = None opt_l = utils.fetch_opts(argl, ["force"]) lifetime = utils.fetch_lifetime_opt(argl) if len(argl) == 1: if xmlutil.is_our_node(argl[0]): node = argl[0] else: context.fatal_error("Not our node: " + argl[0]) opts = '' if node: opts = "--node='%s'" % node if lifetime: opts = "%s --lifetime='%s'" % (opts, lifetime) if "force" in opt_l or config.core.force: opts = "%s --force" % opts return utils.ext_cmd(self.rsc_migrate % (rsc, opts)) == 0 @command.alias('unmove') @command.skill_level('administrator') @command.completers(compl.resources) def do_unmigrate(self, context, rsc): "usage: unmigrate <rsc>" if not utils.is_name_sane(rsc): return False return utils.ext_cmd(self.rsc_unmigrate % rsc) == 0 @command.skill_level('administrator') @command.wait @command.completers(compl.resources, compl.nodes) def do_cleanup(self, context, resource, node=''): "usage: cleanup <rsc> [<node>]" # Cleanup a resource on a node. Omit node to cleanup on # all live nodes. return cleanup_resource(resource, node) @command.completers(compl.resources, _attrcmds, compl.nodes) def do_failcount(self, context, *args): """usage: failcount <rsc> set <node> <value> failcount <rsc> delete <node> failcount <rsc> show <node>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_failcount, args) @command.skill_level('administrator') @command.wait @command.completers(compl.resources, _attrcmds) def do_param(self, context, *args): """usage: param <rsc> set <param> <value> param <rsc> delete <param> param <rsc> show <param>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_param, args) @command.skill_level('administrator') @command.wait @command.completers(compl.resources, compl.choice(['set', 'stash', 'unstash', 'delete', 'show', 'check'])) def do_secret(self, context, *args): """usage: secret <rsc> set <param> <value> secret <rsc> stash <param> secret <rsc> unstash <param> secret <rsc> delete <param> secret <rsc> show <param> secret <rsc> check <param>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_secret, args) @command.skill_level('administrator') @command.wait @command.completers(compl.resources, _attrcmds) def do_meta(self, context, *args): """usage: meta <rsc> set <attr> <value> meta <rsc> delete <attr> meta <rsc> show <attr>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_meta, args) @command.skill_level('administrator') @command.wait @command.completers(compl.resources, _attrcmds) def do_utilization(self, context, *args): """usage: utilization <rsc> set <attr> <value> utilization <rsc> delete <attr> utilization <rsc> show <attr>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_utilization, args) @command.completers(compl.nodes) def do_refresh(self, context, *args): 'usage: refresh [<node>]' if len(args) == 1: if not utils.is_name_sane(args[0]): return False return utils.ext_cmd(self.rsc_refresh_node % args[0]) == 0 else: return utils.ext_cmd(self.rsc_refresh) == 0 @command.wait @command.completers(compl.nodes) def do_reprobe(self, context, *args): 'usage: reprobe [<node>]' if len(args) == 1: if not utils.is_name_sane(args[0]): return False return utils.ext_cmd(self.rsc_reprobe_node % args[0]) == 0 else: return utils.ext_cmd(self.rsc_reprobe) == 0 @command.wait @command.completers(compl.resources, compl.choice(['on', 'off', 'true', 'false'])) def do_maintenance(self, context, resource, on_off='true'): 'usage: maintenance <resource> [on|off|true|false]' on_off = on_off.lower() if on_off not in ('on', 'true', 'off', 'false'): context.fatal_error("Expected <resource> [on|off|true|false]") elif on_off in ('on', 'true'): on_off = 'true' else: on_off = 'false' return utils.ext_cmd(self.rsc_maintenance % (resource, on_off)) == 0 def _get_trace_rsc(self, rsc_id): cib_factory.refresh() if not cib_factory.is_cib_sane(): return None rsc = cib_factory.find_object(rsc_id) if not rsc: common_err("resource %s does not exist" % rsc_id) return None if rsc.obj_type != "primitive": common_err("element %s is not a primitive resource" % rsc_id) return None return rsc @command.wait @command.completers(compl.primitives, _raoperations) def do_trace(self, context, rsc_id, op, interval=None): 'usage: trace <rsc> <op> [<interval>]' rsc = self._get_trace_rsc(rsc_id) if not rsc: return False if not interval: interval = op == "monitor" and "non-0" or "0" if op == "probe": op = "monitor" op_node = xmlutil.find_operation(rsc.node, op, interval) if op_node is None and utils.crm_msec(interval) != 0: common_err("not allowed to create non-0 interval operation %s" % op) return False if op_node is None: head_pl = ["op", []] head_pl[1].append(["name", op]) head_pl[1].append(["interval", interval]) head_pl[1].append([vars.trace_ra_attr, "1"]) cli_list = [] cli_list.append(head_pl) if not rsc.add_operation(cli_list): return False else: op_node = rsc.set_op_attr(op_node, vars.trace_ra_attr, "1") if not cib_factory.commit(): return False if op == "monitor" and utils.crm_msec(interval) != 0: common_warn("please CLEANUP the RA trace directory %s regularly!" % config.path.heartbeat_dir) else: common_info("restart %s to get the trace" % rsc_id) return True @command.wait @command.completers(compl.primitives, _raoperations) def do_untrace(self, context, rsc_id, op, interval=None): 'usage: untrace <rsc> <op> [<interval>]' rsc = self._get_trace_rsc(rsc_id) if not rsc: return False if not interval: interval = op == "monitor" and "non-0" or "0" if op == "probe": op = "monitor" op_node = xmlutil.find_operation(rsc.node, op, interval) if op_node is None: common_err("operation %s does not exist in %s" % (op, rsc.obj_id)) return False op_node = rsc.del_op_attr(op_node, vars.trace_ra_attr) if rsc.is_dummy_operation(op_node): rsc.del_operation(op_node) return cib_factory.commit()
if not set_deep_meta_attr_node(target_node, attr, value): return False return xmlutil.commit_rsc(target_node) def cleanup_resource(rsc, node=''): if not utils.is_name_sane(rsc) or not utils.is_name_sane(node): return False if not node: rc = utils.ext_cmd(RscMgmt.rsc_cleanup_all % (rsc)) == 0 else: rc = utils.ext_cmd(RscMgmt.rsc_cleanup % (rsc, node)) == 0 return rc _attrcmds = compl.choice(['delete', 'set', 'show']) _raoperations = compl.choice(vars.ra_operations) class RscMgmt(command.UI): ''' Resources management class ''' name = "resource" rsc_status_all = "crm_resource -L" rsc_status = "crm_resource -W -r '%s'" rsc_showxml = "crm_resource -q -r '%s'" rsc_setrole = "crm_resource --meta -r '%s' -p target-role -v '%s'" rsc_migrate = "crm_resource -M -r '%s' %s" rsc_unmigrate = "crm_resource -U -r '%s'"
"usage: log [<node> ...]" self._init_source() return crm_report().log(*args) def ptest(self, nograph, scores, utilization, actions, verbosity): 'Send a decompressed self.pe_file to ptest' try: s = bz2.decompress(open(self.pe_file).read()) except IOError, msg: common_err("open: %s" % msg) return False return utils.run_ptest(s, nograph, scores, utilization, actions, verbosity) @command.skill_level('administrator') @command.completers_repeating(compl.join(compl.call(lambda: crm_report().peinputs_list()), compl.choice(['v']))) def do_peinputs(self, context, *args): """usage: peinputs [{<range>|<number>} ...] [v]""" self._init_source() argl = list(args) opt_l = utils.fetch_opts(argl, ["v"]) if argl: l = [] for s in argl: a = utils.convert2ints(s.split(':')) if a and len(a) == 2 and not utils.check_range(a): common_err("%s: invalid peinputs range" % a) return False l += crm_report().pelist(a, long=("v" in opt_l)) else: l = crm_report().pelist(long=("v" in opt_l))
class RscMgmt(command.UI): ''' Resources management class ''' name = "resource" rsc_status_all = "crm_resource -L" rsc_status = "crm_resource -W -r '%s'" rsc_showxml = "crm_resource -q -r '%s'" rsc_setrole = "crm_resource --meta -r '%s' -p target-role -v '%s'" rsc_migrate = "crm_resource -M -r '%s' %s" rsc_unmigrate = "crm_resource -U -r '%s'" rsc_cleanup = "crm_resource -C -r '%s' -H '%s'" rsc_cleanup_all = "crm_resource -C -r '%s'" rsc_maintenance = "crm_resource -r '%s' --meta -p maintenance -v '%s'" rsc_param = { 'set': "crm_resource -r '%s' -p '%s' -v '%s'", 'delete': "crm_resource -r '%s' -d '%s'", 'show': "crm_resource -r '%s' -g '%s'", } rsc_meta = { 'set': "crm_resource --meta -r '%s' -p '%s' -v '%s'", 'delete': "crm_resource --meta -r '%s' -d '%s'", 'show': "crm_resource --meta -r '%s' -g '%s'", } rsc_failcount = { 'set': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -v '%s' -d 0", 'delete': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -D -d 0", 'show': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -G -d 0", } rsc_utilization = { 'set': "crm_resource -z -r '%s' -p '%s' -v '%s'", 'delete': "crm_resource -z -r '%s' -d '%s'", 'show': "crm_resource -z -r '%s' -g '%s'", } rsc_secret = { 'set': "cibsecret set '%s' '%s' '%s'", 'stash': "cibsecret stash '%s' '%s'", 'unstash': "cibsecret unstash '%s' '%s'", 'delete': "cibsecret delete '%s' '%s'", 'show': "cibsecret get '%s' '%s'", 'check': "cibsecret check '%s' '%s'", } rsc_refresh = "crm_resource -C" rsc_refresh_node = "crm_resource -C -H '%s'" rsc_reprobe = "crm_resource -C" rsc_reprobe_node = "crm_resource -C -H '%s'" def requires(self): for program in ('crm_resource', 'crm_attribute'): if not utils.is_program(program): no_prog_err(program) return False return True @command.alias('show', 'list') @command.completers(compl.resources) def do_status(self, context, rsc=None): "usage: status [<rsc>]" if rsc: if not utils.is_name_sane(rsc): return False return utils.ext_cmd(self.rsc_status % rsc) == 0 else: return utils.ext_cmd(self.rsc_status_all) == 0 def _commit_meta_attr(self, context, rsc, name, value): """ Perform change to resource """ if not utils.is_name_sane(rsc): return False commit = not cib_factory.has_cib_changed() if not commit: context.info("Currently editing the CIB, changes will not be committed") return set_deep_meta_attr(rsc, name, value, commit=commit) @command.wait @command.completers(compl.resources) def do_start(self, context, rsc): "usage: start <rsc>" return self._commit_meta_attr(context, rsc, "target-role", "Started") @command.wait @command.completers(compl.resources) def do_stop(self, context, rsc): "usage: stop <rsc>" return self._commit_meta_attr(context, rsc, "target-role", "Stopped") @command.wait @command.completers(compl.resources) def do_restart(self, context, rsc): "usage: restart <rsc>" common_info("ordering %s to stop" % rsc) if not self._commit_meta_attr(context, rsc, "target-role", "Stopped"): return False if not utils.wait4dc("stop", not options.batch): return False common_info("ordering %s to start" % rsc) return self._commit_meta_attr(context, rsc, "target-role", "Started") @command.wait @command.completers(compl.resources) def do_promote(self, context, rsc): "usage: promote <rsc>" if not utils.is_name_sane(rsc): return False if not xmlutil.RscState().is_ms(rsc): common_err("%s is not a master-slave resource" % rsc) return False return utils.ext_cmd(self.rsc_setrole % (rsc, "Master")) == 0 def do_scores(self, context): "usage: scores" if utils.is_program('crm_simulate'): utils.ext_cmd('crm_simulate -sL') elif utils.is_program('ptest'): utils.ext_cmd('ptest -sL') else: context.fatal_error("Need crm_simulate or ptest in path to display scores") @command.wait @command.completers(compl.resources) def do_demote(self, context, rsc): "usage: demote <rsc>" if not utils.is_name_sane(rsc): return False if not xmlutil.RscState().is_ms(rsc): common_err("%s is not a master-slave resource" % rsc) return False return utils.ext_cmd(self.rsc_setrole % (rsc, "Slave")) == 0 @command.completers(compl.resources) def do_manage(self, context, rsc): "usage: manage <rsc>" return self._commit_meta_attr(context, rsc, "is-managed", "true") @command.completers(compl.resources) def do_unmanage(self, context, rsc): "usage: unmanage <rsc>" return self._commit_meta_attr(context, rsc, "is-managed", "false") @command.alias('move') @command.skill_level('administrator') @command.wait @command.completers_repeating(compl.resources, compl.nodes, compl.choice(['reboot', 'forever', 'force'])) def do_migrate(self, context, rsc, *args): """usage: migrate <rsc> [<node>] [<lifetime>] [force]""" if not utils.is_name_sane(rsc): return False node = None argl = list(args) force = "force" in utils.fetch_opts(argl, ["force"]) lifetime = utils.fetch_lifetime_opt(argl) if len(argl) > 0: node = argl[0] if not xmlutil.is_our_node(node): context.fatal_error("Not our node: " + node) opts = '' if node: opts = "--node='%s'" % node if lifetime: opts = "%s --lifetime='%s'" % (opts, lifetime) if force or config.core.force: opts = "%s --force" % opts return utils.ext_cmd(self.rsc_migrate % (rsc, opts)) == 0 @command.alias('unmove') @command.skill_level('administrator') @command.wait @command.completers(compl.resources) def do_unmigrate(self, context, rsc): "usage: unmigrate <rsc>" if not utils.is_name_sane(rsc): return False return utils.ext_cmd(self.rsc_unmigrate % rsc) == 0 @command.skill_level('administrator') @command.wait @command.completers(compl.resources, compl.nodes) def do_cleanup(self, context, resource, node=''): "usage: cleanup <rsc> [<node>]" # Cleanup a resource on a node. Omit node to cleanup on # all live nodes. return cleanup_resource(resource, node) @command.wait @command.completers(compl.resources, _attrcmds, compl.nodes) def do_failcount(self, context, rsc, cmd, node, value=None): """usage: failcount <rsc> set <node> <value> failcount <rsc> delete <node> failcount <rsc> show <node>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_failcount, rsc, cmd, node, value) @command.skill_level('administrator') @command.wait @command.completers(compl.resources, _attrcmds) def do_param(self, context, rsc, cmd, param, value=None): """usage: param <rsc> set <param> <value> param <rsc> delete <param> param <rsc> show <param>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_param, rsc, cmd, param, value) @command.skill_level('administrator') @command.completers(compl.resources, compl.choice(['set', 'stash', 'unstash', 'delete', 'show', 'check'])) def do_secret(self, context, rsc, cmd, param, value=None): """usage: secret <rsc> set <param> <value> secret <rsc> stash <param> secret <rsc> unstash <param> secret <rsc> delete <param> secret <rsc> show <param> secret <rsc> check <param>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_secret, rsc, cmd, param, value) @command.skill_level('administrator') @command.wait @command.completers(compl.resources, _attrcmds) def do_meta(self, context, rsc, cmd, attr, value=None): """usage: meta <rsc> set <attr> <value> meta <rsc> delete <attr> meta <rsc> show <attr>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_meta, rsc, cmd, attr, value) @command.skill_level('administrator') @command.wait @command.completers(compl.resources, _attrcmds) def do_utilization(self, context, rsc, cmd, attr, value=None): """usage: utilization <rsc> set <attr> <value> utilization <rsc> delete <attr> utilization <rsc> show <attr>""" return ui_utils.manage_attr(context.get_command_name(), self.rsc_utilization, rsc, cmd, attr, value) @command.completers(compl.nodes) def do_refresh(self, context, *args): 'usage: refresh [<node>]' if len(args) == 1: if not utils.is_name_sane(args[0]): return False return utils.ext_cmd(self.rsc_refresh_node % args[0]) == 0 else: return utils.ext_cmd(self.rsc_refresh) == 0 @command.wait @command.completers(compl.nodes) def do_reprobe(self, context, *args): 'usage: reprobe [<node>]' if len(args) == 1: if not utils.is_name_sane(args[0]): return False return utils.ext_cmd(self.rsc_reprobe_node % args[0]) == 0 else: return utils.ext_cmd(self.rsc_reprobe) == 0 @command.wait @command.completers(compl.resources, compl.choice(['on', 'off', 'true', 'false'])) def do_maintenance(self, context, resource, on_off='true'): 'usage: maintenance <resource> [on|off|true|false]' on_off = on_off.lower() if on_off not in ('on', 'true', 'off', 'false'): context.fatal_error("Expected <resource> [on|off|true|false]") elif on_off in ('on', 'true'): on_off = 'true' else: on_off = 'false' return utils.ext_cmd(self.rsc_maintenance % (resource, on_off)) == 0 def _get_trace_rsc(self, rsc_id): if not cib_factory.refresh(): return None rsc = cib_factory.find_object(rsc_id) if not rsc: common_err("resource %s does not exist" % rsc_id) return None if rsc.obj_type != "primitive": common_err("element %s is not a primitive resource" % rsc_id) return None return rsc def _add_trace_op(self, rsc, op, interval): from lxml import etree n = etree.Element('op') n.set('name', op) n.set('interval', interval) n.set(constants.trace_ra_attr, '1') return rsc.add_operation(n) def _trace_resource(self, context, rsc_id, rsc): op_nodes = rsc.node.xpath('.//op') def trace(name): for o in op_nodes: if o.get('name') == name: return if not self._add_trace_op(rsc, name, '0'): context.fatal_error("Failed to add trace for %s:%s" % (rsc_id, name)) trace('start') trace('stop') if xmlutil.is_ms(rsc.node): trace('promote') trace('demote') for op_node in op_nodes: rsc.set_op_attr(op_node, constants.trace_ra_attr, "1") def _trace_op(self, context, rsc_id, rsc, op): op_nodes = rsc.node.xpath('.//op[@name="%s"]' % (op)) if not op_nodes: if op == 'monitor': context.fatal_error("No monitor operation configured for %s" % (rsc_id)) if not self._add_trace_op(rsc, op, '0'): context.fatal_error("Failed to add trace for %s:%s" % (rsc_id, op)) for op_node in op_nodes: rsc.set_op_attr(op_node, constants.trace_ra_attr, "1") def _trace_op_interval(self, context, rsc_id, rsc, op, interval): op_node = xmlutil.find_operation(rsc.node, op, interval) if op_node is None and utils.crm_msec(interval) != 0: context.fatal_error("Operation %s with interval %s not found in %s" % (op, interval, rsc_id)) if op_node is None: if not self._add_trace_op(rsc, op, interval): context.fatal_error("Failed to add trace for %s:%s" % (rsc_id, op)) else: rsc.set_op_attr(op_node, constants.trace_ra_attr, "1") @command.completers(compl.primitives, _raoperations) def do_trace(self, context, rsc_id, op=None, interval=None): 'usage: trace <rsc> [<op>] [<interval>]' rsc = self._get_trace_rsc(rsc_id) if not rsc: return False if op == "probe": op = "monitor" if interval is None: interval = "0" if op is None: self._trace_resource(context, rsc_id, rsc) elif interval is None: self._trace_op(context, rsc_id, rsc, op) else: self._trace_op_interval(context, rsc_id, rsc, op, interval) if not cib_factory.commit(): return False if op is not None: common_info("Trace for %s:%s is written to %s/trace_ra/" % (rsc_id, op, config.path.heartbeat_dir)) else: common_info("Trace for %s is written to %s/trace_ra/" % (rsc_id, config.path.heartbeat_dir)) if op is not None and op != "monitor": common_info("Trace set, restart %s to trace the %s operation" % (rsc_id, op)) else: common_info("Trace set, restart %s to trace non-monitor operations" % (rsc_id)) return True def _remove_trace(self, rsc, op_node): from lxml import etree common_debug("op_node: %s" % (etree.tostring(op_node))) op_node = rsc.del_op_attr(op_node, constants.trace_ra_attr) if rsc.is_dummy_operation(op_node): rsc.del_operation(op_node) @command.completers(compl.primitives, _raoperations) def do_untrace(self, context, rsc_id, op=None, interval=None): 'usage: untrace <rsc> [<op>] [<interval>]' rsc = self._get_trace_rsc(rsc_id) if not rsc: return False if op == "probe": op = "monitor" if op is None: n = 0 for tn in rsc.node.xpath('.//*[@%s]' % (constants.trace_ra_attr)): self._remove_trace(rsc, tn) n += 1 for tn in rsc.node.xpath('.//*[@name="%s"]' % (constants.trace_ra_attr)): if tn.getparent().getparent().tag == 'op': self._remove_trace(rsc, tn.getparent().getparent()) n += 1 else: op_node = xmlutil.find_operation(rsc.node, op, interval=interval) if op_node is None: common_err("operation %s does not exist in %s" % (op, rsc.obj_id)) return False self._remove_trace(rsc, op_node) return cib_factory.commit()
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 if options.interactive or options.shell_completion: cib_factory.initialize() def requires(self): # see the configure ptest/simulate command has_ptest = utils.is_program('ptest') has_simulate = utils.is_program('crm_simulate') if not has_ptest: vars.simulate_programs["ptest"] = "crm_simulate" if not has_simulate: vars.simulate_programs["simulate"] = "ptest" if not (has_ptest or has_simulate): common_warn("neither ptest nor crm_simulate exist, check your installation") vars.simulate_programs["ptest"] = "" vars.simulate_programs["simulate"] = "" return True @command.name('_test') @command.skill_level('administrator') def do_check_structure(self, context): 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.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] [<id>...]" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") set_obj = mkset_obj(*args) return set_obj.show() @command.skill_level('administrator') @command.completers_repeating(compl.null, _id_xml_list, _id_list) def do_filter(self, context, filterprog, *args): "usage: filter <prog> [xml] [<id>...]" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") set_obj = mkset_obj(*args) return set_obj.filter(filterprog) @command.skill_level('administrator') @command.completers(_f_group_id_list, compl.choice(['add', 'remove']), _prim_id_list, compl.choice(['after', 'before']), _prim_id_list) def do_modgroup(self, context, group_id, subcmd, prim_id, *args): """usage: modgroup <id> add <id> [after <id>|before <id>] modgroup <id> remove <id>""" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") if subcmd not in ("add", "remove"): common_err("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 <id>' (%d arguments given)" % len(args)) after_before = args[0] ref_member_id = args[1] 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] [<id>...]" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") err_buf.buffer() # keep error messages set_obj = mkset_obj(*args) err_buf.release() # show them, but get an ack from the user 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" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") set_obj_all = mkset_obj("xml") return self._verify(set_obj_all, set_obj_all) @command.skill_level('administrator') def do_save(self, context, *args): "usage: save [xml] <filename>" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") if not args: context.fatal_error("Expected 1 argument (0 given)") if args[0] == "xml": if len(args) != 2: context.fatal_error("Expected 2 arguments (%d given)" % (len(args))) filename = args[1] set_obj = mkset_obj("xml") else: filename = args[0] set_obj = mkset_obj() return set_obj.save_to_file(filename) @command.skill_level('administrator') @command.completers(compl.choice(['xml', 'replace', 'update']), _load_2nd_completer) def do_load(self, context, *args): "usage: load [xml] {replace|update} {<url>|<path>}" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") 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"): context.fatal_error("Unknown method %s" % method) 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(gv_types.keys() + ['exportsettings'])) def do_graph(self, context, *args): "usage: graph [<gtype> [<file> [<img_format>]]]" if args and args[0] == "exportsettings": return utils.save_graphviz_file(userdir.GRAPHVIZ_USER_FILE, vars.graph) if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") rc, gtype, outf, ftype = ui_utils.graph_args(args) if not rc: context.fatal_error("Failed to create graph") rc, d = utils.load_graphviz_file(userdir.GRAPHVIZ_USER_FILE) if rc and d: vars.graph = d set_obj = mkset_obj() if not outf: rc = set_obj.show_graph(gtype) elif gtype == ftype: rc = set_obj.save_graph(gtype, outf) else: rc = set_obj.graph_img(gtype, outf, ftype) return rc @command.skill_level('administrator') @command.completers_repeating(_id_list) def do_delete(self, context, *args): "usage: delete <id> [<id>...]" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") return cib_factory.delete(*args) @command.name('default-timeouts') @command.alias('default_timeouts') @command.completers_repeating(_id_list) def do_default_timeouts(self, context, *args): "usage: default-timeouts <id> [<id>...]" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") return cib_factory.default_timeouts(*args) @command.skill_level('administrator') @command.completers(_id_list, _id_list) def do_rename(self, context, old_id, new_id): "usage: rename <old_id> <new_id>" if not cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") 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 cib_factory.is_cib_sane(): context.fatal_error("CIB is not valid") 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]" if not cib_factory.is_cib_sane(): return False # use ptest/crm_simulate depending on which command was # used config.core.ptest = vars.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=None): if force and force != "force": syntax_err(('configure.commit', force)) return False if not cib_factory.is_cib_sane(): return False if not cib_factory.has_cib_changed(): common_info("apparently there is nothing to commit") common_info("try changing something first") return True rc1 = True if not (force or utils.cibadmin_can_patch()): rc1 = cib_factory.is_current_cib_equal() rc2 = cib_factory.is_cib_empty() or \ self._verify(mkset_obj("xml", "changed"), mkset_obj("xml")) if rc1 and rc2: return cib_factory.commit() if force or config.core.force: common_info("commit forced") return cib_factory.commit(force=True) if utils.ask("Do you still want to commit?"): return cib_factory.commit(force=True) return False @command.skill_level('administrator') @command.wait @command.completers(compl.choice(['force'])) def do_commit(self, context, force=None): "usage: commit [force]" return self._commit(force=force) @command.skill_level('administrator') @command.completers(compl.choice(['force'])) def do_upgrade(self, context, force=None): "usage: upgrade [force]" if not cib_factory.is_cib_sane(): return False if force and force != "force": syntax_err((context.get_command_name(), force)) return False if config.core.force or force: return cib_factory.upgrade_cib_06to10(True) else: return cib_factory.upgrade_cib_06to10() @command.skill_level('administrator') def do_schema(self, context, schema_st=None): "usage: schema [<schema>]" if not cib_factory.is_cib_sane(): return False if not schema_st: print cib_factory.get_schema() return True return cib_factory.change_schema(schema_st) def __conf_object(self, cmd, *args): "The configure object command." if not cib_factory.is_cib_sane(): return False if cmd in vars.cib_cli_map.values() and \ not cib_factory.is_elem_supported(cmd): common_err("%s not supported by the RNG schema" % cmd) return False f = lambda: cib_factory.create_object(cmd, *args) return f() @command.skill_level('administrator') @command.completers(_node_id_list, compl.choice(vars.node_attributes_keyw)) def do_node(self, context, *args): """usage: node <uname>[:<type>] [attributes <param>=<value> [<param>=<value>...]] [utilization <param>=<value> [<param>=<value>...]]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.null, ra_classes_or_tmpl, primitive_complete_complex) def do_primitive(self, context, *args): """usage: primitive <rsc> {[<class>:[<provider>:]]<type>|@<template>} [params <param>=<value> [<param>=<value>...]] [meta <attribute>=<value> [<attribute>=<value>...]] [utilization <attribute>=<value> [<attribute>=<value>...]] [operations id_spec [op op_type [<attribute>=<value>...] ...]]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.null, _group_completer) def do_group(self, context, *args): """usage: group <name> <rsc> [<rsc>...] [params <param>=<value> [<param>=<value>...]] [meta <attribute>=<value> [<attribute>=<value>...]]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.null, _f_children_id_list, _clone_completer) def do_clone(self, context, *args): """usage: clone <name> <rsc> [params <param>=<value> [<param>=<value>...]] [meta <attribute>=<value> [<attribute>=<value>...]]""" return self.__conf_object(context.get_command_name(), *args) @command.alias('master') @command.skill_level('administrator') @command.completers_repeating(compl.null, _f_children_id_list, _ms_completer) def do_ms(self, context, *args): """usage: ms <name> <rsc> [params <param>=<value> [<param>=<value>...]] [meta <attribute>=<value> [<attribute>=<value>...]]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.null, ui_ra.complete_class_provider_type, primitive_complete_complex) def do_rsc_template(self, context, *args): """usage: rsc_template <name> [<class>:[<provider>:]]<type> [params <param>=<value> [<param>=<value>...]] [meta <attribute>=<value> [<attribute>=<value>...]] [utilization <attribute>=<value> [<attribute>=<value>...]] [operations id_spec [op op_type [<attribute>=<value>...] ...]]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers(compl.null, _top_rsc_id_list) def do_location(self, context, *args): """usage: location <id> <rsc> {node_pref|rules} node_pref :: <score>: <node> rules :: rule [id_spec] [$role=<role>] <score>: <expression> [rule [id_spec] [$role=<role>] <score>: <expression> ...] id_spec :: $id=<id> | $id-ref=<id> score :: <number> | <attribute> | [-]inf expression :: <simple_exp> [bool_op <simple_exp> ...] bool_op :: or | and simple_exp :: <attribute> [type:]<binary_op> <value> | <unary_op> <attribute> | date <date_expr> type :: string | version | number binary_op :: lt | gt | lte | gte | eq | ne unary_op :: defined | not_defined""" return self.__conf_object(context.get_command_name(), *args) @command.alias('collocation') @command.skill_level('administrator') @command.completers_repeating(compl.null, compl.null, top_rsc_tmpl_id_list) def do_colocation(self, context, *args): """usage: colocation <id> <score>: <rsc>[:<role>] <rsc>[:<role>] ... [node-attribute=<node_attr>]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.null, compl.call(schema.rng_attr_values, 'rsc_order', 'kind'), top_rsc_tmpl_id_list) def do_order(self, context, *args): """usage: order <id> {kind|<score>}: <rsc>[:<action>] <rsc>[:<action>] ... [symmetrical=<bool>]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.null, compl.null, top_rsc_tmpl_id_list) def do_rsc_ticket(self, context, *args): """usage: rsc_ticket <id> <ticket_id>: <rsc>[:<role>] [<rsc>[:<role>] ...] [loss-policy=<loss_policy_action>]""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(_property_completer) def do_property(self, context, *args): "usage: property [$id=<set_id>] <option>=<value>" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(_prim_meta_completer) def do_rsc_defaults(self, context, *args): "usage: rsc_defaults [$id=<set_id>] <option>=<value>" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(op_attr_list) def do_op_defaults(self, context, *args): "usage: op_defaults [$id=<set_id>] <option>=<value>" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(node_id_colon_list, stonith_resource_list) def do_fencing_topology(self, context, *args): "usage: fencing_topology [<node>:] stonith_resources [stonith_resources ...]" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') def do_xml(self, context, *args): "usage: xml <xml>" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers(_f_children_id_list) def do_monitor(self, context, *args): "usage: monitor <rsc>[:<role>] <interval>[:<timeout>]" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('expert') @command.completers_repeating(compl.null, compl.choice(["role:", "read", "write", "deny"])) def do_user(self, context, *args): """user <uid> {roles|rules} roles :: role:<role-ref> [role:<role-ref> ...] rules :: rule [rule ...] (See the role command for details on rules.)""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('expert') @command.completers_repeating(compl.null, compl.choice(["read", "write", "deny"])) def do_role(self, context, *args): """role <role-id> rule [rule ...] rule :: acl-right cib-spec [attribute:<attribute>] acl-right :: read | write | deny cib-spec :: xpath-spec | tag-ref-spec xpath-spec :: xpath:<xpath> | shortcut tag-ref-spec :: tag:<tag> | ref:<id> | tag:<tag> ref:<id> shortcut :: meta:<rsc>[:<attr>] params:<rsc>[:<attr>] utilization:<rsc> location:<rsc> property[:<attr>] node[:<node>] nodeattr[:<attr>] nodeutil[:<node>] status""" return self.__conf_object(context.get_command_name(), *args) @command.skill_level('administrator') @command.completers_repeating(compl.null, top_rsc_tmpl_id_list) def do_tag(self, context, *args): return self.__conf_object(context.get_command_name(), *args) @command.skill_level('expert') @command.completers_repeating(_rsc_id_list) def do_rsctest(self, context, *args): "usage: rsctest <rsc_id> [<rsc_id> ...] [<node_id> ...]" if not cib_factory.is_cib_sane(): return False rc = True rsc_l = [] node_l = [] current = "r" for ident in args: el = cib_factory.find_object(ident) if not el: common_err("element %s does not exist" % ident) rc = False elif current == "r" and xmlutil.is_resource(el.node): if xmlutil.is_container(el.node): rsc_l += el.node.findall("primitive") else: rsc_l.append(el.node) elif xmlutil.is_normal_node(el.node): current = "n" node_l.append(el.node.get("uname")) else: syntax_err((context.get_command_name(), ident), context='rsctest') return False if not rc: return False if not rsc_l: common_err("specify at least one resource") return False all_nodes = cib_factory.node_id_list() if not node_l: node_l = all_nodes return rsctest.test_resources(rsc_l, node_l, all_nodes) def should_wait(self): return cib_factory.has_cib_changed() def end_game(self, no_questions_asked=False): ok = True if cib_factory.has_cib_changed(): if no_questions_asked or not options.interactive: ok = self._commit() elif utils.ask("There are changes pending. Do you want to commit them?"): ok = self._commit() cib_factory.reset() return ok
class Template(command.UI): ''' Configuration templates. ''' name = "template" def __init__(self): command.UI.__init__(self) self.curr_conf = '' self.init_dir() @command.skill_level('administrator') @command.completers_repeating(compl.null, compl.call(utils.listtemplates)) def do_new(self, context, name, *args): "usage: new <config> <template> [<template> ...] [params name=value ...]" if not utils.is_filename_sane(name): return False if os.path.isfile("%s/%s" % (userdir.CRMCONF_DIR, name)): common_err("config %s exists; delete it first" % name) return False lt = LoadTemplate(name) rc = True mode = 0 params = {} for s in args: if mode == 0 and s == "params": params["id"] = name mode = 1 elif mode == 1: a = s.split('=') if len(a) != 2: syntax_err(args, context='new') rc = False else: params[a[0]] = a[1] elif not lt.load_template(s): rc = False if rc: lt.post_process(params) if not rc or not lt.write_config(name): return False self.curr_conf = name @command.skill_level('administrator') @command.completers(compl.call(utils.listconfigs)) def do_delete(self, context, name, force=''): "usage: delete <config> [force]" if force: if force != "force" and force != "--force": syntax_err((context.get_command_name(), force), context='delete') return False if not self.config_exists(name): return False if name == self.curr_conf: if not force and not config.core.force and \ not utils.ask("Do you really want to remove config %s which is in use?" % self.curr_conf): return False else: self.curr_conf = '' os.remove("%s/%s" % (userdir.CRMCONF_DIR, name)) @command.skill_level('administrator') @command.completers(compl.call(utils.listconfigs)) def do_load(self, context, name=''): "usage: load [<config>]" if not name: self.curr_conf = '' return True if not self.config_exists(name): return False self.curr_conf = name @command.skill_level('administrator') @command.completers(compl.call(utils.listconfigs)) def do_edit(self, context, name=''): "usage: edit [<config>]" if not name and not self.curr_conf: common_err("please load a config first") return False if name: if not self.config_exists(name): return False utils.edit_file("%s/%s" % (userdir.CRMCONF_DIR, name)) else: utils.edit_file("%s/%s" % (userdir.CRMCONF_DIR, self.curr_conf)) @command.completers(compl.call(utils.listconfigs)) def do_show(self, context, name=''): "usage: show [<config>]" if not name and not self.curr_conf: common_err("please load a config first") return False if name: if not self.config_exists(name): return False print self.process(name) else: print self.process() @command.skill_level('administrator') @command.completers( compl.join(compl.call(utils.listconfigs), compl.choice(['replace', 'update'])), compl.call(utils.listconfigs)) def do_apply(self, context, *args): "usage: apply [<method>] [<config>]" method = "replace" name = '' if len(args) > 0: i = 0 if args[0] in ("replace", "update"): method = args[0] i += 1 if len(args) > i: name = args[i] if not name and not self.curr_conf: common_err("please load a config first") return False if name: if not self.config_exists(name): return False s = self.process(name) else: s = self.process() if not s: return False tmp = utils.str2tmp(s) if not tmp: return False 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() set_obj = mkset_obj() rc = set_obj.import_file(method, tmp) try: os.unlink(tmp) except: pass return rc @command.completers(compl.choice(['templates'])) def do_list(self, context, templates=''): "usage: list [templates]" if templates == "templates": utils.multicolumn(utils.listtemplates()) else: utils.multicolumn(utils.listconfigs()) def init_dir(self): '''Create the conf directory, link to templates''' if not os.path.isdir(userdir.CRMCONF_DIR): try: os.makedirs(userdir.CRMCONF_DIR) except os.error, msg: common_err("makedirs: %s" % msg)
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import command import completers import config import options _yesno = completers.choice(['yes', 'no']) _legacy_map = { 'editor': ('core', 'editor'), 'pager': ('core', 'pager'), 'user': ('core', 'user'), 'skill_level': ('core', 'skill_level'), 'sort_elements': ('core', 'sort_elements'), 'check_frequency': ('core', 'check_frequency'), 'check_mode': ('core', 'check_mode'), 'wait': ('core', 'wait'), 'add_quotes': ('core', 'add_quotes'), 'manage_children': ('core', 'manage_children'), 'force': ('core', 'force'), 'debug': ('core', 'debug'), 'ptest': ('core', 'ptest'),
class Maintenance(command.UI): ''' Commands that should only be run while in maintenance mode. ''' name = "maintenance" rsc_maintenance = "crm_resource -r '%s' --meta -p maintenance -v '%s'" def __init__(self): command.UI.__init__(self) def requires(self): return cib_factory.initialize() def _onoff(self, resource, onoff): if resource is not None: return utils.ext_cmd(self.rsc_maintenance % (resource, onoff)) == 0 else: return cib_factory.create_object('property', 'maintenance-mode=%s' % (onoff)) @command.skill_level('administrator') @command.completers_repeating(compl.call(cib_factory.rsc_id_list)) def do_on(self, context, resource=None): ''' Enable maintenance mode (for the optional resource or for everything) ''' return self._onoff(resource, 'true') @command.skill_level('administrator') @command.completers_repeating(compl.call(cib_factory.rsc_id_list)) def do_off(self, context, resource=None): ''' Disable maintenance mode (for the optional resource or for everything) ''' return self._onoff(resource, 'false') def _in_maintenance_mode(self, obj): if cib_factory.get_property("maintenance-mode") == "true": return True v = obj.meta_attributes("maintenance") return v and all(x == 'true' for x in v) def _runs_on_this_node(self, resource): nodes = utils.running_on(resource) return set(nodes) == set([utils.this_node()]) @command.skill_level('administrator') @command.completers(compl.call(cib_factory.rsc_id_list), _compl_actions, compl.choice(["ssh"])) def do_action(self, context, resource, action, ssh=None): ''' Issue action out-of-band to the given resource, making sure that the resource is in maintenance mode first ''' obj = cib_factory.find_object(resource) if not obj: context.fatal_error("Resource not found: %s" % (resource)) if not xmlutil.is_resource(obj.node): context.fatal_error("Not a resource: %s" % (resource)) if not self._in_maintenance_mode(obj): context.fatal_error("Not in maintenance mode.") if ssh is None: if action not in ('start', 'monitor'): if not self._runs_on_this_node(resource): context.fatal_error( "Resource %s must be running on this node (%s)" % (resource, utils.this_node())) import rsctest return rsctest.call_resource(obj.node, action, [utils.this_node()], local_only=True) elif ssh == "ssh": import rsctest if action in ('start', 'promote', 'demote', 'recover', 'meta-data'): return rsctest.call_resource(obj.node, action, [utils.this_node()], local_only=True) else: all_nodes = cib_factory.node_id_list() return rsctest.call_resource(obj.node, action, all_nodes, local_only=False) else: context.fatal_error("Unknown argument: %s" % (ssh))
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import command import completers as compl from cibconfig import cib_factory import utils import xmlutil _compl_actions = compl.choice([ 'start', 'stop', 'monitor', 'meta-data', 'validate-all', 'promote', 'demote', 'notify', 'reload', 'migrate_from', 'migrate_to', 'recover' ]) class Maintenance(command.UI): ''' Commands that should only be run while in maintenance mode. ''' name = "maintenance" rsc_maintenance = "crm_resource -r '%s' --meta -p maintenance -v '%s'" def __init__(self): command.UI.__init__(self)
def ptest(self, nograph, scores, utilization, actions, verbosity): 'Send a decompressed self.pe_file to ptest' try: f = open(self.pe_file) except IOError, msg: common_err("open: %s" % msg) return False s = bz2.decompress(''.join(f)) f.close() return utils.run_ptest(s, nograph, scores, utilization, actions, verbosity) @command.skill_level('administrator') @command.completers_repeating( compl.join(compl.call(crm_report.peinputs_list), compl.choice(['v']))) def do_peinputs(self, context, *args): """usage: peinputs [{<range>|<number>} ...] [v]""" argl = list(args) opt_l = utils.fetch_opts(argl, ["v"]) if argl: l = [] for s in argl: a = utils.convert2ints(s.split(':')) if a and len(a) == 2 and not utils.check_range(a): common_err("%s: invalid peinputs range" % a) return False l += crm_report.pelist(a, long=("v" in opt_l)) else: l = crm_report.pelist(long=("v" in opt_l)) if not l:
class CibStatusUI(command.UI): ''' The CIB status section management user interface class ''' name = "cibstatus" @command.skill_level('expert') def do_load(self, context, org): "usage: load {<file>|shadow:<cib>|live}" return cib_status.load(org) @command.skill_level('expert') def do_save(self, context, dest=None): "usage: save [<file>|shadow:<cib>]" return cib_status.save(dest) @command.skill_level('administrator') def do_origin(self, context): "usage: origin" state = cib_status.modified and " (modified)" or "" print "%s%s" % (cib_status.origin, state) @command.skill_level('administrator') @command.completers(compl.choice(['changed'])) def do_show(self, context, changed=""): "usage: show [changed]" if changed: if changed != "changed": context.fatal_error("Expected 'changed', got '%s'" % (changed)) return cib_status.list_changes() return cib_status.show() @command.skill_level('administrator') @command.completers(compl.booleans) def do_quorum(self, context, opt): "usage: quorum <bool>" if not utils.verify_boolean(opt): context.fatal_error("%s: bad boolean option" % opt) return cib_status.set_quorum(utils.is_boolean_true(opt)) @command.skill_level('expert') @command.completers(_status_node_list, compl.choice(vars.node_states)) def do_node(self, context, node, state): "usage: node <node> {online|offline|unclean}" return cib_status.edit_node(node, state) @command.skill_level('expert') @command.completers(compl.null, compl.choice(cib_status.ticket_ops.keys())) def do_ticket(self, context, ticket, subcmd): "usage: ticket <ticket> {grant|revoke|activate|standby}" return cib_status.edit_ticket(ticket, subcmd) @command.skill_level('expert') @command.completers(compl.choice(vars.ra_operations), compl.call(cib_status.status_rsc_list), compl.choice(vars.lrm_exit_codes.keys()), compl.choice(vars.lrm_status_codes.keys()), compl.choice(vars.node_states)) def do_op(self, context, op, rsc, rc, op_status=None, node=''): "usage: op <operation> <resource> <exit_code> [<op_status>] [<node>]" if rc in vars.lrm_exit_codes: num_rc = vars.lrm_exit_codes[rc] else: num_rc = rc if not num_rc.isdigit(): context.fatal_error("Invalid exit code '%s'" % num_rc) num_op_status = op_status if op_status: if op_status in vars.lrm_status_codes: num_op_status = vars.lrm_status_codes[op_status] if not num_op_status.isdigit(): context.fatal_error("Invalid operation status '%s'" % num_op_status) return cib_status.edit_op(op, rsc, num_rc, num_op_status, node) @command.skill_level('administrator') @command.completers(compl.choice(['nograph'])) def do_run(self, context, *args): "usage: run [nograph] [v...] [scores] [utilization]" return ui_utils.ptestlike(cib_status.run, '', context.get_command_name(), args) @command.skill_level('administrator') @command.completers(compl.choice(['nograph'])) def do_simulate(self, context, *args): "usage: simulate [nograph] [v...] [scores] [utilization]" return ui_utils.ptestlike(cib_status.simulate, '', context.get_command_name(), args)
"usage: log [<node> ...]" self._init_source() return crm_report.log(*args) def ptest(self, nograph, scores, utilization, actions, verbosity): 'Send a decompressed self.pe_file to ptest' try: s = bz2.decompress(open(self.pe_file).read()) except IOError, msg: common_err("open: %s" % msg) return False return utils.run_ptest(s, nograph, scores, utilization, actions, verbosity) @command.skill_level('administrator') @command.completers_repeating(compl.join(compl.call(crm_report.peinputs_list), compl.choice(['v']))) def do_peinputs(self, context, *args): """usage: peinputs [{<range>|<number>} ...] [v]""" self._init_source() argl = list(args) opt_l = utils.fetch_opts(argl, ["v"]) if argl: l = [] for s in argl: a = utils.convert2ints(s.split(':')) if a and len(a) == 2 and not utils.check_range(a): common_err("%s: invalid peinputs range" % a) return False l += crm_report.pelist(a, long=("v" in opt_l)) else: l = crm_report.pelist(long=("v" in opt_l))
class CliOptions(command.UI): ''' Manage user preferences ''' name = "options" @command.completers(completers.choice(config.get_all_options()), _set_completer) def do_set(self, context, option, value): '''usage: set <option> <value>''' parts = option.split('.') if len(parts) != 2: context.fatal_error("Unknown option: " + option) config.set_option(parts[0], parts[1], value) @command.name('skill-level') @command.completers(_getprefs('skill_level')) def do_skill_level(self, context, level): """usage: skill-level <level> level: operator | administrator | expert""" return _legacy_set_pref('skill-level', level) def do_editor(self, context, program): "usage: editor <program>" return _legacy_set_pref('editor', program) def do_pager(self, context, program): "usage: pager <program>" return _legacy_set_pref('pager', program) def do_user(self, context, crm_user=''): "usage: user [<crm_user>]" return _legacy_set_pref('user', crm_user) @command.completers(_getprefs('output')) def do_output(self, context, output_type): "usage: output <type>" _legacy_set_pref("output", output_type) import term term._init() def do_colorscheme(self, context, colors): "usage: colorscheme <colors>" return _legacy_set_pref("colorscheme", colors) @command.name('check-frequency') @command.completers(_getprefs('check_frequency')) def do_check_frequency(self, context, freq): "usage: check-frequency <freq>" return _legacy_set_pref("check-frequency", freq) @command.name('check-mode') @command.completers(_getprefs('check_mode')) def do_check_mode(self, context, mode): "usage: check-mode <mode>" return _legacy_set_pref("check-mode", mode) @command.name('sort-elements') @command.completers(_yesno) def do_sort_elements(self, context, opt): "usage: sort-elements {yes|no}" return _legacy_set_pref("sort-elements", opt) @command.completers(_yesno) def do_wait(self, context, opt): "usage: wait {yes|no}" return _legacy_set_pref("wait", opt) @command.name('add-quotes') @command.completers(_yesno) def do_add_quotes(self, context, opt): "usage: add-quotes {yes|no}" return _legacy_set_pref("add-quotes", opt) @command.name('manage-children') @command.completers(_getprefs('manage_children')) def do_manage_children(self, context, opt): "usage: manage-children <option>" return _legacy_set_pref("manage-children", opt) @command.completers(completers.choice(config.get_all_options())) def do_show(self, context, option=None): "usage: show [all | <option>]" import utils opts = config.get_configured_options( ) if option is None else config.get_all_options() def show_options(fn): s = '' for opt in opts: if fn(opt): parts = opt.split('.') val = (opt, config.get_option(parts[0], parts[1], raw=True)) s += "%s = %s\n" % val utils.page_string(s) if option == 'all' or option is None: show_options(lambda o: True) else: show_options(lambda o: o.startswith(option) or o.endswith(option)) def do_save(self, context): "usage: save" config.save() def do_reset(self, context): "usage: reset" config.reset() def end_game(self, no_questions_asked=False): if no_questions_asked and not options.interactive: self.do_save(None)
return crm_report().log(*args) def ptest(self, nograph, scores, utilization, actions, verbosity): 'Send a decompressed self.pe_file to ptest' try: s = bz2.decompress(open(self.pe_file).read()) except IOError, msg: common_err("open: %s" % msg) return False return utils.run_ptest(s, nograph, scores, utilization, actions, verbosity) @command.skill_level('administrator') @command.completers_repeating( compl.join(compl.call(lambda: crm_report().peinputs_list()), compl.choice(['v']))) def do_peinputs(self, context, *args): """usage: peinputs [{<range>|<number>} ...] [v]""" self._init_source() argl = list(args) opt_l = utils.fetch_opts(argl, ["v"]) if argl: l = [] for s in argl: a = utils.convert2ints(s.split(':')) if a and len(a) == 2 and not utils.check_range(a): common_err("%s: invalid peinputs range" % a) return False l += crm_report().pelist(a, long=("v" in opt_l)) else: l = crm_report().pelist(long=("v" in opt_l))
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import command import completers as compl from cibconfig import cib_factory import utils import xmlutil _compl_actions = compl.choice(['start', 'stop', 'monitor', 'meta-data', 'validate-all', 'promote', 'demote', 'notify', 'reload', 'migrate_from', 'migrate_to', 'recover']) class Maintenance(command.UI): ''' Commands that should only be run while in maintenance mode. ''' name = "maintenance" rsc_maintenance = "crm_resource -r '%s' --meta -p maintenance -v '%s'" def __init__(self): command.UI.__init__(self)
common_error("Failed to commit updates to %s" % (rsc)) return False return True def cleanup_resource(rsc, node=''): if not utils.is_name_sane(rsc) or not utils.is_name_sane(node): return False if not node: rc = utils.ext_cmd(RscMgmt.rsc_cleanup_all % (rsc)) == 0 else: rc = utils.ext_cmd(RscMgmt.rsc_cleanup % (rsc, node)) == 0 return rc _attrcmds = compl.choice(['delete', 'set', 'show']) _raoperations = compl.choice(constants.ra_operations) class RscMgmt(command.UI): ''' Resources management class ''' name = "resource" rsc_status_all = "crm_resource -L" rsc_status = "crm_resource -W -r '%s'" rsc_showxml = "crm_resource -q -r '%s'" rsc_setrole = "crm_resource --meta -r '%s' -p target-role -v '%s'" rsc_migrate = "crm_resource -M -r '%s' %s" rsc_unmigrate = "crm_resource -U -r '%s'"
class CibShadow(command.UI): ''' CIB shadow management class ''' name = "cib" extcmd = ">/dev/null </dev/null crm_shadow" extcmd_stdout = "</dev/null crm_shadow" def requires(self): if not utils.is_program('crm_shadow'): no_prog_err('crm_shadow') return False return True @command.level(ui_cibstatus.CibStatusUI) def do_cibstatus(self): pass @command.skill_level('administrator') @command.completers_repeating(compl.null, compl.choice(_NEWARGS)) def do_new(self, context, *args): "usage: new [<shadow_cib>] [withstatus] [force] [empty]" argl = list(args) opt_l = utils.fetch_opts(argl, ["force", "--force", "withstatus", "empty"]) if len(argl) > 1: context.fatal_error("Unexpected argument(s): " + ','.join(argl)) name = None if argl: name = argl[0] if not utils.is_filename_sane(name): context.fatal_error("Bad filename: " + name) if name in (vars.tmp_cib_prompt, vars.live_cib_prompt): context.fatal_error("Shadow name '%s' is not allowed" % (name)) del argl[0] vars.tmp_cib = False else: fd, fname = tmpfiles.create(dir=xmlutil.cib_shadow_dir(), prefix="shadow.crmsh_") name = os.path.basename(fname).replace("shadow.", "") vars.tmp_cib = True if "empty" in opt_l: new_cmd = "%s -e '%s'" % (self.extcmd, name) else: new_cmd = "%s -c '%s'" % (self.extcmd, name) if vars.tmp_cib or config.core.force or "force" in opt_l or "--force" in opt_l: new_cmd = "%s --force" % new_cmd if utils.ext_cmd(new_cmd) == 0: context.info("%s shadow CIB created" % name) self.do_use(context, name) if "withstatus" in opt_l: cib_status.load("shadow:%s" % name) def _find_pe(self, context, infile): 'Find a pe input' for p in ("%s/%s", "%s/%s.bz2", "%s/pe-*-%s.bz2"): fl = glob.glob(p % (config.path.pe_state_dir, infile)) if fl: break if not fl: context.fatal_error("no %s pe input file" % infile) if len(fl) > 1: context.fatal_error("more than one %s pe input file: %s" % (infile, ' '.join(fl))) if not fl[0]: context.fatal_error("bad %s pe input file" % infile) return fl[0] @command.skill_level('administrator') @command.completers(compl.null, compl.shadows) def do_import(self, context, infile, name=None): "usage: import {<file>|<number>} [<shadow>]" if name and not utils.is_filename_sane(name): context.fatal_error("Bad filename: " + name) # where's the input? if not os.access(infile, os.F_OK): if "/" in infile: context.fatal_error(str(infile) + ": no such file") infile = self._find_pe(context, infile) if not name: name = os.path.basename(infile).replace(".bz2", "") if not xmlutil.pe2shadow(infile, name): context.fatal_error("Error copying PE file to shadow: %s -> %s" % (infile, name)) # use the shadow and load the status from there return self.do_use(context, name, "withstatus") @command.skill_level('administrator') @command.completers(compl.shadows) def do_delete(self, context, name): "usage: delete <shadow_cib>" if not utils.is_filename_sane(name): context.fatal_error("Bad filename: " + name) if utils.get_cib_in_use() == name: context.fatal_error("%s shadow CIB is in use" % name) if utils.ext_cmd("%s -D '%s' --force" % (self.extcmd, name)) == 0: context.info("%s shadow CIB deleted" % name) else: context.fatal_error("failed to delete %s shadow CIB" % name) @command.skill_level('administrator') @command.completers(compl.shadows) def do_reset(self, context, name): "usage: reset <shadow_cib>" if not utils.is_filename_sane(name): context.fatal_error("Bad filename: " + name) if utils.ext_cmd("%s -r '%s'" % (self.extcmd, name)) == 0: context.info("copied live CIB to %s" % name) else: context.fatal_error("failed to copy live CIB to %s" % name) @command.skill_level('administrator') @command.wait @command.completers(compl.shadows) def do_commit(self, context, name=None): "usage: commit [<shadow_cib>]" if name and not utils.is_filename_sane(name): context.fatal_error("Bad filename: " + name) if not name: name = utils.get_cib_in_use() if not name: context.fatal_error("There is nothing to commit") if utils.ext_cmd("%s -C '%s' --force" % (self.extcmd, name)) == 0: context.info("committed '%s' shadow CIB to the cluster" % name) else: context.fatal_error("failed to commit the %s shadow CIB" % name) if vars.tmp_cib: self._use('', '') @command.skill_level('administrator') def do_diff(self, context): "usage: diff" rc, s = utils.get_stdout(utils.add_sudo("%s -d" % self.extcmd_stdout)) utils.page_string(s) @command.skill_level('administrator') def do_list(self, context): "usage: list" if options.regression_tests: for t in xmlutil.listshadows(): print t else: utils.multicolumn(xmlutil.listshadows()) def _use(self, name, withstatus): # Choose a shadow cib for further changes. If the name # provided is empty, then choose the live (cluster) cib. # Don't allow ' in shadow names if not name or name == "live": if withstatus: cib_status.load("live") if vars.tmp_cib: utils.ext_cmd("%s -D '%s' --force" % (self.extcmd, utils.get_cib_in_use())) vars.tmp_cib = False utils.clear_cib_in_use() else: utils.set_cib_in_use(name) if withstatus: cib_status.load("shadow:%s" % name) return True @command.skill_level('administrator') @command.completers(compl.join(compl.shadows, compl.choice(['live'])), compl.choice(['withstatus'])) def do_use(self, context, name='', withstatus=''): "usage: use [<shadow_cib>] [withstatus]" # check the name argument if name and not utils.is_filename_sane(name): context.fatal_error("Bad filename: " + name) if name and name != "live": if not os.access(xmlutil.shadowfile(name), os.F_OK): context.fatal_error("%s: no such shadow CIB" % name) if withstatus and withstatus != "withstatus": context.fatal_error("Expected 'withstatus', got '%s'" % (withstatus)) # If invoked from configure # take special precautions if not context.previous_level_is("cibconfig"): return self._use(name, withstatus) if not cib_factory.has_cib_changed(): ret = self._use(name, withstatus) # new CIB: refresh the CIB factory cib_factory.refresh() return ret saved_cib = utils.get_cib_in_use() self._use(name, '') # don't load the status yet if not cib_factory.is_current_cib_equal(silent=True): # user made changes and now wants to switch to a # different and unequal CIB; we refuse to cooperate context.error_message( "the requested CIB is different from the current one") if config.core.force: context.info("CIB overwrite forced") elif not utils.ask( "All changes will be dropped. Do you want to proceed?"): self._use(saved_cib, '') # revert to the previous CIB return False return self._use(name, withstatus) # now load the status too
import ui_assist from crm_gv import gv_types err_buf = ErrorBuffer.getInstance() cib_factory = CibFactory.getInstance() def _type_completions(): "completer for type: use in show" typelist = cib_factory.type_list() return ['type:%s' % (t) for t in typelist] # 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)) _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) _group_completer = compl.join(_f_prim_free_id_list, compl.choice(['params', 'meta'])) _clone_completer = compl.choice(['params', 'meta']) _ms_completer = compl.choice(['params', 'meta'])
class Cluster(command.UI): ''' Whole cluster management. - Package installation - System configuration - Network troubleshooting - Perform other callouts/cluster-wide devops operations ''' name = "cluster" def requires(self): stack = utils.cluster_stack() if len(stack) > 0 and stack != 'corosync': err_buf.warning("Unsupported cluster stack %s detected." % (stack)) return False return True def __init__(self): command.UI.__init__(self) # ugly hack to allow overriding the node list # for the cluster commands that operate before # there is an actual cluster self._inventory_nodes = None self._inventory_target = None @command.skill_level('administrator') def do_start(self, context): ''' Starts the cluster services on this node ''' rc, out, err = utils.get_stdout_stderr('service corosync start') if rc != 0: context.fatal_error("Failed to start corosync service: %s" % (err)) rc, out, err = utils.get_stdout_stderr('service pacemaker start') if rc != 0: context.fatal_error("Failed to start pacemaker service: %s" % (err)) err_buf.info("Cluster services started") # TODO: optionally start services on all nodes or specific node @command.skill_level('administrator') def do_stop(self, context): ''' Stops the cluster services on this node ''' rc, out, err = utils.get_stdout_stderr('service pacemaker stop') if rc != 0: context.fatal_error("Failed to stop pacemaker service: %s" % (err)) rc, out, err = utils.get_stdout_stderr('service corosync stop') if rc != 0: context.fatal_error("Failed to stop corosync service: %s" % (err)) err_buf.info("Cluster services stopped") # TODO: optionally stop services on all nodes or specific node def _args_implicit(self, context, args, name): ''' handle early non-nvpair arguments as values in an implicit list ''' args = list(args) vals = [] while args and args[0].find('=') == -1: vals.append(args[0]) args = args[1:] if vals: return args + ['%s=%s' % (name, ','.join(vals))] return args @command.completers_repeating( compl.choice(scripts.param_completion_list('init'))) @command.skill_level('administrator') def do_init(self, context, *args): ''' Initialize a cluster with the given hosts as nodes. ''' return scripts.run('init', self._args_implicit(context, args, 'nodes')) @command.completers_repeating( compl.choice(scripts.param_completion_list('add'))) @command.skill_level('administrator') def do_add(self, context, *args): ''' Add the given node(s) to the cluster. Installs packages, sets up corosync and pacemaker, etc. Must be executed from a node in the existing cluster. ''' params = self._args_implicit(context, args, 'node') paramdict = utils.nvpairs2dict(params) node = paramdict.get('node') if node: node = node.replace(',', ' ').split() else: node = [] nodes = paramdict.get('nodes') if not nodes: nodes = utils.list_cluster_nodes() nodes += node params += ['nodes=%s' % (','.join(nodes))] return scripts.run('add', params) @command.completers_repeating(_remove_completer) @command.skill_level('administrator') def do_remove(self, context, *args): ''' Remove the given node(s) from the cluster. ''' params = self._args_implicit(context, args, 'node') return scripts.run('remove', params) @command.completers_repeating( compl.choice(scripts.param_completion_list('health'))) def do_health(self, context, *args): ''' Extensive health check. ''' params = self._args_implicit(context, args, 'nodes') return scripts.run('health', params) def _node_in_cluster(self, node): return node in utils.list_cluster_nodes() def do_status(self, context): ''' Quick cluster health status. Corosync status, DRBD status... ''' stack = utils.cluster_stack() if not stack: err_buf.error( "No supported cluster stack found (tried heartbeat|openais|corosync)" ) if utils.cluster_stack() == 'corosync': print "Services:" for svc in ["corosync", "pacemaker"]: info = utils.service_info(svc) if info: print "%-16s %s" % (svc, info) else: print "%-16s unknown" % (svc) rc, outp = utils.get_stdout(['corosync-cfgtool', '-s'], shell=False) if rc == 0: print "" print outp else: print "Failed to get corosync status" @command.completers_repeating(compl.choice(['10', '60', '600'])) def do_wait_for_startup(self, context, timeout='10'): "usage: wait_for_startup [<timeout>]" import time t0 = time.time() timeout = float(timeout) cmd = 'crm_mon -bD1 2&>1 >/dev/null' ret = utils.ext_cmd(cmd) while ret in (107, 64) and time.time() < t0 + timeout: time.sleep(1) ret = utils.ext_cmd(cmd) if ret != 0: context.fatal_error("Timed out waiting for cluster (rc = %s)" % (ret)) @command.skill_level('expert') def do_run(self, context, cmd): ''' Execute the given command on all nodes, report outcome ''' try: from psshlib import api as pssh _has_pssh = True except ImportError: _has_pssh = False if not _has_pssh: context.fatal_error("PSSH not found") hosts = utils.list_cluster_nodes() opts = pssh.Options() for host, result in pssh.call(hosts, cmd, opts).iteritems(): if isinstance(result, pssh.Error): err_buf.error("[%s]: %s" % (host, result)) else: if result[0] != 0: err_buf.error("[%s]: rc=%s\n%s\n%s" % (host, result[0], result[1], result[2])) else: err_buf.ok("[%s]\n%s" % (host, result[1]))
class NodeMgmt(command.UI): ''' Nodes management class ''' name = "node" node_standby = "crm_attribute -t nodes -N '%s' -n standby -v '%s' %s" node_maint = "crm_attribute -t nodes -N '%s' -n maintenance -v '%s'" node_delete = """cibadmin -D -o nodes -X '<node uname="%s"/>'""" node_delete_status = """cibadmin -D -o status -X '<node_state uname="%s"/>'""" node_clear_state = _oneline("""cibadmin %s -o status --xml-text '<node_state id="%s" uname="%s" ha="active" in_ccm="false" crmd="offline" join="member" expected="down" crm-debug-origin="manual_clear" shutdown="0" />'""") node_clear_state_118 = "stonith_admin --confirm %s" hb_delnode = config.path.hb_delnode + " '%s'" crm_node = "crm_node" node_fence = "crm_attribute -t status -U '%s' -n terminate -v true" dc = "crmadmin -D" node_attr = { 'set': "crm_attribute -t nodes -U '%s' -n '%s' -v '%s'", 'delete': "crm_attribute -D -t nodes -U '%s' -n '%s'", 'show': "crm_attribute -G -t nodes -U '%s' -n '%s'", } node_status = { 'set': "crm_attribute -t status -U '%s' -n '%s' -v '%s'", 'delete': "crm_attribute -D -t status -U '%s' -n '%s'", 'show': "crm_attribute -G -t status -U '%s' -n '%s'", } node_utilization = { 'set': "crm_attribute -z -t nodes -U '%s' -n '%s' -v '%s'", 'delete': "crm_attribute -z -D -t nodes -U '%s' -n '%s'", 'show': "crm_attribute -z -G -t nodes -U '%s' -n '%s'", } def requires(self): for p in ('cibadmin', 'crm_attribute'): if not utils.is_program(p): no_prog_err(p) return False return True @command.completers(compl.nodes) def do_status(self, context, node=None): 'usage: status [<node>]' a = node and ('--xpath "//nodes/node[@uname=\'%s\']"' % node) or \ '-o nodes' return utils.ext_cmd("%s %s" % (xmlutil.cib_dump, a)) == 0 @command.alias('list') @command.completers(compl.nodes) def do_show(self, context, node=None): 'usage: show [<node>]' cib_elem = xmlutil.cibdump2elem() if cib_elem is None: return False try: nodes_node = cib_elem.xpath("//configuration/nodes")[0] status = cib_elem.findall("status")[0] except: return False for c in nodes_node.iterchildren(): if c.tag != "node": continue if node is not None and c.get("uname") != node: continue type = uname = id = "" inst_attr = [] other = {} for attr in c.keys(): v = c.get(attr) if attr == "type": type = v elif attr == "uname": uname = v elif attr == "id": id = v else: other[attr] = v for c2 in c.iterchildren(): if c2.tag == "instance_attributes": inst_attr += nvpairs2list(c2) offline = False for c2 in status.xpath(".//node_state"): if uname != c2.get("uname"): continue offline = c2.get("crmd") == "offline" print_node(uname, id, type, other, inst_attr, offline) @command.wait @command.completers(compl.nodes) def do_standby(self, context, *args): 'usage: standby [<node>] [<lifetime>]' argl = list(args) node = None lifetime = utils.fetch_lifetime_opt(argl, iso8601=False) if not argl: node = utils.this_node() elif len(argl) == 1: if xmlutil.is_our_node(args[0]): node = args[0] else: common_err("%s: node name not recognized" % args[0]) return False else: syntax_err(args, context=context.get_command_name()) return False opts = '' if lifetime: opts = "--lifetime='%s'" % lifetime else: opts = "--lifetime='forever'" return utils.ext_cmd(self.node_standby % (node, "on", opts)) == 0 @command.wait @command.completers(compl.nodes) def do_online(self, context, node=None): 'usage: online [<node>]' if not node: node = utils.this_node() if not utils.is_name_sane(node): return False return utils.ext_cmd(self.node_standby % (node, "off", "--lifetime='forever'")) == 0 @command.wait @command.completers(compl.nodes) def do_maintenance(self, context, node=None): 'usage: maintenance [<node>]' if not node: node = utils.this_node() if not utils.is_name_sane(node): return False return utils.ext_cmd(self.node_maint % (node, "on")) == 0 @command.wait @command.completers(compl.nodes) def do_ready(self, context, node=None): 'usage: ready [<node>]' if not node: node = utils.this_node() if not utils.is_name_sane(node): return False return utils.ext_cmd(self.node_maint % (node, "off")) == 0 @command.wait @command.completers(compl.nodes) def do_fence(self, context, node): 'usage: fence <node>' if not node: node = utils.this_node() if not utils.is_name_sane(node): return False if not config.core.force and \ not utils.ask("Do you really want to shoot %s?" % node): return False return utils.ext_cmd(self.node_fence % (node)) == 0 @command.wait @command.completers(compl.nodes) def do_clearstate(self, context, node): 'usage: clearstate <node>' if not utils.is_name_sane(node): return False if not config.core.force and \ not utils.ask("Do you really want to drop state for node %s?" % node): return False if utils.is_pcmk_118(): return utils.ext_cmd(self.node_clear_state_118 % node) == 0 else: return utils.ext_cmd(self.node_clear_state % ("-M -c", node, node)) == 0 and \ utils.ext_cmd(self.node_clear_state % ("-R", node, node)) == 0 def _call_delnode(self, node): "Remove node (how depends on cluster stack)" rc = True if utils.cluster_stack() == "heartbeat": cmd = (self.hb_delnode % node) else: ec, s = utils.get_stdout("%s -p" % self.crm_node) if not s: common_err('%s -p could not list any nodes (rc=%d)' % (self.crm_node, ec)) rc = False else: partition_l = s.split() if node in partition_l: common_err("according to %s, node %s is still active" % (self.crm_node, node)) rc = False cmd = "%s --force -R %s" % (self.crm_node, node) if not rc: if config.core.force: common_info('proceeding with node %s removal' % node) else: return False ec = utils.ext_cmd(cmd) if ec != 0: common_warn('"%s" failed, rc=%d' % (cmd, ec)) return False return True @command.completers(compl.nodes) def do_delete(self, context, node): 'usage: delete <node>' if not utils.is_name_sane(node): return False if not xmlutil.is_our_node(node): common_err("node %s not found in the CIB" % node) return False if not self._call_delnode(node): return False if utils.ext_cmd(self.node_delete % node) != 0 or \ utils.ext_cmd(self.node_delete_status % node) != 0: common_err("%s removed from membership, but not from CIB!" % node) return False common_info("node %s deleted" % node) return True @command.wait @command.completers(compl.nodes, compl.choice(['set', 'delete', 'show']), compl.resources) def do_attribute(self, context, *args): """usage: attribute <node> set <rsc> <value> attribute <node> delete <rsc> attribute <node> show <rsc>""" return ui_utils.manage_attr(context.get_command_name(), self.node_attr, args) @command.wait @command.completers(compl.nodes, compl.choice(['set', 'delete', 'show']), compl.resources) def do_utilization(self, context, *args): """usage: utilization <node> set <rsc> <value> utilization <node> delete <rsc> utilization <node> show <rsc>""" return ui_utils.manage_attr(context.get_command_name(), self.node_utilization, args) @command.wait @command.name('status-attr') @command.completers(compl.nodes, compl.choice(['set', 'delete', 'show']), compl.resources) def do_status_attr(self, context, *args): """usage: status-attr <node> set <rsc> <value> status-attr <node> delete <rsc> status-attr <node> show <rsc>""" return ui_utils.manage_attr(context.get_command_name(), self.node_status, args)
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) _group_completer = compl.join(_f_prim_free_id_list, compl.choice(['params', 'meta'])) _clone_completer = compl.choice(['params', 'meta']) _ms_completer = compl.choice(['params', 'meta'])