Browse Source

AMBARI-7955 - Alerts: Expose the Ability to Create Host-Only Alerts (jonathanhurley)

Jonathan Hurley 10 years ago
parent
commit
17b05b2363

+ 1 - 0
ambari-agent/pom.xml

@@ -586,6 +586,7 @@
                     <include>stacks/stack_advisor.py</include>
                     <include>stacks/stack_advisor.py</include>
                     <include>stacks/${stack.distribution}/**/*</include>
                     <include>stacks/${stack.distribution}/**/*</include>
                     <include>custom_actions/**/*</include>
                     <include>custom_actions/**/*</include>
+                    <include>host_scripts/**/*</include>
                   </includes>
                   </includes>
                   <filtering>false</filtering>
                   <filtering>false</filtering>
                 </resource>
                 </resource>

+ 4 - 2
ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py

@@ -53,9 +53,10 @@ class AlertSchedulerHandler():
   }
   }
 
 
 
 
-  def __init__(self, cachedir, stacks_dir, in_minutes=True):
+  def __init__(self, cachedir, stacks_dir, host_scripts_dir, in_minutes=True):
     self.cachedir = cachedir
     self.cachedir = cachedir
     self.stacks_dir = stacks_dir
     self.stacks_dir = stacks_dir
+    self.host_scripts_dir = host_scripts_dir
     
     
     if not os.path.exists(cachedir) and AlertSchedulerHandler.make_cachedir:
     if not os.path.exists(cachedir) and AlertSchedulerHandler.make_cachedir:
       try:
       try:
@@ -222,7 +223,8 @@ class AlertSchedulerHandler():
     elif source_type == AlertSchedulerHandler.TYPE_PORT:
     elif source_type == AlertSchedulerHandler.TYPE_PORT:
       alert = PortAlert(json_definition, source)
       alert = PortAlert(json_definition, source)
     elif source_type == AlertSchedulerHandler.TYPE_SCRIPT:
     elif source_type == AlertSchedulerHandler.TYPE_SCRIPT:
-      source['stacks_dir'] = self.stacks_dir
+      source['stacks_directory'] = self.stacks_dir
+      source['host_scripts_directory'] = self.host_scripts_dir
       alert = ScriptAlert(json_definition, source)
       alert = ScriptAlert(json_definition, source)
     elif source_type == AlertSchedulerHandler.TYPE_WEB:
     elif source_type == AlertSchedulerHandler.TYPE_WEB:
       alert = WebAlert(json_definition, source)
       alert = WebAlert(json_definition, source)

+ 4 - 1
ambari-agent/src/main/python/ambari_agent/Controller.py

@@ -81,8 +81,11 @@ class Controller(threading.Thread):
       cache_dir = '/var/lib/ambari-agent/cache'
       cache_dir = '/var/lib/ambari-agent/cache'
 
 
     stacks_cache_dir = os.path.join(cache_dir, FileCache.STACKS_CACHE_DIRECTORY)
     stacks_cache_dir = os.path.join(cache_dir, FileCache.STACKS_CACHE_DIRECTORY)
+    host_scripts_cache_dir = os.path.join(cache_dir, FileCache.HOST_SCRIPTS_CACHE_DIRECTORY)
     alerts_cache_dir = os.path.join(cache_dir, 'alerts')
     alerts_cache_dir = os.path.join(cache_dir, 'alerts')
-    self.alert_scheduler_handler = AlertSchedulerHandler(alerts_cache_dir, stacks_cache_dir)
+    
+    self.alert_scheduler_handler = AlertSchedulerHandler(alerts_cache_dir, 
+        stacks_cache_dir, host_scripts_cache_dir)
 
 
 
 
   def __del__(self):
   def __del__(self):

+ 9 - 1
ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py

@@ -106,7 +106,9 @@ class CustomServiceOrchestrator():
         server_url_prefix = command['hostLevelParams']['jdk_location']
         server_url_prefix = command['hostLevelParams']['jdk_location']
       else:
       else:
         server_url_prefix = command['commandParams']['jdk_location']
         server_url_prefix = command['commandParams']['jdk_location']
+        
       task_id = "status"
       task_id = "status"
+      
       try:
       try:
         task_id = command['taskId']
         task_id = command['taskId']
         command_name = command['roleCommand']
         command_name = command['roleCommand']
@@ -123,18 +125,24 @@ class CustomServiceOrchestrator():
       else:
       else:
         if command_name == self.CUSTOM_COMMAND_COMMAND:
         if command_name == self.CUSTOM_COMMAND_COMMAND:
           command_name = command['hostLevelParams']['custom_command']
           command_name = command['hostLevelParams']['custom_command']
+
+        # forces a hash challenge on the directories to keep them updated, even
+        # if the return type is not used
+        self.file_cache.get_host_scripts_base_dir(server_url_prefix)          
         hook_dir = self.file_cache.get_hook_base_dir(command, server_url_prefix)
         hook_dir = self.file_cache.get_hook_base_dir(command, server_url_prefix)
         base_dir = self.file_cache.get_service_base_dir(command, server_url_prefix)
         base_dir = self.file_cache.get_service_base_dir(command, server_url_prefix)
+        
         script_path = self.resolve_script_path(base_dir, script, script_type)
         script_path = self.resolve_script_path(base_dir, script, script_type)
         script_tuple = (script_path, base_dir)
         script_tuple = (script_path, base_dir)
 
 
       tmpstrucoutfile = os.path.join(self.tmp_dir,
       tmpstrucoutfile = os.path.join(self.tmp_dir,
                                     "structured-out-{0}.json".format(task_id))
                                     "structured-out-{0}.json".format(task_id))
 
 
