diff --git a/vitragedashboard/static/dashboard/project/entities/entities.html b/vitragedashboard/static/dashboard/project/entities/entities.html index 5d5112a..c5a175e 100644 --- a/vitragedashboard/static/dashboard/project/entities/entities.html +++ b/vitragedashboard/static/dashboard/project/entities/entities.html @@ -7,6 +7,7 @@
+
diff --git a/vitragedashboard/static/dashboard/project/entities/entities.scss b/vitragedashboard/static/dashboard/project/entities/entities.scss index c68c8ec..1d80805 100644 --- a/vitragedashboard/static/dashboard/project/entities/entities.scss +++ b/vitragedashboard/static/dashboard/project/entities/entities.scss @@ -1,5 +1,5 @@ .entities { .panel-body { - padding: 3px; + padding: 6px; } } diff --git a/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.directive.js b/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.directive.js index d693336..983b780 100644 --- a/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.directive.js +++ b/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.directive.js @@ -24,14 +24,34 @@ function hzEntitiesGraph() { linkWidth = 1, circleRadius = 14, circlePadding = 1, - pinned = horizon.cookies.get('pinned') || [], zoom = d3.behavior.zoom().scaleExtent([minZoom, maxZoom]), ellipsisWidth = 80, + hightlightDepth = 2, + heightOffset, + pinned, graphCreated, node, link, + linksMap, content; + (function() { + var p = $('.panel.panel-primary'); + heightOffset = (p.length ? p.offset().top : 180) + 75; + + pinned = horizon.cookies.get('pinned') || []; + if (_.isString(pinned)) { + try { + pinned = JSON.parse(pinned); + } + catch(ex) { + pinned = []; + console.error('Failed to parse the pinned cookie'); + } + } + })(); + + scope.$watch('data.ts', function(newVal, oldVal) { if (newVal) { prepareData(); @@ -44,6 +64,22 @@ function hzEntitiesGraph() { } }); + scope.$on('toolbox-pin', function () { + pinAll(); + }); + + scope.$on('toolbox-unpin', function () { + unpinAll(); + }) + + scope.$on('toolbox-zoom-to-fit', function () { + console.log('on toolbox-pin', arguments) + }); + + scope.$on('toolbox-toggle-fullscreen', function () { + console.log('on toolbox-unpin', arguments) + }) + scope.isEmpty = function() { return scope.data && scope.data.nodes && scope.data.nodes.length === 0; }; @@ -90,7 +126,7 @@ function hzEntitiesGraph() { d3.select(window).on('resize', resize); function resize() { - svg.attr('height', window.innerHeight - 168 + 'px') + svg.attr('height', window.innerHeight - heightOffset + 'px') force.size([angular.element(svg[0]).width(), angular.element(svg[0]).height()]) .resume(); @@ -108,6 +144,11 @@ function hzEntitiesGraph() { node.y = pin.y; } }) + + linksMap = {}; + _.each(scope.data.links, function(link) { + linksMap[link.source.id + ',' + link.target.id] = true; + }); } function createGraph() { @@ -228,6 +269,7 @@ function hzEntitiesGraph() { } window.drawGraph = drawGraph; + window.dforce = force; function drawGraph() { link = link.data(force.links(), function(d) { return d.source.id + '-' + d.target.id; }); @@ -275,28 +317,47 @@ function hzEntitiesGraph() { .attr('dominant-baseline', 'central') .attr('transform', 'scale(1)') .attr('class', function(d) { - var cls = ''; - var severity = d.operational_severity; - if (severity) { - switch (severity.toLowerCase()) { - case 'critical': - cls = 'red'; - break; - case 'severe': - cls = 'orange'; - break; - case 'warning': - cls = 'yellow'; - break; - case 'ok': - cls = 'green'; - break; - case 'n/a': - cls = 'gray'; - break; - default: //'DISABLED', 'UNKNOWN', 'UNDEFINED' - cls = 'gray'; - break; + var category = d.category, + cls = ''; + + if (category && category.toLowerCase() === 'alarm') { + var severity = d.operational_severity; + if (severity) { + switch (severity.toLowerCase()) { + case 'critical': + cls = 'red'; + break; + case 'severe': + cls = 'orange'; + break; + case 'warning': + cls = 'yellow'; + break; + case 'ok': + cls = 'green'; + break; + case 'n/a': + cls = 'gray'; + break; + default: //'DISABLED', 'UNKNOWN', 'UNDEFINED' + cls = 'gray'; + break; + } + } + } else { + var state = d.operational_state; + if (state) { + switch (state.toLowerCase()) { + case 'error': + cls = 'red'; + break; + case 'suboptimal': + cls = 'yellow'; + break; + case 'n/a': + cls = 'gray'; + break; + } } } return cls; @@ -440,15 +501,48 @@ function hzEntitiesGraph() { if ($(this).is('.node')) { - d3.select(this).classed('selected', true); + //d3.select(this).classed('selected', true); + + findHighlight(d); } } + function findHighlight(rootNode) { + + _.each(scope.data.nodes, function(node) { + node.high = false; + }) + + var depth = hightlightDepth; + + findNodes(rootNode, depth, scope.data.nodes, linksMap); + + _.each(scope.data.links, function(link) { + link.high = false; + }) + + svg_g.selectAll('.node') + .classed('selected', function(d) { + return d.high; + }) + .select('circle') + .style('stroke-width', function(d) { + return d.high ? (Math.max(d.highDepth + 1, 1) * 2) : null; + }) + + svg_g.selectAll('.link').classed('selected', function(d) { + return d.source.high && d.target.high; + }) + } + function selectNone(d) { nodeClick(null); } function pinNode(d) { + d3.event.stopImmediatePropagation(); + d3.event.preventDefault(); + var node; if ($(this).is('.node')) { @@ -463,9 +557,6 @@ function hzEntitiesGraph() { updatePinnedCookie(d); } - d3.event.stopImmediatePropagation(); - d3.event.preventDefault(); - //fixing some bug with unpinning /*setTimeout(function() { force.resume() @@ -476,7 +567,7 @@ function hzEntitiesGraph() { var pinIndex = -1; pinned.forEach(function(pin, i) { if (pin.id === d.id) { - pinIndex = i + pinIndex = i; } }) @@ -488,7 +579,7 @@ function hzEntitiesGraph() { pinned.push({id: d.id, x: d.x, y: d.y}); } - horizon.cookies.put('pinned', pinned); + horizon.cookies.put('pinned', JSON.stringify(pinned)); } function nodeDragend(d) { @@ -497,6 +588,39 @@ function hzEntitiesGraph() { } } + function pinAll() { + pinned = []; + + svg_g.selectAll('.node') + .classed('pinned', true) + .each(function(d) { + d.fixed = true; + pinned.push({id: d.id, x: d.x, y: d.y}); + }) + + horizon.cookies.put('pinned', JSON.stringify(pinned)); + } + + function unpinAll() { + pinned = []; + + svg_g.selectAll('.node') + .classed('pinned', false) + .each(function(d) { + d.fixed = false; + }) + + horizon.cookies.put('pinned', JSON.stringify([])); + + setTimeout(function() { + force.resume() + }, 100) + } + + function pinAllNodes(isPin) { + + } + function setEllipsis(el, text, width) { el.textContent = text; @@ -516,6 +640,30 @@ function hzEntitiesGraph() { } }; + function findNodes(rootNode, depth, allNodes, linksMap) { + if (rootNode) { + rootNode.high = true; + rootNode.highDepth = depth; + depth--; + + _.each(allNodes, function(node) { + if (linksMap[node.id + ',' + rootNode.id] || linksMap[rootNode.id + ',' + node.id]) { + + if (depth > -1 && !node.high) { + findNodes(node, depth, allNodes, linksMap); + } else if (depth <= -1) { + //Always find 'depth' + alarms & (sdns + alarms) + if (node.category.toLowerCase() === 'alarm') { + node.high = true; + node.highDepth = 0; + } else if (!node.high && node.type && node.type.toLowerCase() === 'sdn_controller') { + findNodes(node, depth, allNodes, linksMap); + } + } + } + }); + } + } /*function nodeDragstart(d) { d3.select(this).classed('pinned', d.fixed = true); diff --git a/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.scss b/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.scss index 176b3f6..0a0182f 100644 --- a/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.scss +++ b/vitragedashboard/static/dashboard/project/entities/graph/entities-graph.scss @@ -27,6 +27,11 @@ $dark_gray: darkgray; .link { stroke-opacity: 0.8; + + &.selected { + stroke: $blue; + stroke-width: 2 !important; + } } .node { diff --git a/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.directive.js b/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.directive.js new file mode 100644 index 0000000..eab78b2 --- /dev/null +++ b/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.directive.js @@ -0,0 +1,24 @@ +angular + .module('horizon.dashboard.project.vitrage') + .directive('hzEntitiesToolbox', hzEntitiesToolbox); + +hzEntitiesToolbox.$inject = ['$rootScope']; + +function hzEntitiesToolbox($rootScope) { + var directive = { + link: link, + templateUrl: STATIC_URL + 'dashboard/project/entities/toolbox/entities-toolbox.html', + restrict: 'E', + scope: { + item: '=' + } + }; + return directive; + + function link(scope, element, attrs) { + scope.broadcast = function(event) { + console.log('click', event); + $rootScope.$broadcast('toolbox-' + event); + } + } +} diff --git a/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.html b/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.html new file mode 100644 index 0000000..c82fe50 --- /dev/null +++ b/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.html @@ -0,0 +1,12 @@ +
+
+ Pin + Unpin +
+ +
\ No newline at end of file diff --git a/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.scss b/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.scss new file mode 100644 index 0000000..1f20790 --- /dev/null +++ b/vitragedashboard/static/dashboard/project/entities/toolbox/entities-toolbox.scss @@ -0,0 +1,17 @@ +.entities-toolbox { + position: absolute; + right: 18px; + margin: 12px; + padding:8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + background: rgba(255, 255, 255, 0.8); + + .btn-group { + margin-right: 6px; + + &:last-child { + margin-right: 0; + } + } +} \ No newline at end of file diff --git a/vitragedashboard/static/dashboard/project/vitrage.scss b/vitragedashboard/static/dashboard/project/vitrage.scss index 7e2ea50..b2b1a27 100644 --- a/vitragedashboard/static/dashboard/project/vitrage.scss +++ b/vitragedashboard/static/dashboard/project/vitrage.scss @@ -10,6 +10,7 @@ @import 'components/information/information'; @import 'entities/graph/entities-graph.scss'; @import 'entities/info/entities-info.scss'; +@import 'entities/toolbox/entities-toolbox.scss'; @import 'entities/entities.scss'; .red {