diff --git a/common/css/main.css b/common/css/main.css index 5d8ad777f2c..4593e870240 100644 --- a/common/css/main.css +++ b/common/css/main.css @@ -60,6 +60,7 @@ margin: 0px; } + [ng\:cloak], [ng-cloak], .ng-cloak { display: none !important; } diff --git a/panels/bbuzz/editor.html b/panels/bbuzz/editor.html new file mode 100644 index 00000000000..ce3118c76eb --- /dev/null +++ b/panels/bbuzz/editor.html @@ -0,0 +1,3 @@ +
+ Nothing to see here folks +
diff --git a/panels/bbuzz/img/0.jpg b/panels/bbuzz/img/0.jpg new file mode 100644 index 00000000000..1ab8ee7286c Binary files /dev/null and b/panels/bbuzz/img/0.jpg differ diff --git a/panels/bbuzz/img/1.jpg b/panels/bbuzz/img/1.jpg new file mode 100644 index 00000000000..8f9d9b1058c Binary files /dev/null and b/panels/bbuzz/img/1.jpg differ diff --git a/panels/bbuzz/img/2.jpg b/panels/bbuzz/img/2.jpg new file mode 100644 index 00000000000..d18671927f3 Binary files /dev/null and b/panels/bbuzz/img/2.jpg differ diff --git a/panels/bbuzz/img/3.jpg b/panels/bbuzz/img/3.jpg new file mode 100644 index 00000000000..f37600bfa7a Binary files /dev/null and b/panels/bbuzz/img/3.jpg differ diff --git a/panels/bbuzz/img/4.jpg b/panels/bbuzz/img/4.jpg new file mode 100644 index 00000000000..f7f75cd0c00 Binary files /dev/null and b/panels/bbuzz/img/4.jpg differ diff --git a/panels/bbuzz/img/5.jpg b/panels/bbuzz/img/5.jpg new file mode 100644 index 00000000000..e533df550ca Binary files /dev/null and b/panels/bbuzz/img/5.jpg differ diff --git a/panels/bbuzz/img/6.jpg b/panels/bbuzz/img/6.jpg new file mode 100644 index 00000000000..20e6a3282ca Binary files /dev/null and b/panels/bbuzz/img/6.jpg differ diff --git a/panels/bbuzz/module.html b/panels/bbuzz/module.html new file mode 100644 index 00000000000..61e044b55e6 --- /dev/null +++ b/panels/bbuzz/module.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + +
+ + Cluster: {{cluster.cluster_name}} + + +
+ + +
+
+ + {{node.name}} / {{bbuzz.ip_address(node.transport_address)}} + + P +
+
+
+
+ R +
+
+