-      if script_type.upper() != self.SCRIPT_TYPE_PYTHON:
       # We don't support anything else yet
       # We don't support anything else yet
+      if script_type.upper() != self.SCRIPT_TYPE_PYTHON:
         message = "Unknown script type {0}".format(script_type)
         message = "Unknown script type {0}".format(script_type)
         raise AgentException(message)
         raise AgentException(message)
+
       # Execute command using proper interpreter
       # Execute command using proper interpreter
       handle = None
       handle = None
       if(command.has_key('__handle')):
       if(command.has_key('__handle')):

+ 11 - 0
ambari-agent/src/main/python/ambari_agent/FileCache.py

@@ -39,6 +39,7 @@ class FileCache():
 
 
   STACKS_CACHE_DIRECTORY="stacks"
   STACKS_CACHE_DIRECTORY="stacks"
   CUSTOM_ACTIONS_CACHE_DIRECTORY="custom_actions"
   CUSTOM_ACTIONS_CACHE_DIRECTORY="custom_actions"
+  HOST_SCRIPTS_CACHE_DIRECTORY="host_scripts"
   HASH_SUM_FILE=".hash"
   HASH_SUM_FILE=".hash"
   ARCHIVE_NAME="archive.zip"
   ARCHIVE_NAME="archive.zip"
 
 
@@ -92,6 +93,16 @@ class FileCache():
                                   server_url_prefix)
                                   server_url_prefix)
 
 
 
 
+  def get_host_scripts_base_dir(self, server_url_prefix):
+    """
+    Returns a base directory for host scripts (host alerts, etc) which
+    are scripts that are not part of the main agent code
+    """
+    return self.provide_directory(self.cache_dir,
+                                  self.HOST_SCRIPTS_CACHE_DIRECTORY,
+                                  server_url_prefix)
+
+
   def provide_directory(self, cache_path, subdirectory, server_url_prefix):
   def provide_directory(self, cache_path, subdirectory, server_url_prefix):
     """
     """
     Ensures that directory at cache is up-to-date. Throws a CachingException
     Ensures that directory at cache is up-to-date. Throws a CachingException

+ 21 - 8
ambari-agent/src/main/python/ambari_agent/alerts/script_alert.py

@@ -40,11 +40,17 @@ class ScriptAlert(BaseAlert):
     super(ScriptAlert, self).__init__(alert_meta, alert_source_meta)
     super(ScriptAlert, self).__init__(alert_meta, alert_source_meta)
     
     
     self.path = None
     self.path = None
+    self.stacks_dir = None
+    self.host_scripts_dir = None
+    
     if 'path' in alert_source_meta:
     if 'path' in alert_source_meta:
       self.path = alert_source_meta['path']
       self.path = alert_source_meta['path']
       
       
-    if 'stacks_dir' in alert_source_meta:
-      self.stacks_dir = alert_source_meta['stacks_dir']
+    if 'stacks_directory' in alert_source_meta:
+      self.stacks_dir = alert_source_meta['stacks_directory']
+      
+    if 'host_scripts_directory' in alert_source_meta:
+      self.host_scripts_dir = alert_source_meta['host_scripts_directory']
       
       
     # execute the get_tokens() method so that this script correctly populates
     # execute the get_tokens() method so that this script correctly populates
     # its list of keys
     # its list of keys
@@ -78,18 +84,25 @@ class ScriptAlert(BaseAlert):
     
     
 
 
   def _load_source(self):
   def _load_source(self):
-    if self.path is None and self.stack_path is None:
+    if self.path is None and self.stack_path is None and self.host_scripts_dir is None:
       raise Exception("The attribute 'path' must be specified")
       raise Exception("The attribute 'path' must be specified")
 
 
+    paths = self.path.split('/')
     path_to_script = self.path
     path_to_script = self.path
-    if not os.path.exists(self.path) and self.stacks_dir is not None:
-      paths = self.path.split('/')
+    
+    # if the path doesn't exist and stacks dir is defined, try that
+    if not os.path.exists(path_to_script) and self.stacks_dir is not None:      
       path_to_script = os.path.join(self.stacks_dir, *paths)
       path_to_script = os.path.join(self.stacks_dir, *paths)
-      
+
+    # if the path doesn't exist and the host script dir is defined, try that
+    if not os.path.exists(path_to_script) and self.host_scripts_dir is not None:
+      path_to_script = os.path.join(self.host_scripts_dir, *paths)
+
+    # if the path can't be evaluated, throw exception      
     if not os.path.exists(path_to_script) or not os.path.isfile(path_to_script):
     if not os.path.exists(path_to_script) or not os.path.isfile(path_to_script):
       raise Exception(
       raise Exception(
-        "Resolved script '{0}' does not appear to be a script".format(
-          path_to_script))
+        "Unable to find '{0}' as an absolute path or part of {1} or {2}".format(self.path,
+          self.stacks_dir, self.host_scripts_dir))
 
 
     if logger.isEnabledFor(logging.DEBUG):
     if logger.isEnabledFor(logging.DEBUG):
       logger.debug("Executing script check {0}".format(path_to_script))
       logger.debug("Executing script check {0}".format(path_to_script))

+ 13 - 7
ambari-agent/src/test/python/ambari_agent/TestAlerts.py

@@ -47,8 +47,9 @@ class TestAlerts(TestCase):
   def test_start(self, aps_add_interval_job_mock, aps_start_mock):
   def test_start(self, aps_add_interval_job_mock, aps_start_mock):
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
+    test_host_scripts_path = os.path.join('ambari_agent', 'dummy_files')
 
 
-    ash = AlertSchedulerHandler(test_file_path, test_stack_path)
+    ash = AlertSchedulerHandler(test_file_path, test_stack_path, test_host_scripts_path)
     ash.start()
     ash.start()
 
 
     self.assertTrue(aps_add_interval_job_mock.called)
     self.assertTrue(aps_add_interval_job_mock.called)
