Hello community,

here is the log from the commit of package hawk2 for openSUSE:Factory checked 
in at 2015-10-25 19:13:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/hawk2 (Old)
 and      /work/SRC/openSUSE:Factory/.hawk2.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "hawk2"

Changes:
--------
--- /work/SRC/openSUSE:Factory/hawk2/hawk2.changes      2015-10-20 
00:09:12.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.hawk2.new/hawk2.changes 2015-10-25 
19:13:20.000000000 +0100
@@ -1,0 +2,17 @@
+Thu Oct 22 21:51:52 UTC 2015 - [email protected]
+
+- Update to version 1.0.0~alpha1+git.1445537620.7637b7f:
+  + Dashboard: Blinkenlights display for dashboard (bsc#951584)
+  + Command Log: Tag simulated commands (bsc#950618)
+
+-------------------------------------------------------------------
+Wed Oct 21 16:03:52 UTC 2015 - [email protected]
+
+- Update to version 1.0.0~alpha1+git.1445441661.907341b:
+  + View: Fix overlapping growl notifications (bsc#950626)
+  + Cib: Patch remote node state from resource (bsc#949240)
+  + Cib: Don't try to read CIB XML if cluster is not running
+  + Dashboard: Fix adding/removing clusters
+  + Status: Show unclean status as error
+
+-------------------------------------------------------------------

Old:
----
  hawk2-1.0.0~alpha1+git.1445271072.1114d0a.tar.bz2

New:
----
  hawk2-1.0.0~alpha1+git.1445537620.7637b7f.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ hawk2.spec ++++++
--- /var/tmp/diff_new_pack.TYSlBv/_old  2015-10-25 19:13:21.000000000 +0100
+++ /var/tmp/diff_new_pack.TYSlBv/_new  2015-10-25 19:13:21.000000000 +0100
@@ -39,7 +39,7 @@
 Summary:        HA Web Konsole
 License:        GPL-2.0
 Group:          %{pkg_group}
-Version:        1.0.0~alpha1+git.1445271072.1114d0a
+Version:        1.0.0~alpha1+git.1445537620.7637b7f
 Release:        0
 Url:            http://www.clusterlabs.org/wiki/Hawk
 Source:         %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.TYSlBv/_old  2015-10-25 19:13:21.000000000 +0100
+++ /var/tmp/diff_new_pack.TYSlBv/_new  2015-10-25 19:13:21.000000000 +0100
@@ -1,4 +1,4 @@
 <servicedata>
 <service name="tar_scm">
             <param name="url">git://github.com/ClusterLabs/hawk.git</param>
-          <param 
name="changesrevision">1114d0a7161ac85b17d1eca5ad73fda1ccf2cc58</param></service></servicedata>
\ No newline at end of file
+          <param 
name="changesrevision">7637b7ffbf6688f8778d574971bff88d96cb204b</param></service></servicedata>
\ No newline at end of file

++++++ hawk2-1.0.0~alpha1+git.1445271072.1114d0a.tar.bz2 -> 
hawk2-1.0.0~alpha1+git.1445537620.7637b7f.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/application.js
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/application.js
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/application.js
    2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/application.js
    2015-10-22 23:51:44.000000000 +0200
@@ -6,6 +6,7 @@
 //= require module/basics
 //= require module/help
 //= require module/forms
+//= require module/statusmatrix
 //= require module/oplist
 //= require module/attrlist
 //= require module/wizattrlist
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/dashboard.js
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/dashboard.js
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/dashboard.js
      2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/dashboard.js
      2015-10-22 23:51:44.000000000 +0200
@@ -1,4 +1,5 @@
 // Copyright (c) 2009-2015 Tim Serong <[email protected]>
+// Copyright (c) 2015 Kristoffer Gronlund <[email protected]>
 // See COPYING for license.
 
 // https://<host>:7630/cib/mini.json
@@ -140,108 +141,46 @@
     }
 
     text += '<div class="row">';
-    text += '<div class="col-md-12">';
-    text += '<div class="list-group">';
+    text += '<div class="col-md-12 text-center">';
 
-    var makePopoverButton = function(id, content, cls, title) {
-      var body = "";
-      body += '<a tabindex="0" id="' + id + '" type="button" style="width: 
100%;" class="btn list-group-item ' + cls + '" ';
-      body += 'data-trigger="focus" data-toggle="popover" title="' + title + 
'" ';
-      body += 'data-content="" ';
-      body += '>';
-      body += content;
-      body += '</a>';
-      return body;
+    var status_summary = {
+      nodes: [],
+      resources: [],
+      tickets: []
     };
 
-    var dlBegin = function() {
-      return '<dl class="dl-horizontal">';
-    };
-
-    var dlEnd = function() {
-      return '</dl>';
-    };
-
-    var dlAdd = function(title, text) {
-      return '<dt>' + title + '</dt><dd>' + text + '</dd>';
-    }
-
-    var descCache = {};
-
-    $.each(cib.node_states, function(state, count) {
-      if (count > 0) {
-        var details = dlBegin();
-        $.each(cib.nodes, function(n, v) {
-          if (state == v) {
-            details += dlAdd(n, v);
-          }
-        });
-        details += dlEnd();
-        descCache['node_btn_' + state] = details;
-
-        text += makePopoverButton('node_btn_' + state, count + ' ' + state + ' 
' + plural('node', count),
-                                  listGroupClassForState(state), 
__("Details"));
-      }
+    $.each(cib.nodes, function(n, v) {
+      status_summary.nodes.push({name: n, state: v});
     });
 
-    $.each(cib.resource_states, function(state, count) {
-      if (count > 0) {
-        var details = dlBegin();
-        $.each(cib.resources, function(rsc, locs) {
-          if (state == "stopped" && $.isEmptyObject(locs)) {
-            details += dlAdd(rsc, "<em>[stopped]</em>");
-          } else {
-            var active = "";
-            $.each(locs, function(node, st) {
-              if (st == state) {
-                active += node + " ";
-              }
-            });
-            if (active != "")
-              details += dlAdd(rsc, active);
-          }
-        });
-        details += dlEnd();
-        descCache['rsc_btn_' + state] = details;
+    $.each(cib.resources, function(n, v) {
+      var rsc = {name: n, instances: []};
+      $.each(v, function(nn, vv) {
+        rsc.instances.push({node: nn, state: vv});
+      });
+      status_summary.resources.push(rsc);
+    });
 
-        text += makePopoverButton('rsc_btn_' + state, count + ' ' + state + ' 
' + plural('resource', count),
-                                  listGroupClassForState(state), 
__("Details"));
-      }
+    $.each(cib.tickets, function(n, v) {
+      status_summary.tickets.push({name: n, state: v});
     });
 
-    $.each(cib.ticket_states, function(state, count) {
-      if (count > 0) {
-        var details = dlBegin();
-        $.each(cib.tickets, function(i, e) {
-          $.each(e, function(name, tstate) {
-            if (state == tstate) {
-              details += dlAdd(name, state);
-            }
-          });
-        });
-        details += dlEnd();
-        descCache['ticket1_btn_' + state] = details;
+    var cwidth = Math.min(status_summary.nodes.length * 36 + 36, 360);
+    var cheight = Math.min(status_summary.resources.length * 24 + 
status_summary.tickets.length * 24, 550);
 
-        text += makePopoverButton('ticket1_btn_' + state, count + ' ' + state 
+ ' ' + plural('ticket', count),
-                                  listGroupClassForState(state), 
__("Details"));
-      }
-    });
+    text += '<canvas width="' + cwidth + '" height="' + cheight + 
'"></canvas>';
 
     text += '</div>';
     text += '</div>';
-    text += '</div>';
 
-    var cs = checksum(text);
+    var cs = checksum(text + JSON.stringify(status_summary));
 
     if (tag.data('hash') != cs) {
       tag.html(text);
       tag.data('hash', cs);
 
-      $.each(descCache, function(id, desc) {
-        var btn = $('#' + clusterId + ' #' + id);
-        btn.data("content", desc);
-        btn.popover({animation: true, html: true});
-      });
+      tag.find('canvas').cibStatusMatrix(status_summary);
+
     }
 
   }
@@ -459,20 +398,39 @@
     return content;
   }
 
+  var updateLayout = function() {
+    var clusters = $("#clusters").children();
+    var nclusters = clusters.length;
+    if (nclusters == 1) {
+      clusters.removeClass().addClass("col-md-6 col-md-offset-3");
+    } else if (nclusters == 2) {
+      clusters.removeClass().addClass("col-md-6");
+    } else if (nclusters == 3) {
+      clusters.removeClass().addClass("col-md-6 col-lg-4");
+    } else if (nclusters == 4) {
+      clusters.removeClass().addClass("col-md-6");
+    } else {
+      clusters.removeClass().addClass("col-md-4");
+    }
+  };
+
   return function(data) {
     var clusterId = newClusterId();
     var title = data.name || __("Local Status");
 
     var content = basicCreateBody(clusterId, data);
 
-    var text = '<div class="col-lg-4 col-sm-6 col-xs-12">' +
-        '<div id="' +
-        clusterId +
-        '" class="panel panel-default" data-epoch="">' +
-        '<div class="panel-heading">' +
-        '<h3 class="panel-title">' +
-        '<span id="refresh"></span> ' +
-        '<a href="' + baseUrl(data) + '/">' + title + '</a>';
+    var text = [
+      '<div id="outer-',
+      clusterId,
+      '" class="col-lg-4 col-sm-6 col-xs-12">',
+      '<div id="',  clusterId, '" class="panel panel-default" data-epoch="">',
+      '<div class="panel-heading">',
+      '<h3 class="panel-title">',
+      '<span id="refresh"></span> ',
+      '<a href="', baseUrl(data), '/">', title, '</a>'
+    ].join('');
+
     if (data.host != null) {
       var s_remove = __('Remove cluster _NAME_ from 
dashboard?').replace('_NAME_', data.name);
       text = text +
@@ -493,16 +451,20 @@
       '</div>';
     $("#clusters").append(text);
 
+    updateLayout();
+
     if (data.host == null) {
       clusterRefresh(clusterId, data);
     } else {
       var close = $("#" + clusterId).find(".panel-title form");
       close.on("ajax:success", function(e, data) {
-        $("#" + clusterId).remove();
+        $("#" + clusterId).parent().remove();
+        updateLayout();
         $.growl({ message: __('Cluster removed successfully.')}, {type: 
'success'});
       });
       close.on("ajax:error", function(e, xhr, status, error) {
-        $.growl({ message: __('Failed to remove cluster.')}, {type: 'danger'});
+        $("#" + clusterId).parent().remove();
+        $.growl({ message: __('Error removing cluster.')}, {type: 'danger'});
       });
       var body = $("#" + clusterId).find(".panel-body");
       body.find("button.btn").click(function() {
@@ -513,8 +475,18 @@
 })();
 
 var dashboardSetupAddClusterForm = function() {
-  $('form').toggleify();
-  $('form').on("ajax:success", function(e, data, status, xhr) {
+  $('#new_cluster').toggleify();
+  $('#new_cluster').on("submit", function() {
+    $('.modal-content .form-errors').append([
+      '<div class="alert alert-info">',
+      '<i class="fa fa-refresh fa-2x fa-spin"></i> ',
+      __("Please wait..."),
+      '</div>'
+    ].join(''));
+    $(this).find('.submit').prop('disabled', true);
+    return true; // ensure submit actually happens
+  });
+  $('#new_cluster').on("ajax:success", function(e, data, status, xhr) {
     $('#modal').modal('hide');
     $('.modal-content').html('');
     dashboardAddCluster(data);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/module/basics.js
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/module/basics.js
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/module/basics.js
  2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/module/basics.js
  2015-10-22 23:51:44.000000000 +0200
@@ -20,7 +20,7 @@
   $.growl(
     false,
     {
-      element: '#middle .container-fluid',
+      element: '#middle #flashes',
       mouse_over: 'pause',
       allow_dismiss: true
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/module/nodes.js
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/module/nodes.js
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/module/nodes.js
   2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/module/nodes.js
   2015-10-22 23:51:44.000000000 +0200
@@ -30,39 +30,32 @@
           clickToSelect: true,
           class: 'col-sm-1',
           formatter: function(value, row, index) {
+            var icon = ['fa', 'fa-lg'];
+            var title = row.state;
             switch(row.state) {
             case 'online':
-              return [
-                '<i class="fa fa-play fa-lg text-success" title="',
-                row.state,
-                '"></i>'
-              ].join('');
+              icon.push('fa-play', 'text-success');
               break;
-
             case 'offline':
-              return [
-                '<i class="fa fa-stop fa-lg text-info" title="',
-                row.state,
-                '"></i>'
-              ].join('');
+              icon.push('fa-stop', 'text-info');
               break;
-
             case 'fence':
-              return [
-                '<i class="fa fa-exclamation-triangle fa-lg text-danger" 
title="',
-                row.state,
-                '"></i>'
-              ].join('');
+              icon.push('fa-exclamation-triangle', 'text-danger');
+              break;
+            case 'unclean':
+              icon.push('fa-exclamation-triangle', 'text-danger');
               break;
-
             default:
-              return [
-                '<i class="fa fa-question-circle fa-lg text-warning" title="',
-                row.state,
-                '"></i>'
-              ].join('');
+              icon.push('fa-question-circle', 'text-warning');
               break;
             }
+            var ret = ['<i class="', icon.join(' '), '" title="', title, 
'"></i>'];
+
+            if (row.remote) {
+              ret.push(' <i class="fa fa-cloud text-info" title="', 
__("Remote"), '"></i>');
+            }
+
+            return ret.join('');
           }
         }, {
           field: 'name',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/module/statusmatrix.js
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/module/statusmatrix.js
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/javascripts/module/statusmatrix.js
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/javascripts/module/statusmatrix.js
    2015-10-22 23:51:44.000000000 +0200
@@ -0,0 +1,201 @@
+// Copyright (c) 2015 Kristoffer Gronlund <[email protected]>
+// See COPYING for license.
+
+// render canvas-based visualizations for cluster status.
+
+
+$(function() {
+  $.fn.cibStatusMatrix = function(status, options) {
+    var canvas = this.get(0);
+
+    var ctx = canvas.getContext("2d");
+
+    var defaults = {
+      colors: {
+        master: "#00aeef",
+        started: "#7ac143",
+        slave: "#f8981d",
+        stopped: "#d9534f",
+        pending: "#ffd457",
+        failed: "#ed1c24",
+        online: "#d0e4a6",
+        standby: "#747678",
+        offline: "#e4e5e6",
+        unclean: "#ec008c",
+        granted: "#c1d82f",
+        revoked: "#b5bbd4",
+      },
+      icons: {
+        master: "fa-circle text-info",
+        started: "fa-circle text-success",
+        slave: "fa-circle text-warning",
+        stopped: "fa-minus-circle text-danger",
+        pending: "fa-question-circle text-warning",
+        failed: "fa-times-circle text-danger",
+        online: "fa-circle-thin text-success",
+        standby: "fa-dot-circle-o text-warning",
+        offline: "fa-minus-circle text-muted",
+        unclean: "fa-circle text-danger",
+        granted: "fa-check-circle text-success",
+        revoked: "fa-times-circle-o text-muted",
+      },
+      title: __("Details"),
+      info_selector: "#cib-status-matrix-details",
+      info_template: [
+        '<ul class="list-unstyled">',
+        '{{if item}}',
+        '<li><h3><i class="fa fa-lg {{>item_icon}}"></i> {{:item.name}} 
<small>{{:item.state}}</small></h3></li>',
+        '{{/if}}',
+        '{{if node}}',
+        '<li><h3><i class="fa fa-lg {{>node_icon}}"></i> {{:node.name}} 
<small>{{:node.state}}</small></h3></li>',
+        '{{/if}}',
+        '</ul>'
+      ].join('')
+    };
+    if (options === undefined) {
+      options = defaults;
+    } else {
+      $.extend(defaults, options);
+      options = defaults;
+    }
+
+    var colorByState = options.colors;
+
+    var render = function(x, y, w, h, node, item) {
+      var inset = 3;
+      ctx.lineWidth = 3;
+      var hasStroke = false;
+      var hasFill = false;
+
+      if (item != null) {
+        if (item.state in colorByState) {
+          hasFill = true;
+          ctx.fillStyle = colorByState[item.state];
+        }
+      }
+
+      if (node != null && node.state in colorByState) {
+        hasStroke = true;
+        ctx.strokeStyle = colorByState[node.state];
+      } else {
+        ctx.strokeStyle = null;
+      }
+
+      if (hasFill && !hasStroke) {
+        ctx.strokeStyle = ctx.fillStyle;
+        ctx.strokeRect(x + inset, y + inset, w - inset*2, h - inset*2);
+      }
+      if (hasFill) {
+        ctx.fillRect(x + inset, y + inset, w - inset*2, h - inset*2);
+      }
+
+      if (hasStroke && !hasFill) {
+        ctx.fillStyle = ctx.strokeStyle;
+        ctx.fillRect(x + inset, y + inset, w - inset*2, h - inset*2);
+      }
+      if (hasStroke) {
+        ctx.strokeRect(x + inset, y + inset, w - inset*2, h - inset*2);
+      }
+
+      if (!hasStroke && !hasFill) {
+        ctx.strokeStyle = options.colors.offline;
+        ctx.strokeRect(x + inset, y + inset, w - inset*2, h - inset*2);
+      }
+    };
+
+
+    var width = $(this).attr("width") || 320;
+    var height = $(this).attr("height") || 240;
+    var ncols = status.nodes.length + 1;
+    var nrows = Math.max(status.resources.length + status.tickets.length, 1);
+    var cellw = width / ncols;
+    var cellh = height / nrows;
+
+    var columns = [];
+    for (var i = 0; i < status.nodes.length; ++i) {
+      columns.push({name: status.nodes[i].name, state: status.nodes[i].state, 
row: []});
+      columns[columns.length-1].row.length = nrows;
+    }
+    var stopped = {name: __("Stopped Resources"), state: null, row: []}
+    columns.push(stopped);
+
+    $.each(columns, function(col, node) {
+      $.each(status.resources, function(row, rsc) {
+        var hasInstance = false;
+        if ("instances" in rsc) {
+          for (var i = 0; i < rsc.instances.length; i++) {
+            if (!hasInstance && rsc.instances[i].node == node.name) {
+              hasInstance = true;
+              var inst = {name: rsc.name, state: rsc.instances[i].state};
+              node.row[row] = inst;
+              render(col*cellw, row*cellh, cellw, cellh, node, inst);
+            }
+          }
+
+          if (rsc.instances.length == 0 && !("_stopped" in rsc)) {
+            rsc._stopped = true;
+            var srsc = {name: rsc.name, state: "stopped"};
+            stopped.row[row] = srsc;
+            render((ncols-1)*cellw, row*cellh, cellw, cellh, stopped, srsc);
+          }
+        }
+        if (!hasInstance) {
+          render(col*cellw, row*cellh, cellw, cellh, node, null);
+        }
+      });
+    });
+
+    var toffset = status.resources.length;
+    $.each(status.tickets, function(trow, ticket) {
+      var row = trow + toffset;
+      columns[0].row[row] = ticket;
+      render(0*cellw, row*cellh, width, cellh, null, ticket);
+    });
+
+    var lasthitx = null;
+    var lasthity = null;
+
+    var infoTmpl = $.templates(options.info_template);
+
+    this.on("mousemove", function(event) {
+      var rx = event.pageX - $(this).offset().left;
+      var ry = event.pageY - $(this).offset().top;
+      var hitx = Math.floor(rx / cellw);
+      var hity = Math.floor(ry / cellh);
+      var hitc = columns[hitx];
+      if (hitc) {
+        var hitr = hitc.row[hity];
+        if (lasthitx != hitx || lasthity != hity) {
+          var data = { title: options.title, node: null, item: null };
+          if (hity >= status.resources.length) {
+            hitr = columns[0].row[hity];
+          } else {
+            data.node = hitc;
+            data.node_icon = options.icons[hitc.state || "offline"];
+          }
+          data.item = hitr;
+          if (hitr) {
+            data.item_icon = options.icons[hitr.state || "stopped"];
+          } else {
+            data.item_icon = options.icons["stopped"];
+          }
+          var abshitx = $(this).offset().left + hitx*cellw;
+          var abshity = $(this).offset().top + hity*cellh;
+          var target = $(options.info_selector);
+          target.html(infoTmpl.render(data)).width("auto");
+          if (abshitx + (cellw / 2) > $(window).width() / 2) {
+            target.addClass("right").removeClass("left").offset({left: abshitx 
- 16 - target.outerWidth(), top: abshity - 16});
+          } else {
+            target.removeClass("right").addClass("left").offset({left: abshitx 
+ cellw + 16, top: abshity - 16});
+          }
+        }
+      }
+      lasthitx = hitx;
+      lasthity = hity;
+    }).on("mouseleave", function(event) {
+      $(options.info_selector).fadeOut(200);
+    }).on("mouseenter", function(event) {
+      $(options.info_selector).fadeIn(200);
+    });
+  };
+});
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/stylesheets/application.scss
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/stylesheets/application.scss
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/stylesheets/application.scss
  2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/stylesheets/application.scss
  2015-10-22 23:51:44.000000000 +0200
@@ -21,3 +21,5 @@
 @import "application/head";
 @import "application/content";
 @import "application/media";
+
+@import "dashboard";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/stylesheets/dashboard.scss
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/stylesheets/dashboard.scss
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/assets/stylesheets/dashboard.scss
    2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/assets/stylesheets/dashboard.scss
    2015-10-22 23:51:44.000000000 +0200
@@ -1,35 +1,17 @@
 // Copyright (c) 2009-2015 Tim Serong <[email protected]>
 // See COPYING for license.
 
-.cluster {
-  border: 1px solid #000;
-  float: left;
-  margin: 0.5em;
-  padding: 1em;
-  width: 18em;
-  background-color: #fff;
-}
-
-.geoset {
-  border: 1px solid #000;
-  float: left;
-  margin: 0.5em;
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-  background-color: #ccc;
-}
-
-.mon-warning {
-  text-align: left;
-  font-weight: normal;
+#cib-status-matrix-details {
+  display: none;
+  position: fixed;
+  padding: 0.5em 2em 0.5em 2em;
+  z-index: 10000;
   width: auto !important;
-}
-
-.cluster-error {
-  font-weight: normal;
-  padding: 0.5em;
-}
 
-.dashboard-login {
-    margin-bottom: 0.5em;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+  background-clip: padding-box;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/controllers/cib_controller.rb
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/controllers/cib_controller.rb
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/controllers/cib_controller.rb
        2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/controllers/cib_controller.rb
        2015-10-22 23:51:44.000000000 +0200
@@ -16,21 +16,21 @@
       format.json do
         render json: { errors: [e.message] }, status: :not_found
       end
-      format.any { head :not_found  }
+      format.any { head :not_found }
     end
   rescue SecurityError => e
     respond_to do |format|
       format.json do
         render json: { errors: [e.message] }, status: :forbidden
       end
-      format.any { head :forbidden  }
+      format.any { head :forbidden }
     end
   rescue RuntimeError => e
     respond_to do |format|
       format.json do
         render json: { errors: [e.message] }, status: :internal_server_error
       end
-      format.any { head :internal_server_error  }
+      format.any { head :internal_server_error }
     end
   end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/models/cib.rb 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/models/cib.rb
--- old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/models/cib.rb        
2015-10-19 18:15:06.000000000 +0200
+++ new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/models/cib.rb        
2015-10-22 23:51:44.000000000 +0200
@@ -100,65 +100,43 @@
     }.tap do |result|
       if minimal
         result[:resources] = {}
-
-        result[:resource_states] = {
-          pending: 0,
-          started: 0,
-          failed: 0,
-          master: 0,
-          slave: 0,
-          stopped: 0
-        }
-
         result[:nodes] = {}
+        result[:tickets] = {}
 
-        result[:node_states] = {
-          pending: 0,
-          online: 0,
-          standby: 0,
-          offline: 0,
-          unclean: 0
-        }
-
-        result[:tickets] = []
-
-        result[:ticket_states] = {
-          granted: 0,
-          revoked: 0
-        }
-
-        current_resources.each do |key, values|
+        resources.each do |rsc|
+          key = rsc[:id]
           result[:resources][key] ||= {}
-
-          values[:instances].each do |_, attrs|
-            found = false
+          rsc[:instances].each do |_, attrs|
             [:master, :slave, :started, :failed, :pending].each do |rstate|
               if attrs[rstate]
                 attrs[rstate].each do |n|
                   result[:resources][key][n[:node]] = rstate
                 end
-                result[:resource_states][rstate] += 1
-                found = true
               end
             end
-            result[:resource_states][:stopped] += 1 unless found
-          end
+          end if rsc.key? :instances
+          rsc[:children].each do |child|
+            child[:instances].each do |_, attrs|
+              [:master, :slave, :started, :failed, :pending].each do |rstate|
+                if attrs[rstate]
+                  attrs[rstate].each do |n|
+                    result[:resources][key][n[:node]] = rstate
+                  end
+                end
+              end
+            end if child.key? :instances
+          end if rsc.key? :children
         end
 
         current_nodes.each do |node|
           result[:nodes][node[:uname]] = node[:state]
-
-          result[:node_states][node[:state]] += 1
         end
 
         current_tickets.each do |key, values|
-          case
-          when values[:granted]
-            result[:tickets].push(key => :granted)
-            result[:ticket_states][:granted] += 1
+          if values[:granted]
+            result[:tickets][key] = :granted
           else
-            result[:tickets].push(key => :revoked)
-            result[:ticket_states][:revoked] += 1
+            result[:tickets][key] = :revoked
           end
         end
 
@@ -214,6 +192,8 @@
   end
 
   def find_node(id)
+    fail(RecordNotFound, id) if @xml.nil?
+
     state = @nodes.select { |n| n[:id] == id }
     can_fence = @crm_config[:stonith_enabled]
 
@@ -231,8 +211,9 @@
   end
 
   def nodes_ordered
-    can_fence = @crm_config[:stonith_enabled]
     ret = []
+    return ret if @xml.nil?
+    can_fence = @crm_config[:stonith_enabled]
     @xml.elements.each('cib/configuration/nodes/node') do |xml|
       id = xml.attributes['id']
       state = @nodes.select { |n| n[:id] == id }
@@ -458,7 +439,7 @@
       case status.exitstatus
       when 0
         @xml = REXML::Document.new(out)
-        unless @xml.root
+        unless @xml && @xml.root
           error _('Error invoking %{cmd}') % {:cmd => '/usr/sbin/cibadmin -Ql' 
}
           init_offline_cluster id, user, use_file
           return
@@ -515,6 +496,7 @@
       state = :unclean
       standby = false
       maintenance = @crm_config[:"maintenance-mode"] ? true : false
+      remote = n.attributes['type'] == 'remote'
       ns = @xml.elements["cib/status/node_state[@uname='#{uname}']"]
       if ns
         state = CibTools.determine_online_status(ns, 
crm_config[:"stonith-enabled"])
@@ -536,17 +518,38 @@
         state = :standby
       end
       @nodes << Hashie::Mash.new(
-        :uname => uname,
-        :state => state,
-        :id => id,
-        :standby => standby,
-        :maintenance => maintenance
+        uname: uname,
+        state: state,
+        id: id,
+        standby: standby,
+        maintenance: maintenance,
+        remote: remote
       )
       if state == :unclean
         error _('Node "%{node}" is UNCLEAN and needs to be fenced.') % { node: 
uname }
       end
     end
 
+    # add remote nodes that may not exist in cib/configuration/nodes/node
+    @xml.elements.each("cib/status/node_state[remote_node='true']") do |n|
+      uname = n.attributes['uname']
+      id = n.attributes['id']
+      # To determine the state of remote nodes, we need to look at
+      # the resource by the same name
+      state = :unknown
+      standby = false
+      unless @nodes.any? { |nod| nod[:id] == id }
+        @nodes << Hashie::Mash.new(
+          uname: uname,
+          state: state,
+          id: id,
+          standby: standby,
+          maintenance: maintenance,
+          remote: true
+        )
+      end
+    end
+
     @resources = []
     @resources_by_id = {}
     @resource_count = 0
@@ -814,6 +817,20 @@
       end
     end
 
+    # Now we can patch up the state of remote nodes
+    @nodes.each do |n|
+      if n[:remote] && n[:state] != :unclean
+        rsc = @resources_by_id[n[:id]]
+        if rsc && [:master, :slave, :started].include?(rsc[:state])
+          n[:state] = :online
+        elsif rsc && [:failed, :pending].include?(rsc[:state])
+          n[:state] = :unclean
+        else
+          n[:state] = :offline
+        end
+      end
+    end
+
     # Now we can sort the node array
     @nodes.sort!{|a,b| a[:uname].natcmp(b[:uname], true)}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/models/cluster.rb 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/models/cluster.rb
--- old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/models/cluster.rb    
2015-10-19 18:15:06.000000000 +0200
+++ new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/models/cluster.rb    
2015-10-22 23:51:44.000000000 +0200
@@ -54,7 +54,7 @@
     Rails.logger.debug "chmod 0660 #{fname}..."
     File.chmod(0660, fname)
     Rails.logger.debug "Copy #{fname}..."
-    out, err, rc = Invoker.instance.crm("cluster", "copy", fname)
+    out, err, rc = Util.run_as("root", "crm", "cluster", "copy", fname)
     Rails.logger.debug "Copy returned #{out} #{err} #{rc}"
     rc == 0 ? true : err
   end
@@ -87,13 +87,13 @@
     def remove(name)
       Rails.logger.debug "remove: Removing #{name}..."
       fname = "#{Rails.root}/tmp/dashboard.js"
-      return true unless File.exists?(fname)
+      return true unless File.exist?(fname)
       clusters = JSON.parse(File.read(fname))
-      clusters = clusters.delete_if {|key, value| key == name }
+      clusters = clusters.delete_if { |key, _| key == name }
       Rails.logger.debug "remove: Writing #{fname}..."
       File.open(fname, "w") { |f| f.write(JSON.pretty_generate(clusters)) }
       File.chmod(0660, fname)
-      out, err, rc = Invoker.instance.crm("cluster", "copy", fname)
+      out, err, rc = Util.run_as("root", "crm", "cluster", "copy", fname)
       Rails.logger.debug "remove: Copy returned #{out} #{err} #{rc}"
       [out, err, rc]
     end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/models/node.rb 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/models/node.rb
--- old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/models/node.rb       
2015-10-19 18:15:06.000000000 +0200
+++ new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/models/node.rb       
2015-10-22 23:51:44.000000000 +0200
@@ -14,6 +14,7 @@
   attribute :online, Boolean
   attribute :standby, Boolean
   attribute :ready, Boolean
+  attribute :remote, Boolean
   attribute :maintenance, Boolean
   attribute :fence, Boolean
 
@@ -78,14 +79,12 @@
       record.state = state[:state]
       record.standby = state[:standby]
       record.maintenance = state[:maintenance]
+      record.remote = state[:remote]
       record.fence = can_fence
 
       record.attrs = if xml.elements['instance_attributes']
         vals = xml.elements['instance_attributes'].elements.collect do |e|
-          [
-            e.attributes['name'],
-            e.attributes['value']
-          ]
+          [e.attributes['name'], e.attributes['value']]
         end
 
         Hash[vals.sort]
@@ -107,7 +106,7 @@
 
       if record.utilization.any?
         Util.safe_x('/usr/sbin/crm_simulate', '-LU').split("\n").each do |line|
-          m = line.match /^Remaining:\s+([^\s]+)\s+capacity:\s+(.*)$/
+          m = line.match(/^Remaining:\s+([^\s]+)\s+capacity:\s+(.*)$/)
 
           next unless m && m[1] == record.name
 
@@ -140,12 +139,10 @@
     # Record#find will fail when looking for nodes by their human-readable
     # name, so have to override here
     def find(id)
-      begin
-        super(id)
-      rescue Cib::RecordNotFound
-        # Can't find by id attribute, try by name attribute
-        super(name, 'name')
-      end
+      super(id)
+    rescue Cib::RecordNotFound
+      # Can't find by id attribute, try by name attribute
+      super(name, 'name')
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/views/dashboards/show.html.haml
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/views/dashboards/show.html.haml
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/views/dashboards/show.html.haml
      2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/views/dashboards/show.html.haml
      2015-10-22 23:51:44.000000000 +0200
@@ -1,5 +1,3 @@
-= content_for :stylesheets do
-  = stylesheet_link_tag "dashboard"
 = content_for :javascripts do
   = javascript_include_tag "dashboard"
 
@@ -13,6 +11,11 @@
 
   #clusters.row
   -# passing null as name and host means to fetch local cluster information
+  - unless current_cib.offline?
+    :javascript
+      dashboardAddCluster({"name":null, "host":null, "port":null, https:null, 
"interval":10});
+
   :javascript
-    dashboardAddCluster({"name":null, "host":null, "port":null, https:null, 
"interval":10});
     #{javascript_for_clusters @clusters}
+
+  #cib-status-matrix-details
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/views/nodes/show.html.haml
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/views/nodes/show.html.haml
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/views/nodes/show.html.haml
   2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/views/nodes/show.html.haml
   2015-10-22 23:51:44.000000000 +0200
@@ -10,6 +10,9 @@
       = icon_text "server", _("Node: %{name}" % { name: @node.name }), class: 
"page"
 
   .modal-body
+    - if @node.remote
+      .alert.alert-info{role: :alert}
+        = _("Remote node.")
     %h4
       = _("Utilization")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/views/shared/_flash.html.haml
 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/views/shared/_flash.html.haml
--- 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/app/views/shared/_flash.html.haml
        2015-10-19 18:15:06.000000000 +0200
+++ 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/app/views/shared/_flash.html.haml
        2015-10-22 23:51:44.000000000 +0200
@@ -1,9 +1,10 @@
-- flash.each do |key, message|
-  .alert.alert-dismissible{ class: flash_class_for(key), role: "alert" }
-    %button.close{ type: "button", data: { dismiss: "alert" } }
-      %span{ aria: { hidden: "true" } }
-        &times;
-      %span.sr-only
-        = _("Close")
+.row#flashes
+  - flash.each do |key, message|
+    .alert.alert-dismissible{ class: flash_class_for(key), role: "alert" }
+      %button.close{ type: "button", data: { dismiss: "alert" } }
+        %span{ aria: { hidden: "true" } }
+          &times;
+        %span.sr-only
+          = _("Close")
 
-    = simple_format message
+      = simple_format message
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/lib/crm_events.rb 
new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/lib/crm_events.rb
--- old/hawk2-1.0.0~alpha1+git.1445271072.1114d0a/hawk/lib/crm_events.rb        
2015-10-19 18:15:06.000000000 +0200
+++ new/hawk2-1.0.0~alpha1+git.1445537620.7637b7f/hawk/lib/crm_events.rb        
2015-10-22 23:51:44.000000000 +0200
@@ -10,19 +10,22 @@
 class CrmEvents
   include Singleton
 
-  TRUNC_LOG_SIZE = 32*1024
-  MAX_LOG_SIZE = 128*1024
+  TRUNC_LOG_SIZE = 32 * 1024
+  MAX_LOG_SIZE = 128 * 1024
 
   def truncate?(f)
     (f.mtime < 1.day.ago && f.size > TRUNC_LOG_SIZE) || f.size > MAX_LOG_SIZE
   end
 
   def push(cmd)
+    shadow_id = ENV["CIB_shadow"]
+
     cmd = cmd.join(" ") if cmd.is_a? Array
     begin
       File.open(path, 'a') do |f|
         f.flock(File::LOCK_EX)
         f.truncate(0) if truncate? f
+        f << "# Shadow CIB: #{shadow_id} (Simulated)\n" unless shadow_id.nil?
         f << cmd
         f << "@@COMMAND-END@@\n"
       end
@@ -33,16 +36,14 @@
   end
 
   def cmds
-    begin
-      File.open(path, "r") do |f|
-        f.flock(File::LOCK_SH)
-        f.read.split("@@COMMAND-END@@\n").map do |cmd|
-          cmd.strip
-        end
+    File.open(path, "r") do |f|
+      f.flock(File::LOCK_SH)
+      f.read.split("@@COMMAND-END@@\n").map do |cmd|
+        cmd.strip
       end
-    rescue
-      []
     end
+  rescue
+    []
   end
 
   private


Reply via email to