You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by xi...@apache.org on 2014/11/14 01:06:12 UTC
ambari git commit: AMBARI-8221. Alerts UI: Create Manage Alerts Group
dialog.(xiwang)
Repository: ambari
Updated Branches:
refs/heads/trunk d6b0db0b5 -> 43704d451
AMBARI-8221. Alerts UI: Create Manage Alerts Group dialog.(xiwang)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/43704d45
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/43704d45
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/43704d45
Branch: refs/heads/trunk
Commit: 43704d451dc8186b829502e4e9e351af37a0ece4
Parents: d6b0db0
Author: Xi Wang <xi...@apache.org>
Authored: Thu Oct 30 11:03:33 2014 -0700
Committer: Xi Wang <xi...@apache.org>
Committed: Thu Nov 13 16:05:15 2014 -0800
----------------------------------------------------------------------
.../app/assets/data/alerts/alertGroup.json | 82 +++
.../app/assets/data/alerts/alertGroups.json | 167 +++++
ambari-web/app/config.js | 1 +
ambari-web/app/controllers.js | 1 +
.../controllers/global/cluster_controller.js | 3 +
.../app/controllers/global/update_controller.js | 11 +
.../alert_definitions_actions_controller.js | 85 ++-
.../alerts/manage_alert_groups_controller.js | 619 +++++++++++++++++++
ambari-web/app/mappers.js | 1 +
ambari-web/app/mappers/alert_groups_mapper.js | 44 ++
ambari-web/app/messages.js | 25 +
ambari-web/app/models.js | 1 +
ambari-web/app/models/alert_group.js | 107 ++++
.../alerts/add_definition_to_group_popup.hbs | 139 +++++
.../main/alerts/create_new_alert_group.hbs | 33 +
.../main/alerts/manage_alert_groups_popup.hbs | 94 +++
ambari-web/app/utils/ajax/ajax.js | 55 +-
ambari-web/app/utils/validator.js | 10 +
ambari-web/app/views.js | 2 +
.../main/alerts/manage_alert_groups_view.js | 105 ++++
20 files changed, 1583 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/assets/data/alerts/alertGroup.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/alerts/alertGroup.json b/ambari-web/app/assets/data/alerts/alertGroup.json
new file mode 100644
index 0000000..a309fd3
--- /dev/null
+++ b/ambari-web/app/assets/data/alerts/alertGroup.json
@@ -0,0 +1,82 @@
+{
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/16",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : false,
+ "definitions" : [
+ {
+ "name" : "mapreduce_history_server_webui",
+ "label" : "History Server Web UI",
+ "service_name" : "MAPREDUCE2",
+ "component_name" : "HISTORYSERVER",
+ "id" : 1
+ },
+ {
+ "name" : "mapreduce_history_server_process",
+ "label" : "History Server Process",
+ "service_name" : "MAPREDUCE2",
+ "component_name" : "HISTORYSERVER",
+ "id" : 2
+ },
+ {
+ "name" : "datanode_process",
+ "label" : "DateNode Process",
+ "service_name" : "HDFS",
+ "component_name" : "DATANODE",
+ "id" : 19
+ },
+ {
+ "name" : "mapreduce_history_server_rpc_latency",
+ "label" : "History Server RPC Latency",
+ "service_name" : "MAPREDUCE2",
+ "component_name" : "HISTORYSERVER",
+ "id" : 3
+ },
+ {
+ "name" : "mapreduce_history_server_cpu",
+ "label" : "History Server CPU Utilization",
+ "service_name" : "MAPREDUCE2",
+ "component_name" : "HISTORYSERVER",
+ "id" : 4
+ },
+ {
+ "name" : "falcon_server_webui",
+ "label" : "Falcon Server Web UI",
+ "service_name" : "FALCON",
+ "component_name" : "FALCON_SERVER",
+ "id" : 5
+ },
+ {
+ "name" : "falcon_server_process",
+ "label" : "Falcon Server Process",
+ "service_name" : "FALCON",
+ "component_name" : "FALCON_SERVER",
+ "id" : 6
+ },
+ {
+ "name" : "flume_agent_status",
+ "label" : "Flume Agent Status",
+ "service_name" : "FLUME",
+ "component_name" : "FLUME_HANDLER",
+ "id" : 7
+ },
+ {
+ "name" : "datanode_storage",
+ "label" : "DataNode Storage",
+ "service_name" : "HDFS",
+ "component_name" : "DATANODE",
+ "id" : 26
+ },
+ {
+ "name" : "datanode_webui",
+ "label" : "DataNode Web UI",
+ "service_name" : "HDFS",
+ "component_name" : "DATANODE",
+ "id" : 14
+ }
+ ],
+ "id" : 16,
+ "name" : "group lalala",
+ "targets" : []
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/assets/data/alerts/alertGroups.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/alerts/alertGroups.json b/ambari-web/app/assets/data/alerts/alertGroups.json
new file mode 100644
index 0000000..20e568d
--- /dev/null
+++ b/ambari-web/app/assets/data/alerts/alertGroups.json
@@ -0,0 +1,167 @@
+{
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups?fields=*",
+ "items" : [
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/1",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 1,
+ "name" : "MAPREDUCE2"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/2",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 2,
+ "name" : "PIG"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/3",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 3,
+ "name" : "FALCON"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/4",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 4,
+ "name" : "FLUME"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/5",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 5,
+ "name" : "NAGIOS"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/6",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 6,
+ "name" : "HIVE"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/7",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 7,
+ "name" : "ZOOKEEPER"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/8",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 8,
+ "name" : "HDFS"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/9",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 9,
+ "name" : "OOZIE"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/10",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 10,
+ "name" : "SQOOP"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/11",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 11,
+ "name" : "YARN"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/12",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 12,
+ "name" : "TEZ"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/13",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 13,
+ "name" : "HBASE"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/14",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 14,
+ "name" : "GANGLIA"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/15",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : true,
+ "id" : 15,
+ "name" : "STORM"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/16",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : false,
+ "id" : 16,
+ "name" : "group lalala"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/17",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : false,
+ "id" : 17,
+ "name" : "group test"
+ }
+ },
+ {
+ "href" : "http://c6407.ambari.apache.org:8080/api/v1/clusters/c789/alert_groups/18",
+ "AlertGroup" : {
+ "cluster_name" : "c789",
+ "default" : false,
+ "id" : 18,
+ "name" : "group def empty"
+ }
+ }
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/config.js b/ambari-web/app/config.js
index 27ab995..e6f08f2 100644
--- a/ambari-web/app/config.js
+++ b/ambari-web/app/config.js
@@ -37,6 +37,7 @@ App.componentsUpdateInterval = 6000;
App.contentUpdateInterval = 15000;
App.alertDefinitionsUpdateInterval = 10000;
App.alertInstancesUpdateInterval = 10000;
+App.alertGroupsUpdateInterval = 10000;
App.maxRunsForAppBrowser = 500;
App.pageReloadTime=3600000;
App.singleNodeInstall = false;
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/controllers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js
index 1b8a96c..687c300 100644
--- a/ambari-web/app/controllers.js
+++ b/ambari-web/app/controllers.js
@@ -72,6 +72,7 @@ require('controllers/main/alert_definitions_controller');
require('controllers/main/alerts/alert_definitions_actions_controller');
require('controllers/main/alerts/definition_details_controller');
require('controllers/main/alerts/alert_instances_controller');
+require('controllers/main/alerts/manage_alert_groups_controller');
require('controllers/main/service');
require('controllers/main/service/item');
require('controllers/main/service/info/summary');
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/controllers/global/cluster_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/cluster_controller.js b/ambari-web/app/controllers/global/cluster_controller.js
index 319ee95..f72d0cd 100644
--- a/ambari-web/app/controllers/global/cluster_controller.js
+++ b/ambari-web/app/controllers/global/cluster_controller.js
@@ -350,6 +350,9 @@ App.ClusterController = Em.Controller.extend({
self.updateLoadStatus('alertDefinitions');
}
});
+ updater.updateAlertGroups(function () {
+ self.updateLoadStatus('alertGroups');
+ });
});
});
self.loadRootService().done(function (data) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/controllers/global/update_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/update_controller.js b/ambari-web/app/controllers/global/update_controller.js
index 1f1609f..00fc486 100644
--- a/ambari-web/app/controllers/global/update_controller.js
+++ b/ambari-web/app/controllers/global/update_controller.js
@@ -131,6 +131,7 @@ App.UpdateController = Em.Controller.extend({
App.updater.run(this, 'updateAlertDefinitions', 'isWorking', App.alertDefinitionsUpdateInterval);
if (App.get('supports.alerts')) {
App.updater.run(this, 'updateAlertDefinitionSummary', 'isWorking', App.alertDefinitionsUpdateInterval);
+ App.updater.run(this, 'updateAlertGroups', 'isWorking', App.alertGroupsUpdateInterval);
}
}
}.observes('isWorking'),
@@ -466,6 +467,16 @@ App.UpdateController = Em.Controller.extend({
App.HttpClient.get(url, App.alertDefinitionSummaryMapper, {
complete: callback
});
+ },
+
+ updateAlertGroups: function (callback) {
+ var testUrl = '/data/alerts/alertGroups.json';
+ var realUrl = '/alert_groups?fields=*';
+ var url = this.getUrl(testUrl, realUrl);
+
+ App.HttpClient.get(url, App.alertGroupsMapper, {
+ complete: callback
+ });
}
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/controllers/main/alerts/alert_definitions_actions_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/alerts/alert_definitions_actions_controller.js b/ambari-web/app/controllers/main/alerts/alert_definitions_actions_controller.js
index e6a5c33..e74e612 100644
--- a/ambari-web/app/controllers/main/alerts/alert_definitions_actions_controller.js
+++ b/ambari-web/app/controllers/main/alerts/alert_definitions_actions_controller.js
@@ -64,7 +64,90 @@ App.MainAlertDefinitionActionsController = Em.ArrayController.extend({
createNewAlertDefinition: Em.K,
- manageAlertGroups: Em.K,
+ /**
+ * handler when clicking on "Manage Alert Groups", a popup will show up
+ */
+ manageAlertGroups: function () {
+ return App.ModalPopup.show({
+ header: Em.I18n.t('alerts.actions.manage_alert_groups_popup.header'),
+ bodyClass: App.MainAlertsManageAlertGroupView.extend({
+ controllerBinding: 'App.router.manageAlertGroupsController'
+ }),
+ classNames: ['sixty-percent-width-modal', 'manage-configuration-group-popup'],
+ primary: Em.I18n.t('common.save'),
+ onPrimary: function () {
+ var modifiedAlertGroups = this.get('subViewController.defsModifiedAlertGroups');
+ // Save modified Alert-groups
+ console.log("manageAlertGroups(): Saving modified Alert groups: ", modifiedAlertGroups);
+ var self = this;
+ var errors = [];
+ var deleteQueriesCounter = modifiedAlertGroups.toDelete.length;
+ var createQueriesCounter = modifiedAlertGroups.toCreate.length;
+ var deleteQueriesRun = false;
+ var createQueriesRun = false;
+ var runNextQuery = function () {
+ if (!deleteQueriesRun && deleteQueriesCounter > 0) {
+ deleteQueriesRun = true;
+ modifiedAlertGroups.toDelete.forEach(function (group) {
+ self.get('subViewController').removeAlertGroup(group, finishFunction, finishFunction);
+ }, this);
+ } else if (!createQueriesRun && deleteQueriesCounter < 1) {
+ createQueriesRun = true;
+ modifiedAlertGroups.toSet.forEach(function (group) {
+ self.get('subViewController').updateAlertGroup(group, finishFunction, finishFunction);
+ }, this);
+ modifiedAlertGroups.toCreate.forEach(function (group) {
+ self.get('subViewController').postNewAlertGroup(group, finishFunction);
+ }, this);
+ }
+ };
+ var finishFunction = function (xhr, text, errorThrown) {
+ if (xhr && errorThrown) {
+ var error = xhr.status + "(" + errorThrown + ") ";
+ try {
+ var json = $.parseJSON(xhr.responseText);
+ error += json.message;
+ } catch (err) {
+ }
+ console.error('Error updating Alert Group:', error);
+ errors.push(error);
+ }
+ if (createQueriesRun) {
+ createQueriesCounter--;
+ } else {
+ deleteQueriesCounter--;
+ }
+ if (deleteQueriesCounter + createQueriesCounter < 1) {
+ if (errors.length > 0) {
+ console.log(errors);
+ self.get('subViewController').set('errorMessage', errors.join(". "));
+ } else {
+ self.hide();
+ }
+ } else {
+ runNextQuery();
+ }
+ };
+ runNextQuery();
+ },
+ onSecondary: function () {
+ this.hide();
+ },
+ onClose: function () {
+ this.hide();
+ },
+ subViewController: function () {
+ return App.router.get('manageAlertGroupsController');
+ }.property('App.router.manageAlertGroupsController'),
+ updateButtons: function () {
+ var modified = this.get('subViewController.isDefsModified');
+ this.set('disablePrimary', !modified);
+ }.observes('subViewController.isDefsModified'),
+ secondary: Em.I18n.t('common.cancel'),
+ didInsertElement: Em.K
+ });
+ },
+
manageNotifications: Em.K
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js b/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js
new file mode 100644
index 0000000..7833b5c
--- /dev/null
+++ b/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js
@@ -0,0 +1,619 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+var validator = require('utils/validator');
+var numberUtils = require('utils/number_utils');
+
+App.ManageAlertGroupsController = Em.Controller.extend({
+ name: 'manageAlertGroupsController',
+
+ isLoaded: false,
+
+ alertGroups: [],
+
+ originalAlertGroups: [],
+
+ selectedAlertGroup: null,
+
+ selectedDefinitions: [],
+
+ alertDefinitions: [],
+
+ // load alert definitions for all services
+ loadAlertDefinitions: function () {
+ App.ajax.send({
+ name: 'alerts.load_all_alert_definitions',
+ sender: this,
+ success: 'onLoadAlertDefinitionsSuccess',
+ error: 'onLoadAlertDefinitionsError'
+ });
+ },
+
+ onLoadAlertDefinitionsSuccess: function (data) {
+ var alertDefinitions = [];
+ if (data && data.items) {
+ data.items.forEach( function(def) {
+ if (def && def.AlertDefinition) {
+ alertDefinitions.pushObject (Em.Object.create({
+ name: def.AlertDefinition.name,
+ serviceName: def.AlertDefinition.service_name,
+ serviceNameDisplay:function() {
+ return App.format.role(this.get('serviceName'));
+ }.property('serviceName'),
+ componentName: def.AlertDefinition.component_name,
+ componentNameDisplay: function() {
+ return App.format.role(this.get('componentName'));
+ }.property('componentName'),
+ label: def.AlertDefinition.label,
+ id: def.AlertDefinition.id
+ }));
+ }
+ });
+ }
+ this.set('alertDefinitions', alertDefinitions);
+ },
+
+ onLoadAlertDefinitionsError: function () {
+ console.error('Unable to load all alert definitions.');
+ },
+
+ loadAlertGroups: function () {
+ this.set('isLoaded', false);
+ this.set('alertGroups.length', 0);
+ this.set('originalAlertGroups.length', 0);
+ App.ajax.send({
+ name: 'alerts.load_alert_groups',
+ sender: this,
+ success: 'onLoadAlertGroupsSuccess',
+ error: 'onLoadAlertGroupsError'
+ });
+ },
+
+ onLoadAlertGroupsSuccess: function (data) {
+ var self = this;
+ if (data && data.items) {
+ this.set('alertGroupsCount', data.items.length);
+ data.items.forEach(function(alert_group) {
+ App.ajax.send({
+ name: 'alerts.load_an_alert_group',
+ sender: self,
+ data: {
+ "group_id": alert_group.AlertGroup.id
+ },
+ success: 'onLoadAlertGroupSuccess',
+ error: 'onLoadAlertGroupError'
+ });
+ }, this);
+ }
+ },
+
+ onLoadAlertGroupSuccess: function (data) {
+ var alertGroups = this.get('alertGroups');
+ if (data && data.AlertGroup) {
+ alertGroups.pushObject ( App.AlertGroupComplex.create({
+ name: data.AlertGroup.name,
+ default: data.AlertGroup.default,
+ id: data.AlertGroup.id,
+ definitions: data.AlertGroup.definitions,
+ targets: data.AlertGroup.targets
+ }));
+ }
+ if (this.get('alertGroupsCount') == alertGroups.length) {
+ this.set('isLoaded', true);
+ this.set('originalAlertGroups', this.copyAlertGroups(alertGroups));
+ }
+ },
+
+
+ onLoadAlertGroupsError: function () {
+ console.error('Unable to load all alert groups.');
+ },
+ onLoadAlertGroupError: function () {
+ console.error('Unable to load an alert group.');
+ },
+
+ resortAlertGroup: function() {
+ var alertGroups = Ember.copy(this.get('alertGroups'));
+ if(alertGroups.length < 2){
+ return;
+ }
+ var defaultGroups = alertGroups.filterProperty('default');
+ defaultGroups.forEach( function(defaultGroup) {
+ alertGroups.removeObject(defaultGroup);
+ });
+ var sorted = defaultGroups.concat(alertGroups.sortProperty('name'));
+ // var sorted = alertGroups.sortProperty('name');
+
+ this.removeObserver('alertGroups.@each.name', this, 'resortAlertGroup');
+ this.set('alertGroups', sorted);
+ this.addObserver('alertGroups.@each.name', this, 'resortAlertGroup');
+ }.observes('alertGroups.@each.name'),
+
+ /**
+ * remove definitions from group
+ */
+ deleteDefinitions: function () {
+ if (this.get('isDeleteDefinitionsDisabled')) {
+ return;
+ }
+ var groupDefinitions = this.get('selectedAlertGroup.definitions');
+ this.get('selectedDefinitions').slice().forEach(function (defObj) {
+ groupDefinitions.removeObject(defObj);
+ }, this);
+ this.set('selectedDefinitions', []);
+ },
+
+ isDeleteDefinitionsDisabled: function () {
+ var selectedGroup = this.get('selectedAlertGroup');
+ if (selectedGroup) {
+ return selectedGroup.default || this.get('selectedDefinitions').length === 0;
+ }
+ return true;
+ }.property('selectedAlertGroup', 'selectedAlertGroup.definitions.length', 'selectedDefinitions.length'),
+
+ /**
+ * add alert definitions to a group
+ */
+ addDefinitions: function () {
+ if (this.get('selectedAlertGroup.isAddDefinitionsDisabled')){
+ return false;
+ }
+ var availableDefinitions = this.get('selectedAlertGroup.availableDefinitions');
+ var popupDescription = {
+ header: Em.I18n.t('alerts.actions.manage_alert_groups_popup.selectDefsDialog.title'),
+ dialogMessage: Em.I18n.t('alerts.actions.manage_alert_groups_popup.selectDefsDialog.message').format(this.get('selectedAlertGroup.displayName'))
+ };
+ var validComponents = App.StackServiceComponent.find().map(function (component) {
+ return Em.Object.create({
+ componentName: component.get('componentName'),
+ displayName: App.format.role(component.get('componentName')),
+ selected: false
+ });
+ });
+ var validServices = App.Service.find().map(function (service) {
+ return Em.Object.create({
+ serviceName: service.get('serviceName'),
+ displayName: App.format.role(service.get('serviceName')),
+ selected: false
+ });
+ });
+ this.launchDefsSelectionDialog (availableDefinitions, [], validServices, validComponents, this.addDefinitionsCallback.bind(this), popupDescription);
+ },
+
+ /**
+ * lauch a table view of all availavle definitions to choose
+ */
+ launchDefsSelectionDialog : function(initialDefs, selectedDefs, validServices, validComponents, callback, popupDescription) {
+
+ App.ModalPopup.show({
+ classNames: [ 'sixty-percent-width-modal' ],
+ header: popupDescription.header,
+ dialogMessage: popupDescription.dialogMessage,
+ warningMessage: null,
+ availableDefs: [],
+ onPrimary: function () {
+ this.set('warningMessage', null);
+ var arrayOfSelectedDefs = this.get('availableDefs').filterProperty('selected', true);
+ if (arrayOfSelectedDefs.length < 1) {
+ this.set('warningMessage', Em.I18n.t('alerts.actions.manage_alert_groups_popup.selectDefsDialog.message.warning'));
+ return;
+ }
+ callback(arrayOfSelectedDefs);
+ console.debug('(new-selectedDefs)=', arrayOfSelectedDefs);
+ this.hide();
+ },
+ disablePrimary: function () {
+ return !this.get('isLoaded');
+ }.property('isLoaded'),
+ onSecondary: function () {
+ callback(null);
+ this.hide();
+ },
+ bodyClass: App.TableView.extend({
+ templateName: require('templates/main/alerts/add_definition_to_group_popup'),
+ controllerBinding: 'App.router.manageAlertGroupsController',
+ isPaginate: true,
+ filteredContent: function() {
+ return this.get('parentView.availableDefs').filterProperty('filtered') || [];
+ }.property('parentView.availableDefs.@each.filtered'),
+
+ showOnlySelectedDefs: false,
+ filterComponents: validComponents,
+ filterServices: validServices,
+ filterComponent: null,
+ filterService: null,
+ isDisabled: function () {
+ return !this.get('parentView.isLoaded');
+ }.property('parentView.isLoaded'),
+ didInsertElement: function(){
+ initialDefs.setEach('filtered', true);
+ this.set('parentView.availableDefs', initialDefs);
+ this.set('parentView.isLoaded', true);
+ },
+ filterDefs: function () {
+ var showOnlySelectedDefs = this.get('showOnlySelectedDefs');
+ var filterComponent = this.get('filterComponent');
+ var filterService = this.get('filterService');
+ this.get('parentView.availableDefs').forEach(function (defObj) {
+ var componentOnObj = true;
+ var serviceOnObj = true;
+ if (filterComponent) {
+ componentOnObj = (defObj.componentName == filterComponent.get('componentName'));
+ }
+ if (defObj.serviceName && filterService) {
+ serviceOnObj = (defObj.serviceName == filterService.get('serviceName'));
+ }
+ if (showOnlySelectedDefs) {
+ defObj.set('filtered', componentOnObj && serviceOnObj && defObj.get('selected'));
+ } else {
+ defObj.set('filtered', componentOnObj && serviceOnObj);
+ }
+ }, this);
+ this.set('startIndex', 1);
+ }.observes('parentView.availableDefs', 'filterService', 'filterService.serviceName', 'filterComponent', 'filterComponent.componentName', 'showOnlySelectedDefs'),
+ defSelectMessage: function () {
+ var defs = this.get('parentView.availableDefs');
+ var selectedDefs = defs.filterProperty('selected', true);
+ return this.t('alerts.actions.manage_alert_groups_popup.selectDefsDialog.selectedDefsLink').format(selectedDefs.get('length'), defs.get('length'));
+ }.property('parentView.availableDefs.@each.selected'),
+
+ selectFilterComponent: function (event) {
+ if (event != null && event.context != null && event.context.componentName != null) {
+ var currentFilter = this.get('filterComponent');
+ if (currentFilter != null) {
+ currentFilter.set('selected', false);
+ }
+ if (currentFilter != null && currentFilter.componentName === event.context.componentName) {
+ // selecting the same filter deselects it.
+ this.set('filterComponent', null);
+ } else {
+ this.set('filterComponent', event.context);
+ event.context.set('selected', true);
+ }
+ }
+ },
+ selectFilterService: function (event) {
+ if (event != null && event.context != null && event.context.serviceName != null) {
+ var currentFilter = this.get('filterService');
+ if (currentFilter != null) {
+ currentFilter.set('selected', false);
+ }
+ if (currentFilter != null && currentFilter.serviceName === event.context.serviceName) {
+ // selecting the same filter deselects it.
+ this.set('filterService', null);
+ } else {
+ this.set('filterService', event.context);
+ event.context.set('selected', true);
+ }
+ }
+ },
+ allDefsSelected: false,
+ toggleSelectAllDefs: function (event) {
+ this.get('parentView.availableDefs').filterProperty('filtered').setEach('selected', this.get('allDefsSelected'));
+ }.observes('allDefsSelected'),
+ toggleShowSelectedDefs: function () {
+ var filter1 = this.get('filterComponent');
+ if (filter1 != null) {
+ filter1.set('selected', false);
+ }
+ var filter2 = this.get('filterService');
+ if (filter2 != null) {
+ filter2.set('selected', false);
+ }
+ this.set('filterComponent', null);
+ this.set('filterService', null);
+ this.set('showOnlySelectedDefs', !this.get('showOnlySelectedDefs'));
+ }
+ })
+ });
+ },
+
+ /**
+ * add alert definitions callback
+ */
+ addDefinitionsCallback: function (selectedDefs) {
+ var group = this.get('selectedAlertGroup');
+ if (selectedDefs) {
+ var alertGroupDefs = group.get('definitions');
+ selectedDefs.forEach(function (defObj) {
+ alertGroupDefs.pushObject(defObj);
+ }, this);
+ }
+ },
+
+ /**
+ * observes if any group changed including: group name, newly created group, deleted group, group with definitions changed
+ */
+ defsModifiedAlertGroups: function () {
+ if (!this.get('isLoaded')) {
+ return false;
+ }
+ var groupsToDelete = [];
+ var groupsToSet = [];
+ var groupsToCreate = [];
+ var groups = this.get('alertGroups'); //current alert groups
+ var originalGroups = this.get('originalAlertGroups'); // original alert groups
+ // remove default group
+ originalGroups = originalGroups.filterProperty('default', false);
+ var originalGroupsIds = originalGroups.mapProperty('id');
+ groups.forEach(function (group) {
+ if (!group.get('default')) {
+ var originalGroup = originalGroups.findProperty('id', group.get('id'));
+ if (originalGroup) {
+ if (!(JSON.stringify(group.get('definitions').slice().sort()) === JSON.stringify(originalGroup.get('definitions').slice().sort()))
+ || !(JSON.stringify(group.get('targets').slice().sort()) === JSON.stringify(originalGroup.get('targets').slice().sort()))) {
+ groupsToSet.push(group.set('id', originalGroup.get('id')));
+ } else if (group.get('name') !== originalGroup.get('name') ) {
+ // should update name
+ groupsToSet.push(group.set('id', originalGroup.get('id')));
+ }
+ originalGroupsIds = originalGroupsIds.without(group.get('id'));
+ } else {
+ groupsToCreate.push(group);
+ }
+ }
+ });
+ originalGroupsIds.forEach(function (id) {
+ groupsToDelete.push(originalGroups.findProperty('id', id));
+ }, this);
+ return {
+ toDelete: groupsToDelete,
+ toSet: groupsToSet,
+ toCreate: groupsToCreate
+ };
+ }.property('selectedAlertGroup.definitions.@each', 'selectedAlertGroup.definitions.length', 'alertGroups', 'isLoaded'),
+
+ isDefsModified: function () {
+ var modifiedGroups = this.get('defsModifiedAlertGroups');
+ if (!this.get('isLoaded')) {
+ return false;
+ }
+ return !!(modifiedGroups.toSet.length || modifiedGroups.toCreate.length || modifiedGroups.toDelete.length);
+ }.property('defsModifiedAlertGroups'),
+
+ /**
+ * copy alert groups for backup, to compare with current alert groups, so will know if some groups changed/added/deleted
+ * @param originGroups
+ * @return {Array}
+ */
+ copyAlertGroups: function (originGroups) {
+ var alertGroups = [];
+ originGroups.forEach(function (alertGroup) {
+ var copiedGroup = App.AlertGroupComplex.create($.extend(true, {}, alertGroup));
+ alertGroups.pushObject(copiedGroup);
+ });
+ return alertGroups;
+ },
+
+ /**
+ * ==============on API side: following are four functions to an alert group: create, delete, update definitions/targets/label of a group ===========
+ */
+ /**
+ * Create a new alert group
+ * @param newAlertGroupData
+ * @param callback Callback function for Success or Error handling
+ * @return Returns the created alert group
+ */
+ postNewAlertGroup: function (newAlertGroupData, callback) {
+ // create a new group with name , definition and target
+ var data = {
+ 'name': newAlertGroupData.get('name')
+ };
+ if (newAlertGroupData.get('definitions').length > 0) {
+ data.definitions = newAlertGroupData.get('definitions').mapProperty('id');
+ }
+ if (newAlertGroupData.get('targets').length > 0) {
+ data.targets = newAlertGroupData.get('targets').mapProperty('id');
+ }
+ var sendData = {
+ name: 'alert_groups.create',
+ data: data,
+ success: 'successFunction',
+ error: 'errorFunction',
+ successFunction: function () {
+ if (callback) {
+ callback();
+ }
+ },
+ errorFunction: function (xhr, text, errorThrown) {
+ if (callback) {
+ callback(xhr, text, errorThrown);
+ }
+ console.error('Error in creating new Alert Group');
+ }
+ };
+ sendData.sender = sendData;
+ App.ajax.send(sendData);
+ return newAlertGroupData;
+ },
+
+ /**
+ * PUTs the new alert group information on the server.
+ * Changes possible here are the name, definitions, targets
+ *
+ * @param {App.ConfigGroup} alertGroup
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ */
+ updateAlertGroup: function (alertGroup, successCallback, errorCallback) {
+ var sendData = {
+ name: 'alert_groups.update',
+ data: {
+ "group_id": alertGroup.id,
+ 'name': alertGroup.get('name'),
+ 'definitions': alertGroup.get('definitions').mapProperty('id'),
+ 'targets': alertGroup.get('targets').mapProperty('id')
+ },
+ success: 'successFunction',
+ error: 'errorFunction',
+ successFunction: function () {
+ if (successCallback) {
+ successCallback();
+ }
+ },
+ errorFunction: function (xhr, text, errorThrown) {
+ if (errorCallback) {
+ errorCallback(xhr, text, errorThrown);
+ }
+ }
+ };
+ sendData.sender = sendData;
+ App.ajax.send(sendData);
+ },
+
+ removeAlertGroup: function (alertGroup, successCallback, errorCallback) {
+ var sendData = {
+ name: 'alert_groups.delete',
+ data: {
+ "group_id": alertGroup.id
+ },
+ success: 'successFunction',
+ error: 'errorFunction',
+ successFunction: function () {
+ if (successCallback) {
+ successCallback();
+ }
+ },
+ errorFunction: function (xhr, text, errorThrown) {
+ if (errorCallback) {
+ errorCallback(xhr, text, errorThrown);
+ }
+ }
+ };
+ sendData.sender = sendData;
+ App.ajax.send(sendData);
+ },
+
+ /**
+ * =============on UI side: following are four operations to an alert group: add, remove, rename and duplicate==================
+ */
+
+ /**
+ * confirm delete alert group
+ */
+ confirmDelete : function () {
+ var self = this;
+ App.showConfirmationPopup(function() {
+ self.deleteAlertGroup();
+ });
+ },
+
+ /**
+ * delete selected alert group
+ */
+ deleteAlertGroup: function () {
+ var selectedAlertGroup = this.get('selectedAlertGroup');
+ if (this.get('isDeleteAlertDisabled')) {
+ return;
+ }
+ this.get('alertGroups').removeObject(selectedAlertGroup);
+ this.set('selectedAlertGroup', this.get('alertGroups')[0]);
+ },
+
+ /**
+ * rename non-default alert group
+ */
+ renameAlertGroup: function () {
+ if(this.get('selectedAlertGroup.default')) {
+ return;
+ }
+ var self = this;
+ this.renameGroupPopup = App.ModalPopup.show({
+ header: Em.I18n.t('alerts.actions.manage_alert_groups_popup.renameButton'),
+ bodyClass: Ember.View.extend({
+ templateName: require('templates/main/alerts/create_new_alert_group')
+ }),
+ alertGroupName: self.get('selectedAlertGroup.name'),
+ warningMessage: null,
+ validate: function () {
+ var warningMessage = '';
+ var originalGroup = self.get('selectedAlertGroup');
+ var groupName = this.get('alertGroupName').trim();
+
+ if (originalGroup.get('name').trim() === groupName) {
+ warningMessage = Em.I18n.t("alerts.actions.manage_alert_groups_popup.addGroup.exist");
+ } else {
+ if (self.get('alertGroups').mapProperty('displayName').contains(groupName)) {
+ warningMessage = Em.I18n.t("alerts.actions.manage_alert_groups_popup.addGroup.exist");
+ }
+ else if (groupName && !validator.isValidAlertGroupName(groupName)) {
+ warningMessage = Em.I18n.t("form.validator.alertGroupName");
+ }
+ }
+ this.set('warningMessage', warningMessage);
+ }.observes('alertGroupName'),
+ disablePrimary: function () {
+ return !(this.get('alertGroupName').trim().length > 0 && (this.get('warningMessage') !== null && !this.get('warningMessage')));
+ }.property('warningMessage', 'alertGroupName'),
+ onPrimary: function () {
+ self.set('selectedAlertGroup.name', this.get('alertGroupName'));
+ this.hide();
+ }
+ });
+ },
+
+ /**
+ * create new alert group
+ */
+ addAlertGroup: function (duplicated) {
+ duplicated = (duplicated === true);
+ var self = this;
+ this.addGroupPopup = App.ModalPopup.show({
+ header: Em.I18n.t('alerts.actions.manage_alert_groups_popup.addButton'),
+ bodyClass: Ember.View.extend({
+ templateName: require('templates/main/alerts/create_new_alert_group')
+ }),
+ alertGroupName: duplicated ? self.get('selectedAlertGroup.name') + ' Copy' : "",
+ warningMessage: '',
+ didInsertElement: function(){
+ this.validate();
+ this.$('input').focus();
+ },
+ validate: function () {
+ var warningMessage = '';
+ var groupName = this.get('alertGroupName').trim();
+ if (self.get('alertGroups').mapProperty('displayName').contains(groupName)) {
+ warningMessage = Em.I18n.t("alerts.actions.manage_alert_groups_popup.addGroup.exist");
+ }
+ else if (groupName && !validator.isValidAlertGroupName(groupName)) {
+ warningMessage = Em.I18n.t("form.validator.alertGroupName");
+ }
+ this.set('warningMessage', warningMessage);
+ }.observes('alertGroupName'),
+ disablePrimary: function () {
+ return !(this.get('alertGroupName').trim().length > 0 && !this.get('warningMessage'));
+ }.property('warningMessage', 'alertGroupName'),
+ onPrimary: function () {
+ var newAlertGroup = App.AlertGroupComplex.create({
+ name: this.get('alertGroupName').trim(),
+ definitions: [],
+ targets: []
+ }) ;
+ self.get('alertGroups').pushObject(newAlertGroup);
+ this.hide();
+ }
+ });
+ },
+
+ duplicateAlertGroup: function() {
+ this.addAlertGroup(true);
+ }
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/mappers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers.js b/ambari-web/app/mappers.js
index 9aec70f..daa2cf6 100644
--- a/ambari-web/app/mappers.js
+++ b/ambari-web/app/mappers.js
@@ -37,4 +37,5 @@ require('mappers/service_config_version_mapper');
require('mappers/alert_definitions_mapper');
require('mappers/alert_definition_summary_mapper');
require('mappers/alert_instances_mapper');
+require('mappers/alert_groups_mapper');
require('mappers/root_service_mapper');
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/mappers/alert_groups_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/alert_groups_mapper.js b/ambari-web/app/mappers/alert_groups_mapper.js
new file mode 100644
index 0000000..255039c
--- /dev/null
+++ b/ambari-web/app/mappers/alert_groups_mapper.js
@@ -0,0 +1,44 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+var stringUtils = require('utils/string_utils');
+
+App.alertGroupsMapper = App.QuickDataMapper.create({
+
+ model: App.AlertGroup,
+
+ config: {
+ id: 'AlertGroup.id',
+ name: 'AlertGroup.name',
+ default: 'AlertGroup.default',
+ definitions: 'AlertGroup.definitions',
+ targets: 'AlertGroup.targets'
+ },
+
+ map: function (json) {
+ if (json && json.items) {
+ var alertGroups = [];
+ var self = this;
+ json.items.forEach (function(item) {
+ alertGroups.push(self.parseIt(item, self.get('config')));
+ }, this);
+ App.store.loadMany(this.get('model'), alertGroups);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index e8b97d0..76dc25f 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -234,6 +234,7 @@ Em.I18n.translations = {
'common.configs': "Configs",
'common.unknown': "Unknown",
'common.install': "Install",
+ 'common.alertDefinition': "Alert Definition",
'passiveState.turnOn':'Turn On Maintenance Mode',
'passiveState.turnOff':'Turn Off Maintenance Mode',
@@ -831,6 +832,7 @@ Em.I18n.translations = {
'form.validator.invalidIp':'Please enter valid ip address',
'form.validator.configKey':'Invalid Key. Only alphanumerics, hyphens, underscores, asterisks and periods are allowed.',
'form.validator.configGroupName':'Invalid Group Name. Only alphanumerics, hyphens, spaces and underscores are allowed.',
+ 'form.validator.alertGroupName':'Invalid Alert Group Name. Only alphanumerics, hyphens, spaces and underscores are allowed.',
'form.validator.configKey.specific':'"{0}" is invalid Key. Only alphanumerics, hyphens, underscores, asterisks and periods are allowed.',
'alerts.actions.create': 'Create Alert',
@@ -1690,6 +1692,29 @@ Em.I18n.translations = {
'services.hbase.master.error':'None of the HBase masters is active',
+ 'alerts.actions.manage_alert_groups_popup.header':'Manage Alert Groups',
+ 'alerts.actions.manage_alert_groups_popup.notice':'You can manage alert groups for each service in this dialog. View the list of alert groups and the alert definitions configured in them. ' +
+ 'You can also add/remove alert definitions, and pick notification for that alert group.',
+ 'alerts.actions.manage_alert_groups_popup.rename':'Rename',
+ 'alerts.actions.manage_alert_groups_popup.duplicate':'Duplicate',
+ 'alerts.actions.manage_alert_groups_popup.group_name_lable':'Name',
+ 'alerts.actions.manage_alert_groups_popup.group_desc_lable':'Description',
+ 'alerts.actions.manage_alert_groups_popup.notifications':'Notifications',
+ 'alerts.actions.manage_alert_groups_popup.addButton':'Create new Alert Group',
+ 'alerts.actions.manage_alert_groups_popup.addGroup.exist': 'Alert Group with given name already exists',
+ 'alerts.actions.manage_alert_groups_popup.removeButton':'Delete Alert Group',
+ 'alerts.actions.manage_alert_groups_popup.renameButton':'Rename Alert Group',
+ 'alerts.actions.manage_alert_groups_popup.duplicateButton':'Duplicate Alert Group',
+ 'alerts.actions.manage_alert_groups_popup.addDefinition':'Add alert definitions to selected Alert Group',
+ 'alerts.actions.manage_alert_groups_popup.addDefinitionDisabled':'There are no available alert definitions to add',
+ 'alerts.actions.manage_alert_groups_popup.addDefinitionToDefault': 'You cannot add alert definition to a default group',
+ 'alerts.actions.manage_alert_groups_popup.removeDefinition':'Remove alert definitions from selected Alert Group',
+ 'alerts.actions.manage_alert_groups_popup.selectDefsDialog.title': 'Select Alert Group\'s Alert Definitions',
+ 'alerts.actions.manage_alert_groups_popup.selectDefsDialog.message': 'Select alert definitions to be added to this "{0}" Alert Group.',
+ 'alerts.actions.manage_alert_groups_popup.selectDefsDialog.filter.placeHolder': 'All',
+ 'alerts.actions.manage_alert_groups_popup.selectDefsDialog.selectedDefsLink': '{0} out of {1} alert definitions selected',
+ 'alerts.actions.manage_alert_groups_popup.selectDefsDialog.message.warning': 'At least one alert definition needs to be selected.',
+
'hosts.host.add':'Add New Hosts',
'hosts.table.noHosts':'No hosts to display',
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/models.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js
index 1b9e9be..0552ef2 100644
--- a/ambari-web/app/models.js
+++ b/ambari-web/app/models.js
@@ -47,6 +47,7 @@ require('models/alert');
require('models/alert_definition');
require('models/alert_instance');
require('models/alert_notification');
+require('models/alert_group');
require('models/user');
require('models/host');
require('models/rack');
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/models/alert_group.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alert_group.js b/ambari-web/app/models/alert_group.js
new file mode 100644
index 0000000..faa46e8
--- /dev/null
+++ b/ambari-web/app/models/alert_group.js
@@ -0,0 +1,107 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+/**
+ * Represents an alert-group on the cluster.
+ * A alert group is a collection of alert definitions
+ *
+ * Alert group hierarchy is at 2 levels. For
+ * each service there is a 'Default' alert group
+ * containing all definitions , this group is read-only
+ *
+ * User can create new alert group containing alert definitions from
+ * any service.
+ */
+App.AlertGroup = DS.Model.extend({
+ id: null,
+ name: null,
+ description: null,
+ default: null,
+ definitions: [],
+ targets: [],
+
+ displayName: function () {
+ var name = this.get('name');
+ if (name && name.length > App.config.CONFIG_GROUP_NAME_MAX_LENGTH) {
+ var middle = Math.floor(App.config.CONFIG_GROUP_NAME_MAX_LENGTH / 2);
+ name = name.substring(0, middle) + "..." + name.substring(name.length-middle);
+ }
+ return this.get('default') ? (name + ' Default') : name;
+ }.property('name', 'default'),
+
+ displayNameDefinitions: function () {
+ return this.get('displayName') + ' (' + this.get('definitions.length') + ')';
+ }.property('displayName', 'definitions.length')
+});
+App.AlertGroup.FIXTURES = [];
+
+App.AlertGroupComplex = Ember.Object.extend({
+ id: null,
+ name: null,
+ description: null,
+ default: null,
+ definitions: [],
+ targets: [],
+
+ /**
+ * all alert definitions that belong to all services
+ */
+ alertDefinitionsBinding: 'App.router.manageAlertGroupsController.alertDefinitions',
+
+ displayName: function () {
+ var name = this.get('name');
+ if (name && name.length > App.config.CONFIG_GROUP_NAME_MAX_LENGTH) {
+ var middle = Math.floor(App.config.CONFIG_GROUP_NAME_MAX_LENGTH / 2);
+ name = name.substring(0, middle) + "..." + name.substring(name.length-middle);
+ }
+ return this.get('default') ? (name + ' Default') : name;
+ }.property('name', 'default'),
+
+ displayNameDefinitions: function () {
+ return this.get('displayName') + ' (' + this.get('definitions.length') + ')';
+ }.property('displayName', 'definitions.length'),
+
+ /**
+ * Provides alert definitions which are available for inclusion in
+ * non-default alert groups.
+ */
+ availableDefinitions: function () {
+ if (this.get('default')) return [];
+ var usedDefinitionsMap = {};
+ var availableDefinitions = [];
+ var sharedDefinitions = this.get('alertDefinitions');
+
+ this.get('definitions').forEach(function (def) {
+ usedDefinitionsMap[def.name] = true;
+ });
+ sharedDefinitions.forEach(function (shared_def) {
+ if (!usedDefinitionsMap[shared_def.get('name')]) {
+ availableDefinitions.pushObject(shared_def);
+ }
+ });
+ return availableDefinitions;
+ }.property('alertDefinitions', 'definitions.@each', 'definitions.length'),
+
+ isAddDefinitionsDisabled: function () {
+ return (this.get('default') || this.get('availableDefinitions.length') === 0);
+ }.property('availableDefinitions.length')
+});
+
+
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/templates/main/alerts/add_definition_to_group_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/alerts/add_definition_to_group_popup.hbs b/ambari-web/app/templates/main/alerts/add_definition_to_group_popup.hbs
new file mode 100644
index 0000000..a6a28db
--- /dev/null
+++ b/ambari-web/app/templates/main/alerts/add_definition_to_group_popup.hbs
@@ -0,0 +1,139 @@
+{{!
+* 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.
+}}
+
+
+<form class="form-horizontal mbm" autocomplete="off">
+ <div class="override-controls">
+ <div class="alert alert-info">{{view.parentView.dialogMessage}}</div>
+ {{#if view.parentView.warningMessage}}
+ <div class="text-warning">
+ {{view.parentView.warningMessage}}
+ </div>
+ {{/if}}
+ <table style="width: 100%;">
+ <tr>
+ <td>
+ <a href="#" {{action toggleShowSelectedDefs target="view" }}>{{view.defSelectMessage}}</a>
+ {{#if view.showOnlySelectedDefs}}
+ <i class='icon-ok-sign'></i>
+ {{/if}}
+ </td>
+ <td width="20%">
+ <div class="row">
+ <div class="span2" id="filter-dropdown-div">
+ <!-- services drop-down -->
+ <div class="btn-group">
+ <button class="btn dropdown-toggle" data-toggle="dropdown" href="#" {{bindAttr disabled="view.isDisabled"}}>
+ {{t common.service}}
+ <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu">
+ <!-- dropdown menu links -->
+ {{#each service in view.filterServices}}
+ <li>
+ <a href="#" {{action selectFilterService service target="view"}}>
+ {{#if service.selected}}
+ <i class='icon-ok-sign'></i>
+ {{else}}
+ <i class='icon-placeholder'></i>
+ {{/if}}
+ {{service.displayName}}
+ </a>
+ </li>
+ {{/each}}
+ </ul>
+ </div>
+ </div>
+ <div class="span2" id="component-dropdown-div">
+ <!-- definition-components drop-down -->
+ <div class="btn-group">
+ <button class="btn dropdown-toggle" data-toggle="dropdown" href="#" {{bindAttr disabled="view.isDisabled"}}>
+ {{t common.component}}
+ <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu">
+ <!-- dropdown menu links -->
+ {{#each component in view.filterComponents}}
+ <li>
+ <a href="#" {{action selectFilterComponent component target="view"}}>
+ {{#if component.selected}}
+ <i class='icon-ok-sign'></i>
+ {{else}}
+ <i class='icon-placeholder'></i>
+ {{/if}}
+ {{component.displayName}}
+ </a>
+ </li>
+ {{/each}}
+ </ul>
+ </div>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <table class="table table-striped hosts-table">
+ <thead>
+ <tr class="success">
+ <th width="10%">
+ {{view Ember.Checkbox checkedBinding="view.allDefsSelected" disabledBinding="view.isDisabled"}}
+ </th>
+ <th width="35%">{{t common.alertDefinition}}</th>
+ <th width="25%">{{t common.service}}</th>
+ <th width="30%">{{t common.component}}</th>
+ </tr>
+ </thead>
+ </table>
+ <div class="hosts-table-container">
+ <table class="table table-striped hosts-table">
+ {{#each entry in view.pageContent}}
+ <tr {{bindAttr class="entry.filtered::hidden"}}>
+ <td width="10%">
+ {{view Ember.Checkbox checkedBinding="entry.selected"}}
+ </td>
+ <td width="35%">
+ {{entry.label}}
+ </td>
+ <td>
+ {{entry.serviceNameDisplay}}
+ </td>
+ <td>
+ {{entry.componentNameDisplay}}
+ </td>
+ </tr>
+ {{/each}}
+ </table>
+ </div>
+</div>
+</form>
+
+{{#if view.isPaginate}}
+ <div class="page-bar pull-right no-borders">
+ <div class="items-on-page">
+ <label>{{t common.show}}: {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
+ </div>
+ <div class="info">{{view.paginationInfo}}</div>
+ <div class="paging_two_button">
+ {{view view.paginationFirst}}
+ {{view view.paginationLeft}}
+ {{view view.paginationRight}}
+ {{view view.paginationLast}}
+ </div>
+ </div>
+{{/if}}
+
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/templates/main/alerts/create_new_alert_group.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/alerts/create_new_alert_group.hbs b/ambari-web/app/templates/main/alerts/create_new_alert_group.hbs
new file mode 100644
index 0000000..07dce96
--- /dev/null
+++ b/ambari-web/app/templates/main/alerts/create_new_alert_group.hbs
@@ -0,0 +1,33 @@
+{{!
+* 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.
+}}
+
+<div class="new-config-group-div">
+ <div class="text-warning">
+ {{#if view.parentView.warningMessage}}
+ {{view.parentView.warningMessage}}
+ {{else}}
+
+ {{/if}}
+ </div>
+ <table>
+ <tr>
+ <td>{{t services.service.config_groups_popup.group_name_lable }}:</td>
+ <td>{{view Ember.TextField maxlength="255" valueBinding="alertGroupName"}}</td>
+ </tr>
+ </table>
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/templates/main/alerts/manage_alert_groups_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/alerts/manage_alert_groups_popup.hbs b/ambari-web/app/templates/main/alerts/manage_alert_groups_popup.hbs
new file mode 100644
index 0000000..3e47b3c
--- /dev/null
+++ b/ambari-web/app/templates/main/alerts/manage_alert_groups_popup.hbs
@@ -0,0 +1,94 @@
+{{!
+* 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.
+}}
+<div class="alert alert-info margin-bottom-5">{{t alerts.actions.manage_alert_groups_popup.notice}}</div>
+{{#if controller.isLoaded}}
+ <div class="row-fluid manage-configuration-group-content">
+ <div class="span12">
+ <div class="row-fluid">
+ <div class="span4">
+ <span> </span>
+ {{view Em.Select
+ contentBinding="alertGroups"
+ optionLabelPath="content.displayNameDefinitions"
+ selectionBinding="view.selectedAlertGroup"
+ multiple="multiple"
+ class="group-select"
+ }}
+ <div class="btn-toolbar pull-right">
+ <button rel="button-info" class="btn" {{bindAttr data-original-title="view.addButtonTooltip"}}
+ {{action addAlertGroup target="controller"}}><i class="icon-plus"></i></button>
+ <button rel="button-info" class="btn" {{bindAttr data-original-title="view.removeButtonTooltip" disabled="view.isRemoveButtonDisabled"}}
+ {{action confirmDelete target="controller"}}><i class="icon-minus"></i></button>
+ <div class="btn-group">
+ <button class="btn dropdown-toggle" data-toggle="dropdown">
+ <i class="icon-cog"></i> <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu">
+ <li {{bindAttr class="view.isRenameButtonDisabled:disabled"}}>
+ <a href="" rel="button-info-dropdown" {{bindAttr data-original-title="view.renameButtonTooltip"}} {{action renameAlertGroup target="controller"}}>{{t alerts.actions.manage_alert_groups_popup.rename}}</a>
+ </li>
+ <li {{bindAttr class="view.isDuplicateButtonDisabled:disabled"}}>
+ <a href="" rel="button-info-dropdown" {{bindAttr data-original-title="view.duplicateButtonTooltip"}} {{action duplicateAlertGroup target="controller"}}>{{t alerts.actions.manage_alert_groups_popup.duplicate}}</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="span8">
+ <span> </span>
+ <div class="row-fluid">
+ <div class="span12 pull-right">
+ {{view Em.Select
+ contentBinding="selectedAlertGroup.definitions"
+ optionLabelPath="content.label"
+ multiple="multiple"
+ class="group-select"
+ selectionBinding="selectedDefinitions"
+ }}
+ </div>
+ <div class="button-group pull-right">
+ <a rel="button-info" {{bindAttr data-original-title="view.addDefinitionTooltip" class=":btn selectedAlertGroup.isAddDefinitionsDisabled:disabled"}} {{action addDefinitions target="controller"}} ><i class="icon-plus"></i></a>
+ <a rel="button-info" {{bindAttr data-original-title="view.removeDefinitionTooltip" class=":btn isDeleteDefinitionsDisabled:disabled"}} {{action deleteDefinitions target="controller"}} ><i class="icon-minus"></i></a>
+ </div>
+ </div>
+ <div class="row-fluid">
+ <div class="span2">{{t alerts.actions.manage_alert_groups_popup.notifications}}</div>
+ <div class="span10">
+ {{#each target in selectedAlertGroup.targets}}
+ {{target.name}}
+ {{/each}}
+ </div>
+ </div>
+
+ </div>
+ <div class="clearfix"></div>
+ <div class="row-fluid">
+ <div class="span12 text-error" id="manage-config-group-error-div">
+ {{#if view.errorMessage}}
+ {{errorMessage}}
+ {{else}}
+
+ {{/if}}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+{{else}}
+ <div class="spinner"></div>
+{{/if}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index bcaecd3..0f67b9b 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -305,7 +305,60 @@ var urls = {
},
'alerts.load_alert_notification': {
'real': '/alert_targets?fields=*',
- 'mock': 'data/alerts/alert_notifications.json'
+ 'mock': 'data/alerts/alertNotifications.json'
+ },
+ 'alerts.load_alert_groups': {
+ 'real': '/clusters/{clusterName}/alert_groups?fields=*',
+ 'mock': 'data/alerts/alertGroups.json'
+ },
+ 'alerts.load_an_alert_group': {
+ 'real': '/clusters/{clusterName}/alert_groups/{group_id}',
+ 'mock': 'data/alerts/alertGroup.json'
+ },
+ 'alert_groups.create': {
+ 'real': '/clusters/{clusterName}/alert_groups',
+ 'mock': '',
+ 'format': function (data) {
+ return {
+ type: 'POST',
+ data: JSON.stringify({
+ "AlertGroup": {
+ "name": data.name,
+ "definitions": data.definitions,
+ "targets": data.targets
+ }
+ })
+ };
+ }
+ },
+ 'alert_groups.update': {
+ 'real': '/clusters/{clusterName}/alert_groups/{group_id}',
+ 'mock': '',
+ 'format': function (data) {
+ return {
+ type: 'PUT',
+ data: JSON.stringify({
+ "AlertGroup": {
+ "name": data.name,
+ "definitions": data.definitions,
+ "targets": data.targets
+ }
+ })
+ };
+ }
+ },
+ 'alert_groups.delete': {
+ 'real': '/clusters/{clusterName}/alert_groups/{group_id}',
+ 'mock': '',
+ 'format': function (data) {
+ return {
+ type: 'DELETE'
+ };
+ }
+ },
+ 'alerts.load_all_alert_definitions': {
+ 'real': '/clusters/{clusterName}/alert_definitions?fields=*',
+ 'mock': 'data/alerts/alertDefinitions.json'
},
'alerts.instances': {
'real': '/clusters/{clusterName}/alerts?fields=*',
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/utils/validator.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/validator.js b/ambari-web/app/utils/validator.js
index fb51d4b..3fcdfb5 100644
--- a/ambari-web/app/utils/validator.js
+++ b/ambari-web/app/utils/validator.js
@@ -156,6 +156,16 @@ module.exports = {
return configKeyRegex.test(value);
},
+ /**
+ * validate alert group name
+ * @param value
+ * @return {Boolean}
+ */
+ isValidAlertGroupName: function(value) {
+ var configKeyRegex = /^[\s0-9a-z_\-]+$/i;
+ return configKeyRegex.test(value);
+ },
+
empty:function (e) {
switch (e) {
case "":
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 0009cfa..16bea99 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -44,6 +44,8 @@ require('views/main/menu');
require('views/main/alert_definitions_view');
require('views/main/alerts/definition_details_view');
require('views/main/alerts/alert_definitions_actions_view');
+require('views/main/alerts');
+require('views/main/alerts/manage_alert_groups_view');
require('views/main/charts');
require('views/main/views/details');
require('views/main/host');
http://git-wip-us.apache.org/repos/asf/ambari/blob/43704d45/ambari-web/app/views/main/alerts/manage_alert_groups_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/alerts/manage_alert_groups_view.js b/ambari-web/app/views/main/alerts/manage_alert_groups_view.js
new file mode 100644
index 0000000..61448f8
--- /dev/null
+++ b/ambari-web/app/views/main/alerts/manage_alert_groups_view.js
@@ -0,0 +1,105 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.MainAlertsManageAlertGroupView = Em.View.extend({
+
+ templateName: require('templates/main/alerts/manage_alert_groups_popup'),
+
+ selectedAlertGroup: null,
+
+ isRemoveButtonDisabled: true,
+
+ isRenameButtonDisabled: true,
+
+ isDuplicateButtonDisabled: true,
+
+ buttonObserver: function () {
+ var selectedAlertGroup = this.get('controller.selectedAlertGroup');
+ if(selectedAlertGroup && selectedAlertGroup.default){
+ this.set('isRemoveButtonDisabled', true);
+ this.set('isRenameButtonDisabled', true);
+ this.set('isDuplicateButtonDisabled', false);
+ }else{
+ this.set('isRemoveButtonDisabled', false);
+ this.set('isRenameButtonDisabled', false);
+ this.set('isDuplicateButtonDisabled', false);
+ }
+ }.observes('controller.selectedAlertGroup'),
+
+ onGroupSelect: function () {
+ var selectedAlertGroup = this.get('selectedAlertGroup');
+ // to unable user select more than one alert group at a time
+ if (selectedAlertGroup && selectedAlertGroup.length) {
+ this.set('controller.selectedAlertGroup', selectedAlertGroup[selectedAlertGroup.length - 1]);
+ }
+ if (selectedAlertGroup && selectedAlertGroup.length > 1) {
+ this.set('selectedConfigGroup', selectedAlertGroup[selectedAlertGroup.length - 1]);
+ }
+ this.set('controller.selectedDefinitions', []);
+ }.observes('selectedAlertGroup'),
+
+ onLoad: function () {
+ if (this.get('controller.isLoaded')) {
+ this.set('selectedAlertGroup', this.get('controller.alertGroups')[0])
+ }
+ }.observes('controller.isLoaded', 'controller.alertGroups'),
+
+ willInsertElement: function() {
+ this.get('controller').loadAlertGroups();
+ this.get('controller').loadAlertDefinitions();
+ },
+
+ didInsertElement: function () {
+
+ this.onLoad();
+ App.tooltip($("[rel='button-info']"));
+ App.tooltip($("[rel='button-info-dropdown']"), {placement: 'left'});
+ },
+
+ addButtonTooltip: function () {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.addButton');
+ }.property(),
+ removeButtonTooltip: function () {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.removeButton');
+ }.property(),
+ renameButtonTooltip: function () {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.renameButton');
+ }.property(),
+ duplicateButtonTooltip: function () {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.duplicateButton');
+ }.property(),
+ addDefinitionTooltip: function () {
+ if (this.get('controller.selectedAlertGroup.default')) {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.addDefinitionToDefault');
+ } else if (this.get('controller.selectedAlertGroup.isAddDefinitionsDisabled')) {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.addDefinitionDisabled');
+ } else {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.addDefinition');
+ }
+ }.property('controller.selectedConfigGroup.isDefault', 'controller.selectedConfigGroup.isAddHostsDisabled'),
+ removeDefinitionTooltip: function () {
+ return Em.I18n.t('alerts.actions.manage_alert_groups_popup.removeDefinition');
+ }.property(),
+
+ errorMessage: function () {
+ return this.get('controller.errorMessage');
+ }.property('controller.errorMessage')
+
+});