@@ -136,13 +137,15 @@ class TestAlerts(TestCase):
     }
     }
 
 
     # normally set by AlertSchedulerHandler
     # normally set by AlertSchedulerHandler
-    json['source']['stacks_dir'] = os.path.join('ambari_agent', 'dummy_files')
+    json['source']['stacks_directory'] = os.path.join('ambari_agent', 'dummy_files')
+    json['source']['host_scripts_directory'] = os.path.join('ambari_agent', 'host_scripts')
 
 
     collector = AlertCollector()
     collector = AlertCollector()
     sa = ScriptAlert(json, json['source'])
     sa = ScriptAlert(json, json['source'])
     sa.set_helpers(collector, {'foo-site/bar': 'rendered-bar', 'foo-site/baz':'rendered-baz'} )
     sa.set_helpers(collector, {'foo-site/bar': 'rendered-bar', 'foo-site/baz':'rendered-baz'} )
     self.assertEquals(json['source']['path'], sa.path)
     self.assertEquals(json['source']['path'], sa.path)
-    self.assertEquals(json['source']['stacks_dir'], sa.stacks_dir)
+    self.assertEquals(json['source']['stacks_directory'], sa.stacks_dir)
+    self.assertEquals(json['source']['host_scripts_directory'], sa.host_scripts_dir)
 
 
     sa.collect()
     sa.collect()
 
 
@@ -369,8 +372,9 @@ class TestAlerts(TestCase):
   def test_reschedule(self):
   def test_reschedule(self):
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
-
-    ash = AlertSchedulerHandler(test_file_path, test_stack_path)
+    test_host_scripts_path = os.path.join('ambari_agent', 'dummy_files')
+    
+    ash = AlertSchedulerHandler(test_file_path, test_stack_path, test_host_scripts_path)
     ash.start()
     ash.start()
     
     
     self.assertEquals(1, ash.get_job_count())
     self.assertEquals(1, ash.get_job_count())
@@ -420,8 +424,9 @@ class TestAlerts(TestCase):
   def test_disabled_definitions(self):
   def test_disabled_definitions(self):
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
+    test_host_scripts_path = os.path.join('ambari_agent', 'dummy_files')
 
 
-    ash = AlertSchedulerHandler(test_file_path, test_stack_path)
+    ash = AlertSchedulerHandler(test_file_path, test_stack_path, test_host_scripts_path)
     ash.start()
     ash.start()
 
 
     self.assertEquals(1, ash.get_job_count())
     self.assertEquals(1, ash.get_job_count())
@@ -472,8 +477,9 @@ class TestAlerts(TestCase):
   def test_immediate_alert(self):
   def test_immediate_alert(self):
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_file_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
     test_stack_path = os.path.join('ambari_agent', 'dummy_files')
+    test_host_scripts_path = os.path.join('ambari_agent', 'dummy_files')
 
 
-    ash = AlertSchedulerHandler(test_file_path, test_stack_path)
+    ash = AlertSchedulerHandler(test_file_path, test_stack_path, test_host_scripts_path)
     ash.start()
     ash.start()
 
 
     self.assertEquals(1, ash.get_job_count())
     self.assertEquals(1, ash.get_job_count())

+ 12 - 2
ambari-agent/src/test/python/ambari_agent/TestCustomServiceOrchestrator.py

@@ -156,6 +156,7 @@ class TestCustomServiceOrchestrator(TestCase):
 
 
   @patch.object(CustomServiceOrchestrator, "resolve_script_path")
   @patch.object(CustomServiceOrchestrator, "resolve_script_path")
   @patch.object(CustomServiceOrchestrator, "resolve_hook_script_path")
   @patch.object(CustomServiceOrchestrator, "resolve_hook_script_path")
+  @patch.object(FileCache, "get_host_scripts_base_dir")
   @patch.object(FileCache, "get_service_base_dir")
   @patch.object(FileCache, "get_service_base_dir")
   @patch.object(FileCache, "get_hook_base_dir")
   @patch.object(FileCache, "get_hook_base_dir")
   @patch.object(CustomServiceOrchestrator, "dump_command_to_json")
   @patch.object(CustomServiceOrchestrator, "dump_command_to_json")
@@ -163,8 +164,11 @@ class TestCustomServiceOrchestrator(TestCase):
   @patch.object(FileCache, "__init__")
   @patch.object(FileCache, "__init__")
   def test_runCommand(self, FileCache_mock,
   def test_runCommand(self, FileCache_mock,
                       run_file_mock, dump_command_to_json_mock,
                       run_file_mock, dump_command_to_json_mock,
-                      get_hook_base_dir_mock, get_service_base_dir_mock,
-                      resolve_hook_script_path_mock, resolve_script_path_mock):
+                      get_hook_base_dir_mock, get_service_base_dir_mock, 
+                      get_host_scripts_base_dir_mock, 
+                      resolve_hook_script_path_mock, 
+                      resolve_script_path_mock):
+    
     FileCache_mock.return_value = None
     FileCache_mock.return_value = None
     command = {
     command = {
       'role' : 'REGION_SERVER',
       'role' : 'REGION_SERVER',
@@ -182,6 +186,8 @@ class TestCustomServiceOrchestrator(TestCase):
       'taskId' : '3',
       'taskId' : '3',
       'roleCommand': 'INSTALL'
       'roleCommand': 'INSTALL'
     }
     }