+
+ +
+ + + + + + + + +
+ + Missing nodes + +
+ + {{bbuzz.ip_address(name)}} {{nodes.status[name]}} + + +
+ +
\ No newline at end of file diff --git a/panels/bbuzz/module.js b/panels/bbuzz/module.js new file mode 100644 index 00000000000..5f79e823f1c --- /dev/null +++ b/panels/bbuzz/module.js @@ -0,0 +1,372 @@ +/* + + ## bbuzz + + A special panel for my Berlin Buzzwords talk + + ### Parameters + * None + ### Group Events + #### Sends + * get_time :: On panel initialization get time range to query + #### Receives + * time :: An object containing the time range to use and the index(es) to query + +*/ +angular.module('kibana.bbuzz', []) +.controller('bbuzz', function($scope, eventBus, $http, bbuzz) { + + // Set and populate defaults + var _d = { + group : "default", + } + _.defaults($scope.panel,_d) + + $scope.init = function () { + $scope.nodes = {}; + $scope.settings = {}; + $scope.indices = []; + $scope.bbuzz = bbuzz + $scope.colors = bbuzz.colors(20) + eventBus.register($scope,'time', function(event,time){ + set_time(time) + }); + // Now that we're all setup, request the time from our group + eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') + } + + $scope.set_replicas = function(replicas) { + $http.put(config.elasticsearch+'/_settings', { + "index" : {"number_of_replicas" : replicas} + }).success(function (data) { + $scope.get_data(); + }); + } + + $scope.status = function(ip) { + var something = $http({ + url: "http://"+ip+':4567/status', + method: "GET" + }).error(function(data, status, headers, config) { + $scope.error = status; + }); + + return something.then(function(p) { + return p.data + }); + } + + $scope.node_mode = function(ip,mode) { + $http({method: 'GET', url: "http://"+ip+':4567/'+mode}). + success(function(data, status, headers, config) { + control.log(data) + $scope.get_data(); + }). + error(function(data, status, headers, config) { + console.log(data, status) + $scope.get_data(); + }); + } + + $scope.get_data = function() { + + // Get cluster health + if (!_.isNumber($scope.settings.replicas)) { + $http({method: 'GET', url: config.elasticsearch+'/_settings'}). + success(function(data, status, headers, config) { + $scope.settings.replicas = parseInt(_.pairs(data)[0][1].settings['index.number_of_replicas']) + }). + error(function(data, status, headers, config) { + console.log('Could not contact elasticsearch at: '+config.elasticsearch) + }); + } + + // Get cluster health + $http({method: 'GET', url: config.elasticsearch+'/_cluster/health'}). + success(function(data, status, headers, config) { + $scope.cluster = data + }). + error(function(data, status, headers, config) { + console.log('Could not contact elasticsearch at: '+config.elasticsearch) + }); + + // And nodes list + $http({method: 'GET', url: config.elasticsearch+'/_cluster/state'}). + success(function(data, status, headers, config) { + var i = [] + _.each(data.routing_nodes.nodes, function(v,node) { + i[node] = _.groupBy(v,'index') + }) + $scope.nodes.routing = i; + $scope.nodes.info = data.nodes + $scope.nodes.live = _.pluck(data.nodes,'transport_address'), + $scope.nodes.dead = _.difference(bbuzz.get_nodes(),_.pluck(data.nodes,'transport_address')) + //$scope.nodes.dead = ['inet[/10.126.115.79:9300]']; + $scope.indices = _.union($scope.indices,_.keys(i)) + + _.each($scope.nodes.dead,function(node) { + $http({ + url: "http://"+bbuzz.ip_address(node)+':4567/status', + method: "GET" + }).success(function(data, status, headers, config) { + if(_.isUndefined($scope.nodes.status)) + $scope.nodes.status = {}; + $scope.nodes.status[node] = 'data'; + }) + }) + + }). + error(function(data, status, headers, config) { + console.log('Could not contact elasticsearch at: '+config.elasticsearch) + }); + } + + // returns list of shards sorted by index + $scope.sorted_shards = function(shards) { + return _.sortBy(shards, function(shard) { + return shard.index + }) + } + + $scope.primary = function(shards,primary) { + return _.filter(shards, function(shard) { + return shard.primary === primary ? true: false; + }) + } + + $scope.shard_class = function(state) { + switch(state) + { + case 'INITIALIZING': + return ['animated','flash','infinite'] + case 'RELOCATING': + return ['faded'] + default: + return '' + } + } + + $scope.cluster_class = function(state) { + switch(state) + { + case 'green': + return 'btn-success' + case 'yellow': + return 'btn-warning' + case 'red': + return 'btn-danger' + default: + return 'btn-inverse' + } + } + + $scope.cluster_color = function(state) { + switch(state) + { + case 'green': + var style = {color: '#FFF', bg: '#5BB75B', light: '#62C462', dark: '#51A351'} + break; + case 'yellow': + var style = {color: '#FFF', bg: '#FAA732', light: '#FBB450', dark: '#F89406'} + break; + case 'red': + var style = {color: '#FFF', bg: "#DA4F49", light: '#EE5F5B', dark: '#BD362F'} + break; + default: + var style = {color: '#000', bg: "#F5F5F5", light: '#FFFFFF', dark: '#E6E6E6'} + break; + } + return { + 'color': style.color, + 'margin-bottom': '0px', + 'font-weight': 200, + 'background-color': style.bg, + 'background-image': 'linear-gradient(to bottom, '+style.light+', '+style.dark+')', + 'background-repeat': 'repeat-x', + 'border-color': 'rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25)', + 'border-style': 'solid', + 'border-width': '1px', + 'box-shadow': '0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.05)', + 'padding': '10px' + }; + } + + function set_time(time) { + $scope.time = time; + $scope.get_data(); + } + +}).directive('started', function(eventBus) { + return { + restrict: 'C', + link: function(scope, elem, attrs, ctrl) { + console.log('shard') + + // Receive render events + scope.$on('render',function(){ + render_panel(); + }); + + // Re-render if the window is resized + angular.element(window).bind('resize', function(){ + render_panel(); + }); + + // Function for rendering panel + function render_panel() { + + var scripts = $LAB.script("common/lib/panels/jquery.flot.js") + + // Populate element. + scripts.wait(function(){ + try { + + } catch(e) { + elem.text(e) + } + }) + } + + } + }; +}).service('bbuzz', function($timeout) { + var nodes = []; + var indices = []; + + this.index_id = function(id) { + if (!_.contains(indices,id)) + indices.push(id) + return _.indexOf(indices,id) + } + + this.index_color = function(id) { + var i = this.index_id(id) + var colors = this.colors(indices.length) + return colors[i]; + } + + this.ip_address = function(transport_address) { + return transport_address.split(/[\/:]/)[1] + } + + this.node_id = function(id) { + if (!_.contains(nodes,id)) + nodes.push(id) + return _.indexOf(nodes,id) + } + + this.get_nodes = function() { + return nodes; + } + + this.picture = function(id) { + return 'panels/bbuzz/img/'+this.node_id(id)+'.jpg' + } + + this.colors = function(count) { + // produce colors as needed + var seed = ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D'] + var colors = [], variation = 0; + i = 0; + while (colors.length < count) { + var c; + if (seed.length == i) // check degenerate case + c = new Color(100, 100, 100); + else + c = parseColor(seed[i]); + + // vary color if needed + var sign = variation % 2 == 1 ? -1 : 1; + var factor = 1 + sign * Math.ceil(variation / 2) * 0.2; + c.scale(factor, factor, factor); + + // FIXME: if we're getting to close to something else, + // we should probably skip this one + colors.push(c.toString()); + + ++i; + if (i >= seed.length) { + i = 0; + ++variation; + } + } + return colors; + } + + function parseColor(str) { + var result; + // Look for #a0b1c2 + if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) + return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)); + // Look for #fff + if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) + return new Color(parseInt(result[1]+result[1], 16), parseInt(result[2]+result[2], 16), parseInt(result[3]+result[3], 16)); + } + + // color helpers, inspiration from the jquery color animation + // plugin by John Resig + function Color (r, g, b, a) { + + var rgba = ['r','g','b','a']; + var x = 4; //rgba.length + + while (-1<--x) { + this[rgba[x]] = arguments[x] || ((x==3) ? 1.0 : 0); + } + + this.toString = function() { + if (this.a >= 1.0) { + return "rgb("+[this.r,this.g,this.b].join(",")+")"; + } else { + return "rgba("+[this.r,this.g,this.b,this.a].join(",")+")"; + } + }; + + this.scale = function(rf, gf, bf, af) { + x = 4; //rgba.length + while (-1<--x) { + if (arguments[x] != null) + this[rgba[x]] *= arguments[x]; + } + return this.normalize(); + }; + + this.adjust = function(rd, gd, bd, ad) { + x = 4; //rgba.length + while (-1<--x) { + if (arguments[x] != null) + this[rgba[x]] += arguments[x]; + } + return this.normalize(); + }; + + this.clone = function() { + return new Color(this.r, this.b, this.g, this.a); + }; + + var limit = function(val,minVal,maxVal) { + return Math.max(Math.min(val, maxVal), minVal); + }; + + this.normalize = function() { + this.r = clamp(0, parseInt(this.r), 255); + this.g = clamp(0, parseInt(this.g), 255); + this.b = clamp(0, parseInt(this.b), 255); + this.a = clamp(0, this.a, 1); + return this; + }; + + this.normalize(); + } + + function clamp(min, value, max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + } + + +}) diff --git a/panels/derivequeries/module.js b/panels/derivequeries/module.js index 6d00706c8d1..fab94a8a177 100644 --- a/panels/derivequeries/module.js +++ b/panels/derivequeries/module.js @@ -83,7 +83,6 @@ angular.module('kibana.derivequeries', []) _.each(results.facets.query.terms, function(v) { data.push($scope.panel.field+':"'+v.term+'"') }); - console.log(data) $scope.send_query(data) }); } @@ -121,4 +120,4 @@ angular.module('kibana.derivequeries', []) -}); \ No newline at end of file +}); diff --git a/panels/table/editor.html b/panels/table/editor.html index e039eaf6a3b..7dc73fac8f7 100644 --- a/panels/table/editor.html +++ b/panels/table/editor.html @@ -16,10 +16,23 @@
-
Selected fields Click to remove
+
Columns Click to remove
{{field}}
+
+
+
+
Add field
+ + +
+
+
+
Highlighted fields Click to remove
+ {{field}} +
+
Options
diff --git a/panels/table/module.html b/panels/table/module.html index ad8062e29c4..4749259415c 100644 --- a/panels/table/module.html +++ b/panels/table/module.html @@ -35,8 +35,8 @@ - - {{row[field]}} + + @@ -46,7 +46,7 @@ Action Value - + {{key}} @@ -59,7 +59,7 @@ -
> +
diff --git a/panels/table/module.js b/panels/table/module.js index b7b7c8f4665..580aa988bd6 100644 --- a/panels/table/module.js +++ b/panels/table/module.js @@ -39,6 +39,7 @@ angular.module('kibana.table', []) group : "default", style : {'font-size': '9pt'}, fields : [], + highlight : [], sortable: true, header : true, paging : true, @@ -92,6 +93,13 @@ angular.module('kibana.table', []) broadcast_results(); } + $scope.toggle_highlight = function(field) { + if (_.indexOf($scope.panel.highlight,field) > -1) + $scope.panel.highlight = _.without($scope.panel.highlight,field) + else + $scope.panel.highlight.push(field) + } + $scope.toggle_details = function(row) { row.kibana = row.kibana || {}; row.kibana.details = !row.kibana.details ? $scope.without_kibana(row) : false; @@ -129,6 +137,12 @@ angular.module('kibana.table', []) .to($scope.time.to) ) ) + .highlight( + ejs.Highlight($scope.panel.highlight) + .fragmentSize(2147483647) // Max size of a 32bit unsigned int + .preTags('@start-highlight@') + .postTags('@end-highlight@') + ) .size($scope.panel.size*$scope.panel.pages) .sort($scope.panel.sort[0],$scope.panel.sort[1]); @@ -155,14 +169,17 @@ angular.module('kibana.table', []) // Check that we're still on the same query, if not stop if($scope.query_id === query_id) { $scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) { - return flatten_json(hit['_source']); + return { + _source : flatten_json(hit['_source']), + highlight : flatten_json(hit['highlight']||{}) + } })); $scope.hits += results.hits.total; // Sort the data $scope.data = _.sortBy($scope.data, function(v){ - return v[$scope.panel.sort[0]] + return v._source[$scope.panel.sort[0]] }); // Reverse if needed @@ -177,7 +194,7 @@ angular.module('kibana.table', []) } // This breaks, use $scope.data for this - $scope.all_fields = get_all_fields($scope.data); + $scope.all_fields = get_all_fields(_.pluck($scope.data,'_source')); broadcast_results(); // If we're not sorting in reverse chrono order, query every index for @@ -205,9 +222,10 @@ angular.module('kibana.table', []) } $scope.without_kibana = function (row) { - row = _.clone(row) - delete row.kibana - return row + return { + _source : row._source, + highlight : row.highlight + } } // Broadcast a list of all fields. Note that receivers of field array @@ -222,15 +240,39 @@ angular.module('kibana.table', []) eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents", { query: $scope.panel.query, - docs : $scope.data, + docs : _.pluck($scope.data,'_source'), index: $scope.index }); } + $scope.set_refresh = function (state) { + $scope.refresh = state; + } + + $scope.close_edit = function() { + if($scope.refresh) + $scope.get_data(); + $scope.refresh = false; + } + + function set_time(time) { $scope.time = time; $scope.index = _.isUndefined(time.index) ? $scope.index : time.index $scope.get_data(); } +}) +.filter('highlight', function() { + return function(text) { + if (text.toString().length) { + return text.toString(). + replace(/&/g, '&'). + replace(//g, '>'). + replace(/@start-highlight@/g, ''). + replace(/@end-highlight@/g, '') + } + return ''; + } }); \ No newline at end of file