def certkey(lib: Any, argv: Sequence[str], modifiers: InputModifiers) -> None: warn("This command is deprecated and will be removed. " "Please use 'pcs pcsd certkey' instead.") try: return pcsd.pcsd_certkey_cmd(lib, argv, modifiers) except CmdLineInputError as e: return exit_on_cmdline_input_errror(e, "pcsd", ["certkey"])
def resources_to_cmd( resources_facade: ResourcesConfigurationFacade, ) -> List[List[str]]: output: List[List[str]] = [] primitives_created_with_bundle = set() for bundle_dto in resources_facade.bundles: if not (bundle_dto.container_type and bundle_dto.container_options): warn( f"Bundle resource '{bundle_dto.id}' uses unsupported container " "type, therefore pcs is unable to create it. The resource will be omitted." ) continue output.extend(_resource_bundle_to_cmd(bundle_dto)) if bundle_dto.member_id: primitive_dto = resources_facade.get_primitive_dto( bundle_dto.member_id) if primitive_dto is None: raise CmdLineInputError( f"Invalid data: bundle '{bundle_dto.id}' has inner " f"primitive resource with id '{bundle_dto.member_id}' " "which was not found") output.extend( _resource_primitive_to_cmd(primitive_dto, bundle_dto.id)) primitives_created_with_bundle.add(bundle_dto.member_id) for primitive_dto in resources_facade.primitives: if primitive_dto.id not in primitives_created_with_bundle: output.extend(_resource_primitive_to_cmd(primitive_dto, None)) for group_dto in resources_facade.groups: output.extend(_resource_group_to_cmd(group_dto)) for clone_dto in resources_facade.clones: output.extend(_resource_clone_to_cmd(clone_dto)) return output
def config_cmd(lib: Any, argv: List[str], modifiers: parse_args.InputModifiers) -> None: is_text_format = ( modifiers.get_output_format() == parse_args.OUTPUT_FORMAT_VALUE_TEXT) if not is_text_format: warn(f"Only '{parse_args.OUTPUT_FORMAT_VALUE_TEXT}' output format is " "supported for stonith levels") resource.config_common(lib, argv, modifiers, stonith=True) if is_text_format: _print_stonith_levels(lib)
def _stonith_level_normalize_devices(argv): """ Commandline options: no options """ # normalize devices - previously it was possible to delimit devices by both # a comma and a space if any("," in arg for arg in argv): warn("Delimiting stonith devices with ',' is deprecated and will be " "removed. Please use a space to delimit stonith devices.") return ",".join(argv).split(",")
def tag_list_cmd( lib: Any, argv: Sequence[str], modifiers: InputModifiers, ) -> None: """ Options: * -f - CIB file """ warn( "This command is deprecated and will be removed. " "Please use 'pcs tag config' instead." ) return tag_config(lib, argv, modifiers)
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 stonith_level_remove_cmd(lib, argv, modifiers): """ Options: * -f - CIB file """ modifiers.ensure_only_supported("-f") if not argv: raise CmdLineInputError() target_type, target_value, devices = None, None, None level = argv[0] allowed_keywords = {"target", "stonith"} if len(argv) > 1 and argv[1] in allowed_keywords: ( target_type, target_value, devices, ) = _stonith_level_parse_target_and_stonith(argv[1:]) target_may_be_a_device = False else: # TODO remove, deprecated backward compatibility layer for old syntax if len(argv) > 1: warn( "Syntax 'pcs stonith level delete | remove <level> [<target>] " "[<stonith id>...]' is deprecated and will be removed. Please " "use 'pcs stonith level delete | remove <level> " "[target <target>] [stonith <stonith id>...]'.") if not parse_args.ARG_TYPE_DELIMITER in argv[1] and "," in argv[1]: warn("Delimiting stonith devices with ',' is deprecated and " "will be removed. Please use a space to delimit stonith " "devices.") target_type, target_value = _stonith_level_parse_node(argv[1]) if len(argv) > 2: devices = _stonith_level_normalize_devices(argv[2:]) target_may_be_a_device = True lib.fencing_topology.remove_levels_by_params( level, target_type, target_value, devices, # backward compatibility mode, see lib command for details target_may_be_a_device=target_may_be_a_device, )
def filter_resources( self, resource_ids_to_find: Iterable[str], ) -> "ResourcesConfigurationFacade": if not resource_ids_to_find: return self primitives = set() groups = set() clones = set() bundles = set() label = "resource/stonith device" if self._only_stonith is True: label = "stonith device" elif self._only_stonith is False: label = "resource" ids_to_process = set(resource_ids_to_find) processed_ids = set() while ids_to_process: resource_id = ids_to_process.pop() if resource_id in processed_ids: continue resource_dto = self._get_any_resource_dto(resource_id) if resource_dto is None: warn(f"Unable to find {label} '{resource_id}'") continue processed_ids.add(resource_id) ids_to_process.update(self._get_children_ids(resource_id)) if isinstance(resource_dto, CibResourcePrimitiveDto): primitives.add(resource_id) elif isinstance(resource_dto, CibResourceGroupDto): groups.add(resource_id) elif isinstance(resource_dto, CibResourceCloneDto): clones.add(resource_id) elif isinstance(resource_dto, CibResourceBundleDto): bundles.add(resource_id) else: AssertionError() if not processed_ids: raise CmdLineInputError(f"No {label} found") return self.__class__( primitives=sorted( (self._primitives_map[res_id] for res_id in primitives), key=lambda obj: obj.id, ), clones=sorted( (self._clones_map[res_id] for res_id in clones), key=lambda obj: obj.id, ), groups=sorted( (self._groups_map[res_id] for res_id in groups), key=lambda obj: obj.id, ), bundles=sorted( (self._bundles_map[res_id] for res_id in bundles), key=lambda obj: obj.id, ), filtered_ids=set(resource_ids_to_find) & set(processed_ids), only_stonith=self._only_stonith, )
def dom_rule_add(dom_element, options, rule_argv, cib_schema_version): # pylint: disable=too-many-branches """ Commandline options: no options """ # validate options if options.get("score") and options.get("score-attribute"): utils.err("can not specify both score and score-attribute") if options.get("score") and not utils.is_score(options["score"]): # preserving legacy behaviour print( "Warning: invalid score '%s', setting score-attribute=pingd instead" % options["score"]) warn("Converting invalid score to score-attribute=pingd is deprecated " "and will be removed.") options["score-attribute"] = "pingd" options["score"] = None if options.get("role"): role = options["role"].capitalize() utils.print_depracation_warning_for_legacy_roles(options["role"]) supported_roles = (const.PCMK_ROLES_PROMOTED + const.PCMK_ROLES_UNPROMOTED) if role not in supported_roles: utils.err("invalid role '{role}', use {supported_roles}".format( role=options["role"], supported_roles=format_list_custom_last_separator( list(supported_roles), " or "), )) options["role"] = pacemaker.role.get_value_for_cib( role, cib_schema_version >= const.PCMK_NEW_ROLES_CIB_VERSION, ) if options.get("id"): id_valid, id_error = utils.validate_xml_id(options["id"], "rule id") if not id_valid: utils.err(id_error) if utils.does_id_exist(dom_element.ownerDocument, options["id"]): utils.err("id '%s' is already in use, please specify another one" % options["id"]) # parse rule if not rule_argv: utils.err("no rule expression was specified") try: preprocessor = TokenPreprocessor() dom_rule = CibBuilder(cib_schema_version).build( dom_element, RuleParser().parse(preprocessor.run(rule_argv)), options.get("id"), ) except SyntaxError as e: utils.err("'%s' is not a valid rule expression: %s" % (" ".join(rule_argv), e)) except UnexpectedEndOfInput as e: utils.err( "'%s' is not a valid rule expression: unexpected end of rule" % " ".join(rule_argv)) except (ParserException, CibBuilderException) as e: utils.err("'%s' is not a valid rule expression" % " ".join(rule_argv)) for msg in preprocessor.warning_list: warn(msg) # add options into rule xml if not options.get("score") and not options.get("score-attribute"): options["score"] = "INFINITY" for name, value in options.items(): if name != "id" and value is not None: dom_rule.setAttribute(name, value) # score or score-attribute is required for the nested rules in order to have # valid CIB, pacemaker does not use the score of the nested rules for rule in dom_rule.getElementsByTagName("rule"): rule.setAttribute("score", "0") if dom_element.hasAttribute("score"): dom_element.removeAttribute("score") if dom_element.hasAttribute("node"): dom_element.removeAttribute("node") return dom_element
def show(lib, argv, modifiers): warn("This command is deprecated and will be removed. " "Please use 'pcs constraint colocation config' instead.") return config_cmd(lib, argv, modifiers)
def config_import_cman(lib, argv, modifiers): """ Options: * --force - skip checks, overwrite files * --interactive - interactive issue resolving * --request-timeout - effective only when ouput is not specified """ # pylint: disable=no-member del lib warn("This command is deprecated and will be removed.") modifiers.ensure_only_supported( "--force", "interactive", "--request-timeout", ) if no_clufter: utils.err("Unable to perform a CMAN cluster conversion due to missing " "python-clufter package") clufter_supports_corosync3 = hasattr(clufter.facts, "cluster_pcs_camelback") # prepare convertor options cluster_conf = settings.cluster_conf_file dry_run_output = None output_format = "corosync.conf" dist = None invalid_args = False for arg in argv: if "=" in arg: name, value = arg.split("=", 1) if name == "input": cluster_conf = value elif name == "output": dry_run_output = value elif name == "output-format": if value in ( "corosync.conf", "pcs-commands", "pcs-commands-verbose", ): output_format = value else: invalid_args = True elif name == "dist": dist = value else: invalid_args = True else: invalid_args = True if output_format not in ("pcs-commands", "pcs-commands-verbose") and ( dry_run_output and not dry_run_output.endswith(".tar.bz2")): dry_run_output += ".tar.bz2" if invalid_args or not dry_run_output: usage.config(["import-cman"]) sys.exit(1) debug = modifiers.get("--debug") force = modifiers.get("--force") interactive = modifiers.get("--interactive") if dist is not None: if not clufter_supports_corosync3: utils.err( "Unable to perform a CMAN cluster conversion due to clufter " "not supporting Corosync 3. Please, upgrade clufter packages.") if not clufter.facts.cluster_pcs_camelback("linux", dist.split(",")): utils.err("dist does not match output-format") elif output_format == "corosync.conf": dist = _get_linux_dist() else: # for output-format=pcs-command[-verbose] dist = _get_linux_dist() clufter_args = { "input": str(cluster_conf), "cib": { "passin": "bytestring" }, "nocheck": force, "batch": True, "sys": "linux", "dist": dist, } if interactive: if "EDITOR" not in os.environ: utils.err("$EDITOR environment variable is not set") clufter_args["batch"] = False clufter_args["editor"] = os.environ["EDITOR"] if debug: logging.getLogger("clufter").setLevel(logging.DEBUG) if output_format == "corosync.conf": clufter_args["coro"] = {"passin": "struct"} cmd_name = "ccs2pcs-camelback" elif output_format in ("pcs-commands", "pcs-commands-verbose"): clufter_args["output"] = {"passin": "bytestring"} clufter_args["start_wait"] = "60" clufter_args["tmp_cib"] = "tmp-cib.xml" clufter_args["force"] = force clufter_args["text_width"] = "80" clufter_args["silent"] = True clufter_args["noguidance"] = True if output_format == "pcs-commands-verbose": clufter_args["text_width"] = "-1" clufter_args["silent"] = False clufter_args["noguidance"] = False if clufter.facts.cluster_pcs_flatiron("linux", dist.split(",")): cmd_name = "ccs2pcscmd-flatiron" elif clufter.facts.cluster_pcs_needle("linux", dist.split(",")): cmd_name = "ccs2pcscmd-needle" elif clufter_supports_corosync3 and clufter.facts.cluster_pcs_camelback( "linux", dist.split(",")): cmd_name = "ccs2pcscmd-camelback" else: utils.err( "unrecognized dist, try something recognized" + " (e. g. rhel,6.8 or redhat,7.3 or debian,7 or ubuntu,trusty)") clufter_args_obj = type(str("ClufterOptions"), (object, ), clufter_args) # run convertor run_clufter( cmd_name, clufter_args_obj, debug, force, "Error: unable to import cluster configuration", ) # save commands if output_format in ("pcs-commands", "pcs-commands-verbose"): ok, message = utils.write_file( dry_run_output, clufter_args_obj.output["passout"].decode()) if not ok: utils.err(message) return # put new config files into tarball file_list = config_backup_path_list() for file_item in file_list.values(): file_item["attrs"]["uname"] = "root" file_item["attrs"]["gname"] = "root" file_item["attrs"]["uid"] = 0 file_item["attrs"]["gid"] = 0 file_item["attrs"]["mode"] = 0o600 tar_data = BytesIO() try: tarball = tarfile.open(fileobj=tar_data, mode="w|bz2") config_backup_add_version_to_tarball(tarball) utils.tar_add_file_data( tarball, clufter_args_obj.cib["passout"], "cib.xml", **file_list["cib.xml"]["attrs"], ) # put uidgid into separate files fmt_simpleconfig = clufter.format_manager.FormatManager.init_lookup( "simpleconfig").plugins["simpleconfig"] corosync_struct = [] uidgid_list = [] for section in clufter_args_obj.coro["passout"][2]: if section[0] == "uidgid": uidgid_list.append(section[1]) else: corosync_struct.append(section) corosync_conf_data = fmt_simpleconfig( "struct", ("corosync", (), corosync_struct))("bytestring") utils.tar_add_file_data( tarball, corosync_conf_data, "corosync.conf", **file_list["corosync.conf"]["attrs"], ) for uidgid in uidgid_list: uid = "" gid = "" for item in uidgid: if item[0] == "uid": uid = item[1] if item[0] == "gid": gid = item[1] filename = utils.get_uid_gid_file_name(uid, gid) uidgid_data = fmt_simpleconfig( "struct", ("corosync", (), [("uidgid", uidgid, None)]))("bytestring") utils.tar_add_file_data( tarball, uidgid_data, "uidgid.d/" + filename, **file_list["uidgid.d"]["attrs"], ) tarball.close() except (tarfile.TarError, EnvironmentError) as e: utils.err("unable to create tarball: %s" % e) tar_data.seek(0) # save tarball / remote restore if dry_run_output: ok, message = utils.write_file(dry_run_output, tar_data.read(), permissions=0o600, binary=True) if not ok: utils.err(message) else: config_restore_remote(None, tar_data) tar_data.close()
def stonith_level_clear_cmd(lib, argv, modifiers): """ Options: * -f - CIB file """ modifiers.ensure_only_supported("-f") if not argv: lib.fencing_topology.remove_all_levels() return allowed_keywords = {"target", "stonith"} if len(argv) > 1 or (len(argv) == 1 and argv[0] in allowed_keywords): ( target_type, target_value, devices, ) = _stonith_level_parse_target_and_stonith(argv) if devices is not None and target_value is not None: raise CmdLineInputError( "Only one of 'target' and 'stonith' can be used") lib.fencing_topology.remove_levels_by_params( None, target_type, target_value, devices, ) return # TODO remove, deprecated backward compatibility mode for old syntax # Command parameters are: node, stonith-list # Both the node and the stonith list are optional. If the node is ommited # and the stonith list is present, there is no way to figure it out, since # there is no specification of what the parameter is. Hence the pre-lib # code tried both. It deleted all levels having the first parameter as # either a node or a device list. Since it was only possible to specify # node as a target back then, this is enabled only in that case. warn("Syntax 'pcs stonith level clear [<target> | <stonith id(s)>] is " "deprecated and will be removed. Please use 'pcs stonith level clear " "[target <target>] | [stonith <stonith id>...]'.") target_type, target_value = _stonith_level_parse_node(argv[0]) was_error = False try: lib.fencing_topology.remove_levels_by_params( None, target_type, target_value, None, # pre-lib code didn't return any error when no level was found ignore_if_missing=True, ) except LibraryError: was_error = True if target_type == TARGET_TYPE_NODE: try: lib.fencing_topology.remove_levels_by_params( None, None, None, argv[0].split(","), # pre-lib code didn't return any error when no level was found ignore_if_missing=True, ) except LibraryError: was_error = True if was_error: raise LibraryError()
def list_property_deprecated(lib, argv, modifiers): warn("This command is deprecated and will be removed. " "Please use 'pcs property config' instead.") return list_property(lib, argv, modifiers)
def config_export_pcs_commands(lib, argv, modifiers, verbose=False): """ Options: * --force - skip checks, overwrite files * --interactive - interactive issue resolving * -f - CIB file * --corosync_conf """ del lib warn("This command is deprecated and will be removed.") modifiers.ensure_only_supported("--force", "--interactive", "-f", "--corosync_conf") if no_clufter: utils.err( "Unable to perform export due to missing python-clufter package") # parse options debug = modifiers.get("--debug") force = modifiers.get("--force") interactive = modifiers.get("--interactive") invalid_args = False output_file = None dist = None for arg in argv: if "=" in arg: name, value = arg.split("=", 1) if name == "output": output_file = value elif name == "dist": dist = value else: invalid_args = True else: invalid_args = True # check options if invalid_args: usage.config(["export pcs-commands"]) sys.exit(1) # complete optional options if dist is None: dist = _get_linux_dist() # prepare convertor options clufter_args = { "nocheck": force, "batch": True, "sys": "linux", "dist": dist, "coro": settings.corosync_conf_file, "start_wait": "60", "tmp_cib": "tmp-cib.xml", "force": force, "text_width": "80", "silent": True, "noguidance": True, } if output_file: clufter_args["output"] = {"passin": "bytestring"} else: clufter_args["output"] = "-" if interactive: if "EDITOR" not in os.environ: utils.err("$EDITOR environment variable is not set") clufter_args["batch"] = False clufter_args["editor"] = os.environ["EDITOR"] if debug: logging.getLogger("clufter").setLevel(logging.DEBUG) if utils.usefile: clufter_args["cib"] = os.path.abspath(utils.filename) else: clufter_args["cib"] = ("bytestring", utils.get_cib()) if verbose: clufter_args["text_width"] = "-1" clufter_args["silent"] = False clufter_args["noguidance"] = False clufter_args_obj = type(str("ClufterOptions"), (object, ), clufter_args) cmd_name = "pcs2pcscmd-camelback" # run convertor run_clufter( cmd_name, clufter_args_obj, debug, force, "Error: unable to export cluster configuration", ) # save commands if not printed to stdout by clufter if output_file: # pylint: disable=no-member ok, message = utils.write_file( output_file, clufter_args_obj.output["passout"].decode()) if not ok: utils.err(message)
def print_alert_show(lib, argv, modifiers): warn( "This command is deprecated and will be removed. " "Please use 'pcs alert config' instead." ) return print_alert_config(lib, argv, modifiers)
def main(argv=None): # pylint: disable=global-statement # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements if completion.has_applicable_environment(os.environ): print( completion.make_suggestions( os.environ, usage.generate_completion_tree_from_usage() ) ) sys.exit() argv = argv if argv else sys.argv[1:] utils.subprocess_setup() global filename, usefile utils.pcs_options = {} # we want to support optional arguments for --wait, so if an argument # is specified with --wait (ie. --wait=30) then we use them waitsecs = None new_argv = [] for arg in argv: if arg.startswith("--wait="): tempsecs = arg.replace("--wait=", "") if tempsecs: waitsecs = tempsecs arg = "--wait" new_argv.append(arg) argv = new_argv try: if "--" in argv: pcs_options, argv = getopt.gnu_getopt( argv, parse_args.PCS_SHORT_OPTIONS, parse_args.PCS_LONG_OPTIONS ) else: # DEPRECATED # TODO remove # We want to support only the -- version ( args_without_negative_nums, args_filtered_out, ) = parse_args.filter_out_non_option_negative_numbers(argv) if args_filtered_out: options_str = "', '".join(args_filtered_out) output.warn( f"Using '{options_str}' without '--' is deprecated, those " "parameters will be considered position independent " "options in future pcs versions" ) pcs_options, dummy_argv = getopt.gnu_getopt( args_without_negative_nums, parse_args.PCS_SHORT_OPTIONS, parse_args.PCS_LONG_OPTIONS, ) argv = parse_args.filter_out_options(argv) except getopt.GetoptError as err: usage.main() print(err) if err.opt in {"V", "clone", "device", "watchdog"}: # Print error messages which point users to the changes section in # pcs manpage. # TODO remove # To be removed in the next significant version. print(f"Hint: {errors.HINT_SYNTAX_CHANGE}") sys.exit(1) full = False for option, dummy_value in pcs_options: if option == "--full": full = True break for opt, val in pcs_options: if not opt in utils.pcs_options: utils.pcs_options[opt] = val else: # If any options are a list then they've been entered twice which # isn't valid utils.err("%s can only be used once" % opt) if opt in ("-h", "--help"): if not argv: usage.main() sys.exit() else: argv = [argv[0], "help"] + argv[1:] elif opt == "-f": usefile = True filename = val utils.usefile = usefile utils.filename = filename elif opt == "--corosync_conf": settings.corosync_conf_file = val elif opt == "--version": print(settings.pcs_version) if full: print( " ".join( sorted( [ feat["id"] for feat in capabilities.get_pcs_capabilities() ] ) ) ) sys.exit() elif opt == "--fullhelp": usage.full_usage() sys.exit() elif opt == "--wait": utils.pcs_options[opt] = waitsecs elif opt == "--request-timeout": request_timeout_valid = False try: timeout = int(val) if timeout > 0: utils.pcs_options[opt] = timeout request_timeout_valid = True except ValueError: pass if not request_timeout_valid: utils.err( ( "'{0}' is not a valid --request-timeout value, use " "a positive integer" ).format(val) ) logger = logging.getLogger("pcs") logger.propagate = 0 logger.handlers = [] if (os.getuid() != 0) and (argv and argv[0] != "help") and not usefile: _non_root_run(argv) cmd_map = { "resource": resource.resource_cmd, "cluster": cluster.cluster_cmd, "stonith": stonith.stonith_cmd, "property": prop.property_cmd, "constraint": constraint.constraint_cmd, "acl": acl.acl_cmd, "status": status.status_cmd, "config": config.config_cmd, "pcsd": pcsd.pcsd_cmd, "node": node.node_cmd, "quorum": quorum.quorum_cmd, "qdevice": qdevice.qdevice_cmd, "alert": alert.alert_cmd, "booth": booth.booth_cmd, "host": host.host_cmd, "client": client.client_cmd, "dr": dr.dr_cmd, "tag": tag.tag_cmd, "help": lambda lib, argv, modifiers: usage.main(), } try: routing.create_router(cmd_map, [])( utils.get_library_wrapper(), argv, utils.get_input_modifiers() ) except LibraryError as e: process_library_reports(e.args) except errors.CmdLineInputError: if argv and argv[0] in cmd_map: usage.show(argv[0], []) else: usage.main() sys.exit(1)
def show_acl_config(lib, argv, modifiers): warn("This command is deprecated and will be removed. " "Please use 'pcs acl config' instead.") return acl_config(lib, argv, modifiers)