+    
+    get_host_scripts_base_dir_mock.return_value = "/host_scripts"
     get_service_base_dir_mock.return_value = "/basedir/"
     get_service_base_dir_mock.return_value = "/basedir/"
     resolve_script_path_mock.return_value = "/basedir/scriptpath"
     resolve_script_path_mock.return_value = "/basedir/scriptpath"
     resolve_hook_script_path_mock.return_value = \
     resolve_hook_script_path_mock.return_value = \
@@ -238,6 +244,7 @@ class TestCustomServiceOrchestrator(TestCase):
   @patch("shell.kill_process_with_children")
   @patch("shell.kill_process_with_children")
   @patch.object(CustomServiceOrchestrator, "resolve_script_path")
   @patch.object(CustomServiceOrchestrator, "resolve_script_path")
   @patch.object(CustomServiceOrchestrator, "resolve_hook_script_path")
   @patch.object(CustomServiceOrchestrator, "resolve_hook_script_path")
+  @patch.object(FileCache, "get_host_scripts_base_dir")
   @patch.object(FileCache, "get_service_base_dir")
   @patch.object(FileCache, "get_service_base_dir")
   @patch.object(FileCache, "get_hook_base_dir")
   @patch.object(FileCache, "get_hook_base_dir")
   @patch.object(CustomServiceOrchestrator, "dump_command_to_json")
   @patch.object(CustomServiceOrchestrator, "dump_command_to_json")
@@ -246,6 +253,7 @@ class TestCustomServiceOrchestrator(TestCase):
   def test_cancel_command(self, FileCache_mock,
   def test_cancel_command(self, FileCache_mock,
                       run_file_mock, dump_command_to_json_mock,
                       run_file_mock, dump_command_to_json_mock,
                       get_hook_base_dir_mock, get_service_base_dir_mock,
                       get_hook_base_dir_mock, get_service_base_dir_mock,
+                      get_host_scripts_base_dir_mock,
                       resolve_hook_script_path_mock, resolve_script_path_mock,
                       resolve_hook_script_path_mock, resolve_script_path_mock,
                       kill_process_with_children_mock):
                       kill_process_with_children_mock):
     FileCache_mock.return_value = None
     FileCache_mock.return_value = None
@@ -265,6 +273,8 @@ class TestCustomServiceOrchestrator(TestCase):
       'taskId' : '3',
       'taskId' : '3',
       'roleCommand': 'INSTALL'
       'roleCommand': 'INSTALL'
     }
     }
+    
+    get_host_scripts_base_dir_mock.return_value = "/host_scripts"
     get_service_base_dir_mock.return_value = "/basedir/"
     get_service_base_dir_mock.return_value = "/basedir/"
     resolve_script_path_mock.return_value = "/basedir/scriptpath"
     resolve_script_path_mock.return_value = "/basedir/scriptpath"
     resolve_hook_script_path_mock.return_value = \
     resolve_hook_script_path_mock.return_value = \

+ 312 - 290
ambari-server/pom.xml

@@ -555,6 +555,17 @@
                 </source>
                 </source>
               </sources>
               </sources>
             </mapping>
             </mapping>
+            <mapping>
+              <directory>/var/lib/ambari-server/resources/host_scripts</directory>
+              <filemode>755</filemode>
+              <username>root</username>
+              <groupname>root</groupname>
+              <sources>
+                <source>
+                  <location>src/main/resources/host_scripts</location>
+                </source>
+              </sources>
+            </mapping>
           </mappings>
           </mappings>
         </configuration>
         </configuration>
       </plugin>
       </plugin>
@@ -647,296 +658,307 @@
                 <filemode>755</filemode>
                 <filemode>755</filemode>
               </mapper>
               </mapper>
             </data>
             </data>
