def sbd_config(lib, argv, modifiers): """ Options: * --request-timeout - HTTP request timeout """ modifiers.ensure_only_supported("--request-timeout") if argv: raise CmdLineInputError() config_list = lib.sbd.get_cluster_sbd_config() if not config_list: utils.err("No config obtained.") config = config_list[0]["config"] filtered_options = [ "SBD_WATCHDOG_DEV", "SBD_OPTS", "SBD_PACEMAKER", "SBD_DEVICE" ] with_device = False for key, val in config.items(): if key == "SBD_DEVICE": with_device = True if key in filtered_options: continue print("{key}={val}".format(key=key, val=val)) print() print("Watchdogs:") _print_per_node_option(config_list, "SBD_WATCHDOG_DEV") if with_device: print() print("Devices:") _print_per_node_option(config_list, "SBD_DEVICE")
def stonith_list_available(lib, argv, modifiers): """ Options: * --nodesc - do not show description of the agents """ modifiers.ensure_only_supported("--nodesc") if len(argv) > 1: raise CmdLineInputError() search = argv[0] if argv else None agent_list = lib.stonith_agent.list_agents( describe=not modifiers.get("--nodesc"), search=search, ) if not agent_list: if search: utils.err("No stonith agents matching the filter.") utils.err( "No stonith agents available. " "Do you have fence agents installed?" ) for agent_info in agent_list: name = agent_info["name"] shortdesc = agent_info["shortdesc"] if shortdesc: print("{0} - {1}".format( name, resource._format_desc( len(name + " - "), shortdesc.replace("\n", " ") ) )) else: print(name)
def sbd_config(lib, argv, modifiers): if argv: raise CmdLineInputError() config_list = lib.sbd.get_cluster_sbd_config() if not config_list: utils.err("No config obtained.") config = config_list[0]["config"] filtered_options = ["SBD_WATCHDOG_DEV", "SBD_OPTS"] for key, val in config.items(): if key in filtered_options: continue print("{key}={val}".format(key=key, val=val)) print() print("Watchdogs:") for config in config_list: watchdog = "<unknown>" if config["config"] is not None: watchdog = config["config"].get("SBD_WATCHDOG_DEV", "<unknown>") print(" {node}: {watchdog}".format( node=config["node"].label, watchdog=watchdog ))
def stonith_confirm(lib, argv, modifiers): """ Options: * --force - do not warn user """ del lib modifiers.ensure_only_supported("--force") if len(argv) != 1: utils.err("must specify one (and only one) node to confirm fenced") node = argv.pop(0) if not modifiers.get("--force"): answer = utils.get_terminal_input( ( "WARNING: If node {node} is not powered off or it does" + " have access to shared resources, data corruption and/or" + " cluster failure may occur. Are you sure you want to" + " continue? [y/N] " ).format(node=node) ) if answer.lower() not in ["y", "yes"]: print("Canceled") return args = ["stonith_admin", "-C", node] output, retval = utils.run(args) if retval != 0: utils.err("unable to confirm fencing of node '%s'\n" % node + output) else: print("Node: %s confirmed fenced" % node)
def config_backup(lib, argv, modifiers): """ Options: * --force - overwrite file if already exists """ del lib modifiers.ensure_only_supported("--force") if len(argv) > 1: usage.config(["backup"]) sys.exit(1) outfile_name = None if argv: outfile_name = argv[0] if not outfile_name.endswith(".tar.bz2"): outfile_name += ".tar.bz2" tar_data = config_backup_local() if outfile_name: ok, message = utils.write_file( outfile_name, tar_data, permissions=0o600, binary=True ) if not ok: utils.err(message) else: # in python3 stdout accepts str so we need to use buffer sys.stdout.buffer.write(tar_data)
def config_backup_local(): """ Commandline options: no options """ file_list = config_backup_path_list() tar_data = BytesIO() try: tarball = tarfile.open(fileobj=tar_data, mode="w|bz2") config_backup_add_version_to_tarball(tarball) for tar_path, path_info in file_list.items(): if ( not os.path.exists(path_info["path"]) and not path_info["required"] ): continue tarball.add(path_info["path"], tar_path) tarball.close() except (tarfile.TarError, EnvironmentError) as e: utils.err("unable to create tarball: %s" % e) tar = tar_data.getvalue() tar_data.close() return tar
def list_property(argv): print_all = len(argv) == 0 if "--all" in utils.pcs_options and "--defaults" in utils.pcs_options: utils.err("you cannot specify both --all and --defaults") if "--all" in utils.pcs_options or "--defaults" in utils.pcs_options: if len(argv) != 0: utils.err("you cannot specify a property when using --all or --defaults") properties = get_default_properties() else: properties = {} if "--defaults" not in utils.pcs_options: properties = utils.get_set_properties( None if print_all else argv[0], properties ) print("Cluster Properties:") for prop,val in sorted(properties.items()): print(" " + prop + ": " + val) node_attributes = utils.get_node_attributes( filter_attr=(None if print_all else argv[0]) ) if node_attributes: print("Node Attributes:") node.attribute_print(node_attributes)
def config_checkpoint_list(lib, argv, modifiers): """ Options: no options """ del lib modifiers.ensure_only_supported() if argv: raise CmdLineInputError() try: file_list = os.listdir(settings.cib_dir) except OSError as e: utils.err("unable to list checkpoints: %s" % e) cib_list = [] cib_name_re = re.compile(r"^cib-(\d+)\.raw$") for filename in file_list: match = cib_name_re.match(filename) if not match: continue file_path = os.path.join(settings.cib_dir, filename) try: if os.path.isfile(file_path): cib_list.append( (float(os.path.getmtime(file_path)), match.group(1)) ) except OSError: pass cib_list.sort() if not cib_list: print("No checkpoints available") return for cib_info in cib_list: print( "checkpoint %s: date %s" % (cib_info[1], datetime.datetime.fromtimestamp(round(cib_info[0]))) )
def run_role_delete(argv): if len(argv) < 1: usage.acl(["role delete"]) sys.exit(1) role_id = argv.pop(0) dom = utils.get_cib_dom() found = False for elem in dom.getElementsByTagName("acl_role"): if elem.getAttribute("id") == role_id: found = True elem.parentNode.removeChild(elem) break if not found: utils.err("unable to find acl role: %s" % role_id) # Remove any references to this role in acl_target or acl_group for elem in dom.getElementsByTagName("role"): if elem.getAttribute("id") == role_id: user_group = elem.parentNode user_group.removeChild(elem) if "--autodelete" in utils.pcs_options: if not user_group.getElementsByTagName("role"): user_group.parentNode.removeChild(user_group) utils.replace_cib_configuration(dom)
def full_status(): if "--hide-inactive" in utils.pcs_options and "--full" in utils.pcs_options: utils.err("you cannot specify both --hide-inactive and --full") monitor_command = ["crm_mon", "--one-shot"] if "--hide-inactive" not in utils.pcs_options: monitor_command.append('--inactive') if "--full" in utils.pcs_options: monitor_command.extend( ["--show-detail", "--show-node-attributes", "--failcounts"] ) output, retval = utils.run(monitor_command) if (retval != 0): utils.err("cluster is not currently running on this node") if not utils.usefile or "--corosync_conf" in utils.pcs_options: cluster_name = utils.getClusterName() print("Cluster name: %s" % cluster_name) if utils.stonithCheck(): print("WARNING: no stonith devices and stonith-enabled is not false") if not utils.is_rhel6() and utils.corosyncPacemakerNodeCheck(): print("WARNING: corosync and pacemaker node names do not match (IPs used in setup?)") print(output) if not utils.usefile: if "--full" in utils.pcs_options: print_pcsd_daemon_status() print() utils.serviceStatus(" ")
def get_fence_agent_info(argv): if len(argv) != 1: utils.err("One parameter expected") agent = argv[0] if not agent.startswith("stonith:"): utils.err("Invalid fence agent name") runner = utils.cmd_runner() try: metadata_dom = lib_ra.get_fence_agent_metadata( runner, agent.split("stonith:", 1)[1] ) metadata = lib_ra.get_agent_desc(metadata_dom) metadata["name"] = agent metadata["parameters"] = lib_ra.get_fence_agent_parameters( runner, metadata_dom ) print(json.dumps(metadata)) except lib_ra.ResourceAgentLibError as e: utils.process_library_reports( [lib_ra.resource_agent_lib_error_to_report_item(e)] ) except LibraryError as e: utils.process_library_reports(e.args)
def location_remove(argv): # This code was originally merged in the location_add function and was # documented to take 1 or 4 arguments: # location remove <id> [<resource id> <node> <score>] # However it has always ignored all arguments but constraint id. Therefore # this command / function has no use as it can be fully replaced by "pcs # constraint remove" which also removes constraints by id. For now I keep # things as they are but we should solve this when moving these functions # to pcs.lib. if len(argv) != 1: usage.constraint(["location remove"]) sys.exit(1) constraint_id = argv.pop(0) dom, constraintsElement = getCurrentConstraints() elementsToRemove = [] for rsc_loc in constraintsElement.getElementsByTagName('rsc_location'): if constraint_id == rsc_loc.getAttribute("id"): elementsToRemove.append(rsc_loc) if (len(elementsToRemove) == 0): utils.err("resource location id: " + constraint_id + " not found.") for etr in elementsToRemove: constraintsElement.removeChild(etr) utils.replace_cib_configuration(dom)
def config_checkpoint_list(): try: file_list = os.listdir(settings.cib_dir) except OSError as e: utils.err("unable to list checkpoints: %s" % e) cib_list = [] cib_name_re = re.compile("^cib-(\d+)\.raw$") for filename in file_list: match = cib_name_re.match(filename) if not match: continue file_path = os.path.join(settings.cib_dir, filename) try: if os.path.isfile(file_path): cib_list.append( (float(os.path.getmtime(file_path)), match.group(1)) ) except OSError: pass cib_list.sort() if not cib_list: print("No checkpoints available") return for cib_info in cib_list: print( "checkpoint %s: date %s" % (cib_info[1], datetime.datetime.fromtimestamp(round(cib_info[0]))) )
def get_fence_agent_info(lib, argv, modifiers): """ Options: no options """ del lib modifiers.ensure_only_supported() if len(argv) != 1: utils.err("One parameter expected") agent = argv[0] if not agent.startswith("stonith:"): utils.err("Invalid fence agent name") runner = utils.cmd_runner() try: metadata = lib_ra.StonithAgent(runner, agent[len("stonith:"):]) info = metadata.get_full_info() info["name"] = "stonith:{0}".format(info["name"]) print(json.dumps(info)) except lib_ra.ResourceAgentError as e: utils.process_library_reports( [lib_ra.resource_agent_error_to_report_item(e)] ) except LibraryError as e: utils.process_library_reports(e.args)
def print_node_utilization(filter_node=None, filter_name=None): cib = utils.get_cib_dom() node_element_list = cib.getElementsByTagName("node") if( filter_node and filter_node not in [ node_element.getAttribute("uname") for node_element in node_element_list ] and ( utils.usefile or filter_node not in [ node_attrs.name for node_attrs in utils.getNodeAttributesFromPacemaker() ] ) ): utils.err("Unable to find a node: {0}".format(filter_node)) utilization = {} for node_el in node_element_list: node = node_el.getAttribute("uname") if filter_node is not None and node != filter_node: continue u = utils.get_utilization_str(node_el, filter_name) if u: utilization[node] = u print("Node Utilization:") for node in sorted(utilization): print(" {0}: {1}".format(node, utilization[node]))
def location_prefer(argv): rsc = argv.pop(0) prefer_option = argv.pop(0) if prefer_option == "prefers": prefer = True elif prefer_option == "avoids": prefer = False else: usage.constraint() sys.exit(1) for nodeconf in argv: nodeconf_a = nodeconf.split("=",1) if len(nodeconf_a) == 1: node = nodeconf_a[0] if prefer: score = "INFINITY" else: score = "-INFINITY" else: score = nodeconf_a[1] if not utils.is_score(score): utils.err("invalid score '%s', use integer or INFINITY or -INFINITY" % score) if not prefer: if score[0] == "-": score = score[1:] else: score = "-" + score node = nodeconf_a[0] location_add(["location-" +rsc+"-"+node+"-"+score,rsc,node,score])
def order_rm(argv): if len(argv) == 0: usage.constraint() sys.exit(1) elementFound = False (dom,constraintsElement) = getCurrentConstraints() for resource in argv: for ord_loc in constraintsElement.getElementsByTagName('rsc_order')[:]: if ord_loc.getAttribute("first") == resource or ord_loc.getAttribute("then") == resource: constraintsElement.removeChild(ord_loc) elementFound = True resource_refs_to_remove = [] for ord_set in constraintsElement.getElementsByTagName('resource_ref'): if ord_set.getAttribute("id") == resource: resource_refs_to_remove.append(ord_set) elementFound = True for res_ref in resource_refs_to_remove: res_set = res_ref.parentNode res_order = res_set.parentNode res_ref.parentNode.removeChild(res_ref) if len(res_set.getElementsByTagName('resource_ref')) <= 0: res_set.parentNode.removeChild(res_set) if len(res_order.getElementsByTagName('resource_set')) <= 0: res_order.parentNode.removeChild(res_order) if elementFound == True: utils.replace_cib_configuration(dom) else: utils.err("No matching resources found in ordering list")
def stonith_list_available(lib, argv, modifiers): if len(argv) > 1: raise CmdLineInputError() search = argv[0] if argv else None agent_list = lib.stonith_agent.list_agents(modifiers["describe"], search) if not agent_list: if search: utils.err("No stonith agents matching the filter.") utils.err( "No stonith agents available. " "Do you have fence agents installed?" ) for agent_info in agent_list: name = agent_info["name"] shortdesc = agent_info["shortdesc"] if shortdesc: print("{0} - {1}".format( name, resource._format_desc( len(name + " - "), shortdesc.replace("\n", " ") ) )) else: print(name)
def _get_uid(user_name): """ Commandline options: no options """ try: return pwd.getpwnam(user_name).pw_uid except KeyError: utils.err("Unable to determine uid of user '{0}'".format(user_name))
def stonith_list_options(stonith_agent): metadata = utils.get_stonith_metadata(utils.fence_bin + stonith_agent) if not metadata: utils.err("unable to get metadata for %s" % stonith_agent) try: dom = parseString(metadata) except ExpatError as e: utils.err("Unable to parse xml for '%s': %s" % (stonith_agent, e)) title = dom.documentElement.getAttribute("name") or stonith_agent short_desc = dom.documentElement.getAttribute("shortdesc") if not short_desc: for sd in dom.documentElement.getElementsByTagName("shortdesc"): if sd.parentNode.tagName == "resource-agent" and sd.firstChild: short_desc = sd.firstChild.data.strip() break long_desc = "" for ld in dom.documentElement.getElementsByTagName("longdesc"): if ld.parentNode.tagName == "resource-agent" and ld.firstChild: long_desc = ld.firstChild.data.strip() break if short_desc: title += " - " + resource.format_desc(len(title + " - "), short_desc) print(title) print() if long_desc: print(long_desc) print() print("Stonith options:") params = dom.documentElement.getElementsByTagName("parameter") for param in params: name = param.getAttribute("name") if param.getAttribute("required") == "1": name += " (required)" desc = "" shortdesc_els = param.getElementsByTagName("shortdesc") if shortdesc_els and shortdesc_els[0].firstChild: desc = shortdesc_els[0].firstChild.nodeValue.strip().replace("\n", " ") if not desc: desc = "No description available" indent = name.__len__() + 4 desc = resource.format_desc(indent, desc) print(" " + name + ": " + desc) default_stonith_options = utils.get_default_stonith_options() for do in default_stonith_options: name = do.attrib["name"] desc = "" if len(do.findall(str("shortdesc"))) > 0: if do.findall(str("shortdesc"))[0].text: desc = do.findall(str("shortdesc"))[0].text.strip() if not desc: desc = "No description available" indent = len(name) + 4 desc = resource.format_desc(indent, desc) print(" " + name + ": " + desc)
def constraint_rule(argv): if len(argv) < 2: usage.constraint("rule") sys.exit(1) found = False command = argv.pop(0) constraint_id = None if command == "add": constraint_id = argv.pop(0) cib = utils.get_cib_dom() constraint = utils.dom_get_element_with_id( cib.getElementsByTagName("constraints")[0], "rsc_location", constraint_id ) if not constraint: utils.err("Unable to find constraint: " + constraint_id) options, rule_argv = rule_utils.parse_argv(argv) rule_utils.dom_rule_add(constraint, options, rule_argv) location_rule_check_duplicates(cib, constraint) utils.replace_cib_configuration(cib) elif command in ["remove","delete"]: cib = utils.get_cib_etree() temp_id = argv.pop(0) constraints = cib.find('.//constraints') loc_cons = cib.findall(str('.//rsc_location')) for loc_con in loc_cons: for rule in loc_con: if rule.get("id") == temp_id: if len(loc_con) > 1: print("Removing Rule: {0}".format(rule.get("id"))) loc_con.remove(rule) found = True break else: print( "Removing Constraint: {0}".format(loc_con.get("id")) ) constraints.remove(loc_con) found = True break if found == True: break if found: utils.replace_cib_configuration(cib) else: utils.err("unable to find rule with id: %s" % temp_id) else: usage.constraint("rule") sys.exit(1)
def print_node_utilization(node): cib = utils.get_cib_dom() node_el = utils.dom_get_node(cib, node) if node_el is None: utils.err("Unable to find a node: {0}".format(node)) utilization = utils.get_utilization_str(node_el) print("Node Utilization:") print(" {0}: {1}".format(node, utilization))
def pcsd_deauth(lib, argv, modifiers): """ Options: No options """ del lib modifiers.ensure_only_supported() filepath = settings.pcsd_users_conf_location if not argv: try: users_file = open(filepath, "w") users_file.write(json.dumps([])) users_file.close() except EnvironmentError as e: utils.err( "Unable to edit data in {file}: {err}".format( file=filepath, err=e ) ) return try: tokens_to_remove = set(argv) users_file = open(filepath, "r+") old_data = json.loads(users_file.read()) new_data = [] removed_tokens = set() for old_item in old_data: if old_item["token"] in tokens_to_remove: removed_tokens.add(old_item["token"]) else: new_data.append(old_item) tokens_not_found = sorted(tokens_to_remove - removed_tokens) if tokens_not_found: utils.err("Following tokens were not found: '{tokens}'".format( tokens="', '".join(tokens_not_found) )) if removed_tokens: users_file.seek(0) users_file.truncate() users_file.write(json.dumps(new_data, indent=2)) users_file.close() except KeyError as e: utils.err( "Unable to parse data in {file}: missing key {key}".format( file=filepath, key=e ) ) except ValueError as e: utils.err( "Unable to parse data in {file}: {err}".format(file=filepath, err=e) ) except EnvironmentError as e: utils.err( "Unable to edit data in {file}: {err}".format(file=filepath, err=e) )
def set_node_utilization(node, argv): cib = utils.get_cib_dom() node_el = utils.dom_get_node(cib, node) if node_el is None: utils.err("Unable to find a node: {0}".format(node)) utils.dom_update_utilization( node_el, utils.convert_args_to_tuples(argv), "nodes-" ) utils.replace_cib_configuration(cib)
def _get_gid(group_name): """ Commandline options: no options """ try: return grp.getgrnam(group_name).gr_gid except KeyError: utils.err( "Unable to determine gid of group '{0}'".format(group_name) )
def config_checkpoint_view(argv): if len(argv) != 1: usage.config(["checkpoint", "view"]) sys.exit(1) utils.usefile = True utils.filename = os.path.join(settings.cib_dir, "cib-%s.raw" % argv[0]) if not os.path.isfile(utils.filename): utils.err("unable to read the checkpoint") config_show_cib()
def config_checkpoint_restore(argv): if len(argv) != 1: usage.config(["checkpoint", "restore"]) sys.exit(1) cib_path = os.path.join(settings.cib_dir, "cib-%s.raw" % argv[0]) try: snapshot_dom = parse(cib_path) except Exception as e: utils.err("unable to read the checkpoint: %s" % e) utils.replace_cib_configuration(snapshot_dom)
def stonith_confirm(argv): if len(argv) != 1: utils.err("must specify one (and only one) node to confirm fenced") node = argv.pop(0) args = ["stonith_admin", "-C", node] output, retval = utils.run(args) if retval != 0: utils.err("unable to confirm fencing of node '%s'\n" % node + output) else: print("Node: %s confirmed fenced" % node)
def stonith_level_verify(): dom = utils.get_cib_dom() fls = dom.getElementsByTagName("fencing-level") for fl in fls: node = fl.getAttribute("target") devices = fl.getAttribute("devices") for dev in devices.split(","): if not utils.is_stonith_resource(dev): utils.err("%s is not a stonith id" % dev) if not utils.is_corosync_node(node) and not utils.is_pacemaker_node(node): utils.err("%s is not currently a node" % node)
def quorum_unblock_cmd(argv): if len(argv) > 0: usage.quorum(["unblock"]) sys.exit(1) if utils.is_rhel6(): utils.err("operation is not supported on CMAN clusters") output, retval = utils.run( ["corosync-cmapctl", "-g", "runtime.votequorum.wait_for_all_status"] ) if retval != 0: utils.err("unable to check quorum status") if output.split("=")[-1].strip() != "1": utils.err("cluster is not waiting for nodes to establish quorum") unjoined_nodes = ( set(utils.getNodesFromCorosyncConf()) - set(utils.getCorosyncActiveNodes()) ) if not unjoined_nodes: utils.err("no unjoined nodes found") if "--force" not in utils.pcs_options: answer = utils.get_terminal_input( ( "WARNING: If node(s) {nodes} are not powered off or they do" + " have access to shared resources, data corruption and/or" + " cluster failure may occur. Are you sure you want to" + " continue? [y/N] " ).format(nodes=", ".join(unjoined_nodes)) ) if answer.lower() not in ["y", "yes"]: print("Canceled") return for node in unjoined_nodes: stonith.stonith_confirm([node], skip_question=True) output, retval = utils.run( ["corosync-cmapctl", "-s", "quorum.cancel_wait_for_all", "u8", "1"] ) if retval != 0: utils.err("unable to cancel waiting for nodes") print("Quorum unblocked") startup_fencing = utils.get_set_properties().get("startup-fencing", "") utils.set_cib_property( "startup-fencing", "false" if startup_fencing.lower() != "false" else "true" ) utils.set_cib_property("startup-fencing", startup_fencing) print("Waiting for nodes canceled")
def getCurrentConstraints(passed_dom=None): if passed_dom: dom = passed_dom else: current_constraints_xml = utils.get_cib_xpath('//constraints') if current_constraints_xml == "": utils.err("unable to process cib") # Verify current constraint doesn't already exist # If it does we replace it with the new constraint dom = parseString(current_constraints_xml) constraintsElement = dom.getElementsByTagName('constraints')[0] return (dom, constraintsElement)
def config_checkpoint_view(lib, argv, modifiers): """ Options: no options """ modifiers.ensure_only_supported() if len(argv) != 1: usage.config(["checkpoint view"]) sys.exit(1) loaded, lines = _checkpoint_to_lines(lib, argv[0]) if not loaded: utils.err("unable to read the checkpoint") print("\n".join(lines))
def corosync_status(lib, argv, modifiers): """ Options: no options """ del lib modifiers.ensure_only_supported() if argv: raise CmdLineInputError() (output, retval) = utils.run(["corosync-quorumtool", "-l"]) if retval != 0: utils.err("corosync not running") else: print(output.rstrip())
def cluster_pcsd_status(argv, dont_exit=False): bad_nodes = False if len(argv) == 0: nodes = utils.getNodesFromCorosyncConf() if len(nodes) == 0: if utils.is_rhel6(): utils.err("no nodes found in cluster.conf") else: utils.err("no nodes found in corosync.conf") bad_nodes = check_nodes(nodes, " ") else: bad_nodes = check_nodes(argv, " ") if bad_nodes and not dont_exit: sys.exit(2)
def config_checkpoint_view(lib, argv, modifiers): """ Options: no options """ modifiers.ensure_only_supported() if len(argv) != 1: usage.config(["checkpoint", "view"]) sys.exit(1) utils.usefile = True utils.filename = os.path.join(settings.cib_dir, "cib-%s.raw" % argv[0]) if not os.path.isfile(utils.filename): utils.err("unable to read the checkpoint") config_show_cib(lib)
def pcsd_certkey(dummy_lib, argv, modifiers): """ Options: * --force - overwrite existing file """ modifiers.ensure_only_supported("--force") if len(argv) != 2: raise CmdLineInputError() certfile = argv[0] keyfile = argv[1] try: with open(certfile, 'r') as myfile: cert = myfile.read() with open(keyfile, 'r') as myfile: key = myfile.read() except IOError as e: utils.err(e) errors = utils.verify_cert_key_pair(cert, key) if errors: for err in errors: utils.err(err, False) sys.exit(1) if (not modifiers.get("--force") and (os.path.exists(settings.pcsd_cert_location) or os.path.exists(settings.pcsd_key_location))): utils.err( "certificate and/or key already exists, use --force to overwrite") try: try: os.chmod(settings.pcsd_cert_location, 0o700) except OSError: # If the file doesn't exist, we don't care pass try: os.chmod(settings.pcsd_key_location, 0o700) except OSError: # If the file doesn't exist, we don't care pass with os.fdopen( os.open(settings.pcsd_cert_location, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o700), 'w') as myfile: myfile.write(cert) with os.fdopen( os.open(settings.pcsd_key_location, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o700), 'w') as myfile: myfile.write(key) except IOError as e: utils.err(e) print( "Certificate and key updated, you may need to restart pcsd (service pcsd restart) for new settings to take effect" )
def xml_status(dummy_lib, argv, modifiers): """ Options: * -f - CIB file """ modifiers.ensure_only_supported("-f") if argv: raise CmdLineInputError() (output, retval) = utils.run(["crm_mon", "-1", "-r", "-X"], ignore_stderr=True) if (retval != 0): utils.err("running crm_mon, is pacemaker running?") print(output.rstrip())
def location_rule_check_duplicates(dom, constraint_el): if "--force" not in utils.pcs_options: duplicates = location_rule_find_duplicates(dom, constraint_el) if duplicates: lines = [] for dup in duplicates: lines.append(" Constraint: %s" % dup.getAttribute("id")) for dup_rule in utils.dom_get_children_by_tag_name( dup, "rule"): lines.append(rule_utils.ExportDetailed().get_string( dup_rule, True, " ")) utils.err( "duplicate constraint already exists, use --force to override\n" + "\n".join(lines))
def config_backup_check_version(version): try: version_number = int(version) supported_version = config_backup_version() if version_number > supported_version: utils.err("Unsupported version of the backup, " "supported version is %d, backup version is %d" % (supported_version, version_number)) if version_number < supported_version: print("Warning: restoring from the backup version %d, " "current supported version is %s" % (version_number, supported_version)) except TypeError: utils.err("Cannot determine version of the backup")
def get_fence_agent_info(lib, argv, modifiers): """ Options: no options """ modifiers.ensure_only_supported() if len(argv) != 1: utils.err("One parameter expected") agent_name = argv[0] if not agent_name.startswith("stonith:"): utils.err("Invalid fence agent name") print( json.dumps( lib.stonith_agent.describe_agent(agent_name[len("stonith:"):])))
def set_property(lib, argv, modifiers): """ Options: * --force - allow unknown options * -f - CIB file """ del lib modifiers.ensure_only_supported( "--force", "-f", # The hint is defined to print error messages which point users to the # changes section in pcs manpage. # To be removed in the next significant version. hint_syntax_changed=modifiers.is_specified("--node"), ) if not argv: raise CmdLineInputError() prop_def_dict = utils.get_cluster_properties_definition() failed = False forced = modifiers.get("--force") properties = {} for arg in argv: args = arg.split("=") if len(args) != 2: utils.err("invalid property format: '{0}'".format(arg), False) failed = True elif not args[0]: utils.err("empty property name: '{0}'".format(arg), False) failed = True elif forced or args[1].strip() == "": properties[args[0]] = args[1] else: try: if utils.is_valid_cluster_property(prop_def_dict, args[0], args[1]): properties[args[0]] = args[1] else: utils.err( "invalid value of property: '{0}', (use --force to " "override)".format(arg), False, ) failed = True except utils.UnknownPropertyException: utils.err( "unknown cluster property: '{0}', (use --force to " "override)".format(args[0]), False, ) failed = True if failed: sys.exit(1) cib_dom = utils.get_cib_dom() for prop, value in properties.items(): utils.set_cib_property(prop, value, cib_dom) utils.replace_cib_configuration(cib_dom)
def quorum_unblock_cmd(lib, argv, modifiers): """ Options: * --force - no error when removing non existing property and no warning about this action """ modifiers.ensure_only_supported("--force") if argv: raise CmdLineInputError() output, retval = utils.run( ["corosync-cmapctl", "-g", "runtime.votequorum.wait_for_all_status"] ) if retval != 0: utils.err("unable to check quorum status") if output.split("=")[-1].strip() != "1": utils.err("cluster is not waiting for nodes to establish quorum") all_nodes, report_list = get_existing_nodes_names( utils.get_corosync_conf_facade() ) if report_list: process_library_reports(report_list) unjoined_nodes = set(all_nodes) - set(utils.getCorosyncActiveNodes()) if not unjoined_nodes: utils.err("no unjoined nodes found") if not modifiers.get("--force"): answer = utils.get_terminal_input( ( "WARNING: If node(s) {nodes} are not powered off or they do" + " have access to shared resources, data corruption and/or" + " cluster failure may occur. Are you sure you want to" + " continue? [y/N] " ).format(nodes=", ".join(unjoined_nodes)) ) if answer.lower() not in ["y", "yes"]: print("Canceled") return for node in unjoined_nodes: # pass --force so no warning will be displayed stonith.stonith_confirm( lib, [node], parse_args.InputModifiers({"--force": ""}) ) output, retval = utils.run( ["corosync-cmapctl", "-s", "quorum.cancel_wait_for_all", "u8", "1"] ) if retval != 0: utils.err("unable to cancel waiting for nodes") print("Quorum unblocked") startup_fencing = utils.get_set_properties().get("startup-fencing", "") utils.set_cib_property( "startup-fencing", "false" if startup_fencing.lower() != "false" else "true" ) utils.set_cib_property("startup-fencing", startup_fencing) print("Waiting for nodes canceled")
def stonith_fence(argv): if len(argv) != 1: utils.err("must specify one (and only one) node to fence") node = argv.pop(0) if "--off" in utils.pcs_options: args = ["stonith_admin", "-F", node] else: args = ["stonith_admin", "-B", node] output, retval = utils.run(args) if retval != 0: utils.err("unable to fence '%s'\n" % node + output) else: print("Node: %s fenced" % node)
def config_checkpoint_restore(dummy_lib, argv, modifiers): """ Options: * -f - CIB file, a checkpoint will be restored into a specified file """ modifiers.ensure_only_supported("-f") if len(argv) != 1: usage.config(["checkpoint", "restore"]) sys.exit(1) cib_path = os.path.join(settings.cib_dir, "cib-%s.raw" % argv[0]) try: snapshot_dom = parse(cib_path) except Exception as e: utils.err("unable to read the checkpoint: %s" % e) utils.replace_cib_configuration(snapshot_dom)
def cluster_pcsd_status(lib, argv, modifiers, dont_exit=False): """ Options: * --request-timeout - HTTP timeout for node authorization check """ modifiers.ensure_only_supported("--request-timeout") bad_nodes = False if len(argv) == 0: nodes = utils.get_corosync_conf_facade().get_nodes_names() if len(nodes) == 0: utils.err("no nodes found in corosync.conf") bad_nodes = check_nodes(nodes, " ") else: bad_nodes = check_nodes(argv, " ") if bad_nodes and not dont_exit: sys.exit(2)
def wait_for_nodes_started(node_list, timeout=None): timeout = 60 * 15 if timeout is None else timeout interval = 2 stop_at = datetime.datetime.now() + datetime.timedelta(seconds=timeout) print("Waiting for node(s) to start...") if not node_list: code, output = wait_for_local_node_started(stop_at, interval) if code != 0: utils.err(output) else: print(output) else: node_errors = parallel_for_nodes(wait_for_remote_node_started, node_list, stop_at, interval) if node_errors: utils.err("unable to verify all nodes have started")
def destroy_cluster(argv): if len(argv) > 0: # stop pacemaker and resources while cluster is still quorate nodes = argv node_errors = parallel_for_nodes(utils.repeat_if_timeout( utils.stopPacemaker), nodes, quiet=True) # proceed with destroy regardless of errors # destroy will stop any remaining cluster daemons node_errors = parallel_for_nodes(utils.destroyCluster, nodes, quiet=True) if node_errors: utils.err("unable to destroy cluster\n" + "\n".join(node_errors.values()))
def stonith_level_verify(): dom = utils.get_cib_dom() corosync_nodes = [] if utils.hasCorosyncConf(): corosync_nodes = utils.getNodesFromCorosyncConf() pacemaker_nodes = utils.getNodesFromPacemaker() fls = dom.getElementsByTagName("fencing-level") for fl in fls: node = fl.getAttribute("target") devices = fl.getAttribute("devices") for dev in devices.split(","): if not utils.is_stonith_resource(dev): utils.err("%s is not a stonith id" % dev) if node not in corosync_nodes and node not in pacemaker_nodes: utils.err("%s is not currently a node" % node)
def pcsd_certkey(argv): if len(argv) != 2: usage.pcsd(["certkey"]) exit(1) certfile = argv[0] keyfile = argv[1] try: with open(certfile, 'r') as myfile: cert = myfile.read() with open(keyfile, 'r') as myfile: key = myfile.read() except IOError as e: utils.err(e) errors = utils.verify_cert_key_pair(cert, key) if errors: for err in errors: utils.err(err, False) sys.exit(1) if "--force" not in utils.pcs_options and ( os.path.exists(settings.pcsd_cert_location) or os.path.exists(settings.pcsd_key_location)): utils.err( "certificate and/or key already exists, your must use --force to overwrite" ) try: try: os.chmod(settings.pcsd_cert_location, 0o700) except OSError: # If the file doesn't exist, we don't care pass try: os.chmod(settings.pcsd_key_location, 0o700) except OSError: # If the file doesn't exist, we don't care pass with os.fdopen( os.open(settings.pcsd_cert_location, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o700), 'w') as myfile: myfile.write(cert) with os.fdopen( os.open(settings.pcsd_key_location, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o700), 'w') as myfile: myfile.write(key) except IOError as e: utils.err(e) print( "Certificate and key updated, you may need to restart pcsd (service pcsd restart) for new settings to take effect" )
def pcsd_deauth(lib, argv, modifiers): """ Options: No options """ del lib modifiers.ensure_only_supported() filepath = settings.pcsd_users_conf_location if not argv: try: users_file = open(filepath, "w") users_file.write(json.dumps([])) users_file.close() except EnvironmentError as e: utils.err("Unable to edit data in {file}: {err}".format( file=filepath, err=e)) return try: tokens_to_remove = set(argv) users_file = open(filepath, "r+") old_data = json.loads(users_file.read()) new_data = [] removed_tokens = set() for old_item in old_data: if old_item["token"] in tokens_to_remove: removed_tokens.add(old_item["token"]) else: new_data.append(old_item) tokens_not_found = sorted(tokens_to_remove - removed_tokens) if tokens_not_found: utils.err("Following tokens were not found: '{tokens}'".format( tokens="', '".join(tokens_not_found))) if removed_tokens: users_file.seek(0) users_file.truncate() users_file.write(json.dumps(new_data, indent=2)) users_file.close() except KeyError as e: utils.err("Unable to parse data in {file}: missing key {key}".format( file=filepath, key=e)) except ValueError as e: utils.err("Unable to parse data in {file}: {err}".format(file=filepath, err=e)) except EnvironmentError as e: utils.err("Unable to edit data in {file}: {err}".format(file=filepath, err=e))
def config_backup_check_version(version): """ Commandline options: no options """ try: version_number = int(version) supported_version = config_backup_version() if version_number > supported_version: utils.err( f"Unsupported version of the backup, supported version is " f"{supported_version}, backup version is {version_number}") if version_number < supported_version: warn( f"Restoring from the backup version {version_number}, current " f"supported version is {supported_version}") except TypeError: utils.err("Cannot determine version of the backup")
def location_prefer(lib, argv, modifiers): """ Options: * --force - allow unknown options, allow constraint for any resource type * -f - CIB file """ modifiers.ensure_only_supported("--force", "-f") rsc = argv.pop(0) prefer_option = argv.pop(0) dummy_rsc_type, rsc_value = parse_args.parse_typed_arg( rsc, [RESOURCE_TYPE_RESOURCE, RESOURCE_TYPE_REGEXP], RESOURCE_TYPE_RESOURCE) if prefer_option == "prefers": prefer = True elif prefer_option == "avoids": prefer = False else: raise CmdLineInputError() for nodeconf in argv: nodeconf_a = nodeconf.split("=", 1) if len(nodeconf_a) == 1: node = nodeconf_a[0] if prefer: score = "INFINITY" else: score = "-INFINITY" else: score = nodeconf_a[1] if not utils.is_score(score): utils.err( "invalid score '%s', use integer or INFINITY or -INFINITY" % score) if not prefer: if score[0] == "-": score = score[1:] else: score = "-" + score node = nodeconf_a[0] location_add(lib, [ sanitize_id("location-{0}-{1}-{2}".format(rsc_value, node, score)), rsc, node, score ], modifiers.get_subset("--force", "-f"))
def set_property(lib, argv, modifiers): """ Options: * --force - allow unknown options * -f - CIB file """ del lib modifiers.ensure_only_supported( "--force", "-f", ) if not argv: raise CmdLineInputError() prop_def_dict = utils.get_cluster_properties_definition() failed = False forced = modifiers.get("--force") properties = {} for arg in argv: args = arg.split("=") if len(args) != 2: utils.err("invalid property format: '{0}'".format(arg), False) failed = True elif not args[0]: utils.err("empty property name: '{0}'".format(arg), False) failed = True elif forced or args[1].strip() == "": properties[args[0]] = args[1] else: try: if utils.is_valid_cluster_property(prop_def_dict, args[0], args[1]): properties[args[0]] = args[1] else: utils.err( "invalid value of property: '{0}', (use --force to " "override)".format(arg), False, ) failed = True except utils.UnknownPropertyException: utils.err( "unknown cluster property: '{0}', (use --force to " "override)".format(args[0]), False, ) failed = True if failed: sys.exit(1) cib_dom = utils.get_cib_dom() for prop, value in properties.items(): utils.set_cib_property(prop, value, cib_dom) utils.replace_cib_configuration(cib_dom)
def cluster_get_corosync_conf(argv): if utils.is_rhel6(): utils.err("corosync.conf is not supported on CMAN clusters") if len(argv) > 1: usage.cluster() exit(1) if len(argv) == 0: print(utils.getCorosyncConf().rstrip()) return node = argv[0] retval, output = utils.getCorosyncConfig(node) if retval != 0: utils.err(output) else: print(output.rstrip())
def _get_uid(user_name): """ Commandline options: no options """ try: return pwd.getpwnam(user_name).pw_uid except KeyError: return utils.err( "Unable to determine uid of user '{0}'".format(user_name))
def start_cluster_nodes(nodes): # Large clusters take longer time to start up. So we make the timeout longer # for each 8 nodes: # 1 - 8 nodes: 1 * timeout # 9 - 16 nodes: 2 * timeout # 17 - 24 nodes: 3 * timeout # and so on # Users can override this and set their own timeout by specifying # the --request-timeout option (see utils.sendHTTPRequest). timeout = int(settings.default_request_timeout * math.ceil(len(nodes) / 8.0)) node_errors = parallel_for_nodes(utils.startCluster, nodes, quiet=True, timeout=timeout) if node_errors: utils.err("unable to start all nodes\n" + "\n".join(node_errors.values()))
def _get_gid(group_name): """ Commandline options: no options """ try: return grp.getgrnam(group_name).gr_gid except KeyError: return utils.err( "Unable to determine gid of group '{0}'".format(group_name))
def config_checkpoint_diff(lib, argv, modifiers): """ Commandline options: * -f - CIB file """ modifiers.ensure_only_supported("-f") if len(argv) != 2: usage.config(["checkpoint diff"]) sys.exit(1) if argv[0] == argv[1]: utils.err("cannot diff a checkpoint against itself") errors = [] checkpoints_lines = [] for checkpoint in argv: if checkpoint == "live": lines = _config_show_cib_lines(lib) if not lines: errors.append("unable to read live configuration") else: checkpoints_lines.append(lines) else: loaded, lines = _checkpoint_to_lines(lib, checkpoint) if not loaded: errors.append( "unable to read checkpoint '{0}'".format(checkpoint) ) else: checkpoints_lines.append(lines) if errors: utils.err("\n".join(errors)) print("Differences between {0} (-) and {1} (+):".format(*[ "live configuration" if label == "live" else f"checkpoint {label}" for label in argv ])) print("\n".join([ line.rstrip() for line in difflib.Differ().compare( checkpoints_lines[0], checkpoints_lines[1] )] ))
def config_restore(lib, argv, modifiers): """ Options: * --local - restore config only on local node * --request-timeout - timeout for HTTP requests, used only if --local was not defined or user is not root """ del lib modifiers.ensure_only_supported("--local", "--request-timeout") if len(argv) > 1: usage.config(["restore"]) sys.exit(1) infile_name = infile_obj = None if argv: infile_name = argv[0] if not infile_name: # in python3 stdin returns str so we need to use buffer infile_obj = BytesIO(sys.stdin.buffer.read()) if os.getuid() == 0: if modifiers.get("--local"): config_restore_local(infile_name, infile_obj) else: config_restore_remote(infile_name, infile_obj) else: new_argv = ["config", "restore"] new_stdin = None if modifiers.get("--local"): new_argv.append("--local") if infile_name: new_argv.append(os.path.abspath(infile_name)) else: new_stdin = infile_obj.read() err_msgs, exitcode, std_out, std_err = utils.call_local_pcsd( new_argv, new_stdin ) if err_msgs: for msg in err_msgs: utils.err(msg, False) sys.exit(1) print(std_out) sys.stderr.write(std_err) sys.exit(exitcode)
def list_property(lib, argv, modifiers): """ Options: * -f - CIB file * --all - list all properties * --defaults - list only default values of properties """ del lib modifiers.ensure_only_supported( "--defaults", "--all", "-f", # The hint is defined to print error messages which point users to the # changes section in pcs manpage. # To be removed in the next significant version. hint_syntax_changed=modifiers.is_specified("--node"), ) if len(argv) > 1: raise CmdLineInputError() print_all = len(argv) == 0 if modifiers.is_specified("--all") and modifiers.is_specified( "--defaults"): utils.err("you cannot specify both --all and --defaults") if modifiers.get("--all") or modifiers.get("--defaults"): if not print_all: utils.err( "you cannot specify a property when using --all or --defaults") properties = get_default_properties() else: properties = {} if not modifiers.get("--defaults"): configured_properties = utils.get_set_properties( None if print_all else argv[0], properties) if modifiers.get("--all"): properties.update(configured_properties) else: properties = configured_properties print("Cluster Properties:") for prop, val in sorted(properties.items()): print(" {0}: {1}".format(prop, val))