Repository: ambari Updated Branches: refs/heads/trunk ba557d103 -> 12ed8b553
AMBARI-9615 - Agents Do Not Update Cached Alert JSON On New Configurations (jonathanhurley) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/12ed8b55 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/12ed8b55 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/12ed8b55 Branch: refs/heads/trunk Commit: 12ed8b5538faf0fd4093ddd68ceee26f39d486ec Parents: ba557d1 Author: Jonathan Hurley <jhur...@hortonworks.com> Authored: Fri Feb 13 01:32:09 2015 -0500 Committer: Jonathan Hurley <jhur...@hortonworks.com> Committed: Fri Feb 13 10:37:40 2015 -0500 ---------------------------------------------------------------------- .../ambari_agent/AlertSchedulerHandler.py | 97 +++++++++++++------- .../src/main/python/ambari_agent/Controller.py | 24 ++--- .../python/ambari_agent/alerts/base_alert.py | 12 +-- .../python/ambari_agent/alerts/metric_alert.py | 7 +- .../python/ambari_agent/alerts/port_alert.py | 25 +++-- .../python/ambari_agent/alerts/script_alert.py | 13 ++- .../python/ambari_agent/alerts/web_alert.py | 6 +- .../src/test/python/ambari_agent/TestAlerts.py | 40 ++++++++ .../AMBARI_METRICS/0.1.0/alerts.json | 2 +- 9 files changed, 151 insertions(+), 75 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py b/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py index 87295d2..ba25936 100644 --- a/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py +++ b/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py @@ -63,8 +63,7 @@ class AlertSchedulerHandler(): try: os.makedirs(cachedir) except: - logger.critical("Could not create the cache directory {0}".format(cachedir)) - pass + logger.critical("[AlertScheduler] Could not create the cache directory {0}".format(cachedir)) self._collector = AlertCollector() self.__scheduler = Scheduler(AlertSchedulerHandler.APS_CONFIG) @@ -82,33 +81,53 @@ class AlertSchedulerHandler(): if reschedule_jobs: self.reschedule() - def __update_definition_configs(self): - """ updates the persisted configs and restarts the scheduler """ - definitions = [] + def __update_definition_configs(self, newConfigurations, reschedule_jobs=False): + """ + Updates the definitions and configurations stored on disk. Optionally + can reschedule jobs. Job rescheduling is only necessary when data that + an existing job uses has changed. In many cases, configuration values + have changed, yet no jobs need rescheduling. + + :param reschedule_jobs: + :return: + """ + + if reschedule_jobs: + logger.info("[AlertScheduler] Updating {0} with the latest configuration values and rescheduling alert jobs".format(self.FILENAME)) + else: + logger.info("[AlertScheduler] Updating {0} with the latest configuration values".format(self.FILENAME)) - all_commands = None # Load definitions from json try: with open(os.path.join(self.cachedir, self.FILENAME), 'r') as fp: all_commands = json.load(fp) except IOError, ValueError: - if (logger.isEnabledFor(logging.DEBUG)): - logger.exception("Failed to load definitions. {0}".format(traceback.format_exc())) + if logger.isEnabledFor(logging.DEBUG): + logger.exception("[AlertScheduler] Failed to load definitions. {0}".format(traceback.format_exc())) return # Update definitions with current config for command_json in all_commands: - clusterName = '' if not 'clusterName' in command_json else command_json['clusterName'] - hostName = '' if not 'hostName' in command_json else command_json['hostName'] + if 'clusterName' in command_json: + clusterName = command_json['clusterName'] + else: + clusterName = '' - self.__update_config_values(command_json['configurations'],self.__config_maps[clusterName]) + self.__update_config_values(command_json['configurations'], + self.__config_maps[clusterName]) + + # update the configurations before writing the file back out + command_json['configurations'] = newConfigurations # Save definitions to file with open(os.path.join(self.cachedir, self.FILENAME), 'w') as f: json.dump(all_commands, f, indent=2) - self.reschedule_all() + # only reschdule jobs if instructed to + if reschedule_jobs: + self.reschedule_all() + def __make_function(self, alert_def): return lambda: alert_def.collect() @@ -130,7 +149,7 @@ class AlertSchedulerHandler(): for _callable in alert_callables: self.schedule_definition(_callable) - logger.debug("Starting scheduler {0}; currently running: {1}".format( + logger.debug("[AlertScheduler] Starting {0}; currently running: {1}".format( str(self.__scheduler), str(self.__scheduler.running))) self.__scheduler.start() @@ -166,7 +185,7 @@ class AlertSchedulerHandler(): # jobs without valid UUIDs should be unscheduled if uuid_valid == False: jobs_removed += 1 - logger.info("Unscheduling {0}".format(scheduled_job.name)) + logger.info("[AlertScheduler] Unscheduling {0}".format(scheduled_job.name)) self._collector.remove_by_uuid(scheduled_job.name) self.__scheduler.unschedule_job(scheduled_job) @@ -184,7 +203,7 @@ class AlertSchedulerHandler(): jobs_scheduled += 1 self.schedule_definition(definition) - logger.info("Alert Reschedule Summary: {0} rescheduled, {1} unscheduled".format( + logger.info("[AlertScheduler] Reschedule Summary: {0} rescheduled, {1} unscheduled".format( str(jobs_scheduled), str(jobs_removed))) def reschedule_all(self): @@ -200,9 +219,8 @@ class AlertSchedulerHandler(): # unschedule all scheduled jobs for scheduled_job in scheduled_jobs: - jobs_removed += 1 - logger.info("Unscheduling {0}".format(scheduled_job.name)) + logger.info("[AlertScheduler] Unscheduling {0}".format(scheduled_job.name)) self._collector.remove_by_uuid(scheduled_job.name) self.__scheduler.unschedule_job(scheduled_job) @@ -211,7 +229,7 @@ class AlertSchedulerHandler(): jobs_scheduled += 1 self.schedule_definition(definition) - logger.info("Alert Reschedule Summary: {0} rescheduled, {1} unscheduled".format( + logger.info("[AlertScheduler] Reschedule Summary: {0} rescheduled, {1} unscheduled".format( str(jobs_scheduled), str(jobs_removed))) @@ -230,7 +248,7 @@ class AlertSchedulerHandler(): with open(alerts_definitions_path) as fp: all_commands = json.load(fp) except: - logger.warning('Alert definitions file was not found under "{0}". No alerts will be scheduled.'.format(alerts_definitions_path)) + logger.warning('[AlertScheduler] {0} not found. No alerts will be scheduled.'.format(alerts_definitions_path)) return definitions for command_json in all_commands: @@ -270,7 +288,7 @@ class AlertSchedulerHandler(): source_type = source.get('type', '') if logger.isEnabledFor(logging.DEBUG): - logger.debug("Creating job type {0} with {1}".format(source_type, str(json_definition))) + logger.debug("[AlertScheduler] Creating job type {0} with {1}".format(source_type, str(json_definition))) alert = None @@ -328,22 +346,31 @@ class AlertSchedulerHandler(): def update_configurations(self, commands): """ - when an execution command comes in, update any necessary values. - status commands do not contain useful configurations + Checks the execution command's configurations against those stored in + memory. If there are differences, this will reschedule alerts. The + on-disk JSON file is always updated so that it reflects the correct state + of configurations """ for command in commands: clusterName = command['clusterName'] if not clusterName in self.__config_maps: continue - - if 'configurations' in command: - configmap = command['configurations'] - keylist = self.__config_maps[clusterName].keys() - vals = self.__find_config_values(configmap, keylist) - # if we have updated values push them to config_maps and reschedule - if vals != self.__config_maps[clusterName]: - self.__config_maps[clusterName].update(vals) - self.__update_definition_configs() + + if not 'configurations' in command: + continue + + existingConfigurationKeys = self.__config_maps[clusterName].keys() + newConfigurations = command['configurations'] + newConfigurationValues = self.__find_config_values(newConfigurations, + existingConfigurationKeys) + + # if we have updated values push them to config_maps and reschedule + rescheduleJobs = False + if newConfigurationValues != self.__config_maps[clusterName]: + rescheduleJobs = True + self.__config_maps[clusterName].update(newConfigurationValues) + + self.__update_definition_configs(newConfigurations, rescheduleJobs) def schedule_definition(self,definition): @@ -356,7 +383,7 @@ class AlertSchedulerHandler(): """ # NOOP if the definition is disabled; don't schedule it if definition.is_enabled() == False: - logger.info("The alert {0} with UUID {1} is disabled and will not be scheduled".format( + logger.info("[AlertScheduler] The alert {0} with UUID {1} is disabled and will not be scheduled".format( definition.get_name(),definition.get_uuid())) return @@ -374,7 +401,7 @@ class AlertSchedulerHandler(): if job is not None: job.name = definition.get_uuid() - logger.info("Scheduling {0} with UUID {1}".format( + logger.info("[AlertScheduler] Scheduling {0} with UUID {1}".format( definition.get_name(), definition.get_uuid())) @@ -410,13 +437,13 @@ class AlertSchedulerHandler(): if alert is None: continue - logger.info("Executing on-demand alert {0} ({1})".format(alert.get_name(), + logger.info("[AlertScheduler] Executing on-demand alert {0} ({1})".format(alert.get_name(), alert.get_uuid())) alert.set_helpers(self._collector, self.__config_maps[clusterName]) alert.collect() except: - logger.exception("Unable to execute the alert outside of the job scheduler") + logger.exception("[AlertScheduler] Unable to execute the alert outside of the job scheduler") def main(): args = list(sys.argv) http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/main/python/ambari_agent/Controller.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/Controller.py b/ambari-agent/src/main/python/ambari_agent/Controller.py index d597bca..1bf2613 100644 --- a/ambari-agent/src/main/python/ambari_agent/Controller.py +++ b/ambari-agent/src/main/python/ambari_agent/Controller.py @@ -258,33 +258,29 @@ class Controller(threading.Thread): else: self.responseId = serverId - if 'cancelCommands' in response.keys(): + response_keys = response.keys() + if 'cancelCommands' in response_keys: self.cancelCommandInQueue(response['cancelCommands']) - pass - if 'executionCommands' in response.keys(): - self.addToQueue(response['executionCommands']) - self.alert_scheduler_handler.update_configurations(response['executionCommands']) - pass + if 'executionCommands' in response_keys: + execution_commands = response['executionCommands'] + self.addToQueue(execution_commands) + self.alert_scheduler_handler.update_configurations(execution_commands) - if 'statusCommands' in response.keys(): + if 'statusCommands' in response_keys: self.addToStatusQueue(response['statusCommands']) - pass - if 'alertDefinitionCommands' in response.keys(): + if 'alertDefinitionCommands' in response_keys: self.alert_scheduler_handler.update_definitions(response['alertDefinitionCommands'], True) - pass - - if 'alertExecutionCommands' in response.keys(): + + if 'alertExecutionCommands' in response_keys: self.alert_scheduler_handler.execute_alert(response['alertExecutionCommands']) - pass if "true" == response['restartAgent']: logger.error("Received the restartAgent command") self.restartAgent() else: logger.info("No commands sent from %s", self.serverHostname) - pass if retry: logger.info("Reconnected to %s", self.heartbeatUrl) http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py index 283da9c..0b49b9b 100644 --- a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py +++ b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py @@ -97,7 +97,7 @@ class BaseAlert(object): # this is useful for cases where the alert might run on multiple hosts # but only 1 host should report the data if result_state == BaseAlert.RESULT_SKIPPED: - logger.debug('Alert {0} with UUID {1} was skipped.'.format(self.get_name(), + logger.debug('[Alert][{0}] Skipping UUID {1}.'.format(self.get_name(), self.get_uuid())) return @@ -113,7 +113,7 @@ class BaseAlert(object): res_base_text = self._get_reporting_text(result_state) except Exception as e: - message = "Unable to run alert {0}".format(str(self.alert_meta['name'])) + message = "[Alert][{0}] Unable to run the alert".format(self.get_name()) # print the exception if in DEBUG, otherwise just log the warning if logger.isEnabledFor(logging.DEBUG): @@ -126,7 +126,7 @@ class BaseAlert(object): if logger.isEnabledFor(logging.DEBUG): - logger.debug("debug alert result: {0}".format(str(res))) + logger.debug("[Alert][{0}] result = {1}".format(self.get_name(), str(res))) data = {} data['name'] = self._find_value('name') @@ -141,7 +141,7 @@ class BaseAlert(object): data['enabled'] = self._find_value('enabled') if logger.isEnabledFor(logging.DEBUG): - logger.debug("debug alert text: {0}".format(data['text'])) + logger.debug("[Alert][{0}] text = {1}".format(self.get_name(), data['text'])) self.collector.put(self.cluster, data) @@ -168,8 +168,8 @@ class BaseAlert(object): if len(keys) > 0: if logger.isEnabledFor(logging.DEBUG): - logger.debug("Found parameterized key {0} for {1}".format( - str(keys), str(self))) + logger.debug("[Alert][{0}] Found parameterized key {1} for {2}".format( + self.get_name(), str(keys), str(self))) self._lookup_keys.append(keys[0]) return keys[0] http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/main/python/ambari_agent/alerts/metric_alert.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/metric_alert.py b/ambari-agent/src/main/python/ambari_agent/alerts/metric_alert.py index ebd205d..d378254 100644 --- a/ambari-agent/src/main/python/ambari_agent/alerts/metric_alert.py +++ b/ambari-agent/src/main/python/ambari_agent/alerts/metric_alert.py @@ -55,8 +55,8 @@ class MetricAlert(BaseAlert): # use the URI lookup keys to get a final URI value to query alert_uri = self._get_uri_from_structure(self.uri_property_keys) - logger.debug("Calculated metric URI to be {0} (ssl={1})".format(alert_uri.uri, - str(alert_uri.is_ssl_enabled))) + logger.debug("[Alert][{0}] Calculated metric URI to be {1} (ssl={2})".format( + self.get_name(), alert_uri.uri, str(alert_uri.is_ssl_enabled))) host = BaseAlert.get_host_from_url(alert_uri.uri) if host is None: @@ -79,7 +79,8 @@ class MetricAlert(BaseAlert): collect_result = self.__get_result(value_list[0] if check_value is None else check_value) - logger.debug("Resolved value list is: {0}".format(str(value_list))) + logger.debug("[Alert][{0}] Resolved values = {1}".format( + self.get_name(), str(value_list))) return (collect_result, value_list) http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py b/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py index 1ae625c..bc2e554 100644 --- a/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py +++ b/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py @@ -65,16 +65,16 @@ class PortAlert(BaseAlert): # check warning threshold for sanity if self.warning_timeout >= 30: - logger.warn("The alert warning threshold of {0}s is too large, resetting to {1}s".format( - str(self.warning_timeout), str(DEFAULT_WARNING_TIMEOUT))) + logger.warn("[Alert][{0}] The warning threshold of {1}s is too large, resetting to {2}s".format( + self.get_name(), str(self.warning_timeout), str(DEFAULT_WARNING_TIMEOUT))) self.warning_timeout = DEFAULT_WARNING_TIMEOUT # check critical threshold for sanity if self.critical_timeout >= 30: - logger.warn("The alert critical threshold of {0}s is too large, resetting to {1}s".format( - str(self.critical_timeout), str(DEFAULT_CRITICAL_TIMEOUT))) + logger.warn("[Alert][{0}] The critical threshold of {1}s is too large, resetting to {2}s".format( + self.get_name(), str(self.critical_timeout), str(DEFAULT_CRITICAL_TIMEOUT))) self.critical_timeout = DEFAULT_CRITICAL_TIMEOUT @@ -84,6 +84,8 @@ class PortAlert(BaseAlert): uri_value = self._lookup_property_value(self.uri) if uri_value is None: uri_value = self.host_name + logger.debug("[Alert][{0}] Setting the URI to this host since it wasn't specified".format( + self.get_name())) # in some cases, a single property is a comma-separated list like # host1:8080,host2:8081,host3:8083 @@ -92,6 +94,11 @@ class PortAlert(BaseAlert): for item in uri_value_array: if self.host_name in item: uri_value = item + if logger.isEnabledFor(logging.DEBUG): + logger.debug("[Alert][{0}] Extracted {1} as the host name while parsing the CSV URI {2}".format( + self.get_name(), uri_value, str(uri_value_array))) + break + host = BaseAlert.get_host_from_url(uri_value) if host is None: @@ -108,19 +115,21 @@ class PortAlert(BaseAlert): if logger.isEnabledFor(logging.DEBUG): - logger.debug("checking {0} listening on port {1}".format(host, str(port))) + logger.debug("[Alert][{0}] Checking {1} on port {2}".format( + self.get_name(), host, str(port))) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(self.critical_timeout) - t = time.time() if OSCheck.is_windows_family(): # on windows 0.0.0.0 is invalid address to connect but on linux it resolved to 127.0.0.1 host = resolve_address(host) + + start_time = time.time() s.connect((host, port)) - milliseconds = time.time() - t - seconds = milliseconds/1000.0 + milliseconds = time.time() - start_time + seconds = milliseconds / 1000.0 # not sure why this happens sometimes, but we don't always get a # socket exception if the connect() is > than the critical threshold http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/main/python/ambari_agent/alerts/script_alert.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/script_alert.py b/ambari-agent/src/main/python/ambari_agent/alerts/script_alert.py index 35cd42e..8f24f62 100644 --- a/ambari-agent/src/main/python/ambari_agent/alerts/script_alert.py +++ b/ambari-agent/src/main/python/ambari_agent/alerts/script_alert.py @@ -72,8 +72,8 @@ class ScriptAlert(BaseAlert): # append the key to the list of keys for this alert self._find_lookup_property(token) except: - logger.exception("Unable to parameterize tokens for script {0}".format(self.path)) - pass + logger.exception("[Alert][{0}] Unable to parameterize tokens for script {1}".format( + self.get_name(), self.path)) def _collect(self): @@ -91,7 +91,7 @@ class ScriptAlert(BaseAlert): matchObj = re.match( r'((.*)services\/(.*)\/package\/)', self.path_to_script) if matchObj: basedir = matchObj.group(1) - with Environment(basedir, tmp_dir=self.config.get.get('agent', 'tmp_dir')) as env: + with Environment(basedir, tmp_dir=self.config.get('agent', 'tmp_dir')) as env: return cmd_module.execute(parameters, self.host_name) else: return cmd_module.execute(parameters, self.host_name) @@ -125,11 +125,14 @@ class ScriptAlert(BaseAlert): self.stacks_dir, self.host_scripts_dir)) if logger.isEnabledFor(logging.DEBUG): - logger.debug("Executing script check {0}".format(self.path_to_script)) + logger.debug("[Alert][{0}] Executing script check {1}".format( + self.get_name(), self.path_to_script)) if (not self.path_to_script.endswith('.py')): - logger.error("Unable to execute script {0}".format(self.path_to_script)) + logger.error("[Alert][{0}] Unable to execute script {1}".format( + self.get_name(), self.path_to_script)) + return None return imp.load_source(self._find_value('name'), self.path_to_script) http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/main/python/ambari_agent/alerts/web_alert.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/web_alert.py b/ambari-agent/src/main/python/ambari_agent/alerts/web_alert.py index f736135..fa578ce 100644 --- a/ambari-agent/src/main/python/ambari_agent/alerts/web_alert.py +++ b/ambari-agent/src/main/python/ambari_agent/alerts/web_alert.py @@ -48,8 +48,8 @@ class WebAlert(BaseAlert): # use the URI lookup keys to get a final URI value to query alert_uri = self._get_uri_from_structure(self.uri_property_keys) - logger.debug("Calculated web URI to be {0} (ssl={1})".format(alert_uri.uri, - str(alert_uri.is_ssl_enabled))) + logger.debug("[Alert][{0}] Calculated web URI to be {1} (ssl={2})".format( + self.get_name(), alert_uri.uri, str(alert_uri.is_ssl_enabled))) url = self._build_web_query(alert_uri) web_response = self._make_web_request(url) @@ -116,7 +116,7 @@ class WebAlert(BaseAlert): time_millis = time.time() - start_time except: if logger.isEnabledFor(logging.DEBUG): - logger.exception("Unable to make a web request.") + logger.exception("[Alert][{0}] Failed to make a web request".format(self.get_name())) return WebResponse(status_code=0, time_millis=0) http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-agent/src/test/python/ambari_agent/TestAlerts.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/test/python/ambari_agent/TestAlerts.py b/ambari-agent/src/test/python/ambari_agent/TestAlerts.py index 4cbab14..813f478 100644 --- a/ambari-agent/src/test/python/ambari_agent/TestAlerts.py +++ b/ambari-agent/src/test/python/ambari_agent/TestAlerts.py @@ -744,6 +744,46 @@ class TestAlerts(TestCase): with patch("__builtin__.open") as open_mock: open_mock.side_effect = open_side_effect ash.update_configurations(commands) + self.assertTrue(json_mock.called) self.assertTrue(json_mock.called_with(all_commands)) + + @patch.object(AlertSchedulerHandler,"reschedule_all") + @patch("json.dump") + def test_update_configurations_without_reschedule(self, json_mock, reschedule_mock): + + def open_side_effect(file, mode): + if mode == 'w': + file_mock = MagicMock() + return file_mock + else: + return self.original_open(file, mode) + + test_file_path = os.path.join('ambari_agent', 'dummy_files') + test_stack_path = os.path.join('ambari_agent', 'dummy_files') + test_common_services_path = os.path.join('ambari_agent', 'dummy_files') + test_host_scripts_path = os.path.join('ambari_agent', 'dummy_files') + + with open(os.path.join(test_stack_path, "definitions.json"),"r") as fp: + all_commands = json.load(fp) + + # create a copy of the configurations from definitions.json, then add + # a brand new property - this should not cause a restart since there are + # no alerts that use this new property + commands = [{"clusterName": "c1" }] + commands[0]['configurations'] = all_commands[0]['configurations'] + commands[0]['configurations'].update({ "foo" : "bar" }) + + ash = AlertSchedulerHandler(test_file_path, test_stack_path, + test_common_services_path, test_host_scripts_path, None) + + ash.start() + + with patch("__builtin__.open") as open_mock: + open_mock.side_effect = open_side_effect + ash.update_configurations(commands) + + self.assertTrue(json_mock.called) + self.assertTrue(json_mock.called_with(all_commands)) + self.assertFalse(reschedule_mock.called) http://git-wip-us.apache.org/repos/asf/ambari/blob/12ed8b55/ambari-server/src/main/resources/common-services/AMBARI_METRICS/0.1.0/alerts.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/AMBARI_METRICS/0.1.0/alerts.json b/ambari-server/src/main/resources/common-services/AMBARI_METRICS/0.1.0/alerts.json index 86276bd..9b29b0b 100644 --- a/ambari-server/src/main/resources/common-services/AMBARI_METRICS/0.1.0/alerts.json +++ b/ambari-server/src/main/resources/common-services/AMBARI_METRICS/0.1.0/alerts.json @@ -81,7 +81,7 @@ }, { "name": "ams_metrics_collector_hbase_master_cpu", - "label": "Metrics Collector HBase Maser CPU Utilization", + "label": "Metrics Collector - HBase Master CPU Utilization", "description": "This host-level alert is triggered if CPU utilization of the Metrics Collector's HBase Master exceeds certain warning and critical thresholds. It checks the HBase Master JMX Servlet for the SystemCPULoad property. The threshold values are in percent.", "interval": 5, "scope": "ANY",