-          <data>
-            <src>conf/unix/ambari.properties</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/etc/ambari-server/conf</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>conf/unix/log4j.properties</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/etc/ambari-server/conf</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>conf/unix/krb5JAASLogin.conf</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/etc/ambari-server/conf</prefix>
-            </mapper>
-          </data>
-           <!-- /q001 -->
-          <data>
-            <src>conf/unix/ambari-env.sh</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>700</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>conf/unix/install-helper.sh</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>700</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/slider_resources/README.txt</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/apps/</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>conf/unix/ca.config</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/keys</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/db</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/keys/db</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>700</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>target/classes/Ambari-DDL-Postgres-CREATE.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/Ambari-DDL-Postgres-DROP.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>target/classes/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/Ambari-DDL-Postgres-EMBEDDED-DROP.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>target/classes/Ambari-DDL-Oracle-CREATE.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>target/classes/Ambari-DDL-MySQL-CREATE.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/Ambari-DDL-Oracle-DROP.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/Ambari-DDL-MySQL-DROP.sql</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>${project.build.directory}/DBConnectionVerification.jar</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/role_command_order.json</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/scripts</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/scripts</prefix>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>${ambari-admin-dir}/target</src>
-            <type>directory</type>
-            <includes>*.jar</includes>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/views</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/python/UpgradeHelper_HDP2.py</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/scripts</prefix>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/upgrade/ddl</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/upgrade/ddl</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/upgrade/dml</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/upgrade/dml</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>target/classes/stacks/${stack.distribution}</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/stacks/${stack.distribution}</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>target/classes/stacks/stack_advisor.py</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/stacks</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/python/ambari_server</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/python/bootstrap.py</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/python/setupAgent.py</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/python/os_check_type.py</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
-          <data>
-            <src>${basedir}/target/version</src>
-            <type>file</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/custom_action_definitions</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/custom_action_definitions</prefix>
-            </mapper>
-          </data>
-          <data>
-            <src>src/main/resources/custom_actions</src>
-            <type>directory</type>
-            <mapper>
-              <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/custom_actions</prefix>
-              <user>root</user>
-              <group>root</group>
-              <filemode>755</filemode>
-            </mapper>
-          </data>
+            <data>
+              <src>conf/unix/ambari.properties</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/etc/ambari-server/conf</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>conf/unix/log4j.properties</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/etc/ambari-server/conf</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>conf/unix/krb5JAASLogin.conf</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/etc/ambari-server/conf</prefix>
+              </mapper>
+            </data>
+             <!-- /q001 -->
+            <data>
+              <src>conf/unix/ambari-env.sh</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>700</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>conf/unix/install-helper.sh</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>700</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/slider_resources/README.txt</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/apps/</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>conf/unix/ca.config</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/keys</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/db</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/keys/db</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>700</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>target/classes/Ambari-DDL-Postgres-CREATE.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/Ambari-DDL-Postgres-DROP.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>target/classes/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/Ambari-DDL-Postgres-EMBEDDED-DROP.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>target/classes/Ambari-DDL-Oracle-CREATE.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>target/classes/Ambari-DDL-MySQL-CREATE.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/Ambari-DDL-Oracle-DROP.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/Ambari-DDL-MySQL-DROP.sql</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>${project.build.directory}/DBConnectionVerification.jar</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/role_command_order.json</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/scripts</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/scripts</prefix>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>${ambari-admin-dir}/target</src>
+              <type>directory</type>
+              <includes>*.jar</includes>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/views</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/python/UpgradeHelper_HDP2.py</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/scripts</prefix>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/upgrade/ddl</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/upgrade/ddl</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/upgrade/dml</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/upgrade/dml</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>target/classes/stacks/${stack.distribution}</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/stacks/${stack.distribution}</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>target/classes/stacks/stack_advisor.py</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/stacks</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/python/ambari_server</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/python/bootstrap.py</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/python/setupAgent.py</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/python/os_check_type.py</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/usr/lib/python2.6/site-packages/ambari_server</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>${basedir}/target/version</src>
+              <type>file</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/custom_action_definitions</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/custom_action_definitions</prefix>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/custom_actions</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/custom_actions</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>
+            <data>
+              <src>src/main/resources/host_scripts</src>
+              <type>directory</type>
+              <mapper>
+                <type>perm</type>
+                <prefix>/var/lib/ambari-server/resources/host_scripts</prefix>
+                <user>root</user>
+                <group>root</group>
+                <filemode>755</filemode>
+              </mapper>
+            </data>          
             <data>
             <data>
               <src>
               <src>
                 ${project.basedir}/../ambari-common/src/main/python/ambari_commons
                 ${project.basedir}/../ambari-common/src/main/python/ambari_commons

+ 25 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java

@@ -48,6 +48,7 @@ import org.apache.ambari.server.customactions.ActionDefinition;
 import org.apache.ambari.server.customactions.ActionDefinitionManager;
 import org.apache.ambari.server.customactions.ActionDefinitionManager;
 import org.apache.ambari.server.events.AlertDefinitionRegistrationEvent;
 import org.apache.ambari.server.events.AlertDefinitionRegistrationEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.metadata.AgentAlertDefinitions;
 import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.dao.MetainfoDAO;
 import org.apache.ambari.server.orm.dao.MetainfoDAO;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
@@ -68,10 +69,10 @@ import org.apache.ambari.server.state.alert.AlertDefinition;
 import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
 import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
 import org.apache.ambari.server.state.stack.LatestRepoCallable;
 import org.apache.ambari.server.state.stack.LatestRepoCallable;
 import org.apache.ambari.server.state.stack.MetricDefinition;
 import org.apache.ambari.server.state.stack.MetricDefinition;
+import org.apache.ambari.server.state.stack.OsFamily;
 import org.apache.ambari.server.state.stack.RepositoryXml;
 import org.apache.ambari.server.state.stack.RepositoryXml;
 import org.apache.ambari.server.state.stack.RepositoryXml.Os;
 import org.apache.ambari.server.state.stack.RepositoryXml.Os;
 import org.apache.ambari.server.state.stack.RepositoryXml.Repo;
 import org.apache.ambari.server.state.stack.RepositoryXml.Repo;
-import org.apache.ambari.server.state.stack.OsFamily;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 
 
