Browse Source

AMBARI-8341. Alerts UI: Create host level alert summary page

Srimanth Gunturi 10 years ago
parent
commit
88b29ea2d6

+ 1 - 0
ambari-web/app/assets/test/tests.js

@@ -45,6 +45,7 @@ var files = ['test/init_model_test',
   'test/controllers/global/configuration_controller_test',
   'test/controllers/global/configuration_controller_test',
   'test/controllers/main/app_contoller_test',
   'test/controllers/main/app_contoller_test',
   'test/controllers/main/alert_definitions_controller_test',
   'test/controllers/main/alert_definitions_controller_test',
+  'test/controllers/main/alerts/alert_instances_controller_test',
   'test/controllers/main/admin/stack_and_upgrade_test',
   'test/controllers/main/admin/stack_and_upgrade_test',
   'test/controllers/main/admin/serviceAccounts_controller_test',
   'test/controllers/main/admin/serviceAccounts_controller_test',
   'test/controllers/main/admin/highAvailability_controller_test',
   'test/controllers/main/admin/highAvailability_controller_test',

+ 1 - 0
ambari-web/app/controllers.js

@@ -92,6 +92,7 @@ require('controllers/main/host/details');
 require('controllers/main/host/configs_service');
 require('controllers/main/host/configs_service');
 require('controllers/main/host/add_controller');
 require('controllers/main/host/add_controller');
 require('controllers/main/host/addHost/step4_controller');
 require('controllers/main/host/addHost/step4_controller');
+require('controllers/main/host/host_alerts_controller');
 require('controllers/main/charts');
 require('controllers/main/charts');
 require('controllers/main/charts/heatmap_metrics/heatmap_metric');
 require('controllers/main/charts/heatmap_metrics/heatmap_metric');
 require('controllers/main/charts/heatmap_metrics/heatmap_metric_processrun');
 require('controllers/main/charts/heatmap_metrics/heatmap_metric_processrun');

+ 3 - 1
ambari-web/app/controllers/main/alerts/alert_instances_controller.js

@@ -34,7 +34,6 @@ App.MainAlertInstancesController = Em.Controller.extend({
   sourceName: null,
   sourceName: null,
 
 
   fetchAlertInstances: function () {
   fetchAlertInstances: function () {
-    this.set('isLoaded', false);
     switch (this.get('sourceType')) {
     switch (this.get('sourceType')) {
       case 'HOST':
       case 'HOST':
         App.ajax.send({
         App.ajax.send({
@@ -75,18 +74,21 @@ App.MainAlertInstancesController = Em.Controller.extend({
   },
   },
 
 
   loadAlertInstances: function () {
   loadAlertInstances: function () {
+    this.set('isLoaded', false);
     this.set('sourceType', null);
     this.set('sourceType', null);
     this.set('sourceName', null);
     this.set('sourceName', null);
     this.fetchAlertInstances();
     this.fetchAlertInstances();
   },
   },
 
 
   loadAlertInstancesByHost: function (hostName) {
   loadAlertInstancesByHost: function (hostName) {
+    this.set('isLoaded', false);
     this.set('sourceType', 'HOST');
     this.set('sourceType', 'HOST');
     this.set('sourceName', hostName);
     this.set('sourceName', hostName);
     this.fetchAlertInstances();
     this.fetchAlertInstances();
   },
   },
 
 
   loadAlertInstancesByAlertDefinition: function (definitionName) {
   loadAlertInstancesByAlertDefinition: function (definitionName) {
+    this.set('isLoaded', false);
     this.set('sourceType', 'ALERT_DEFINITION');
     this.set('sourceType', 'ALERT_DEFINITION');
     this.set('sourceName', definitionName);
     this.set('sourceName', definitionName);
     this.fetchAlertInstances();
     this.fetchAlertInstances();

+ 36 - 0
ambari-web/app/controllers/main/host/host_alerts_controller.js

@@ -0,0 +1,36 @@
+/**
+ * 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.MainHostAlertsController = Em.ArrayController.extend({
+  name: 'mainHostAlertsController',
+
+  selectedHost: function() {
+    return App.get('router.mainHostDetailsController.content');
+  }.property('App.router.mainHostDetailsController.content'),
+
+  /**
+   * List of all <code>App.AlertDefinition</code> by Host
+   * @type {App.AlertDefinition[]}
+   */
+  content: function() {
+    return App.AlertInstance.find().toArray().filterProperty('host', this.get('selectedHost'));
+  }.property('App.router.mainAlertInstancesController.isLoaded', 'selectedHost')
+
+});

+ 1 - 0
ambari-web/app/messages.js

@@ -841,6 +841,7 @@ Em.I18n.translations = {
 
 
   'alerts.table.noAlerts': 'No Alerts to display',
   'alerts.table.noAlerts': 'No Alerts to display',
   'alerts.table.header.lastTriggered': 'Last Triggered',
   'alerts.table.header.lastTriggered': 'Last Triggered',
+  'alerts.table.header.notification': 'Notification',
   'alerts.filters.filteredAlertsInfo': '{0} of {1} alerts showing',
   'alerts.filters.filteredAlertsInfo': '{0} of {1} alerts showing',
 
 
   'alerts.thresholds': 'Thresholds',
   'alerts.thresholds': 'Thresholds',

+ 27 - 93
ambari-web/app/models/alert_instance.js

@@ -35,105 +35,39 @@ App.AlertInstance = DS.Model.extend({
   text: DS.attr('string'),
   text: DS.attr('string'),
   notification: DS.hasMany('App.AlertNotification'),
   notification: DS.hasMany('App.AlertNotification'),
 
 
+  formattedNotifications: function () {
+    return this.get('notification').mapProperty('name').join(', ');
+  }.property('notification'),
+
+  /**
+   * Status icon markup
+   * @type {App.AlertDefinition[]}
+   */
+  status: function () {
+    var typeIcons = this.get('typeIcons');
+    var state = this.get('state');
+    return '<span class="' + typeIcons[state] + ' alert-state-' + state + '"></span>';
+  }.property('state'),
+
   /**
   /**
    * Formatted timestamp for latest instance triggering
    * Formatted timestamp for latest instance triggering
    * @type {string}
    * @type {string}
    */
    */
   lastTriggered: function() {
   lastTriggered: function() {
     return dateUtils.dateFormat(this.get('latestTimestamp'));
     return dateUtils.dateFormat(this.get('latestTimestamp'));
-  }.property('latestTimestamp')
-});
+  }.property('latestTimestamp'),
 
 
-App.AlertInstance.FIXTURES = [
-  {
-    "id": 1,
-    "cluster_name": "tdk",
-    "component_name": "SECONDARY_NAMENODE",
-    "host_name": "tr-2.c.pramod-thangali.internal",
-    "instance": null,
-    "label": "Secondary NameNode Process",
-    "latest_timestamp": 1414664775337,
-    "maintenance_state": "OFF",
-    "name": "secondary_namenode_process",
-    "original_timestamp": 1414585335334,
-    "scope": "ANY",
-    "service_name": "HDFS",
-    "state": "CRITICAL",
-    "text": "Connection failed: [Errno 111] Connection refused on host tr-2.c.pramod-thangali.internal:50090",
-    "alert_definition": 1,
-    "notification": 1
-  },
-  {
-    "cluster_name" : "tdk",
-    "component_name" : "DATANODE",
-    "host_name" : "tr-3.c.pramod-thangali.internal",
-    "id" : 2,
-    "instance" : null,
-    "label" : "DataNode Web UI",
-    "latest_timestamp" : 1414666905645,
-    "maintenance_state" : "OFF",
-    "name" : "datanode_webui",
-    "original_timestamp" : 1414585365674,
-    "scope" : "HOST",
-    "service_name" : "HDFS",
-    "state" : "CRITICAL",
-    "text" : "Connection failed to 0.0.0.0:50075",
-    "alert_definition": 2,
-    "notification": 2
-  },
-  {
-    "cluster_name": "tdk",
-    "component_name": "ZOOKEEPER_SERVER",
-    "host_name": "tr-1.c.pramod-thangali.internal",
-    "id": 3,
-    "instance": null,
-    "label": "ZooKeeper Server Process",
-    "latest_timestamp": 1414665174611,
-    "maintenance_state": "OFF",
-    "name": "zookeeper_server_process",
-    "original_timestamp": 1414585014606,
-    "scope": "ANY",
-    "service_name": "ZOOKEEPER",
-    "state": "CRITICAL",
-    "text": "TCP OK - 0.0000 response on port 2181",
-    "alert_definition": 3,
-    "notification": 3
-  },
-  {
-    "cluster_name": "tdk",
-    "component_name": "ZOOKEEPER_SERVER",
-    "host_name": "tr-2.c.pramod-thangali.internal",
-    "id": 4,
-    "instance": null,
-    "label": "ZooKeeper Server Process",
-    "latest_timestamp": 1414665135341,
-    "maintenance_state": "OFF",
-    "name": "zookeeper_server_process",
-    "original_timestamp": 1414585035316,
-    "scope": "ANY",
-    "service_name": "ZOOKEEPER",
-    "state": "OK",
-    "text": "TCP OK - 0.0000 response on port 2181",
-    "alert_definition": 3,
-    "notification": 3
-  },
-  {
-    "cluster_name": "tdk",
-    "component_name": "ZOOKEEPER_SERVER",
-    "host_name": "tr-3.c.pramod-thangali.internal",
-    "id": 5,
-    "instance": null,
-    "label": "ZooKeeper Server Process",
-    "latest_timestamp": 1414665165640,
-    "maintenance_state": "OFF",
-    "name": "zookeeper_server_process",
-    "original_timestamp": 1414585065616,
-    "scope": "ANY",
-    "service_name": "ZOOKEEPER",
-    "state": "OK",
-    "text": "TCP OK - 0.0000 response on port 2181",
-    "alert_definition": 3,
-    "notification": 3
+  /**
+   * List of css-classes for alert instance status
+   * @type {object}
+   */
+  typeIcons: {
+    'OK': 'icon-ok-sign',
+    'WARNING': 'icon-warning-sign',
+    'CRITICAL': 'icon-remove',
+    'DISABLED': 'icon-off',
+    'UNKNOWN': 'icon-question-sign'
   }
   }
-];
+});
+
 App.AlertInstance.FIXTURES = [];
 App.AlertInstance.FIXTURES = [];

+ 8 - 0
ambari-web/app/routes/main.js

@@ -272,6 +272,14 @@ module.exports = Em.Route.extend({
         route: '/alerts',
         route: '/alerts',
         connectOutlets: function (router, context) {
         connectOutlets: function (router, context) {
           router.get('mainHostDetailsController').connectOutlet('mainHostAlerts');
           router.get('mainHostDetailsController').connectOutlet('mainHostAlerts');
+        },
+        enter: function(router) {
+          var hostName = router.get('mainHostDetailsController.content.hostName');
+          router.get('mainAlertInstancesController').loadAlertInstancesByHost(hostName);
+          router.set('mainAlertInstancesController.isUpdating', true);
+        },
+        exit: function(router) {
+          router.set('mainAlertInstancesController.isUpdating', false);
         }
         }
       }),
       }),
 
 

+ 43 - 0
ambari-web/app/styles/alerts.less

@@ -190,3 +190,46 @@
     cursor: pointer;
     cursor: pointer;
   }
   }
 }
 }
+
+#host-alerts-table {
+  a {
+    &.disabled {
+      color: #000;
+    }
+  }
+
+  .col0,
+  td:first-child,
+  th:first-child {
+    width: 30%;
+  }
+
+  .col1,
+  td:first-child + td,
+  th:first-child + th {
+    width: 10%;
+    .filter-input-width {
+      width: 90%;
+    }
+  }
+
+  .col2,
+  td:first-child + td + td,
+  th:first-child + th + th {
+    width: 20%;
+    .filter-input-width {
+      width: 90%;
+    }
+  }
+  .col3,
+  td:first-child + td + td + td,
+  th:first-child + th + th + th {
+    width: 20%
+  }
+
+  .col4,
+  td:first-child + td + td + td + td,
+  th:first-child + th + th + th + th {
+    width: 20%;
+  }
+}

+ 87 - 0
ambari-web/app/templates/main/host/host_alerts.hbs

@@ -0,0 +1,87 @@
+{{!
+* 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 id="alerts">
+  <div class="box-header row">
+  </div>
+
+  {{#if App.router.mainAlertInstancesController.isLoaded}}
+
+  <table class="table advanced-header-table table-bordered table-striped alerts-table" id="host-alerts-table">
+    <thead>
+      {{#view view.sortView classNames="label-row" contentBinding="view.filteredContent"}}
+        {{view view.parentView.nameSort class="first"}}
+        {{view view.parentView.statusSort}}
+        {{view view.parentView.serviceSort}}
+        {{view view.parentView.lastTriggeredSort}}
+        {{view view.parentView.formattedNotificationsSort}}
+      {{/view}}
+    <tr class="filter-row">
+      <th class="first">{{view view.nameFilterView}}</th>
+      <th>{{view view.stateFilterView}}</th>
+      <th>{{view view.serviceFilterView}}</th>
+      <th>{{view view.triggeredFilterView}}</th>
+      <th>{{view view.formattedNotificationsView}}</th>
+      <th></th>
+    </tr>
+    </thead>
+    <tbody>
+    {{#if view.pageContent}}
+      {{#each alertInstance in view.pageContent}}
+        <tr>
+          <td class="first">
+            <a href="#">{{alertInstance.label}}</a>
+          </td>
+          <td>{{{alertInstance.status}}}</td>
+          <td>{{alertInstance.service.serviceName}}</td>
+          <td>{{alertInstance.lastTriggered}}</td>
+          <td>{{alertInstance.formattedNotifications}}</td>
+        </tr>
+      {{/each}}
+    {{else}}
+      <tr>
+        <td class="first"></td>
+        <td colspan="4">
+          {{t alerts.table.noAlerts}}
+        </td>
+      </tr>
+    {{/if}}
+    </tbody>
+  </table>
+
+  {{else}}
+    <div class="spinner"></div>
+  {{/if}}
+
+  <div class="page-bar">
+    <div class="filtered-info span4">
+      <label>{{view.filteredContentInfo}} - <a {{action clearFilters target="view"}}
+          href="#">{{t tableView.filters.clearAllFilters}}</a></label>
+    </div>
+    <div class="selected-hosts-info span4">
+    </div>
+    <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">
+      <a {{bindAttr class="view.paginationLeftClass"}}{{action previousPage target="view"}}><i class="icon-arrow-left"></i></a>
+      <a {{bindAttr class="view.paginationRightClass"}}{{action nextPage target="view"}}><i class="icon-arrow-right"></i></a>
+    </div>
+  </div>
+</div>

+ 1 - 0
ambari-web/app/views.js

@@ -65,6 +65,7 @@ require('views/main/host/configs_service_menu');
 require('views/main/host/metrics');
 require('views/main/host/metrics');
 require('views/main/host/stack_versions_view');
 require('views/main/host/stack_versions_view');
 require('views/main/host/add_view');
 require('views/main/host/add_view');
+require('views/main/host/host_alerts_view');
 require('views/main/host/metrics/cpu');
 require('views/main/host/metrics/cpu');
 require('views/main/host/metrics/disk');
 require('views/main/host/metrics/disk');
 require('views/main/host/metrics/load');
 require('views/main/host/metrics/load');

+ 56 - 0
ambari-web/app/views/main/host/host_alerts_view.js

@@ -0,0 +1,56 @@
+/**
+ * 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 filters = require('views/common/filter_view'),
+  sort = require('views/common/sort_view');
+
+App.MainHostAlertsView = App.MainAlertDefinitionsView.extend({
+  templateName: require('templates/main/host/host_alerts'),
+
+  /**
+   * Sorting header for <label>alertInstance.formattedNotifications</label>
+   * @type {Em.View}
+   */
+  formattedNotificationsSort: sort.fieldView.extend({
+    column: 5,
+    name: 'formattedNotifications',
+    displayName: Em.I18n.t('alerts.table.header.notification'),
+    type: 'string'
+  }),
+
+  /**
+   * Filtering header for <label>alertDefinition.service.serviceName</label>
+   * @type {Em.View}
+   */
+  formattedNotificationsView: filters.createSelectView({
+    column: 5,
+    fieldType: 'filter-input-width',
+    content: function () {
+      return [
+        {
+          value: '',
+          label: Em.I18n.t('common.all')
+        }
+      ]
+    }.property(''),
+    onChangeValue: function () {
+    }
+  })
+
+});

+ 69 - 0
ambari-web/test/controllers/main/alerts/alert_instances_controller_test.js

@@ -0,0 +1,69 @@
+/**
+ * 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 controller;
+
+describe('App.MainAlertDefinitionActionsController', function () {
+
+  beforeEach(function () {
+    controller = App.MainAlertInstancesController.create({});
+  });
+
+  describe('#fetchAlertInstances', function () {
+
+    describe('loading instances from correct endpoint', function () {
+
+      beforeEach(function () {
+        sinon.stub(App.ajax, 'send', Em.K);
+      });
+
+      afterEach(function () {
+        App.ajax.send.restore();
+      });
+
+      it('should load by Host name', function () {
+
+        controller.loadAlertInstancesByHost();
+        console.log(App.ajax.send.args[0]);
+        expect(App.ajax.send.args[0][0].name).to.equal('alerts.instances.by_host');
+
+      });
+
+      it('should load by AlertDefinition name', function () {
+
+        controller.loadAlertInstancesByAlertDefinition();
+        console.log(App.ajax.send.args[0]);
+        expect(App.ajax.send.args[0][0].name).to.equal('alerts.instances.by_definition');
+
+      });
+
+      it('should load all', function () {
+
+        controller.loadAlertInstances();
+        console.log(App.ajax.send.args[0]);
+        expect(App.ajax.send.args[0][0].name).to.equal('alerts.instances');
+
+      });
+
+    });
+
+  });
+
+});