class CommunicatorLoggerTest(TestCase): def setUp(self): self.logger = mock.MagicMock(spec_set=logging.Logger) self.reporter = MockLibraryReportProcessor() self.com_logger = lib.LibCommunicatorLogger(self.logger, self.reporter) def test_log_request_start(self): request = fixture_request() self.com_logger.log_request_start(request) self.reporter.assert_reports( fixture_report_item_list_send(request.url, request.data) ) self.assertEqual( [fixture_logger_call_send(request.url, request.data)], self.logger.mock_calls ) def test_log_response_connected(self): expected_code = 200 expected_data = "data" expected_debug_data = "* text\n>> data out\n" response = Response.connection_successful( MockCurlSimple( info={pycurl.RESPONSE_CODE: expected_code}, output=expected_data.encode("utf-8"), debug_output=expected_debug_data.encode("utf-8"), request=fixture_request(), ) ) self.com_logger.log_response(response) self.reporter.assert_reports( fixture_report_item_list_on_success( response.request.url, expected_code, expected_data, expected_debug_data ) ) logger_calls = fixture_logger_calls_on_success( response.request.url, expected_code, expected_data, expected_debug_data ) self.assertEqual(logger_calls, self.logger.mock_calls) @mock.patch("pcs.lib.node_communication.is_proxy_set") def test_log_response_not_connected(self, mock_proxy): mock_proxy.return_value = False expected_debug_data = "* text\n>> data out\n" error_msg = "error" response = Response.connection_failure( MockCurlSimple( debug_output=expected_debug_data.encode("utf-8"), request=fixture_request(), ), pycurl.E_HTTP_POST_ERROR, error_msg, ) self.com_logger.log_response(response) self.reporter.assert_reports( fixture_report_item_list_not_connected( response.request.host_label, error_msg ) + fixture_report_item_list_debug( response.request.url, expected_debug_data ) ) logger_calls = [ fixture_logger_call_not_connected( response.request.host_label, error_msg ), fixture_logger_call_debug_data( response.request.url, expected_debug_data ) ] self.assertEqual(logger_calls, self.logger.mock_calls) @mock.patch("pcs.lib.node_communication.is_proxy_set") def test_log_response_not_connected_with_proxy(self, mock_proxy): mock_proxy.return_value = True expected_debug_data = "* text\n>> data out\n" error_msg = "error" response = Response.connection_failure( MockCurlSimple( debug_output=expected_debug_data.encode("utf-8"), request=fixture_request(), ), pycurl.E_HTTP_POST_ERROR, error_msg, ) self.com_logger.log_response(response) self.reporter.assert_reports( fixture_report_item_list_not_connected( response.request.host_label, error_msg ) + fixture_report_item_list_proxy_set( response.request.host_label, response.request.host_label ) + fixture_report_item_list_debug( response.request.url, expected_debug_data ) ) logger_calls = [ fixture_logger_call_not_connected( response.request.host_label, error_msg ), fixture_logger_call_proxy_set(), fixture_logger_call_debug_data( response.request.url, expected_debug_data ) ] self.assertEqual(logger_calls, self.logger.mock_calls) def test_log_retry(self): prev_addr = "addr" prev_port = 2225 prev_host = Destination(prev_addr, prev_port) response = Response.connection_failure( MockCurlSimple(request=fixture_request()), pycurl.E_HTTP_POST_ERROR, "e", ) self.com_logger.log_retry(response, prev_host) self.reporter.assert_reports([( severity.WARNING, report_codes.NODE_COMMUNICATION_RETRYING, { "node": response.request.host_label, "failed_address": prev_addr, "failed_port": prev_port, "next_address": response.request.dest.addr, "next_port": settings.pcsd_default_port, "request": response.request.url, }, None )]) logger_call = mock.call.warning( ( "Unable to connect to '{label}' via address '{old_addr}' and " "port '{old_port}'. Retrying request '{req}' via address " "'{new_addr}' and port '{new_port}'" ).format( label=response.request.host_label, old_addr=prev_addr, old_port=prev_port, new_addr=response.request.dest.addr, new_port=settings.pcsd_default_port, req=response.request.url, ) ) self.assertEqual([logger_call], self.logger.mock_calls) def test_log_no_more_addresses(self): response = Response.connection_failure( MockCurlSimple(request=fixture_request()), pycurl.E_HTTP_POST_ERROR, "e" ) self.com_logger.log_no_more_addresses(response) self.reporter.assert_reports([( severity.WARNING, report_codes.NODE_COMMUNICATION_NO_MORE_ADDRESSES, { "node": response.request.host_label, "request": response.request.url, }, None )]) logger_call = mock.call.warning( "No more addresses for node {label} to run '{req}'".format( label=response.request.host_label, req=response.request.url, ) ) self.assertEqual([logger_call], self.logger.mock_calls)
class PrepareOptionsPlainTest(TestCase): def setUp(self): self.cib = "cib" self.report_processor = MockLibraryReportProcessor(debug=False) self.prepare = partial(ticket.prepare_options_plain, self.cib, self.report_processor) @mock.patch("pcs.lib.cib.constraint.ticket._create_id") def test_prepare_correct_options(self, mock_create_id, _): mock_create_id.return_value = "generated_id" role = str(const.PCMK_ROLE_PROMOTED_LEGACY).lower() self.assertEqual( { "id": "generated_id", "loss-policy": "fence", "rsc": "resourceA", "rsc-role": const.PCMK_ROLE_PROMOTED_PRIMARY, "ticket": "ticket_key", }, self.prepare( { "loss-policy": "fence", "rsc-role": role }, "ticket_key", "resourceA", ), ) self.report_processor.assert_reports([ fixture.warn( report_codes.DEPRECATED_OPTION_VALUE, option_name="role", deprecated_value=role, replaced_by=const.PCMK_ROLE_PROMOTED, ) ]) @mock.patch("pcs.lib.cib.constraint.ticket._create_id") def test_does_not_include_role_if_not_presented(self, mock_create_id, _): mock_create_id.return_value = "generated_id" self.assertEqual( { "id": "generated_id", "loss-policy": "fence", "rsc": "resourceA", "ticket": "ticket_key", }, self.prepare( { "loss-policy": "fence", "rsc-role": "" }, "ticket_key", "resourceA", ), ) self.report_processor.assert_reports([]) def test_refuse_unknown_attributes(self, _): assert_raise_library_error( lambda: self.prepare( { "unknown": "nonsense", "rsc-role": "master" }, "ticket_key", "resourceA", ), ( severities.ERROR, report_codes.INVALID_OPTIONS, { "option_names": ["unknown"], "option_type": None, "allowed": [ "id", "loss-policy", "rsc", "rsc-role", "ticket", ], "allowed_patterns": [], }, ), ) def test_refuse_bad_role(self, _): assert_raise_library_error( lambda: self.prepare({ "id": "id", "rsc-role": "bad_role" }, "ticket_key", "resourceA"), ) self.report_processor.assert_reports([ fixture.error( report_codes.INVALID_OPTION_VALUE, allowed_values=const.PCMK_ROLES, option_value="bad_role", option_name="role", cannot_be_empty=False, forbidden_characters=None, ), ]) def test_refuse_missing_ticket(self, _): role = str(const.PCMK_ROLE_UNPROMOTED_LEGACY).lower() assert_raise_library_error( lambda: self.prepare({ "id": "id", "rsc-role": role }, "", "resourceA"), ) self.report_processor.assert_reports([ fixture.error( report_codes.REQUIRED_OPTIONS_ARE_MISSING, option_names=["ticket"], option_type=None, ), fixture.warn( report_codes.DEPRECATED_OPTION_VALUE, option_name="role", deprecated_value=role, replaced_by=const.PCMK_ROLE_UNPROMOTED, ), ]) def test_refuse_missing_resource_id(self, _): assert_raise_library_error( lambda: self.prepare( { "id": "id", "rsc-role": const.PCMK_ROLE_PROMOTED }, "ticket_key", "", ), ) self.report_processor.assert_reports([ fixture.error( report_codes.REQUIRED_OPTIONS_ARE_MISSING, option_names=["rsc"], option_type=None, ), ]) def test_refuse_unknown_lost_policy(self, mock_check_new_id_applicable): # pylint: disable=unused-argument assert_raise_library_error( lambda: self.prepare( { "loss-policy": "unknown", "ticket": "T", "id": "id" }, "ticket_key", "resourceA", ), ) self.report_processor.assert_reports([ fixture.error( report_codes.INVALID_OPTION_VALUE, allowed_values=("fence", "stop", "freeze", "demote"), option_value="unknown", option_name="loss-policy", cannot_be_empty=False, forbidden_characters=None, ), ]) @mock.patch("pcs.lib.cib.constraint.ticket._create_id") def test_complete_id(self, mock_create_id, _): mock_create_id.return_value = "generated_id" options = { "loss-policy": "freeze", "ticket": "T", "rsc-role": const.PCMK_ROLE_PROMOTED, } ticket_key = "ticket_key" resource_id = "resourceA" expected_options = options.copy() expected_options.update({ "id": "generated_id", "rsc": resource_id, "rsc-role": const.PCMK_ROLE_PROMOTED_PRIMARY, "ticket": ticket_key, }) self.assertEqual( expected_options, self.prepare( options, ticket_key, resource_id, ), ) mock_create_id.assert_called_once_with( self.cib, ticket_key, resource_id, const.PCMK_ROLE_PROMOTED_PRIMARY, )
class NodeTargetLibFactory(TestCase): def setUp(self): self.known_hosts = { "host{}".format(i): PcsKnownHost( "host{}".format(i), "token{}".format(i), [ Destination( "addr{}{}".format(i, j), "port{}{}".format(i, j) ) for j in range(2) ] ) for i in range(2) } self.report_processor = MockLibraryReportProcessor() self.factory = lib.NodeTargetLibFactory( self.known_hosts, self.report_processor ) def assert_equal_known_host_target(self, known_host, target): self.assertEqual(known_host.name, target.label) self.assertEqual(known_host.token, target.token) self.assertEqual(known_host.dest_list, target.dest_list) def test_one_host(self): host = "host0" self.assert_equal_known_host_target( self.known_hosts[host], self.factory.get_target_list([host])[0] ) self.report_processor.assert_reports([]) def test_multiple_hosts(self): host_list = ["host0", "host1"] target_list = self.factory.get_target_list(host_list) for i, host in enumerate(host_list): self.assert_equal_known_host_target( self.known_hosts[host], target_list[i] ) self.report_processor.assert_reports([]) def test_multiple_not_found(self): host = "host0" unknown_hosts = ["node0", "node1"] report = fixture.error( report_codes.HOST_NOT_FOUND, force_code=report_codes.SKIP_OFFLINE_NODES, host_list=unknown_hosts ) assert_raise_library_error( lambda: self.factory.get_target_list([host] + unknown_hosts), report ) self.report_processor.assert_reports([report]) def test_multiple_skip_not_allowed(self): host = "host0" unknown_hosts = ["node0", "node1"] report = fixture.error( report_codes.HOST_NOT_FOUND, host_list=unknown_hosts ) assert_raise_library_error( lambda: self.factory.get_target_list( [host] + unknown_hosts, allow_skip=False, ), report ) self.report_processor.assert_reports([report]) def test_multiple_not_found_skip_offline(self): host = "host0" unknown_hosts = ["node0", "node1"] target_list = self.factory.get_target_list( [host] + unknown_hosts, skip_non_existing=True ) self.assert_equal_known_host_target( self.known_hosts[host], target_list[0] ) self.report_processor.assert_reports([ fixture.warn(report_codes.HOST_NOT_FOUND, host_list=unknown_hosts) ]) def test_no_host_found(self): unknown_hosts = ["node0", "node1"] report_list = [ fixture.error( report_codes.HOST_NOT_FOUND, force_code=report_codes.SKIP_OFFLINE_NODES, host_list=unknown_hosts ), fixture.error(report_codes.NONE_HOST_FOUND) ] assert_raise_library_error( lambda: self.factory.get_target_list(unknown_hosts), *report_list ) self.report_processor.assert_reports(report_list) def test_no_host_found_skip_offline(self): unknown_hosts = ["node0", "node1"] report_list = [ fixture.warn(report_codes.HOST_NOT_FOUND, host_list=unknown_hosts), fixture.error(report_codes.NONE_HOST_FOUND) ] assert_raise_library_error( lambda: self.factory.get_target_list( unknown_hosts, skip_non_existing=True ), report_list[1] ) self.report_processor.assert_reports(report_list) def test_empty_host_list(self): self.assertEqual([], self.factory.get_target_list([])) self.report_processor.assert_reports([])
class PrepareSetTest(TestCase): def setUp(self): self.report_processor = MockLibraryReportProcessor(debug=False) def test_return_corrected_resource_set(self): find_valid_id = mock.Mock() find_valid_id.side_effect = lambda id: {"A": "AA", "B": "BB"}[id] self.assertEqual( { "ids": ["AA", "BB"], "options": { "sequential": "true" } }, resource_set.prepare_set( find_valid_id, { "ids": ["A", "B"], "options": { "sequential": "true" } }, self.report_processor, ), ) self.report_processor.assert_reports([]) def test_refuse_invalid_attribute_name(self): assert_raise_library_error( lambda: resource_set.prepare_set( mock.Mock(), { "ids": ["A", "B"], "options": { "invalid_name": "true" } }, self.report_processor, ), ) self.report_processor.assert_reports([ fixture.error( reports.codes.INVALID_OPTIONS, option_names=["invalid_name"], option_type="set", allowed=["action", "require-all", "role", "sequential"], allowed_patterns=[], ), ]) def test_refuse_invalid_attribute_value(self): assert_raise_library_error( lambda: resource_set.prepare_set( mock.Mock(), { "ids": ["A", "B"], "options": { "role": "invalid" } }, self.report_processor, ), ) self.report_processor.assert_reports([ fixture.error( reports.codes.INVALID_OPTION_VALUE, option_name="role", allowed_values=const.PCMK_ROLES, option_value="invalid", cannot_be_empty=False, forbidden_characters=None, ), ])