def test_with_id_provider_booked_ids(self): context_element = etree.fromstring('<context id="a"/>') provider = IdProvider(context_element) provider.book_ids("a-instance_attributes", "a-instance_attributes-1-a") nvpair.append_new_nvset( "instance_attributes", context_element, { "a": "b", "c": "d", }, provider ) assert_xml_equal( """ <context id="a"> <instance_attributes id="a-instance_attributes-1"> <nvpair id="a-instance_attributes-1-a-1" name="a" value="b" /> <nvpair id="a-instance_attributes-1-c" name="c" value="d" /> </instance_attributes> </context> """, etree_to_str(context_element) )
def test_with_id_provider(self): nvset_element = etree.fromstring('<nvset id="a"/>') provider = IdProvider(nvset_element) provider.book_ids("a-b") nvpair._append_new_nvpair(nvset_element, "b", "c", provider) assert_xml_equal( etree_to_str(nvset_element), """ <nvset id="a"> <nvpair id="a-b-1" name="b" value="c"></nvpair> </nvset> """ )
def test_append_new_nvpair_to_given_element(self): nvset_element = etree.fromstring('<nvset id="a"/>') id_provider = IdProvider(nvset_element) nvpair._append_new_nvpair(nvset_element, "b", "c", id_provider) assert_xml_equal( etree_to_str(nvset_element), """ <nvset id="a"> <nvpair id="a-b" name="b" value="c"></nvpair> </nvset> """)
def test_used_id(self): id_provider = IdProvider(etree.fromstring("<a><test id='used' /></a>")) assert_report_item_list_equal( validate.value_id("id", "test id", id_provider)({ "id": "used" }), [ (severities.ERROR, report_codes.ID_ALREADY_EXISTS, { "id": "used", }, None), ])
def _defaults_create( env: LibraryEnvironment, cib_section_name: str, validator_options: Mapping[str, Any], nvpairs: Mapping[str, str], nvset_options: Mapping[str, str], nvset_rule: Optional[str] = None, force_flags: Container[reports.types.ForceCode] = (), ) -> None: required_cib_version = None nice_to_have_cib_version = None if nvset_rule: # Parse the rule to see if we need to upgrade CIB schema. All errors # would be properly reported by a validator called bellow, so we can # safely ignore them here. try: rule_tree = parse_rule(nvset_rule) if has_rsc_or_op_expression(rule_tree): required_cib_version = Version(3, 4, 0) if has_node_attr_expr_with_type_integer(rule_tree): nice_to_have_cib_version = Version(3, 5, 0) except RuleParseError: pass cib = env.get_cib( minimal_version=required_cib_version, nice_to_have_version=nice_to_have_cib_version, ) id_provider = IdProvider(cib) validator = nvpair_multi.ValidateNvsetAppendNew( id_provider, nvpairs, nvset_options, nvset_rule=nvset_rule, **validator_options, ) if env.report_processor.report_list( validator.validate( force_options=reports.codes.FORCE in force_flags)).has_errors: raise LibraryError() nvpair_multi.nvset_append_new( sections.get(cib, cib_section_name), id_provider, get_pacemaker_version_by_which_cib_was_validated(cib), nvpair_multi.NVSET_META, nvpairs, nvset_options, nvset_rule=validator.get_parsed_rule(), ) env.report_processor.report( ReportItem.warning(reports.messages.DefaultsCanBeOverriden())) env.push_cib()
def _validate_tag_id(tag_id: str, id_provider: IdProvider) -> ReportItemList: """ Validate that tag_id is an valid xml id an it is uniqe in the cib. tag_id -- identifier of new tag id_provider -- elements' ids generator """ report_list: ReportItemList = [] validate_id(tag_id, reporter=report_list) report_list.extend(id_provider.book_ids(tag_id)) return report_list
def test_used_id(self): id_provider = IdProvider(etree.fromstring("<a><test id='used' /></a>")) assert_report_item_list_equal( validate.ValueId("id", id_provider=id_provider).validate({"id": "used"}), [ fixture.error( report_codes.ID_ALREADY_EXISTS, id="used", ), ])
def validate_clone_id(clone_id: str, id_provider: IdProvider) -> ReportItemList: """ Validate that clone_id is a valid xml id and it is unique in the cib. clone_id -- identifier of clone element id_provider -- elements' ids generator """ report_list: ReportItemList = [] validate_id(clone_id, reporter=report_list) report_list.extend(id_provider.book_ids(clone_id)) return report_list
def _set_instance_attrs_local_node(lib_env, attrs, wait): if not lib_env.is_cib_live: # If we are not working with a live cluster we cannot get the local node # name. raise LibraryError(reports.live_environment_required_for_local_node()) with cib_runner_nodes(lib_env, wait) as (cib, runner, state_nodes): update_node_instance_attrs(cib, IdProvider(cib), get_local_node_name(runner), attrs, state_nodes=state_nodes)
def setUp(self): self.nvset = etree.Element("nvset", id="nvset") etree.SubElement( self.nvset, "nvpair", id="nvset-attr", name="attr", value="1" ) etree.SubElement( self.nvset, "nvpair", id="nvset-attr2", name="attr2", value="2" ) etree.SubElement( self.nvset, "notnvpair", id="nvset-test", name="test", value="0" ) self.id_provider = IdProvider(self.nvset)
def bundle_update( env, bundle_id, container_options=None, network_options=None, port_map_add=None, port_map_remove=None, storage_map_add=None, storage_map_remove=None, force_options=False, wait=False, ): """ Modify an existing bundle (does not touch encapsulated resources) LibraryEnvironment env -- provides communication with externals string bundle_id -- id of the bundle to modify dict container_options -- container options to modify dict network_options -- network options to modify list of dict port_map_add -- list of port mapping options to add list of string port_map_remove -- list of port mapping ids to remove list of dict storage_map_add -- list of storage mapping options to add list of string storage_map_remove -- list of storage mapping ids to remove bool force_options -- return warnings instead of forceable errors mixed wait -- False: no wait, None: wait default timeout, int: wait timeout """ container_options = container_options or {} network_options = network_options or {} port_map_add = port_map_add or [] port_map_remove = port_map_remove or [] storage_map_add = storage_map_add or [] storage_map_remove = storage_map_remove or [] with resource_environment( env, wait, [bundle_id], # bundles are always enabled, currently there is no way to disable them disabled_after_wait=False, required_cib_version=(2, 8, 0)) as resources_section: id_provider = IdProvider(resources_section) bundle_element = find_element_by_tag_and_id(resource.bundle.TAG, resources_section, bundle_id) env.report_processor.process_list( resource.bundle.validate_update(id_provider, bundle_element, container_options, network_options, port_map_add, port_map_remove, storage_map_add, storage_map_remove, force_options)) resource.bundle.update(id_provider, bundle_element, container_options, network_options, port_map_add, port_map_remove, storage_map_add, storage_map_remove)
def test_with_id_provider_booked_ids(self): context_element = etree.fromstring('<context id="a"/>') provider = IdProvider(context_element) provider.book_ids("a-instance_attributes", "a-instance_attributes-1-a") nvpair.append_new_nvset("instance_attributes", context_element, { "a": "b", "c": "d", }, provider) assert_xml_equal( """ <context id="a"> <instance_attributes id="a-instance_attributes-1"> <nvpair id="a-instance_attributes-1-a-1" name="a" value="b" /> <nvpair id="a-instance_attributes-1-c" name="c" value="d" /> </instance_attributes> </context> """, etree_to_str(context_element))
def setUp(self): self.cib = etree.fromstring(""" <cib> <resources> <clone id="CloneId"> <meta_attributes id="CloneId-meta_attributes"/> </clone> </resources> </cib> """) self.resources = self.cib.find(".//resources") self.id_provider = IdProvider(self.resources)
def test_empty_value_has_no_effect(self): xml = """ <instance_attributes id="iattrs"> <nvpair id="iattrs-b" name="a" value="b"/> <nvpair id="iattrs-d" name="c" value="d"/> <nvpair id="iattrs-f" name="e" value="f"/> </instance_attributes> """ nvset_element = etree.fromstring(xml) id_provider = IdProvider(nvset_element) nvpair.update_nvset(nvset_element, {}, id_provider) assert_xml_equal(xml, etree_to_str(nvset_element))
def test_minimal(self): context_element = etree.fromstring("""<context id="a" />""") id_provider = IdProvider(context_element) nvpair_multi.nvset_append_new(context_element, id_provider, nvpair_multi.NVSET_META, {}, {}) assert_xml_equal( """ <context id="a"> <meta_attributes id="a-meta_attributes" /> </context> """, etree_to_str(context_element), )
def test_pair_used_id(self): id_provider = IdProvider(etree.fromstring("<a><test id='used' /></a>")) assert_report_item_list_equal( validate.ValueId("id", id_provider=id_provider).validate( {"id": validate.ValuePair("not-used", "used")}), [ fixture.error( report_codes.ID_ALREADY_EXISTS, # TODO: This should be "not-used". However an old # validator is used and it doesn't work with pairs. id="used", ), ])
def create_in_cluster(env, ip, allow_absent_resource_agent=False): """ Create group with ip resource and booth resource LibraryEnvironment env provides all for communication with externals string ip determines float ip for the operation of the booth bool allow_absent_resource_agent is flag allowing create booth resource even if its agent is not installed """ resources_section = get_resources(env.get_cib()) id_provider = IdProvider(resources_section) name = env.booth.name booth_config_file_path = get_config_file_name(name) if resource.find_for_config(resources_section, booth_config_file_path): raise LibraryError(booth_reports.booth_already_in_cib(name)) create_id = partial( resource.create_resource_id, resources_section, name ) get_agent = partial( find_valid_resource_agent_by_name, env.report_processor, env.cmd_runner(), allowed_absent=allow_absent_resource_agent ) create_primitive = partial( primitive.create, env.report_processor, resources_section, id_provider ) into_booth_group = partial( group.place_resource, group.provide_group(resources_section, create_id("group")), ) into_booth_group(create_primitive( create_id("ip"), get_agent("ocf:heartbeat:IPaddr2"), instance_attributes={"ip": ip}, )) into_booth_group(create_primitive( create_id("service"), get_agent("ocf:pacemaker:booth-site"), instance_attributes={"config": booth_config_file_path}, )) env.push_cib()
def test_everything(self): context_element = etree.fromstring("""<context id="a" />""") id_provider = IdProvider(context_element) nvpair_multi.nvset_append_new( context_element, id_provider, Version(3, 5, 0), nvpair_multi.NVSET_META, { "attr1": "value1", "attr-empty": "", "attr2": "value2" }, { "id": "custom-id", "score": "INFINITY", "empty-attr": "" }, nvset_rule=BoolExpr( BOOL_AND, [RscExpr("ocf", "pacemaker", "Dummy"), OpExpr("start", None)], ), ) assert_xml_equal( """ <context id="a"> <meta_attributes id="custom-id" score="INFINITY"> <rule id="custom-id-rule" boolean-op="and" score="INFINITY" > <rsc_expression id="custom-id-rule-rsc-ocf-pacemaker-Dummy" class="ocf" provider="pacemaker" type="Dummy" /> <op_expression id="custom-id-rule-op-start" name="start" /> </rule> <nvpair id="custom-id-attr1" name="attr1" value="value1" /> <nvpair id="custom-id-attr2" name="attr2" value="value2" /> </meta_attributes> </context> """, etree_to_str(context_element), )
def manage(env, resource_ids, with_monitor=False): """ Set specified resource to be managed by the cluster LibraryEnvironment env -- strings resource_ids -- ids of the resources to become managed bool with_monitor -- enable resources' monitor operations """ with resource_environment(env) as resources_section: id_provider = IdProvider(resources_section) report_list = [] resource_el_list = _find_resources_or_raise( resources_section, resource_ids, resource.common.find_resources_to_manage ) primitives = [] for resource_el in resource_el_list: resource.common.manage(resource_el, id_provider) primitives.extend( resource.common.find_primitives(resource_el) ) for resource_el in sorted( set(primitives), key=lambda element: element.get("id", "") ): op_list = resource.operations.get_resource_operations( resource_el, ["monitor"] ) if with_monitor: for op in op_list: resource.operations.enable(op) else: monitor_enabled = False for op in op_list: if resource.operations.is_enabled(op): monitor_enabled = True break if op_list and not monitor_enabled: # do not advise enabling monitors if there are none defined report_list.append( reports.resource_managed_no_monitor_enabled( resource_el.get("id", "") ) ) env.report_processor.process_list(report_list)
def setUp(self): self.resources_section = etree.fromstring("<resources/>") self.instance_attributes = {"a": "b"} self.meta_attributes = {"c": "d"} self.operation_list = [{"name": "monitoring"}] self.id_provider = IdProvider(self.resources_section) self.run = partial( primitive.append_new, self.resources_section, self.id_provider, instance_attributes=self.instance_attributes, meta_attributes=self.meta_attributes, operation_list=self.operation_list, )
def _set_instance_attrs_node_list(lib_env, attrs, node_names, wait): with cib_runner_nodes(lib_env, wait) as (cib, dummy_runner, state_nodes): known_nodes = [node.attrs.name for node in state_nodes] report = [] for node in node_names: if node not in known_nodes: report.append(reports.node_not_found(node)) if report: raise LibraryError(*report) for node in node_names: update_node_instance_attrs(cib, IdProvider(cib), node, attrs, state_nodes=state_nodes)
def update_scsi_devices_add_remove( env: LibraryEnvironment, stonith_id: str, add_device_list: Iterable[str], remove_device_list: Iterable[str], force_flags: Container[reports.types.ForceCode] = (), ) -> None: """ Update scsi fencing devices without restart and affecting other resources. env -- provides all for communication with externals stonith_id -- id of stonith resource add_device_list -- paths to the scsi devices that would be added to the stonith resource remove_device_list -- paths to the scsi devices that would be removed from the stonith resource force_flags -- list of flags codes """ runner = env.cmd_runner() ( stonith_el, current_device_list, ) = _update_scsi_devices_get_element_and_devices(runner, env.report_processor, env.get_cib(), stonith_id) if env.report_processor.report_list( validate_add_remove_items( add_device_list, remove_device_list, current_device_list, reports.const.ADD_REMOVE_CONTAINER_TYPE_STONITH_RESOURCE, reports.const.ADD_REMOVE_ITEM_TYPE_DEVICE, stonith_el.get("id", ""), )).has_errors: raise LibraryError() updated_device_set = (set(current_device_list).union( add_device_list).difference(remove_device_list)) resource.stonith.update_scsi_devices_without_restart( env.cmd_runner(), env.get_cluster_state(), stonith_el, IdProvider(stonith_el), updated_device_set, ) _unfencing_scsi_devices(env, stonith_el, current_device_list, updated_device_set, force_flags) env.push_cib()
def _set_instance_attrs_local_node(lib_env: LibraryEnvironment, attrs, wait: WaitType): if not lib_env.is_cib_live: # If we are not working with a live cluster we cannot get the local node # name. raise LibraryError( ReportItem.error( reports.messages.LiveEnvironmentRequiredForLocalNode())) with cib_runner_nodes(lib_env, wait) as (cib, runner, state_nodes): update_node_instance_attrs( cib, IdProvider(cib), get_local_node_name(runner), attrs, state_nodes=state_nodes, )
def disable(env, resource_ids, wait): """ Disallow specified resource to be started by the cluster LibraryEnvironment env -- strings resource_ids -- ids of the resources to be disabled mixed wait -- False: no wait, None: wait default timeout, int: wait timeout """ with resource_environment( env, wait, resource_ids, _ensure_disabled_after_wait(True)) as resources_section: id_provider = IdProvider(resources_section) resource_el_list = _find_resources_or_raise(resources_section, resource_ids) env.report_processor.process_list( _resource_list_enable_disable(resource_el_list, resource.common.disable, id_provider, env.get_cluster_state()))
def add_recipient( lib_env, alert_id, recipient_value, instance_attribute_dict, meta_attribute_dict, recipient_id=None, description=None, allow_same_value=False ): """ Add new recipient to alert witch id alert_id. lib_env -- LibraryEnvironment alert_id -- id of alert to which new recipient should be added recipient_value -- value of new recipient instance_attribute_dict -- dictionary of instance attributes to update meta_attribute_dict -- dictionary of meta attributes to update recipient_id -- id of new recipient, if None it will be generated description -- recipient description allow_same_value -- if True unique recipient value is not required """ if not recipient_value: raise LibraryError( reports.required_options_are_missing(["value"]) ) cib = lib_env.get_cib(REQUIRED_CIB_VERSION) id_provider = IdProvider(cib) recipient = alert.add_recipient( lib_env.report_processor, cib, alert_id, recipient_value, recipient_id=recipient_id, description=description, allow_same_value=allow_same_value ) arrange_first_instance_attributes( recipient, instance_attribute_dict, id_provider ) arrange_first_meta_attributes( recipient, meta_attribute_dict, id_provider ) lib_env.push_cib()
def bundle_create( env, bundle_id, container_type, container_options=None, network_options=None, port_map=None, storage_map=None, force_options=False, wait=False, ): """ Create a new bundle containing no resources LibraryEnvironment env -- provides communication with externals string bundle_id -- id of the new bundle string container_type -- container engine name (docker, lxc...) dict container_options -- container options dict network_options -- network options list of dict port_map -- list of port mapping options list of dict storage_map -- list of storage mapping options bool force_options -- return warnings instead of forceable errors mixed wait -- False: no wait, None: wait default timeout, int: wait timeout """ container_options = container_options or {} network_options = network_options or {} port_map = port_map or [] storage_map = storage_map or [] with resource_environment( env, wait, [bundle_id], # bundles are always enabled, currently there is no way to disable them disabled_after_wait=False, required_cib_version=(2, 8, 0)) as resources_section: id_provider = IdProvider(resources_section) env.report_processor.process_list( resource.bundle.validate_new(id_provider, bundle_id, container_type, container_options, network_options, port_map, storage_map, force_options)) resource.bundle.append_new(resources_section, id_provider, bundle_id, container_type, container_options, network_options, port_map, storage_map)
def test_pair_used_id(self): id_provider = IdProvider(etree.fromstring("<a><test id='used' /></a>")) assert_report_item_list_equal( validate.value_id("id", "test id", id_provider)({ "id": validate.ValuePair("not-used", "used") }), [ ( severities.ERROR, report_codes.ID_ALREADY_EXISTS, { # TODO: This should be "not-used". However an old # validator is used and it doesn't work with pairs. "id": "used", }, None), ])
def test_keep_empty_nvset(self): xml_pre = """ <resource> <instance_attributes id="iattrs"> <nvpair id="iattrs-a" name="a" value="b"/> </instance_attributes> </resource> """ xml_post = """ <resource> <instance_attributes id="iattrs" /> </resource> """ xml = etree.fromstring(xml_pre) nvset_element = xml.find("instance_attributes") id_provider = IdProvider(nvset_element) nvpair.update_nvset(nvset_element, {"a": ""}, id_provider) assert_xml_equal(xml_post, etree_to_str(xml))
def _set_instance_attrs_node_list(lib_env: LibraryEnvironment, attrs, node_names, wait: WaitType): with cib_runner_nodes(lib_env, wait) as (cib, dummy_runner, state_nodes): known_nodes = [node.attrs.name for node in state_nodes] report_list = [] for node in node_names: if node not in known_nodes: report_list.append( ReportItem.error(reports.messages.NodeNotFound(node))) if report_list: raise LibraryError(*report_list) for node in node_names: update_node_instance_attrs(cib, IdProvider(cib), node, attrs, state_nodes=state_nodes)
def update_recipient( lib_env, recipient_id, instance_attribute_dict, meta_attribute_dict, recipient_value=None, description=None, allow_same_value=False ): """ Update existing recipient. lib_env -- LibraryEnvironment recipient_id -- id of recipient to be updated instance_attribute_dict -- dictionary of instance attributes to update meta_attribute_dict -- dictionary of meta attributes to update recipient_value -- new recipient value, if None old value will stay unchanged description -- new description, if empty string, old description will be deleted, if None old value will stay unchanged allow_same_value -- if True unique recipient value is not required """ if not recipient_value and recipient_value is not None: raise LibraryError( reports.cib_alert_recipient_invalid_value(recipient_value) ) cib = lib_env.get_cib(REQUIRED_CIB_VERSION) id_provider = IdProvider(cib) recipient = alert.update_recipient( lib_env.report_processor, cib, recipient_id, recipient_value=recipient_value, description=description, allow_same_value=allow_same_value ) arrange_first_instance_attributes( recipient, instance_attribute_dict, id_provider ) arrange_first_meta_attributes( recipient, meta_attribute_dict, id_provider ) lib_env.push_cib()
def _defaults_create( env: LibraryEnvironment, cib_section_name: str, validator_options: Mapping[str, Any], nvpairs: Mapping[str, str], nvset_options: Mapping[str, str], nvset_rule: Optional[str] = None, force_flags: Optional[Container] = None, ) -> None: if force_flags is None: force_flags = set() force = (reports.codes.FORCE in force_flags) or (reports.codes.FORCE_OPTIONS in force_flags) required_cib_version = None if nvset_rule: required_cib_version = Version(3, 4, 0) cib = env.get_cib(required_cib_version) id_provider = IdProvider(cib) validator = nvpair_multi.ValidateNvsetAppendNew( id_provider, nvpairs, nvset_options, nvset_rule=nvset_rule, **validator_options, ) if env.report_processor.report_list( validator.validate(force_options=force)).has_errors: raise LibraryError() nvpair_multi.nvset_append_new( sections.get(cib, cib_section_name), id_provider, nvpair_multi.NVSET_META, nvpairs, nvset_options, nvset_rule=validator.get_parsed_rule(), ) env.report_processor.report( ReportItem.warning(reports.messages.DefaultsCanBeOverriden())) env.push_cib()
def test_options(self): context_element = etree.fromstring("""<context id="a" />""") id_provider = IdProvider(context_element) nvpair_multi.nvset_append_new( context_element, id_provider, Version(3, 5, 0), nvpair_multi.NVSET_META, {}, {"score": "INFINITY", "empty-attr": ""}, ) assert_xml_equal( """ <context id="a"> <meta_attributes id="a-meta_attributes" score="INFINITY" /> </context> """, etree_to_str(context_element), )