def apply(next_in_line, env, *args, **kwargs): if local_file_path: try: with open(local_file_path, "r") as local_file: # the lock is released when the file gets closed on leaving # the with statement fcntl.flock(local_file.fileno(), fcntl.LOCK_SH) original_content = local_file.read() except EnvironmentError as e: raise error("Unable to read {0}: {1}".format( local_file_path, e.strerror)) from e env.corosync_conf_data = original_content result_of_next = next_in_line(env, *args, **kwargs) if local_file_path and env.corosync_conf_data != original_content: try: with open(local_file_path, "w") as local_file: # the lock is released when the file gets closed on leaving # the with statement fcntl.flock(local_file.fileno(), fcntl.LOCK_EX) local_file.write(env.corosync_conf_data) except EnvironmentError as e: raise error("Unable to write {0}: {1}".format( local_file_path, e.strerror)) from e return result_of_next
def apply(next_in_line, env, *args, **kwargs): if filename: touch_cib_file(filename) try: with open(filename, mode="r") as cib_file: # the lock is released when the file gets closed on leaving # the with statement fcntl.flock(cib_file.fileno(), fcntl.LOCK_SH) original_content = cib_file.read() except EnvironmentError as e: raise error("Cannot read cib file '{0}': '{1}'".format( filename, str(e))) from e env.cib_data = original_content result_of_next = next_in_line(env, *args, **kwargs) if filename and env.cib_data != original_content: try: with open(filename, mode="w") as cib_file: # the lock is released when the file gets closed on leaving # the with statement fcntl.flock(cib_file.fileno(), fcntl.LOCK_EX) cib_file.write(env.cib_data) except EnvironmentError as e: raise error("Cannot write cib file '{0}': '{1}'".format( filename, str(e))) from e return result_of_next
def status( lib: Any, argv: Sequence[str], modifiers: InputModifiers, ) -> None: """ Options: * --full - show full details, node attributes and failcount * --hide-inactive - hide inactive resources * --request-timeout - HTTP timeout for node authorization check """ modifiers.ensure_only_supported( "--full", "--hide-inactive", "--request-timeout", ) if argv: raise CmdLineInputError() status_list_raw = lib.dr.status_all_sites_plaintext( hide_inactive_resources=modifiers.get("--hide-inactive"), verbose=modifiers.get("--full"), ) try: status_list = [ dto.from_dict(DrSiteStatusDto, status_raw) for status_raw in status_list_raw ] except (KeyError, TypeError, ValueError) as e: raise error( "Unable to communicate with pcsd, received response:\n" f"{status_list_raw}" ) from e has_errors = False plaintext_parts = [] for site_status in status_list: plaintext_parts.append( "--- {local_remote} cluster - {role} site ---".format( local_remote=("Local" if site_status.local_site else "Remote"), role=site_status.site_role.capitalize(), ) ) if site_status.status_successfully_obtained: plaintext_parts.append(site_status.status_plaintext.strip()) plaintext_parts.extend(["", ""]) else: has_errors = True plaintext_parts.extend( ["Error: Unable to get status of the cluster from any node", ""] ) print("\n".join(plaintext_parts).strip()) if has_errors: raise error("Unable to get status of all sites")
def config( lib: Any, argv: Sequence[str], modifiers: InputModifiers, ) -> None: """ Options: None """ modifiers.ensure_only_supported() if argv: raise CmdLineInputError() config_raw = lib.dr.get_config() try: config_dto = dto.from_dict(DrConfigDto, config_raw) except (KeyError, TypeError, ValueError) as e: raise error( "Unable to communicate with pcsd, received response:\n" f"{config_raw}" ) from e lines = ["Local site:"] lines.extend(indent(_config_site_lines(config_dto.local_site))) for site_dto in config_dto.remote_site_list: lines.append("Remote site:") lines.extend(indent(_config_site_lines(site_dto))) print("\n".join(lines))
def apply(next_in_line, env, *args, **kwargs): if local_file_path: try: env.corosync_conf_data = open(local_file_path).read() except EnvironmentError as e: raise error("Unable to read {0}: {1}".format( local_file_path, e.strerror)) from e result_of_next = next_in_line(env, *args, **kwargs) if local_file_path: try: file = open(local_file_path, "w") file.write(env.corosync_conf_data) file.close() except EnvironmentError as e: raise error("Unable to write {0}: {1}".format( local_file_path, e.strerror)) from e return result_of_next
def remove(lib, argv, modifiers): """ Options: * -f - CIB file """ modifiers.ensure_only_supported("-f") if len(argv) != 2: raise CmdLineInputError() ticket, resource_id = argv if not lib.constraint_ticket.remove(ticket, resource_id): raise error("no matching ticket constraint found")
def apply(next_in_line, env, *args, **kwargs): if filename: touch_cib_file(filename) try: with open(filename, mode="r") as cib_file: original_content = cib_file.read() except EnvironmentError as e: raise error("Cannot read cib file '{0}': '{1}'".format( filename, str(e))) from e env.cib_data = original_content result_of_next = next_in_line(env, *args, **kwargs) if filename and env.cib_data != original_content: try: with open(filename, mode="w") as cib_file: cib_file.write(env.cib_data) except EnvironmentError as e: raise error("Cannot write cib file '{0}': '{1}'".format( filename, str(e))) from e return result_of_next
def print_pcsd_daemon_status(lib, modifiers): """ Commandline options: * --request-timeout - HTTP timeout for node authorization check or when not running under root to call local pcsd """ print("PCSD Status:") if os.getuid() == 0: pcsd_status_cmd(lib, [], modifiers.get_subset("--request-timeout"), dont_exit=True) else: err_msgs, exitcode, std_out, dummy_std_err = utils.call_local_pcsd( ["status", "pcsd"], []) if err_msgs: for msg in err_msgs: print(msg) if exitcode == 0: print(std_out) else: raise error("Unable to get PCSD status")
def get_capabilities_definition(): """ Read and parse capabilities file The point is to return all data in python structures for further processing. """ filename = os.path.join(settings.pcsd_exec_location, "capabilities.xml") try: with open(filename, mode="r") as file_: capabilities_xml = xml_fromstring(file_.read()) except (EnvironmentError, etree.XMLSyntaxError, etree.DocumentInvalid) as e: raise error( "Cannot read capabilities definition file '{0}': '{1}'".format( filename, str(e))) from e capabilities = [] for feat_xml in capabilities_xml.findall(".//capability"): feat = dict(feat_xml.attrib) desc = feat_xml.find("./description") # dedent and strip remove indentation in the XML file feat["description"] = "" if desc is None else dedent(desc.text).strip() capabilities.append(feat) return capabilities
def flush(modified_env): if not is_mocked_environment: return if not modified_env: # TODO now this would not happen # for more information see comment in # pcs.cli.common.lib_wrapper.lib_env_to_cli_env raise output.error("Error during library communication") try: key_file.write(modified_env["key_file"]["content"], can_overwrite=True) config_file.write(modified_env["config_file"]["content"], can_overwrite=True) # TODO write custom error handling, do not use pcs.lib specific code # and LibraryError except pcs_file.RawFileError as e: raise LibraryError( ReportItem.error( reports.messages.FileIoError( e.metadata.file_type_code, e.action, e.reason, file_path=e.metadata.path, ))) from e
def middleware_config(config_path, key_path): if config_path and not key_path: raise output.error("When --booth-conf is specified, " "--booth-key must be specified as well") if key_path and not config_path: raise output.error("When --booth-key is specified, " "--booth-conf must be specified as well") is_mocked_environment = config_path and key_path if is_mocked_environment: config_file = pcs_file.RawFile( metadata.for_file_type(file_type_codes.BOOTH_CONFIG, config_path)) key_file = pcs_file.RawFile( metadata.for_file_type(file_type_codes.BOOTH_KEY, key_path)) def create_booth_env(): try: config_data = config_file.read() if config_file.exists() else None key_data = key_file.read() if key_file.exists() else None # TODO write custom error handling, do not use pcs.lib specific code # and LibraryError except pcs_file.RawFileError as e: raise LibraryError( ReportItem.error( reports.messages.FileIoError( e.metadata.file_type_code, e.action, e.reason, file_path=e.metadata.path, ))) from e return { "config_data": config_data, "key_data": key_data, "key_path": key_path, } def flush(modified_env): if not is_mocked_environment: return if not modified_env: # TODO now this would not happen # for more information see comment in # pcs.cli.common.lib_wrapper.lib_env_to_cli_env raise output.error("Error during library communication") try: key_file.write(modified_env["key_file"]["content"], can_overwrite=True) config_file.write(modified_env["config_file"]["content"], can_overwrite=True) # TODO write custom error handling, do not use pcs.lib specific code # and LibraryError except pcs_file.RawFileError as e: raise LibraryError( ReportItem.error( reports.messages.FileIoError( e.metadata.file_type_code, e.action, e.reason, file_path=e.metadata.path, ))) from e def apply(next_in_line, env, *args, **kwargs): env.booth = create_booth_env() if is_mocked_environment else {} result_of_next = next_in_line(env, *args, **kwargs) if is_mocked_environment: flush(env.booth["modified_env"]) return result_of_next return apply
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) deprecation_warning( 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: error(str(err)) print_to_stderr(usage.main()) 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: print(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)) # initialize logger logging.getLogger("pcs") 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: print(usage.main()), } try: routing.create_router(cmd_map, [])(utils.get_library_wrapper(), argv, utils.get_input_modifiers()) except LibraryError as e: if e.output: sys.stderr.write(e.output) sys.exit(1) process_library_reports(e.args) except errors.CmdLineInputError: if argv and argv[0] in cmd_map: usage.show(argv[0], []) else: print_to_stderr(usage.main()) sys.exit(1)
def stonith_create(lib, argv, modifiers): """ Options: * --before - specified resource inside a group before which new resource will be placed inside the group * --after - specified resource inside a group after which new resource will be placed inside the group * --group - specifies group in which resource will be created * --force - allow not existing agent, invalid operations or invalid instance attributes * --disabled - created resource will be disabled * --no-default-ops - do not add default operations * --wait * -f - CIB file """ modifiers.ensure_only_supported( "--before", "--after", "--group", "--force", "--disabled", "--no-default-ops", "--wait", "-f", ) if modifiers.is_specified("--before") and modifiers.is_specified( "--after"): raise error("you cannot specify both --before and --after{0}".format( "" if modifiers. is_specified("--group") else " and you have to specify --group")) if not modifiers.is_specified("--group"): if modifiers.is_specified("--before"): raise error("you cannot use --before without --group") if modifiers.is_specified("--after"): raise error("you cannot use --after without --group") if len(argv) < 2: raise CmdLineInputError() stonith_id = argv[0] stonith_type = argv[1] parts = parse_create_args(argv[2:]) settings = dict( allow_absent_agent=modifiers.get("--force"), allow_invalid_operation=modifiers.get("--force"), allow_invalid_instance_attributes=modifiers.get("--force"), ensure_disabled=modifiers.get("--disabled"), use_default_operations=not modifiers.get("--no-default-ops"), wait=modifiers.get("--wait"), ) if not modifiers.get("--group"): lib.stonith.create( stonith_id, stonith_type, parts["op"], parts["meta"], parts["options"], **settings, ) else: deprecation_warning( "Option to group stonith resource is deprecated and will be " "removed in a future release.") adjacent_resource_id = None put_after_adjacent = False if modifiers.get("--after"): adjacent_resource_id = modifiers.get("--after") put_after_adjacent = True if modifiers.get("--before"): adjacent_resource_id = modifiers.get("--before") put_after_adjacent = False lib.stonith.create_in_group( stonith_id, stonith_type, modifiers.get("--group"), parts["op"], parts["meta"], parts["options"], adjacent_resource_id=adjacent_resource_id, put_after_adjacent=put_after_adjacent, **settings, )