def replace_cib_configuration_xml(runner, xml): cmd = [ __exec("cibadmin"), "--replace", "--verbose", "--xml-pipe", "--scope", "configuration", ] stdout, stderr, retval = runner.run(cmd, stdin_string=xml) if retval != 0: raise LibraryError( ReportItem.error(reports.messages.CibPushError(stderr, stdout)))
def history_update(env: LibraryEnvironment): """ Update fencing history in a cluster (sync with other nodes) env """ runner = env.cmd_runner() if not is_fence_history_supported_management(runner): raise LibraryError( ReportItem.error(reports.messages.FenceHistoryNotSupported()) ) try: return fence_history_update(runner) except FenceHistoryCommandErrorException as e: raise LibraryError( ReportItem.error( reports.messages.FenceHistoryCommandError( str(e), reports.const.FENCE_HISTORY_COMMAND_UPDATE ) ) ) from e
def remove_quorum_device_heuristics(self): """ Remove quorum device heuristics configuration """ if not self.has_quorum_device(): raise LibraryError( ReportItem.error(reports.messages.QdeviceNotDefined())) for quorum in self.config.get_sections("quorum"): for device in quorum.get_sections("device"): for heuristics in device.get_sections("heuristics"): device.del_section(heuristics) self.__remove_empty_sections(self.config) self._need_qdevice_reload = True
def _get_cib_version(cib, attribute, regexp, none_if_missing=False): version = cib.get(attribute) if version is None: if none_if_missing: return None raise LibraryError( ReportItem.error( reports.messages.CibLoadErrorBadFormat( f"the attribute '{attribute}' of the element 'cib' " "is missing"))) match = regexp.match(version) if not match: raise LibraryError( ReportItem.error( reports.messages.CibLoadErrorBadFormat( f"the attribute '{attribute}' of the element 'cib' has " f"an invalid value: '{version}'"))) return Version( int(match.group("major")), int(match.group("minor")), int(match.group("rev")) if match.group("rev") else None, )
def remove_node(runner, node_name): stdout, stderr, retval = runner.run( [__exec("crm_node"), "--force", "--remove", node_name,] ) if retval != 0: raise LibraryError( ReportItem.error( reports.messages.NodeRemoveInPacemakerFailed( node_list_to_remove=[node_name], reason=join_multilines([stderr, stdout]), ) ) )
def get_local_corosync_conf(): """ Read corosync.conf file from local machine """ path = settings.corosync_conf_file try: with open(path, "r", encoding="utf-8") as a_file: return a_file.read() except EnvironmentError as e: raise LibraryError( ReportItem.error( reports.messages.UnableToReadCorosyncConfig( path, e.strerror))) from e
def _validate_level(level) -> Tuple[ReportItemList, Optional[int]]: report_list: ReportItemList = [] try: candidate = int(level) if candidate > 0: return report_list, candidate except ValueError: pass report_list.append( ReportItem.error( reports.messages.InvalidOptionValue("level", level, "a positive integer"))) return report_list, None
def check_instance_name(name): """ Check that specified booth instance name is valid string name -- booth instance name """ report_list = [] if "/" in name: report_list.append( ReportItem.error( report.messages.BoothInvalidName(name, forbidden_characters="/"))) return report_list
def get_wait_timeout(self, wait): if wait is False: return False if wait not in self.__timeout_cache: if not self.is_cib_live: raise LibraryError( ReportItem.error( reports.messages.WaitForIdleNotLiveCluster() ) ) self.__timeout_cache[wait] = get_valid_timeout_seconds(wait) return self.__timeout_cache[wait]
def prepare_options_with_set(cib, options, resource_set_list): options = constraint.prepare_options( tuple(SCORE_NAMES), options, partial(constraint.create_id, cib, "colocation", resource_set_list), partial(check_new_id_applicable, cib, DESCRIPTION), ) if "score" in options and not is_score(options["score"]): raise LibraryError( ReportItem.error(reports.messages.InvalidScore(options["score"]))) score_attrs_count = len( [name for name in options.keys() if name in SCORE_NAMES]) if score_attrs_count > 1: raise LibraryError( ReportItem.error(reports.messages.MultipleScoreOptions())) if score_attrs_count == 0: options["score"] = SCORE_INFINITY return options
def get_local_sbd_config(): """ Get local SBD configuration. Returns SBD configuration file as string. Raises LibraryError on any failure. """ try: with open(settings.sbd_config, "r") as sbd_cfg: return sbd_cfg.read() except EnvironmentError as e: raise LibraryError( ReportItem.error( reports.messages.UnableToGetSbdConfig("local node", str(e))))
def _validate_level_target_devices_does_not_exist( tree, level, target_type, target_value, devices ) -> ReportItemList: report_list: ReportItemList = [] if _find_level_elements(tree, level, target_type, target_value, devices): report_list.append( ReportItem.error( reports.messages.CibFencingLevelAlreadyExists( level, target_type, target_value, devices ) ) ) return report_list
def _get_output_certificate(cert_tool_output, report_message_func): regexp = re.compile(r"^Certificate( request)? stored in (?P<path>.+)$") filename = None for line in cert_tool_output.splitlines(): match = regexp.search(line) if match: filename = match.group("path") if not filename: raise LibraryError( ReportItem.error(report_message_func(cert_tool_output)) ) try: with open(filename, "rb") as cert_file: return cert_file.read() except EnvironmentError as e: raise LibraryError( ReportItem.error( report_message_func( "{path}: {error}".format(path=filename, error=e.strerror) ) ) ) from e
def client_destroy(): """ delete qdevice client config files on local host """ try: if client_initialized(): shutil.rmtree(settings.corosync_qdevice_net_client_certs_dir) except EnvironmentError as e: raise LibraryError( ReportItem.error( reports.messages.QdeviceDestroyError(__model, e.strerror) ) ) from e
def remove_ticket(conf_facade, ticket_name): """ Validate removing a ticket from an existing booth config pcs.lib.booth.config_facade.ConfigFacade conf_facade -- a booth config string ticket_name -- the name of the ticket """ if not conf_facade.has_ticket(ticket_name): return [ ReportItem.error( report.messages.BoothTicketDoesNotExist(ticket_name)) ] return []
def client_setup(runner, ca_certificate): """ initialize qdevice client on local host ca_certificate qnetd CA certificate """ client_destroy() # save CA certificate, corosync tool only works with files ca_file_path = os.path.join( settings.corosync_qdevice_net_client_certs_dir, settings.corosync_qdevice_net_client_ca_file_name, ) try: if not os.path.exists(ca_file_path): os.makedirs( settings.corosync_qdevice_net_client_certs_dir, mode=0o700 ) with open(ca_file_path, "wb") as ca_file: ca_file.write(ca_certificate) except EnvironmentError as e: raise LibraryError( ReportItem.error( reports.messages.QdeviceInitializationError( __model, e.strerror, ) ) ) from e # initialize client's certificate storage stdout, stderr, retval = runner.run( [__qdevice_certutil, "-i", "-c", ca_file_path] ) if retval != 0: raise LibraryError( ReportItem.error( reports.messages.QdeviceInitializationError( __model, join_multilines([stderr, stdout]), ) ) )
def history_get_text(env: LibraryEnvironment, node: Optional[str] = None): """ Get full fencing history in plain text env node -- get history for the specified node or all nodes if None """ runner = env.cmd_runner() if not is_fence_history_supported_management(runner): raise LibraryError( ReportItem.error(reports.messages.FenceHistoryNotSupported()) ) try: return fence_history_text(runner, node) except FenceHistoryCommandErrorException as e: raise LibraryError( ReportItem.error( reports.messages.FenceHistoryCommandError( str(e), reports.const.FENCE_HISTORY_COMMAND_SHOW ) ) ) from e
def history_cleanup(env: LibraryEnvironment, node: Optional[str] = None): """ Clear fencing history env node -- clear history for the specified node or all nodes if None """ runner = env.cmd_runner() if not is_fence_history_supported_management(runner): raise LibraryError( ReportItem.error(reports.messages.FenceHistoryNotSupported()) ) try: return fence_history_cleanup(runner, node) except FenceHistoryCommandErrorException as e: raise LibraryError( ReportItem.error( reports.messages.FenceHistoryCommandError( str(e), reports.const.FENCE_HISTORY_COMMAND_CLEANUP ) ) ) from e
def _process_response(self, response): report = self._get_response_report(response) if report is not None: self._report(report) return target = response.request.target try: self._output_data.append((target, base64.b64decode(response.data))) except (TypeError, binascii.Error): self._report( ReportItem.error( reports.messages.InvalidResponseFormat(target.label) ) )
def _service_kill(lib_env: LibraryEnvironment, func): try: func(lib_env.cmd_runner()) except external.KillServicesError as e: raise LibraryError(*[ ReportItem.error( reports.messages.ServiceActionFailed( reports.const.SERVICE_ACTION_KILL, service, e.message)) for service in e.service ]) lib_env.report_processor.report( ReportItem.info( reports.messages.ServiceActionSucceeded( reports.const.SERVICE_ACTION_KILL, "quorum device")))
def _process_response(self, response): report = self._get_response_report(response) if report is not None: self._report(report) return target = response.request.target try: self._data.append((target, json.loads(response.data))) except ValueError: self._report( ReportItem.error( reports.messages.InvalidResponseFormat(target.label) ) )
def get_valid_timeout_seconds(timeout_candidate): """ Transform pacemaker style timeout to number of seconds, raise LibraryError on invalid timeout timeout_candidate timeout string or None """ if timeout_candidate is None: return None wait_timeout = timeout_to_seconds(timeout_candidate) if wait_timeout is None: raise LibraryError( ReportItem.error( reports.messages.InvalidTimeoutValue(timeout_candidate))) return wait_timeout
def _validate_container(container_type, container_options, force_options=False): if container_type not in GENERIC_CONTAINER_TYPES: return [ ReportItem.error( reports.messages.InvalidOptionValue( "container type", container_type, GENERIC_CONTAINER_TYPES, )) ] return _validate_generic_container_options(container_options, force_options)
def qdevice_start(lib_env: LibraryEnvironment, model): """ start qdevice now on local host """ _check_model(model) if not qdevice_net.qdevice_initialized(): raise LibraryError( ReportItem.error(reports.messages.QdeviceNotInitialized(model)) ) _service_start( lib_env.report_processor, lib_env.service_manager, qdevice_net.SERVICE_NAME, )
def _validate_target_typewise(target_type) -> ReportItemList: report_list: ReportItemList = [] if target_type not in [ TARGET_TYPE_NODE, TARGET_TYPE_ATTRIBUTE, TARGET_TYPE_REGEXP, ]: report_list.append( ReportItem.error( reports.messages.InvalidOptionType( "target", ["node", "regular expression", "attribute_name=value"], ))) return report_list
def _process_response(self, response): report = self._get_response_report(response) if report: self._report(report) return host_name = response.request.target.label try: self._responses[host_name] = json.loads(response.data) except json.JSONDecodeError: self._report( ReportItem.error( reports.messages.InvalidResponseFormat(host_name) ) )
def validate_new_nodes_devices(nodes_devices): """ Validate if SBD devices are set for new nodes when they should be dict nodes_devices -- name: node name, key: list of SBD devices """ if is_device_set_local(): return validate_nodes_devices(nodes_devices, adding_nodes_to_sbd_enabled_cluster=True) return [ ReportItem.error( reports.messages.SbdWithDevicesNotUsedCannotSetDevice(node)) for node, devices in nodes_devices.items() if devices ]
def _validate_options_common(options): report_list = [] if "loss-policy" in options: loss_policy = options["loss-policy"].lower() if options["loss-policy"] not in ATTRIB["loss-policy"]: report_list.append( ReportItem.error( reports.messages.InvalidOptionValue( "loss-policy", options["loss-policy"], ATTRIB["loss-policy"], ))) options["loss-policy"] = loss_policy return report_list
def from_string(cls, config_string): """ Parse corosync config and create a facade around it config_string corosync config text """ try: return cls(config_parser.parse_string(config_string)) except config_parser.MissingClosingBraceException: raise LibraryError( ReportItem.error( reports.messages.ParseErrorCorosyncConfMissingClosingBrace() ) ) except config_parser.UnexpectedClosingBraceException: # pylint: disable=line-too-long raise LibraryError( ReportItem.error( reports.messages.ParseErrorCorosyncConfUnexpectedClosingBrace() ) ) except config_parser.MissingSectionNameBeforeOpeningBraceException: # pylint: disable=line-too-long raise LibraryError( ReportItem.error( reports.messages.ParseErrorCorosyncConfMissingSectionNameBeforeOpeningBrace() ) ) except config_parser.ExtraCharactersAfterOpeningBraceException: # pylint: disable=line-too-long raise LibraryError( ReportItem.error( reports.messages.ParseErrorCorosyncConfExtraCharactersAfterOpeningBrace() ) ) except config_parser.ExtraCharactersBeforeOrAfterClosingBraceException: # pylint: disable=line-too-long raise LibraryError( ReportItem.error( reports.messages.ParseErrorCorosyncConfExtraCharactersBeforeOrAfterClosingBrace() ) ) except config_parser.LineIsNotSectionNorKeyValueException: # pylint: disable=line-too-long raise LibraryError( ReportItem.error( reports.messages.ParseErrorCorosyncConfLineIsNotSectionNorKeyValue() ) ) except config_parser.CorosyncConfParserException: raise LibraryError( ReportItem.error(reports.messages.ParseErrorCorosyncConf()) )
def create(site_list, arbitrator_list): """ Validate creating a minimal booth config iterable site_list -- list of booth sites' addresses iterable arbitrator_list -- list of arbitrators' addresses """ report_list = [] peer_list = site_list + arbitrator_list if len(site_list) < 2: report_list.append( ReportItem.error( report.messages.BoothLackOfSites(sorted(site_list)) ) ) if len(peer_list) % 2 == 0: report_list.append( ReportItem.error( report.messages.BoothEvenPeersNumber(len(peer_list)) ) ) duplicate_addresses = { address for address, count in Counter(peer_list).items() if count > 1 } if duplicate_addresses: report_list.append( ReportItem.error( report.messages.BoothAddressDuplication( sorted(duplicate_addresses) ) ) ) return report_list