def _get_agent_facade( report_processor: reports.ReportProcessor, factory: ResourceAgentFacadeFactory, name: str, allow_absent_agent: bool, ) -> ResourceAgentFacade: try: if ":" in name: raise InvalidResourceAgentName(name) full_name = ResourceAgentName("stonith", None, name) return factory.facade_from_parsed_name(full_name) except (UnableToGetAgentMetadata, UnsupportedOcfVersion) as e: if allow_absent_agent: report_processor.report( resource_agent_error_to_report_item( e, reports.ReportItemSeverity.warning(), is_stonith=True)) return factory.void_facade_from_parsed_name(full_name) report_processor.report( resource_agent_error_to_report_item( e, reports.ReportItemSeverity.error(reports.codes.FORCE), is_stonith=True, )) raise LibraryError() from e except ResourceAgentError as e: report_processor.report( resource_agent_error_to_report_item( e, reports.ReportItemSeverity.error(), is_stonith=True)) raise LibraryError() from e
def describe_agent( lib_env: LibraryEnvironment, agent_name: str ) -> Dict[str, Any]: """ Get agent's description (metadata) in a structure agent_name -- name of the agent """ runner = lib_env.cmd_runner() report_processor = lib_env.report_processor agent_factory = ResourceAgentFacadeFactory(runner, report_processor) try: found_name = ( split_resource_agent_name(agent_name) if ":" in agent_name else find_one_resource_agent_by_type( runner, report_processor, agent_name ) ) return _agent_metadata_to_dict( agent_factory.facade_from_parsed_name(found_name).metadata, describe=True, ) except ResourceAgentError as e: lib_env.report_processor.report(resource_agent_error_to_report_item(e)) raise LibraryError() from e
def _complete_agent_list( runner: CommandRunner, report_processor: ReportProcessor, agent_names: Iterable[ResourceAgentName], describe: bool, search: Optional[str], ) -> List[Dict[str, Any]]: agent_factory = ResourceAgentFacadeFactory(runner, report_processor) search_lower = search.lower() if search else None agent_list = [] for name in agent_names: if search_lower and search_lower not in name.full_name.lower(): continue try: metadata = ( agent_factory.facade_from_parsed_name(name).metadata if describe else name_to_void_metadata(name) ) agent_list.append(_agent_metadata_to_dict(metadata, describe)) except ResourceAgentError as e: report_processor.report( resource_agent_error_to_report_item( e, ReportItemSeverity.warning() ) ) return agent_list
def _get_agent_facade( report_processor: reports.ReportProcessor, factory: ResourceAgentFacadeFactory, allow_absent_agent: bool, name: ResourceAgentName, ) -> ResourceAgentFacade: try: return factory.facade_from_parsed_name(name) except UnableToGetAgentMetadata as e: if allow_absent_agent: report_processor.report( resource_agent_error_to_report_item( e, reports.ReportItemSeverity.warning() ) ) return factory.void_facade_from_parsed_name(name) report_processor.report( resource_agent_error_to_report_item( e, reports.ReportItemSeverity.error(reports.codes.FORCE) ) ) raise LibraryError() from e except ResourceAgentError as e: report_processor.report( resource_agent_error_to_report_item( e, reports.ReportItemSeverity.error() ) ) raise LibraryError() from e
def _get_agent_metadata( runner: CommandRunner, report_processor: ReportProcessor, agent_name: ResourceAgentNameDto, ) -> ResourceAgentMetadata: agent_factory = ResourceAgentFacadeFactory(runner, report_processor) try: return agent_factory.facade_from_parsed_name( ResourceAgentName.from_dto(agent_name)).metadata except ResourceAgentError as e: report_processor.report(resource_agent_error_to_report_item(e)) raise LibraryError() from e
def get_agent_metadata( lib_env: LibraryEnvironment, agent_name: ResourceAgentNameDto) -> ResourceAgentMetadataDto: """ Return agent's metadata agent_name -- name of the agent """ runner = lib_env.cmd_runner() report_processor = lib_env.report_processor agent_factory = ResourceAgentFacadeFactory(runner, report_processor) try: return agent_factory.facade_from_parsed_name( ResourceAgentName.from_dto(agent_name)).metadata.to_dto() except ResourceAgentError as e: lib_env.report_processor.report(resource_agent_error_to_report_item(e)) raise LibraryError() from e
def describe_agent(lib_env: LibraryEnvironment, agent_name: str) -> Dict[str, Any]: """ Get agent's description (metadata) in a structure agent_name -- name of the agent (not containing "stonith:" prefix) """ runner = lib_env.cmd_runner() agent_factory = ResourceAgentFacadeFactory(runner, lib_env.report_processor) try: if ":" in agent_name: raise InvalidResourceAgentName(agent_name) return _agent_metadata_to_dict( agent_factory.facade_from_parsed_name( ResourceAgentName("stonith", None, agent_name)).metadata, describe=True, ) except ResourceAgentError as e: lib_env.report_processor.report( resource_agent_error_to_report_item(e, is_stonith=True)) raise LibraryError() from e
def create( env: LibraryEnvironment, stonith_id: str, stonith_agent_name: str, operations: Iterable[Mapping[str, str]], meta_attributes: Mapping[str, str], instance_attributes: Mapping[str, str], allow_absent_agent: bool = False, allow_invalid_operation: bool = False, allow_invalid_instance_attributes: bool = False, use_default_operations: bool = True, ensure_disabled: bool = False, wait: WaitType = False, ): # pylint: disable=too-many-arguments, too-many-locals """ Create stonith as resource in a cib. env -- provides all for communication with externals stonith_id -- an identifier of stonith resource stonith_agent_name -- contains name for the identification of agent operations -- contains attributes for each entered operation meta_attributes -- contains attributes for primitive/meta_attributes instance_attributes -- contains attributes for primitive/instance_attributes allow_absent_agent -- a flag for allowing agent not installed in a system allow_invalid_operation -- a flag for allowing to use operations that are not listed in a stonith agent metadata allow_invalid_instance_attributes -- a flag for allowing to use instance attributes that are not listed in a stonith agent metadata or for allowing to not use the instance_attributes that are required in stonith agent metadata use_default_operations -- a flag for stopping of adding default cib operations (specified in a stonith agent) ensure_disabled -- flag that keeps resource in target-role "Stopped" wait -- flag for controlling waiting for pacemaker idle mechanism """ runner = env.cmd_runner() agent_factory = ResourceAgentFacadeFactory(runner, env.report_processor) stonith_agent = _get_agent_facade( env.report_processor, agent_factory, stonith_agent_name, allow_absent_agent, ) if stonith_agent.metadata.provides_unfencing: meta_attributes = dict(meta_attributes, provides="unfencing") with resource_environment( env, wait, [stonith_id], _ensure_disabled_after_wait( ensure_disabled or resource.common.are_meta_disabled(meta_attributes), ), ) as resources_section: id_provider = IdProvider(resources_section) stonith_element = resource.primitive.create( env.report_processor, resources_section, id_provider, stonith_id, stonith_agent, raw_operation_list=operations, meta_attributes=meta_attributes, instance_attributes=instance_attributes, allow_invalid_operation=allow_invalid_operation, allow_invalid_instance_attributes=allow_invalid_instance_attributes, use_default_operations=use_default_operations, resource_type="stonith", ) if ensure_disabled: resource.common.disable(stonith_element, id_provider)
def create_in_group( env: LibraryEnvironment, stonith_id: str, stonith_agent_name: str, group_id: str, operations: Iterable[Mapping[str, str]], meta_attributes: Mapping[str, str], instance_attributes: Mapping[str, str], allow_absent_agent: bool = False, allow_invalid_operation: bool = False, allow_invalid_instance_attributes: bool = False, use_default_operations: bool = True, ensure_disabled: bool = False, adjacent_resource_id: Optional[str] = None, put_after_adjacent: bool = False, wait: WaitType = False, ): # pylint: disable=too-many-arguments, too-many-locals """ DEPRECATED Create stonith as resource in a cib and put it into defined group. env -- provides all for communication with externals stonith_id --an identifier of stonith resource stonith_agent_name -- contains name for the identification of agent group_id -- identificator for group to put stonith inside operations -- contains attributes for each entered operation meta_attributes -- contains attributes for primitive/meta_attributes instance_attributes -- contains attributes for primitive/instance_attributes allow_absent_agent -- a flag for allowing agent not installed in a system allow_invalid_operation -- a flag for allowing to use operations that are not listed in a stonith agent metadata allow_invalid_instance_attributes -- a flag for allowing to use instance attributes that are not listed in a stonith agent metadata or for allowing to not use the instance_attributes that are required in stonith agent metadata use_default_operations -- a flag for stopping of adding default cib operations (specified in a stonith agent) ensure_disabled -- flag that keeps resource in target-role "Stopped" adjacent_resource_id -- identify neighbor of a newly created stonith put_after_adjacent -- is flag to put a newly create resource befor/after adjacent stonith wait -- flag for controlling waiting for pacemaker idle mechanism """ runner = env.cmd_runner() agent_factory = ResourceAgentFacadeFactory(runner, env.report_processor) stonith_agent = _get_agent_facade( env.report_processor, agent_factory, stonith_agent_name, allow_absent_agent, ) if stonith_agent.metadata.provides_unfencing: meta_attributes = dict(meta_attributes, provides="unfencing") with resource_environment( env, wait, [stonith_id], _ensure_disabled_after_wait( ensure_disabled or resource.common.are_meta_disabled(meta_attributes), ), ) as resources_section: id_provider = IdProvider(resources_section) adjacent_resource_element = None if adjacent_resource_id: try: adjacent_resource_element = get_element_by_id( get_root(resources_section), adjacent_resource_id) except ElementNotFound: # We cannot continue without adjacent element because # the validator might produce misleading reports if env.report_processor.report( ReportItem.error( reports.messages.IdNotFound( adjacent_resource_id, []))).has_errors: raise LibraryError() from None try: group_element = get_element_by_id(get_root(resources_section), group_id) except ElementNotFound: group_id_reports: List[ReportItem] = [] validate_id(group_id, description="group name", reporter=group_id_reports) env.report_processor.report_list(group_id_reports) group_element = resource.group.append_new(resources_section, group_id) stonith_element = resource.primitive.create( env.report_processor, resources_section, id_provider, stonith_id, stonith_agent, operations, meta_attributes, instance_attributes, allow_invalid_operation, allow_invalid_instance_attributes, use_default_operations, ) if ensure_disabled: resource.common.disable(stonith_element, id_provider) if env.report_processor.report_list( resource.validations.validate_move_resources_to_group( group_element, [stonith_element], adjacent_resource_element, )).has_errors: raise LibraryError() resource.hierarchy.move_resources_to_group( group_element, [stonith_element], adjacent_resource_element, put_after_adjacent, )
def node_add_remote( env: LibraryEnvironment, node_name: str, node_addr: Optional[str], operations: Iterable[Mapping[str, str]], meta_attributes: Mapping[str, str], instance_attributes: Mapping[str, str], skip_offline_nodes: bool = False, allow_incomplete_distribution: bool = False, allow_pacemaker_remote_service_fail: bool = False, allow_invalid_operation: bool = False, allow_invalid_instance_attributes: bool = False, use_default_operations: bool = True, wait: WaitType = False, ): # pylint: disable=too-many-arguments # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements """ create an ocf:pacemaker:remote resource and use it as a remote node env -- provides all for communication with externals node_name -- the name of the new node node_addr -- the address of the new node or None for default operations -- attributes for each entered operation meta_attributes -- attributes for primitive/meta_attributes instance_attributes -- attributes for primitive/instance_attributes skip_offline_nodes -- if True, ignore when some nodes are offline allow_incomplete_distribution -- if True, allow this command to finish successfully even if file distribution did not succeed allow_pacemaker_remote_service_fail -- if True, allow this command to finish successfully even if starting/enabling pacemaker_remote did not succeed allow_invalid_operation -- if True, allow to use operations that are not listed in a resource agent metadata allow_invalid_instance_attributes -- if True, allow to use instance attributes that are not listed in a resource agent metadata and allow to omit required instance_attributes use_default_operations -- if True, add operations specified in a resource agent metadata to the resource wait -- a flag for controlling waiting for pacemaker idle mechanism """ wait_timeout = env.ensure_wait_satisfiable(wait) report_processor = env.report_processor cib = env.get_cib( minimal_version=get_required_cib_version_for_primitive(operations)) id_provider = IdProvider(cib) if env.is_cib_live: corosync_conf: Optional[CorosyncConfigFacade] = env.get_corosync_conf() else: corosync_conf = None report_processor.report( ReportItem.info( reports.messages.CorosyncNodeConflictCheckSkipped( reports.const.REASON_NOT_LIVE_CIB, ))) ( existing_nodes_names, existing_nodes_addrs, report_list, ) = get_existing_nodes_names_addrs(corosync_conf, cib) if env.is_cib_live: # We just reported corosync checks are going to be skipped so we # shouldn't complain about errors related to corosync nodes report_processor.report_list(report_list) try: resource_agent_facade = ResourceAgentFacadeFactory( env.cmd_runner(), report_processor).facade_from_parsed_name(remote_node.AGENT_NAME) except ResourceAgentError as e: report_processor.report(resource_agent_error_to_report_item(e)) raise LibraryError() from e existing_target_list = [] if env.is_cib_live: target_factory = env.get_node_target_factory() existing_target_list, new_target_list = _get_targets_for_add( target_factory, report_processor, existing_nodes_names, [node_name], skip_offline_nodes, ) new_target = new_target_list[0] if new_target_list else None # default node_addr to an address from known-hosts if node_addr is None: if new_target: node_addr = new_target.first_addr node_addr_source = ( reports.const.DEFAULT_ADDRESS_SOURCE_KNOWN_HOSTS) else: node_addr = node_name node_addr_source = ( reports.const.DEFAULT_ADDRESS_SOURCE_HOST_NAME) report_processor.report( ReportItem.info( reports.messages.UsingDefaultAddressForHost( node_name, node_addr, node_addr_source))) else: # default node_addr to an address from known-hosts if node_addr is None: known_hosts = env.get_known_hosts([node_name]) if known_hosts: node_addr = known_hosts[0].dest.addr node_addr_source = ( reports.const.DEFAULT_ADDRESS_SOURCE_KNOWN_HOSTS) else: node_addr = node_name node_addr_source = ( reports.const.DEFAULT_ADDRESS_SOURCE_HOST_NAME) report_processor.report( ReportItem.info( reports.messages.UsingDefaultAddressForHost( node_name, node_addr, node_addr_source))) # validate inputs report_list = remote_node.validate_create( existing_nodes_names, existing_nodes_addrs, resource_agent_facade.metadata, node_name, node_addr, instance_attributes, ) if report_processor.report_list(report_list).has_errors: raise LibraryError() # validation + cib setup # TODO extract the validation to a separate function try: remote_resource_element = remote_node.create( env.report_processor, resource_agent_facade, get_resources(cib), id_provider, node_addr, node_name, operations, meta_attributes, instance_attributes, allow_invalid_operation, allow_invalid_instance_attributes, use_default_operations, ) except LibraryError as e: # Check unique id conflict with check against nodes. Until validation # resource create is not separated, we need to make unique post # validation. already_exists = [] unified_report_list = [] for report_item in report_list + list(e.args): # pylint: disable=no-member dto_obj = report_item.message.to_dto() if dto_obj.code not in ( reports.codes.ID_ALREADY_EXISTS, reports.codes.RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE, ): unified_report_list.append(report_item) elif ("id" in dto_obj.payload and dto_obj.payload["id"] not in already_exists): unified_report_list.append(report_item) already_exists.append(dto_obj.payload["id"]) report_list = unified_report_list report_processor.report_list(report_list) if report_processor.has_errors: raise LibraryError() # everything validated, let's set it up if env.is_cib_live: _prepare_pacemaker_remote_environment( env, report_processor, existing_target_list, new_target, node_name, skip_offline_nodes, allow_incomplete_distribution, allow_pacemaker_remote_service_fail, ) else: report_processor.report_list( _reports_skip_new_node(node_name, "not_live_cib")) env.push_cib(wait_timeout=wait_timeout) if wait_timeout >= 0: _ensure_resource_running(env, remote_resource_element.attrib["id"])
def create_in_cluster( env: LibraryEnvironment, ip: str, instance_name: Optional[str] = None, allow_absent_resource_agent: bool = False, ): """ Create group with ip resource and booth resource env -- provides all for communication with externals ip -- float ip address for the operation of the booth instance_name -- booth instance name allow_absent_resource_agent -- allowing creating booth resource even if its agent is not installed """ report_processor = env.report_processor booth_env = env.get_booth_env(instance_name) # Booth config path goes to CIB. Working with a mocked booth configs would # not work coorectly as the path would point to a mock file (the path to a # mock file is unknown to us in the lib anyway) # It makes sense to work with a mocked CIB, though. Users can do other # changes to the CIB and push them to the cluster at once. _ensure_live_booth_env(booth_env) resources_section = get_resources(env.get_cib()) id_provider = IdProvider(resources_section) instance_name = booth_env.instance_name # validate if resource.find_for_config(resources_section, booth_env.config_path): report_processor.report( ReportItem.error(reports.messages.BoothAlreadyInCib(instance_name)) ) # verify the config exists and is readable try: booth_env.config.raw_file.read() except RawFileError as e: report_processor.report(raw_file_error_report(e)) if report_processor.has_errors: raise LibraryError() # validation done create_id = partial( resource.create_resource_id, resources_section, instance_name ) create_primitive = partial( primitive.create, env.report_processor, resources_section, id_provider ) agent_factory = ResourceAgentFacadeFactory( env.cmd_runner(), report_processor ) # Group id validation is not needed since create_id creates a new unique # booth group identifier hierarchy.move_resources_to_group( group.append_new(resources_section, create_id("group")), [ create_primitive( create_id("ip"), _get_agent_facade( env.report_processor, agent_factory, allow_absent_resource_agent, ResourceAgentName("ocf", "heartbeat", "IPaddr2"), ), instance_attributes={"ip": ip}, ), create_primitive( create_id("service"), _get_agent_facade( env.report_processor, agent_factory, allow_absent_resource_agent, ResourceAgentName("ocf", "pacemaker", "booth-site"), ), instance_attributes={"config": booth_env.config_path}, ), ], ) env.push_cib()