@@ -156,6 +157,12 @@ public class AmbariMetaInfo {
    */
    */
   private AlertDefinitionFactory alertDefinitionFactory;
   private AlertDefinitionFactory alertDefinitionFactory;
 
 
+  /**
+   * All of the {@link AlertDefinition}s that are scoped for the agents.
+   */
+  @Inject
+  private AgentAlertDefinitions agentAlertDefinitions;
+
   /**
   /**
    * Publishes the following events:
    * Publishes the following events:
    * <ul>
    * <ul>
@@ -211,6 +218,7 @@ public class AmbariMetaInfo {
     alertDefinitionFactory = injector.getInstance(AlertDefinitionFactory.class);
     alertDefinitionFactory = injector.getInstance(AlertDefinitionFactory.class);
     alertDefinitionDao = injector.getInstance(AlertDefinitionDAO.class);
     alertDefinitionDao = injector.getInstance(AlertDefinitionDAO.class);
     eventPublisher = injector.getInstance(AmbariEventPublisher.class);
     eventPublisher = injector.getInstance(AmbariEventPublisher.class);
+    agentAlertDefinitions = injector.getInstance(AgentAlertDefinitions.class);
   }
   }
 
 
   /**
   /**
@@ -1220,6 +1228,10 @@ public class AmbariMetaInfo {
    * database and merges any new or updated definitions. This method will first
    * database and merges any new or updated definitions. This method will first
    * determine the services that are installed on each cluster to prevent alert
    * determine the services that are installed on each cluster to prevent alert
    * definitions from undeployed services from being shown.
    * definitions from undeployed services from being shown.
+   * <p/>
+   * This method will also detect "agent" alert definitions, which are
+   * definitions that should be run on agent hosts but are not associated with a
+   * service.
    *
    *
    * @param clusters
    * @param clusters
    * @throws AmbariException
    * @throws AmbariException
@@ -1307,6 +1319,18 @@ public class AmbariMetaInfo {
         }
         }
       }
       }
 
 
+      // host-only alert definitions
+      List<AlertDefinition> agentDefinitions = agentAlertDefinitions.getDefinitions();
+      for (AlertDefinition agentDefinition : agentDefinitions) {
+        AlertDefinitionEntity entity = mappedEntities.get(agentDefinition.getName());
+
+        // no entity means this is new; create a new entity
+        if (null == entity) {
+          entity = alertDefinitionFactory.coerce(clusterId, agentDefinition);
+          persist.add(entity);
+        }
+      }
+
       // persist any new or updated definition
       // persist any new or updated definition
       for (AlertDefinitionEntity entity : persist) {
       for (AlertDefinitionEntity entity : persist) {
         if (LOG.isDebugEnabled()) {
         if (LOG.isDebugEnabled()) {

+ 93 - 0
ambari-server/src/main/java/org/apache/ambari/server/metadata/AgentAlertDefinitions.java

@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.metadata;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ambari.server.controller.RootServiceResponseFactory.Components;
+import org.apache.ambari.server.state.alert.AlertDefinition;
+import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * The {@link AgentAlertDefinitions} class is used to represent the alerts
+ * defined in {@code alerts.json} which are for {@link Components#AMBARI_AGENT}.
+ * These alerts are bound to the host and are not part of a cluster or hadoop
+ * service.
+ */
+@Singleton
+public class AgentAlertDefinitions {
+
+  /**
+   * Logger.
+   */
+  private final static Logger LOG = LoggerFactory.getLogger(AgentAlertDefinitions.class);
+
+  /**
+   * The agent host definitions.
+   */
+  private List<AlertDefinition> m_definitions = null;
+
+  /**
+   * The factory that will load the definitions from the alerts.json file.
+   */
+  @Inject
+  private AlertDefinitionFactory m_factory;
+
+  /**
+   * Gets all of the {@link AlertDefinition}s that exist on the path for all
+   * agent hosts.
+   *
+   * @return the alerts with {@link Components#AMBARI_AGENT} as the component
+   *         and {@code AMBARI} as the service.
+   */
+  public List<AlertDefinition> getDefinitions() {
+    if (null == m_definitions) {
+      m_definitions = new ArrayList<AlertDefinition>();
+
+      InputStream inputStream = ClassLoader.getSystemResourceAsStream("alerts.json");
+      InputStreamReader reader = new InputStreamReader(inputStream);
+
+      try {
+        Set<AlertDefinition> definitions = m_factory.getAlertDefinitions(
+            reader, "AMBARI");
+
+        String agentComponent = Components.AMBARI_AGENT.name();
+
+        for (AlertDefinition definition : definitions) {
+          if (agentComponent.equals(definition.getComponentName())) {
+            m_definitions.add(definition);
+          }
+        }
+
+      } catch (Exception exception) {
+        LOG.error("Unable to load the Ambari alerts JSON file", exception);
+      }
+    }
+
+    return m_definitions;
+  }
+}

+ 39 - 5
ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionFactory.java

@@ -19,6 +19,8 @@ package org.apache.ambari.server.state.alert;
 
 
 import java.io.File;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
 import java.lang.reflect.Type;
 import java.lang.reflect.Type;
 import java.util.HashSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.List;
@@ -29,6 +31,7 @@ import java.util.UUID;
 
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 
 
@@ -91,18 +94,49 @@ public class AlertDefinitionFactory {
    */
    */
   public Set<AlertDefinition> getAlertDefinitions(File alertDefinitionFile,
   public Set<AlertDefinition> getAlertDefinitions(File alertDefinitionFile,
       String serviceName) throws AmbariException {
       String serviceName) throws AmbariException {
+    try {
+      FileReader fileReader = new FileReader(alertDefinitionFile);
+      return getAlertDefinitions(fileReader, serviceName);
+    } catch (IOException ioe) {
+      String message = "Could not read the alert definition file";
+      LOG.error(message, ioe);
+      throw new AmbariException(message, ioe);
+    }
+  }
+
+  /**
+   * Gets a list of all of the alert definitions defined in the resource pointed
+   * to by the specified reader for the given service. There should have a
+   * mapping between the service and the alerts defined for that service. This
+   * is necessary since some services are combined in a single
+   * {@code metainfo.xml} and only have a single directory on the stack.
+   * <p/>
+   * The supplied reader is closed when this method completes.
+   *
+   * @param reader
+   *          the reader to read from (not {@code null}). This will be closed
+   *          after reading is done.
+   * @param serviceName
+   *          the name of the service to extract definitions for (not
+   *          {@code null}).
+   * @return the definitions for the specified service, or an empty set.
+   * @throws AmbariException
+   *           if there was a problem reading or parsing the JSON.
+   */
+  public Set<AlertDefinition> getAlertDefinitions(Reader reader,
+      String serviceName) throws AmbariException {
 
 
     // { MAPR : {definitions}, YARN : {definitions} }
     // { MAPR : {definitions}, YARN : {definitions} }
     Map<String, Map<String, List<AlertDefinition>>> serviceDefinitionMap = null;
     Map<String, Map<String, List<AlertDefinition>>> serviceDefinitionMap = null;
 
 
     try {
     try {
       Type type = new TypeToken<Map<String, Map<String, List<AlertDefinition>>>>() {}.getType();
       Type type = new TypeToken<Map<String, Map<String, List<AlertDefinition>>>>() {}.getType();
-
-      FileReader fileReader = new FileReader(alertDefinitionFile);
-      serviceDefinitionMap = m_gson.fromJson(fileReader, type);
+      serviceDefinitionMap = m_gson.fromJson(reader, type);
     } catch (Exception e) {
     } catch (Exception e) {
-      LOG.error("Could not read the alert definition file", e);
-      throw new AmbariException("Could not read alert definition file", e);
+      LOG.error("Could not read the alert definitions", e);
+      throw new AmbariException("Could not read alert definitions", e);
+    } finally {
+      IOUtils.closeQuietly(reader);
     }
     }
 
 
     Set<AlertDefinition> definitions = new HashSet<AlertDefinition>();
     Set<AlertDefinition> definitions = new HashSet<AlertDefinition>();

+ 1 - 0
ambari-server/src/main/python/ambari-server.py

@@ -555,6 +555,7 @@ NR_ADJUST_OWNERSHIP_LIST = [
   ("/var/lib/ambari-server/keys/.ssh", "700", "{0}", False),
   ("/var/lib/ambari-server/keys/.ssh", "700", "{0}", False),
   ("/var/lib/ambari-server/resources/stacks/", "755", "{0}", True),
   ("/var/lib/ambari-server/resources/stacks/", "755", "{0}", True),
   ("/var/lib/ambari-server/resources/custom_actions/", "755", "{0}", True),
   ("/var/lib/ambari-server/resources/custom_actions/", "755", "{0}", True),
+  ("/var/lib/ambari-server/resources/host_scripts/", "755", "{0}", True),
   ("/etc/ambari-server/conf", "644", "{0}", True),
   ("/etc/ambari-server/conf", "644", "{0}", True),
   ("/etc/ambari-server/conf", "755", "{0}", False),
   ("/etc/ambari-server/conf", "755", "{0}", False),
   ("/etc/ambari-server/conf/password.dat", "640", "{0}", False),
   ("/etc/ambari-server/conf/password.dat", "640", "{0}", False),

+ 13 - 5
ambari-server/src/main/python/ambari_server/resourceFilesKeeper.py

@@ -38,6 +38,7 @@ class ResourceFilesKeeper():
   PACKAGE_DIR="package"
   PACKAGE_DIR="package"
   STACKS_DIR="stacks"
   STACKS_DIR="stacks"
   CUSTOM_ACTIONS_DIR="custom_actions"
   CUSTOM_ACTIONS_DIR="custom_actions"
+  HOST_SCRIPTS_DIR="host_scripts"
 
 
   # For these directories archives are created
   # For these directories archives are created
   ARCHIVABLE_DIRS = [HOOKS_DIR, PACKAGE_DIR]
   ARCHIVABLE_DIRS = [HOOKS_DIR, PACKAGE_DIR]
@@ -87,12 +88,19 @@ class ResourceFilesKeeper():
             self.update_directory_archive(full_path)
             self.update_directory_archive(full_path)
 
 
 
 
-    custom_actions_root = os.path.join(self.resources_dir,
-                                       self.CUSTOM_ACTIONS_DIR)
-    self.dbg_out("Updating archive for custom_actions dir at {0}...".format(
-                                       custom_actions_root))
+    # custom actions
+    custom_actions_root = os.path.join(self.resources_dir,self.CUSTOM_ACTIONS_DIR)        
+    self.dbg_out("Updating archive for {0} dir at {1}...".format(self.CUSTOM_ACTIONS_DIR, 
+        custom_actions_root))
+        
+    # agent host scripts
+    host_scripts_root = os.path.join(self.resources_dir,self.HOST_SCRIPTS_DIR)    
+    self.dbg_out("Updating archive for {0} dir at {1}...".format(self.HOST_SCRIPTS_DIR, 
+        host_scripts_root))
+    
+    # update the directories so that the .hash is generated
     self.update_directory_archive(custom_actions_root)
     self.update_directory_archive(custom_actions_root)
-
+    self.update_directory_archive(host_scripts_root)
 
 
 
 
   def list_stacks(self, stacks_root):
   def list_stacks(self, stacks_root):

+ 19 - 0
ambari-server/src/main/resources/alerts.json

@@ -0,0 +1,19 @@
+{
+  "AMBARI": {
+    "service": [
+    ],
+    "AMBARI_AGENT" : [
+      {
+        "name": "ambari_agent_disk_usage",
+        "label": "Ambari Agent Disk Usage",
+        "interval": 1,
+        "scope": "HOST",
+        "enabled": true,
+        "source": {
+          "type": "SCRIPT",
+          "path": "alert_disk_space.py"
+        }
+      }
+    ]
+  }
+}

+ 95 - 0
ambari-server/src/main/resources/host_scripts/alert_disk_space.py

@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import collections
+import os
+import platform
+
+def get_tokens():
+  return None
+  
+
+def execute(parameters=None):
+  """
+  returns a tuple containing the result code and a pre-formatted result label
+  """
+  disk_usage = None
+  try:
+    disk_usage = _get_disk_usage()
+  except NotImplementedError, platform_error:
+    return (('CRITICAL', [str(platform_error)]))
+    pass
+  
+  disk_usage = _get_disk_usage()
+  if disk_usage is None or disk_usage.total == 0:
+    return (('CRITICAL', ['Unable to determine the disk usage']))
+  
+  result_code = 'OK'
+  percent = disk_usage.used / float(disk_usage.total) * 100
+  if percent > 50:
+    result_code = 'WARNING'
+  elif percent > 80:
+    result_code = 'CRTICAL'
+    
+  label = 'Capacity Used: [{0:.2f}%, {1}], Capacity Total: [{2}]'.format( 
+      percent, _get_formatted_size(disk_usage.used), 
+      _get_formatted_size(disk_usage.total) )
+  
+  return ((result_code, [label]))
+
+
+def _get_disk_usage(path='/'):
+  """
+  returns a named tuple that contains the total, used, and free disk space
+  in bytes
+  """
+  used = 0
+  total = 0
+  free = 0
+  
+  if 'statvfs' in dir(os):
+    disk_stats = os.statvfs(path)
+    free = disk_stats.f_bavail * disk_stats.f_frsize
+    total = disk_stats.f_blocks * disk_stats.f_frsize
+    used = (disk_stats.f_blocks - disk_stats.f_bfree) * disk_stats.f_frsize
+  else:
+    raise NotImplementedError("{0} is not a supported platform for this alert".format(platform.platform()))
+  
+  DiskInfo = collections.namedtuple('DiskInfo', 'total used free')
+  return DiskInfo(total=total, used=used, free=free)
+
+
+def _get_formatted_size(bytes):
+  """
+  formats the supplied bytes 
+  """  
+  if bytes < 1000:
+    return '%i' % bytes + ' B'
+  elif 1000 <= bytes < 1000000:
+    return '%.1f' % (bytes/1000.0) + ' KB'
+  elif 1000000 <= bytes < 1000000000:
+    return '%.1f' % (bytes / 1000000.0) + ' MB'
+  elif 1000000000 <= bytes < 1000000000000:
+    return '%.1f' % (bytes/1000000000.0) + ' GB'
+  else:
+    return '%.1f' % (bytes/1000000000000.0) + ' TB'
+
+if __name__ == '__main__':
+    print _get_disk_usage(os.getcwd())

+ 15 - 2
ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java

@@ -1658,7 +1658,20 @@ public class AmbariMetaInfoTest {
 
 
     AlertDefinitionDAO dao = injector.getInstance(AlertDefinitionDAO.class);
     AlertDefinitionDAO dao = injector.getInstance(AlertDefinitionDAO.class);
     List<AlertDefinitionEntity> definitions = dao.findAll();
     List<AlertDefinitionEntity> definitions = dao.findAll();
-    assertEquals(5, definitions.size());
+    assertEquals(6, definitions.size());
+
+    // figure out how many of these alerts were merged into from the
+    // non-stack alerts.json
+    int hostAlertCount = 0;
+    for (AlertDefinitionEntity definition : definitions) {
+      if (definition.getServiceName().equals("AMBARI")
+          && definition.getComponentName().equals("AMBARI_AGENT")) {
+        hostAlertCount++;
+      }
+    }
+
+    assertEquals(1, hostAlertCount);
+    assertEquals(5, definitions.size() - hostAlertCount);
 
 
     for (AlertDefinitionEntity definition : definitions) {
     for (AlertDefinitionEntity definition : definitions) {
       definition.setScheduleInterval(28);
       definition.setScheduleInterval(28);
@@ -1668,7 +1681,7 @@ public class AmbariMetaInfoTest {
     metaInfo.reconcileAlertDefinitions(clusters);
     metaInfo.reconcileAlertDefinitions(clusters);
 
 
     definitions = dao.findAll();
     definitions = dao.findAll();
-    assertEquals(5, definitions.size());
+    assertEquals(6, definitions.size());
 
 
     for (AlertDefinitionEntity definition : definitions) {
     for (AlertDefinitionEntity definition : definitions) {
       assertEquals(28, definition.getScheduleInterval().intValue());
       assertEquals(28, definition.getScheduleInterval().intValue());

+ 61 - 0
ambari-server/src/test/java/org/apache/ambari/server/metadata/AgentAlertDefinitionsTest.java

@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.metadata;
+
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.apache.ambari.server.controller.RootServiceResponseFactory.Components;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.apache.ambari.server.state.alert.AlertDefinition;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * Tets {@link AgentAlertDefinitions}.
+ */
+public class AgentAlertDefinitionsTest {
+
+  private Injector m_injector;
+
+  @Before
+  public void before() {
+    m_injector = Guice.createInjector(new InMemoryDefaultTestModule());
+  }
+
+  /**
+   * Tests loading the agent alerts.
+   */
+  @Test
+  public void testLoadingAlerts() {
+    AgentAlertDefinitions agentAlerts = m_injector.getInstance(AgentAlertDefinitions.class);
+    List<AlertDefinition> definitions = agentAlerts.getDefinitions();
+    Assert.assertEquals(1, definitions.size());
+
+    for( AlertDefinition definition : definitions){
+      Assert.assertEquals(Components.AMBARI_AGENT.name(),
+          definition.getComponentName());
+
+      Assert.assertEquals("AMBARI", definition.getServiceName());
+    }
+  }
+}

+ 2 - 1
ambari-server/src/test/python/TestResourceFilesKeeper.py

@@ -85,7 +85,8 @@ class TestResourceFilesKeeper(TestCase):
             "dummy_stack/HIVE/package'),\n "
             "dummy_stack/HIVE/package'),\n "
             "call('../resources/TestAmbaryServer.samples/"
             "call('../resources/TestAmbaryServer.samples/"
             "dummy_stack/HIVE/package'),\n "
             "dummy_stack/HIVE/package'),\n "
-            "call('../resources/stacks/custom_actions')]")
+            "call('../resources/stacks/custom_actions'),\n "
+            "call('../resources/stacks/host_scripts')]")