def load(self, node_name_list=None, name="corosync_conf.load", filename="corosync.conf", auto_tie_breaker=None, instead=None): content = open(rc(filename)).read() corosync_conf = None if node_name_list: corosync_conf = ConfigFacade.from_string(content).config for nodelist in corosync_conf.get_sections(name="nodelist"): corosync_conf.del_section(nodelist) nodelist_section = Section("nodelist") corosync_conf.add_section(nodelist_section) for i, node_name in enumerate(node_name_list): node_section = Section("node") node_section.add_attribute("ring0_addr", node_name) node_section.add_attribute("nodeid", i) node_section.add_attribute("name", node_name) nodelist_section.add_section(node_section) if auto_tie_breaker is not None: corosync_conf = (corosync_conf if corosync_conf else ConfigFacade.from_string(content).config) for quorum in corosync_conf.get_sections(name="quorum"): quorum.set_attribute("auto_tie_breaker", "1" if auto_tie_breaker else "0") if corosync_conf: content = corosync_conf.export() self.load_content(content, name=name, instead=instead)
def _check_if_atb_can_be_disabled( service_manager: ServiceManagerInterface, report_processor: ReportProcessor, corosync_conf: CorosyncConfFacade, was_enabled: bool, force: bool = False, ) -> None: """ Check whenever auto_tie_breaker can be changed without affecting SBD. Raises LibraryError if change of ATB will affect SBD functionality. service_manager -- report_processor -- report processor corosync_conf -- corosync conf facade was_enabled -- True if ATB was enabled, False otherwise force -- force change """ if (was_enabled and not corosync_conf.is_enabled_auto_tie_breaker() and sbd.is_auto_tie_breaker_needed(service_manager, corosync_conf)): report_processor.report( ReportItem( severity=reports.item.get_severity( reports.codes.FORCE, force, ), message=(reports.messages. CorosyncQuorumAtbCannotBeDisabledDueToSbd()), )) if report_processor.has_errors: raise LibraryError()
def fixture_corosync_conf(self, node1_name=True, node2_name=True): # nodelist is enough, nothing else matters for the tests config = dedent("""\ nodelist { node { ring0_addr: node-1-addr nodeid: 1 name: node-1 } node { ring0_addr: node-2-addr nodeid: 2 name: node-2 } } """ ) if not node1_name: config = re.sub(r"\s+name: node-1\n", "\n", config) if not node2_name: config = re.sub(r"\s+name: node-2\n", "\n", config) self.corosync_conf_text = config self.corosync_conf_facade = CorosyncConfigFacade.from_string(config) CorosyncConfigFacade.need_stopped_cluster = False CorosyncConfigFacade.need_qdevice_reload = False
def test_corosync_conf_not_set_need_offline_success( self, mock_get_corosync, mock_distribute, mock_is_running, mock_reload, mock_check_offline, mock_qdevice_reload): corosync_data = open(rc("corosync.conf")).read() new_corosync_data = corosync_data.replace("version: 2", "version: 3") mock_get_corosync.return_value = corosync_data mock_is_running.return_value = False env = LibraryEnvironment(self.mock_logger, self.mock_reporter) self.assertTrue(env.is_corosync_conf_live) self.assertEqual(corosync_data, env.get_corosync_conf_data()) self.assertEqual(corosync_data, env.get_corosync_conf().config.export()) self.assertEqual(2, mock_get_corosync.call_count) conf_facade = CorosyncConfigFacade.from_string(new_corosync_data) conf_facade._need_stopped_cluster = True env.push_corosync_conf(conf_facade) mock_check_offline.assert_called_once_with("mock node communicator", self.mock_reporter, "mock node list", False) mock_distribute.assert_called_once_with("mock node communicator", self.mock_reporter, "mock node list", new_corosync_data, False) mock_reload.assert_not_called() mock_qdevice_reload.assert_not_called()
def test_corosync_conf_set(self, mock_get_corosync, mock_distribute, mock_reload, mock_check_offline, mock_qdevice_reload): corosync_data = "totem {\n version: 2\n}\n" new_corosync_data = "totem {\n version: 3\n}\n" env = LibraryEnvironment(self.mock_logger, self.mock_reporter, corosync_conf_data=corosync_data) self.assertFalse(env.is_corosync_conf_live) self.assertEqual(corosync_data, env.get_corosync_conf_data()) self.assertEqual(corosync_data, env.get_corosync_conf().config.export()) self.assertEqual(0, mock_get_corosync.call_count) env.push_corosync_conf( CorosyncConfigFacade.from_string(new_corosync_data)) self.assertEqual(0, mock_distribute.call_count) self.assertEqual(new_corosync_data, env.get_corosync_conf_data()) self.assertEqual(0, mock_get_corosync.call_count) mock_check_offline.assert_not_called() mock_reload.assert_not_called() mock_qdevice_reload.assert_not_called()
def test_corosync_conf_not_set_need_offline_success( self, mock_get_corosync, mock_distribute, mock_is_running, mock_reload, mock_check_offline, mock_qdevice_reload ): corosync_data = open(rc("corosync.conf")).read() new_corosync_data = corosync_data.replace("version: 2", "version: 3") mock_get_corosync.return_value = corosync_data mock_is_running.return_value = False env = LibraryEnvironment(self.mock_logger, self.mock_reporter) self.assertTrue(env.is_corosync_conf_live) self.assertEqual(corosync_data, env.get_corosync_conf_data()) self.assertEqual(corosync_data, env.get_corosync_conf().config.export()) self.assertEqual(2, mock_get_corosync.call_count) conf_facade = CorosyncConfigFacade.from_string(new_corosync_data) conf_facade._need_stopped_cluster = True env.push_corosync_conf(conf_facade) mock_check_offline.assert_called_once_with( "mock node communicator", self.mock_reporter, "mock node list", False ) mock_distribute.assert_called_once_with( "mock node communicator", self.mock_reporter, "mock node list", new_corosync_data, False ) mock_reload.assert_not_called() mock_qdevice_reload.assert_not_called()
def test_corosync_conf_set( self, mock_get_corosync, mock_distribute, mock_reload, mock_check_offline, mock_qdevice_reload ): corosync_data = "totem {\n version: 2\n}\n" new_corosync_data = "totem {\n version: 3\n}\n" env = LibraryEnvironment( self.mock_logger, self.mock_reporter, corosync_conf_data=corosync_data ) self.assertFalse(env.is_corosync_conf_live) self.assertEqual(corosync_data, env.get_corosync_conf_data()) self.assertEqual(corosync_data, env.get_corosync_conf().config.export()) self.assertEqual(0, mock_get_corosync.call_count) env.push_corosync_conf( CorosyncConfigFacade.from_string(new_corosync_data) ) self.assertEqual(0, mock_distribute.call_count) self.assertEqual(new_corosync_data, env.get_corosync_conf_data()) self.assertEqual(0, mock_get_corosync.call_count) mock_check_offline.assert_not_called() mock_reload.assert_not_called() mock_qdevice_reload.assert_not_called()
def test_corosync_conf_not_set( self, mock_get_corosync, mock_distribute, mock_reload ): corosync_data = open(rc("corosync.conf")).read() new_corosync_data = corosync_data.replace("version: 2", "version: 3") mock_get_corosync.return_value = corosync_data env = LibraryEnvironment(self.mock_logger, self.mock_reporter) self.assertTrue(env.is_corosync_conf_live) self.assertEqual(corosync_data, env.get_corosync_conf_data()) self.assertEqual(corosync_data, env.get_corosync_conf().config.export()) self.assertEqual(2, mock_get_corosync.call_count) env.push_corosync_conf( CorosyncConfigFacade.from_string(new_corosync_data) ) mock_distribute.assert_called_once_with( "mock node communicator", self.mock_reporter, "mock node list", new_corosync_data, False ) mock_reload.assert_called_once_with("mock cmd runner")
def test_success(self): new_corosync_conf_data = "totem {\n version: 3\n}\n" self.config.env.push_corosync_conf( corosync_conf_text=new_corosync_conf_data) env = self.env_assistant.get_env() env.push_corosync_conf( CorosyncConfigFacade.from_string(new_corosync_conf_data))
def test_success(self): new_corosync_conf_data = "totem {\n version: 3\n}\n" self.config.env.push_corosync_conf( corosync_conf_text=new_corosync_conf_data ) env = self.env_assistant.get_env() env.push_corosync_conf( CorosyncConfigFacade.from_string(new_corosync_conf_data) )
def load( self, node_name_list=None, name="corosync_conf.load", filename="corosync.conf", auto_tie_breaker=None, instead=None, ): # pylint: disable=too-many-locals with open(rc(filename)) as a_file: content = a_file.read() corosync_conf = None if node_name_list: corosync_conf = ConfigFacade( Parser.parse(content.encode("utf-8")) ).config for nodelist in corosync_conf.get_sections(name="nodelist"): corosync_conf.del_section(nodelist) nodelist_section = Section("nodelist") corosync_conf.add_section(nodelist_section) for i, node_name in enumerate(node_name_list): node_section = Section("node") node_section.add_attribute("ring0_addr", node_name) node_section.add_attribute("nodeid", i) node_section.add_attribute("name", node_name) nodelist_section.add_section(node_section) if auto_tie_breaker is not None: corosync_conf = ( corosync_conf if corosync_conf else ConfigFacade(Parser.parse(content.encode("utf-8"))).config ) for quorum in corosync_conf.get_sections(name="quorum"): quorum.set_attribute( "auto_tie_breaker", "1" if auto_tie_breaker else "0" ) if corosync_conf: content = corosync_conf.export() self.load_content(content, name=name, instead=instead)
def load( self, node_name_list=None, name="corosync_conf.load", filename="corosync.conf", auto_tie_breaker=None, instead=None ): content = open(rc(filename)).read() corosync_conf = None if node_name_list: corosync_conf = ConfigFacade.from_string(content).config for nodelist in corosync_conf.get_sections(name="nodelist"): corosync_conf.del_section(nodelist) nodelist_section = Section("nodelist") corosync_conf.add_section(nodelist_section) for i, node_name in enumerate(node_name_list): node_section = Section("node") node_section.add_attribute("ring0_addr", node_name) node_section.add_attribute("nodeid", i) node_section.add_attribute("name", node_name) nodelist_section.add_section(node_section) if auto_tie_breaker is not None: corosync_conf = ( corosync_conf if corosync_conf else ConfigFacade.from_string(content).config ) for quorum in corosync_conf.get_sections(name="quorum"): quorum.set_attribute( "auto_tie_breaker", "1" if auto_tie_breaker else "0" ) if corosync_conf: content = corosync_conf.export() self.load_content(content, name=name, instead=instead)
def atb_has_to_be_enabled( service_manager: ServiceManagerInterface, corosync_conf_facade: CorosyncConfFacade, node_number_modifier: int = 0, ) -> bool: """ Return True whenever quorum option auto tie breaker has to be enabled for proper working of SBD fencing. False if it's not needed or it is already enabled. service_manager -- corosync_conf_facade -- node_number_modifier -- this value vill be added to current number of nodes. This can be useful to test whenever is ATB needed when adding/removeing node. """ return (not corosync_conf_facade.is_enabled_auto_tie_breaker() and is_auto_tie_breaker_needed( service_manager, corosync_conf_facade, node_number_modifier))
def test_corosync_conf_not_set_need_offline_fail( self, mock_get_corosync, mock_distribute, mock_reload, mock_check_offline, mock_qdevice_reload ): corosync_data = open(rc("corosync.conf")).read() new_corosync_data = corosync_data.replace("version: 2", "version: 3") mock_get_corosync.return_value = corosync_data def raiser(dummy_communicator, dummy_reporter, dummy_nodes, dummy_force): raise LibraryError( reports.corosync_not_running_check_node_error("test node") ) mock_check_offline.side_effect = raiser env = LibraryEnvironment(self.mock_logger, self.mock_reporter) self.assertTrue(env.is_corosync_conf_live) self.assertEqual(corosync_data, env.get_corosync_conf_data()) self.assertEqual(corosync_data, env.get_corosync_conf().config.export()) self.assertEqual(2, mock_get_corosync.call_count) conf_facade = CorosyncConfigFacade.from_string(new_corosync_data) conf_facade._need_stopped_cluster = True assert_raise_library_error( lambda: env.push_corosync_conf(conf_facade), ( severity.ERROR, report_codes.COROSYNC_NOT_RUNNING_CHECK_NODE_ERROR, {"node": "test node"} ) ) mock_check_offline.assert_called_once_with( "mock node communicator", self.mock_reporter, "mock node list", False ) mock_distribute.assert_not_called() mock_reload.assert_not_called() mock_qdevice_reload.assert_not_called()
def test_corosync_conf_not_set_need_offline_fail(self, mock_get_corosync, mock_distribute, mock_reload, mock_check_offline, mock_qdevice_reload): corosync_data = open(rc("corosync.conf")).read() new_corosync_data = corosync_data.replace("version: 2", "version: 3") mock_get_corosync.return_value = corosync_data def raiser(dummy_communicator, dummy_reporter, dummy_nodes, dummy_force): raise LibraryError( reports.corosync_not_running_check_node_error("test node")) mock_check_offline.side_effect = raiser env = LibraryEnvironment(self.mock_logger, self.mock_reporter) self.assertTrue(env.is_corosync_conf_live) self.assertEqual(corosync_data, env.get_corosync_conf_data()) self.assertEqual(corosync_data, env.get_corosync_conf().config.export()) self.assertEqual(2, mock_get_corosync.call_count) conf_facade = CorosyncConfigFacade.from_string(new_corosync_data) conf_facade._need_stopped_cluster = True assert_raise_library_error( lambda: env.push_corosync_conf(conf_facade), (severity.ERROR, report_codes.COROSYNC_NOT_RUNNING_CHECK_NODE_ERROR, { "node": "test node" })) mock_check_offline.assert_called_once_with("mock node communicator", self.mock_reporter, "mock node list", False) mock_distribute.assert_not_called() mock_reload.assert_not_called() mock_qdevice_reload.assert_not_called()
def get_corosync_conf(self): return CorosyncConfigFacade.from_string(self.get_corosync_conf_data())
def set_recovery_site(env: LibraryEnvironment, node_name: str) -> None: """ Set up disaster recovery with the local cluster being the primary site env node_name -- a known host from the recovery site """ if env.ghost_file_codes: raise LibraryError( reports.live_environment_required(env.ghost_file_codes)) report_processor = SimpleReportProcessor(env.report_processor) dr_env = env.get_dr_env() if dr_env.config.raw_file.exists(): report_processor.report(reports.dr_config_already_exist()) target_factory = env.get_node_target_factory() local_nodes, report_list = get_existing_nodes_names( env.get_corosync_conf(), error_on_missing_name=True) report_processor.report_list(report_list) if node_name in local_nodes: report_processor.report(reports.node_in_local_cluster(node_name)) report_list, local_targets = target_factory.get_target_list_with_reports( local_nodes, allow_skip=False, report_none_host_found=False) report_processor.report_list(report_list) report_list, remote_targets = (target_factory.get_target_list_with_reports( [node_name], allow_skip=False, report_none_host_found=False)) report_processor.report_list(report_list) if report_processor.has_errors: raise LibraryError() com_cmd = GetCorosyncConf(env.report_processor) com_cmd.set_targets(remote_targets) remote_cluster_nodes, report_list = get_existing_nodes_names( CorosyncConfigFacade.from_string( run_and_raise(env.get_node_communicator(), com_cmd)), error_on_missing_name=True) if report_processor.report_list(report_list): raise LibraryError() # ensure we have tokens for all nodes of remote cluster report_list, remote_targets = target_factory.get_target_list_with_reports( remote_cluster_nodes, allow_skip=False, report_none_host_found=False) if report_processor.report_list(report_list): raise LibraryError() dr_config_exporter = (get_file_toolbox( file_type_codes.PCS_DR_CONFIG).exporter) # create dr config for remote cluster remote_dr_cfg = dr_env.create_facade(DrRole.RECOVERY) remote_dr_cfg.add_site(DrRole.PRIMARY, local_nodes) # send config to all node of remote cluster distribute_file_cmd = DistributeFilesWithoutForces( env.report_processor, node_communication_format.pcs_dr_config_file( dr_config_exporter.export(remote_dr_cfg.config))) distribute_file_cmd.set_targets(remote_targets) run_and_raise(env.get_node_communicator(), distribute_file_cmd) # create new dr config, with local cluster as primary site local_dr_cfg = dr_env.create_facade(DrRole.PRIMARY) local_dr_cfg.add_site(DrRole.RECOVERY, remote_cluster_nodes) distribute_file_cmd = DistributeFilesWithoutForces( env.report_processor, node_communication_format.pcs_dr_config_file( dr_config_exporter.export(local_dr_cfg.config))) distribute_file_cmd.set_targets(local_targets) run_and_raise(env.get_node_communicator(), distribute_file_cmd)
def test_success(self): env = self.env_assistant.get_env() new_corosync_conf_data = "totem {\n version: 3\n}\n" env.push_corosync_conf( CorosyncConfigFacade.from_string(new_corosync_conf_data)) self.assertEqual(new_corosync_conf_data, env.get_corosync_conf_data())