You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2014/12/12 02:14:41 UTC

ambari git commit: AMBARI-8343. Components should indicate Security State (via ambari-agent). (Robert Levas via yusaku)

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 <yu...@hortonworks.com>
Authored: Thu Dec 11 17:14:11 2014 -0800
Committer: Yusaku Sako <yu...@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__")