Repository: ambari Updated Branches: refs/heads/trunk 8235e7d63 -> 5cba41704
AMBARI-8343. Components should indicate Security State (via ambari-agent). (Robert Levas via yusaku) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/5cba4170 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/5cba4170 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/5cba4170 Branch: refs/heads/trunk Commit: 5cba41704c4a5c1defbba522253b00e5a2b8a345 Parents: 8235e7d Author: Yusaku Sako <yus...@hortonworks.com> Authored: Thu Dec 11 17:14:11 2014 -0800 Committer: Yusaku Sako <yus...@hortonworks.com> Committed: Thu Dec 11 17:14:11 2014 -0800 ---------------------------------------------------------------------- .../src/main/python/ambari_agent/ActionQueue.py | 4 ++ .../ambari_agent/CustomServiceOrchestrator.py | 31 ++++++++++++ .../test/python/ambari_agent/TestActionQueue.py | 17 +++++-- .../TestCustomServiceOrchestrator.py | 51 ++++++++++++++++++++ 4 files changed, 99 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/5cba4170/ambari-agent/src/main/python/ambari_agent/ActionQueue.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/ActionQueue.py b/ambari-agent/src/main/python/ambari_agent/ActionQueue.py index fbde26f..b60736d 100644 --- a/ambari-agent/src/main/python/ambari_agent/ActionQueue.py +++ b/ambari-agent/src/main/python/ambari_agent/ActionQueue.py @@ -329,6 +329,7 @@ class ActionQueue(threading.Thread): # For custom services, responsibility to determine service status is # delegated to python scripts component_status_result = self.customServiceOrchestrator.requestComponentStatus(command) + component_security_status_result = self.customServiceOrchestrator.requestComponentSecurityState(command) if component_status_result['exitcode'] == 0: component_status = LiveStatus.LIVE_STATUS @@ -340,6 +341,9 @@ class ActionQueue(threading.Thread): result = livestatus.build(forsed_component_status= component_status) + # Add security state to the result + result['securityState'] = component_security_status_result + if component_extra is not None and len(component_extra) != 0: if component_extra.has_key('alerts'): result['alerts'] = component_extra['alerts'] http://git-wip-us.apache.org/repos/asf/ambari/blob/5cba4170/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py b/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py index 08dddae..61997f2 100644 --- a/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py +++ b/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py @@ -41,6 +41,7 @@ class CustomServiceOrchestrator(): SCRIPT_TYPE_PYTHON = "PYTHON" COMMAND_NAME_STATUS = "STATUS" + COMMAND_NAME_SECURITY_STATUS = "SECURITY_STATUS" CUSTOM_ACTION_COMMAND = 'ACTIONEXECUTE' CUSTOM_COMMAND_COMMAND = 'CUSTOM_COMMAND' @@ -229,6 +230,36 @@ class CustomServiceOrchestrator(): override_output_files=override_output_files) return res + def requestComponentSecurityState(self, command): + """ + Determines the current security state of the component + A command will be issued to trigger the security_status check and the result of this check will + returned to the caller. If the component lifecycle script has no security_status method the + check will return non zero exit code and "UNKNOWN" will be returned. + """ + override_output_files=True # by default, we override status command output + if logger.level == logging.DEBUG: + override_output_files = False + security_check_res = self.runCommand(command, self.status_commands_stdout, + self.status_commands_stderr, self.COMMAND_NAME_SECURITY_STATUS, + override_output_files=override_output_files) + result = 'UNKNOWN' + + if security_check_res is None: + logger.warn("The return value of the security_status check was empty, the security status is unknown") + elif 'exitcode' not in security_check_res: + logger.warn("Missing 'exitcode' value from the security_status check result, the security status is unknown") + elif security_check_res['exitcode'] != 0: + logger.debug("The 'exitcode' value from the security_status check result indicated the check routine failed to properly execute, the security status is unknown") + elif 'structuredOut' not in security_check_res: + logger.warn("Missing 'structuredOut' value from the security_status check result, the security status is unknown") + elif 'securityState' not in security_check_res['structuredOut']: + logger.warn("Missing 'securityState' value from the security_status check structuredOut data set, the security status is unknown") + else: + result = security_check_res['structuredOut']['securityState'] + + return result + def resolve_script_path(self, base_dir, script, script_type): """ Incapsulates logic of script location determination. http://git-wip-us.apache.org/repos/asf/ambari/blob/5cba4170/ambari-agent/src/test/python/ambari_agent/TestActionQueue.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/test/python/ambari_agent/TestActionQueue.py b/ambari-agent/src/test/python/ambari_agent/TestActionQueue.py index 52b586b..9aeb024 100644 --- a/ambari-agent/src/test/python/ambari_agent/TestActionQueue.py +++ b/ambari-agent/src/test/python/ambari_agent/TestActionQueue.py @@ -520,24 +520,31 @@ class TestActionQueue(TestCase): @patch.object(ActionQueue, "status_update_callback") @patch.object(StackVersionsFileHandler, "read_stack_version") @patch.object(CustomServiceOrchestrator, "requestComponentStatus") + @patch.object(CustomServiceOrchestrator, "requestComponentSecurityState") @patch.object(ActionQueue, "execute_command") @patch.object(LiveStatus, "build") @patch.object(CustomServiceOrchestrator, "__init__") def test_execute_status_command(self, CustomServiceOrchestrator_mock, - build_mock, execute_command_mock, + build_mock, execute_command_mock, requestComponentSecurityState_mock, requestComponentStatus_mock, read_stack_version_mock, status_update_callback): CustomServiceOrchestrator_mock.return_value = None dummy_controller = MagicMock() actionQueue = ActionQueue(AmbariConfig().getConfig(), dummy_controller) - build_mock.return_value = "dummy report" + build_mock.return_value = {'dummy report': '' } requestComponentStatus_mock.reset_mock() - requestComponentStatus_mock.return_value = {'exitcode': 0} + requestComponentStatus_mock.return_value = {'exitcode': 0 } + + requestComponentSecurityState_mock.reset_mock() + requestComponentSecurityState_mock.return_value = 'UNKNOWN' + actionQueue.execute_status_command(self.status_command) report = actionQueue.result() - expected = 'dummy report' + expected = {'dummy report': '', + 'securityState' : 'UNKNOWN'} + self.assertEqual(len(report['componentStatus']), 1) self.assertEqual(report['componentStatus'][0], expected) self.assertTrue(requestComponentStatus_mock.called) @@ -545,10 +552,12 @@ class TestActionQueue(TestCase): @patch.object(ActionQueue, "status_update_callback") @patch.object(StackVersionsFileHandler, "read_stack_version") @patch.object(CustomServiceOrchestrator, "requestComponentStatus") + @patch.object(CustomServiceOrchestrator, "requestComponentSecurityState") @patch.object(ActionQueue, "execute_command") @patch.object(LiveStatus, "build") @patch.object(CustomServiceOrchestrator, "__init__") def test_execute_status_command_with_alerts(self, CustomServiceOrchestrator_mock, + requestComponentSecurityState_mock, build_mock, execute_command_mock, requestComponentStatus_mock, read_stack_version_mock, status_update_callback): http://git-wip-us.apache.org/repos/asf/ambari/blob/5cba4170/ambari-agent/src/test/python/ambari_agent/TestCustomServiceOrchestrator.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/test/python/ambari_agent/TestCustomServiceOrchestrator.py b/ambari-agent/src/test/python/ambari_agent/TestCustomServiceOrchestrator.py index 24ee259..46c0166 100644 --- a/ambari-agent/src/test/python/ambari_agent/TestCustomServiceOrchestrator.py +++ b/ambari-agent/src/test/python/ambari_agent/TestCustomServiceOrchestrator.py @@ -476,6 +476,57 @@ class TestCustomServiceOrchestrator(TestCase): status = orchestrator.requestComponentStatus(status_command) self.assertEqual(runCommand_mock.return_value, status) + @patch.object(CustomServiceOrchestrator, "runCommand") + @patch.object(FileCache, "__init__") + def test_requestComponentSecurityState(self, FileCache_mock, runCommand_mock): + FileCache_mock.return_value = None + status_command = { + "serviceName" : 'HDFS', + "commandType" : "STATUS_COMMAND", + "clusterName" : "", + "componentName" : "DATANODE", + 'configurations':{} + } + dummy_controller = MagicMock() + orchestrator = CustomServiceOrchestrator(self.config, dummy_controller) + # Test securityState + runCommand_mock.return_value = { + 'exitcode' : 0, + 'structuredOut' : {'securityState': 'UNSECURED'} + } + + status = orchestrator.requestComponentSecurityState(status_command) + self.assertEqual('UNSECURED', status) + + # Test case where exit code indicates failure + runCommand_mock.return_value = { + "exitcode" : 1 + } + status = orchestrator.requestComponentSecurityState(status_command) + self.assertEqual('UNKNOWN', status) + + @patch.object(FileCache, "__init__") + def test_requestComponentSecurityState_realFailure(self, FileCache_mock): + ''' + Tests the case where the CustomServiceOrchestrator attempts to call a service's security_status + method, but fails to do so because the script or method was not found. + :param FileCache_mock: + :return: + ''' + FileCache_mock.return_value = None + status_command = { + "serviceName" : 'BOGUS_SERVICE', + "commandType" : "STATUS_COMMAND", + "clusterName" : "", + "componentName" : "DATANODE", + 'configurations':{} + } + dummy_controller = MagicMock() + orchestrator = CustomServiceOrchestrator(self.config, dummy_controller) + + status = orchestrator.requestComponentSecurityState(status_command) + self.assertEqual('UNKNOWN', status) + @patch.object(CustomServiceOrchestrator, "dump_command_to_json") @patch.object(FileCache, "__init__")