diff --git a/webroot/common/ui/js/controller.constants.js b/webroot/common/ui/js/controller.constants.js index 1b7105511..ef0d72402 100644 --- a/webroot/common/ui/js/controller.constants.js +++ b/webroot/common/ui/js/controller.constants.js @@ -231,6 +231,7 @@ define([ this.UNDERLAY_LINK = 'link'; this.TRACEFLOW_MAXATTEMPTS = 3; this.TRACEFLOW_INTERVAL = 5; + this.UNDERLAY_FLOW_INFO_TEMPLATE = "flow-info-template"; this.getProjectsURL = function (domainObj, dropdownOptions) { /* Default: get projects from keystone or API Server as specified in diff --git a/webroot/common/ui/templates/controller.tmpl b/webroot/common/ui/templates/controller.tmpl index 681edd6eb..524668e95 100644 --- a/webroot/common/ui/templates/controller.tmpl +++ b/webroot/common/ui/templates/controller.tmpl @@ -32,6 +32,18 @@ + + + + + \ No newline at end of file diff --git a/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js b/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js index 2fc1a5c57..b89c0ef1d 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js @@ -24,7 +24,6 @@ define(['backbone', 'contrail-view-model'], this.adjacencyList = [], this.underlayAdjacencyList = [], this.connectedElements = [], - this.underlayPathIds = [], this.underlayPathReqObj = {}, this.uveMissingNodes = [], this.configMissingNodes = [], @@ -153,7 +152,7 @@ define(['backbone', 'contrail-view-model'], } } this.firstLevelNodes = firstLevelNodes; - this.parseTree(firstLevelNodes, tree, tmpTree); + this.parseTree(firstLevelNodes, tree, tmpTree, null); if(JSON.stringify(tmpTree) !== "{}") { $.each(tmpTree, function (elementKey, elementValue) { tree[elementKey] = elementValue; @@ -201,7 +200,7 @@ define(['backbone', 'contrail-view-model'], } }, - parseTree : function (parents, tree, tmpTree) { + parseTree : function (parents, tree, tmpTree, parent) { if(null !== parents && false !== parents && typeof parents === "object" && parents.length > 0) { for(var i=0; i', '', ]; footer = false; + gridTitle = contrail.format("{0} ({1})",'Active flows of Virtual Router', + formModel.vrouter_dropdown_name()); + } else { + var vmDetails = + underlayGraphModel.vmMap[formModel.instance_dropdown_name()]; + var name = + getValueByJsonPath(vmDetails, 'more_attributes;vm_name', '-'); + if(name == '-') + name = getValueByJsonPath(vmDetails, 'name', '-'); + gridTitle = contrail.format('{0} ({1})','Last 10 minute flows of Virtual Machine', name); } function resetLoadingIcon () { $("#" +ctwc.TRACEFLOW_RESULTS_GRID_ID @@ -160,7 +172,7 @@ define([ var gridElementConfig = { header: { title: { - text: 'Flows', + text: gridTitle, }, customControls: customControls, defaultControls: { @@ -335,6 +347,7 @@ define([ showInfoWindow("Cannot Trace route for the selected flow", "Info"); return; } + postData['action'] = 'Trace Flow'; if (postData['vrfId'] != null) { doTraceFlowRequest(postData, graphModel, deferredObj); } else { @@ -425,6 +438,7 @@ define([ showInfoWindow("Cannot Trace route for the selected flow", "Info"); return; } + postData['action'] = 'Reverse Trace Flow'; if(postData['vrfId'] != null) { doTraceFlowRequest(postData, graphModel, deferredObj); } else { @@ -510,10 +524,18 @@ define([ graphModel.flowPath.set({ 'nodes': ifNull(response['nodes'], []), 'links': ifNull(response['links'], []) - }); + },{silent: true}); + graphModel.flowPath.trigger('change:nodes'); if (ifNull(response['nodes'], []).length == 0 || ifNull(response['links'], []).length == 0) { - graphModel.flowPath.trigger('change:nodes'); + } else { + monitorInfraUtils.addUnderlayFlowInfoToBreadCrumb({ + action: postData['action'], + sourceip: postData['srcIP'], + destip: postData['destIP'], + sport: postData['srcPort'], + dport: postData['destPort'] + }); } } if(typeof response != 'string') diff --git a/webroot/monitor/infrastructure/underlay/ui/js/views/TrafficStatisticsView.js b/webroot/monitor/infrastructure/underlay/ui/js/views/TrafficStatisticsView.js index ce28f28cb..84f8ed9ac 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/views/TrafficStatisticsView.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/views/TrafficStatisticsView.js @@ -33,11 +33,11 @@ define([ } } }; - // Displaying widget with loading icon till the ajax call for - // stats finish. - self.renderView4Config($("#"+ctwc.UNDERLAY_TRAFFICSTATS_TAB_ID+'-tempdiv'), - null, tempViewConfig, null, null); if(trafficStatsViewConfig.modelConfig != null) { + // Displaying widget with loading icon till the ajax call for + // stats finish. + self.renderView4Config($("#"+ctwc.UNDERLAY_TRAFFICSTATS_TAB_ID+'-tempdiv'), + null, tempViewConfig, null, null); var contrailListModel = new ContrailListModel( trafficStatsConfig.viewConfig.modelConfig); contrailListModel.onAllRequestsComplete.subscribe( @@ -94,6 +94,7 @@ define([ } }); } else { + self.$el.html(''); // vrouter and vm link self.renderView4Config(self.$el, null, trafficStatsConfig, null, null, trafficStatsConfig.modelMap); diff --git a/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js b/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js index 7860a4f23..879510026 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js @@ -10,24 +10,47 @@ define([ var UnderlayGraphView = ContrailView.extend({ network: null, model : null, + underlayPathIds: { + nodes: [], + links: [] + }, + duplicatePathsDrawn: false, tooltipConfigWidth: 0, tooltipConfigHeight: 0, cursorPosition: {}, style: { default: { - color: "rgba(85,85,85,1)" + color: "rgba(85,85,85,.9)" }, defaultDimlight: { - color: "rgba(85,85,85,0.3)" + color: "rgba(85,85,85,0.5)" }, defaultSelected: { color: "rgba(73,138,185,1)" }, selectedDimlight: { - color: "rgba(73,138,185,0.3)" + color: "rgba(73,138,185,0.5)" }, errorNode: { color: "rgba(185,74,72,1)" + }, + linkDefault: { + color: "rgba(57,57,57,.6)" + }, + flowPathDefault: { + //color: "rgba(148,226,241,1)" //orange + //color: "rgba(154,215,210,1)" //blue + //color: "rgba(226,208,146,1)" //brown + //color: "rgba(186,162,74,1)" //dark brown + color: "rgba(0,150,136,.8)" //teal + + }, + flowPathDimlight: { + //color: "rgba(148,226,241,.6)" //orange + //color: "rgba(154,215,210,.6)" //blue + //color: "rgba(226,208,146,.6)" //brown + //color: "rgba(186,162,74,.5)" //dark brown + color: "rgba(0,150,136,.5)" //teal } }, cidMap: { @@ -50,28 +73,55 @@ define([ hoverConnectedEdges: false, zoomView: false }, + physics: { + stabilization: { + iterations: 100 + } + }, layout: { hierarchical: { direction: 'UD', - sortMethod: 'directed' + sortMethod: 'directed', + levelSeparation: 80, }, improvedLayout: true }, - /*edges: { + nodes: { + shape: 'icon', + icon: { + face: 'contrailFonts', + size: 40, + color: "rgba(85,85,85,.9)" + }, + font: { + face : 'Arial, helvetica, sans-serif', + size: 10, + color: '#333', + strokeColor: '#333', + strokeWidth: 0.4 + }, + labelHighlightBold: true + }, + edges: { + width: 2, + color: "rgba(57,57,57,.6)", smooth: { enabled: true, type: "cubicBezier", forceDirection: "horizontal", roundness: .7 //not to be used with dynamic } - }*/ - edges: { + } + /*edges: { smooth: { enabled: true, type: "dynamic", forceDirection: "none" } - } + }*/ + }, + enableFreeflow: function() { + self.network.setOptions({physics:{enabled: false}, layout: {hierarchical: {enabled:false}}}); }, resetTooltip: function() { $(".vis-network-tooltip").popover('destroy'); @@ -85,14 +135,24 @@ define([ selectorId = '#' + ctwl.UNDERLAY_GRAPH_ID; graphModel = new UnderlayGraphModel(this.getUnderlayGraphModelConfig()); + self.$el.html(graphTemplate()); + $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphModel', graphModel); this.model = graphModel; this.model.listenTo(graphModel, "change", function (updatedGraphModel) { self.populateModelAndAddToGraph(updatedGraphModel.attributes); }); - self.$el.html(graphTemplate()); - - $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphModel', graphModel); + $('body').on("click", '#flow-info span.reset', function () { + self.resetTopology({ + resetBelowTabs: true + }); + monitorInfraUtils.removeUndelrayFlowInfoFromBreadCrumb(); + //Removing the selected row styling in trace flow and map flow results. + $("#" + ctwc.TRACEFLOW_RESULTS_GRID_ID+ + " div.slick-row.selected-slick-row").removeClass('selected-slick-row'); + $("#searchFlow-results div.slick-row.selected-slick-row").removeClass('selected-slick-row'); + self.clearFlowPath(); + }); var graphWidth = $(selectorId).width(); var graphHeight = $(selectorId).height(); this.nodesDataSet = new vis.DataSet([]); @@ -103,22 +163,52 @@ define([ edges: this.edgesDataSet }, this.visOptions); + /*var rearrange = $('
'); + $(rearrange).on("click", function(){ + _network.setOptions(self.visOptions); + $('.vis-button vis-zoomExtends').click(); + self.enableFreeflow(); + }); + $('.vis-navigation').append(rearrange); + var resetTopo = $('
'); + $(resetTopo).on("click", function(){ + self.resetTopology({ + resetBelowTabs: false, + model: graphModel + }); + }); + $('.vis-navigation').append(resetTopo);*/ + var _network = this.network; + window.network = _network; + window.view = this; this.network.on("blurNode", function(node){ self.resetTooltip(); }); + this.network.on("dragStart", function(node){ + self.duplicatePathsDrawn = false; + self.resetTooltip(); + self.removeUnderlayPathIds(); + }); this.network.on("dragEnd", function(node){ self.resetTooltip(); + self.duplicatePaths("dragEnd"); + }); + this.network.on("stabilized", function(params){ + self.duplicatePaths("stabilized"); + self.enableFreeflow(); + }); + this.network.on("stabilizationIterationsDone", function(params){ + self.enableFreeflow(); }); - $(document).bind('mousemove',function(e) { self.cursorPosition = { "left": e.pageX, "top": e.pageY }; }); - this.network.on("showPopup", function(elementId){ + this.network.on("showPopup", function(elementId) { var timer = null; var tooltipConfig = null; var hoveredElement = _network.canvas.body.nodes[elementId]; @@ -201,11 +291,15 @@ define([ this.network.on("click", function(params) { var parameters = params; timeout = setTimeout(function() { + if($("#resetTopologyModal").is(":visible")) { + return; + } var params = parameters; if (params.nodes.length == 1) { var clickedElement = _network.canvas.body.nodes[params.nodes[0]]; - self.resetConnectedElements(); + //self.resetConnectedElements(); + self.addDimlightToConnectedElements(); var node = self.nodesDataSet.get(params.nodes[0]); node.icon.color = self.style.defaultSelected.color; self.nodesDataSet.update(node); @@ -245,7 +339,8 @@ define([ var data = {}; var linkDetails = _network.body.data.edges._data[params.edges[0]].linkDetails; var clickedElement = _network.canvas.body.edges[params.edges[0]]; - self.resetConnectedElements(); + //self.resetConnectedElements(); + self.addDimlightToConnectedElements(); var edge = self.edgesDataSet.get(params.edges[0]); edge.color = self.style.defaultSelected.color; self.edgesDataSet.update(edge); @@ -281,114 +376,37 @@ define([ clearTimeout(timeout); timeout = null; } + $(".vis-network-tooltip").popover("hide"); if (params.nodes.length == 1) { - self.removeUnderlayPathIds(); - var dblClickedElement = (_network.findNode(params.nodes[0]))[0]; - var nodeDetails = dblClickedElement.options.nodeDetails; - var elementType = dblClickedElement.options.type; - switch (elementType) { - case 'PhysicalRouter': - var chassis_type = nodeDetails['chassis_type']; - if (chassis_type === "tor") { - graphModel.selectedElement.set({ - 'nodeType': ctwc.PROUTER, - 'nodeDetail': nodeDetails}); - var children = graphModel.getChildren( - nodeDetails['name'], "virtual-router"); - var adjList = _.clone( - graphModel['underlayAdjacencyList']); - if (children.length > 0) { - var childrenName = []; - for (var i = 0; i < children.length; i++) { - childrenName.push(children[i]["name"]); - adjList[children[i]["name"]] = []; - } - adjList[nodeDetails['name']] = childrenName; - graphModel['adjacencyList'] = adjList; - var childElementsArray = self - .createElementsFromAdjacencyList(graphModel); - self.addElementsToGraph(childElementsArray, - graphModel); - self.markErrorNodes(); - self.addDimlightToConnectedElements(); - var thisNode = [nodeDetails]; - self.addHighlightToNodesAndLinks(thisNode, childElementsArray, graphModel); - var graphData = { - nodes: this.nodesDataSet, - edges: this.edgesDataSet - }; - } + if(getCookie('nodeDoubleClick') != "true" && + (self.underlayPathIds.nodes.length > 0 || + self.underlayPathIds.links.length > 0)) { + var textTemplate = + contrail.getTemplate4Id("monitor-infra-topology-reset-template"); + cowu.createModal({ + 'modalId': 'resetTopologyModal', + 'className': 'modal-500', + 'title': 'Clear map/trace flow', + 'btnName': 'Confirm', + 'body': textTemplate(), + 'onSave': function () { + self.underlayPathIds.nodes = []; + self.underlayPathIds.links = []; + self.expandNodes(params); + $("#resetTopologyModal").modal('hide'); + }, + 'onCancel': function () { + $("#resetTopologyModal").modal('hide'); } - break; - case 'VirtualRouter': - graphModel.selectedElement.set({ - 'nodeType': ctwc.VROUTER, - 'nodeDetail': nodeDetails}); - var parentNode = null; - if(graphModel.underlayPathIds.length > 0) { - $.each(_network.getConnectedEdges(params.nodes[0]), function(idx, edge_id) { - var connectedNodes = _network.getConnectedNodes(edge_id); - var connectedNode0 = self.nodesDataSet.get(connectedNodes[0]); - var connectedNode1 = self.nodesDataSet.get(connectedNodes[1]); - if(connectedNode0 && - connectedNode0.nodeDetails.node_type == "physical-router") { - parentNode = connectedNode0; - } else if (connectedNode1 && - connectedNode1.nodeDetails.node_type == "physical-router") { - parentNode = connectedNode1; - } - }); - } - var siblings = []; //vrouter siblings - if(parentNode !== null) { - siblings = graphModel.getChildren(parentNode.nodeDetails['name'], - "virtual-router"); - } - var children = graphModel.getChildren(nodeDetails['name'], - "virtual-machine"); - var newAdjList = {}; - var oldAdjList = {}; - if(graphModel['underlayPathIds'].length > 0) { - newAdjList = _.clone(graphModel['underlayAdjacencyList']); - oldAdjList = _.clone(graphModel['underlayAdjacencyList']); - } - else { - newAdjList = _.clone(graphModel['adjacencyList']); - oldAdjList = _.clone(graphModel['adjacencyList']); - } - if (siblings.length > 0) { - var siblingName = []; - for (var i = 0; i < siblings.length; i++) { - if(siblings[i]["name"] !== dblClickedElement.options.nodeDetails.name) { - siblingName.push(siblings[i]["name"]); - newAdjList[siblings[i]["name"]] = []; - } - } - newAdjList[parentNode.nodeDetails['name']] = siblingName; - oldAdjList = _.clone(newAdjList); - oldAdjList[parentNode.nodeDetails['name']] = []; - } - if (children.length > 0) { - var childrenName = []; - for (var i = 0; i < children.length; i++) { - childrenName.push(children[i]["name"]); - newAdjList[children[i]["name"]] = []; - } - newAdjList[nodeDetails['name']] = childrenName; - } else { - newAdjList = oldAdjList; - } - graphModel['adjacencyList'] = newAdjList; - var childElementsArray = self.createElementsFromAdjacencyList(graphModel); - self.addElementsToGraph(childElementsArray, graphModel); - self.addDimlightToConnectedElements(); - var thisNode = [nodeDetails]; - self.addHighlightToNodesAndLinks(thisNode, childElementsArray, graphModel); - self.markErrorNodes(); - graphModel['adjacencyList'] = oldAdjList; - self.removeUnderlayPathIds(); - break; + }); + } else { + self.expandNodes(params) } + } else if(params.nodes.length == 0 && params.edges.length == 0) { + self.resetConnectedElements(); + monitorInfraUtils.removeUnderlayTabs( + self.rootView.childViewMap[ctwc.UNDERLAY_TABS_VIEW_ID] + ); } }); @@ -397,12 +415,13 @@ define([ var nodes = graphModel.flowPath.get('nodes'); var links = graphModel.flowPath.get('links'); if(nodes.length <=0 || links.length <= 0){ + graphModel.flowPath.set('nodes',graphModel.flowPath._previousAttributes.nodes, {silent: true}); + graphModel.flowPath.set('links',graphModel.flowPath._previousAttributes.links, {silent: true}); showInfoWindow("Cannot find the underlay path for selected flow", "Info"); return false; } else { self.resetTopology({ resetBelowTabs: true, - model: graphModel }); } var elementMap = graphModel['elementMap']; @@ -478,7 +497,6 @@ define([ self.addElementsToGraph(childElementsArray); self.markErrorNodes(); $(".popover").popover().hide(); - var connectionWrapIds = []; if(links.length > 0) self.addDimlightToConnectedElements(); for (var i = 0; i < links.length; i++) { @@ -523,10 +541,54 @@ define([ elementMap.node[childName] = currentElId; } } + } + // When the underlay path is same for earlier flow and + // current flow change events are not triggering so we need to + // reset the nodes and links to empty array once the path is plotted. + graphModel["elementMap"] = elementMap; + graphModel['adjacencyList'] = graphModel['underlayAdjacencyList']; + //graphModel.flowPath.set('nodes',[], {silent: true}); + //graphModel.flowPath.set('links',[], {silent: true}); + }); + + }, + duplicatePaths: function(eventName) { + var self = this; + + if(self.model.flowPath.attributes.nodes.length > 0 && + self.model.flowPath.attributes.links.length > 0 && + self.duplicatePathsDrawn == false) { + self.duplicatePathsDrawn = true; + var dupNodes = {}, dupLinks = {}; + var underlayPathIds = { + nodes: [], + links: [] + }; + var links = self.model.flowPath.attributes.links; + var elementMap = self.model.elementMap; + for (var i = 0; i < links.length; i++) { + var endpoints = links[i].endpoints; + var endpoint0 = endpoints[0]; + var endpoint1 = endpoints[1]; + var linkName = endpoint0 + "<->" + endpoint1; + var altLinkName = endpoint1 + "<->" + endpoint0; + var link = elementMap.link[linkName]; + var existingLink = self.edgesDataSet.get(link); + + var parentNode = null, childNode = null; + if(existingLink.from == elementMap.node[endpoint0]) { + parentNode = self.nodesDataSet.get(elementMap.node[endpoint0]); + childNode = self.nodesDataSet.get(elementMap.node[endpoint1]); + } else { + parentNode = self.nodesDataSet.get(elementMap.node[endpoint1]); + childNode = self.nodesDataSet.get(elementMap.node[endpoint0]); + } + if(parentNode == null || childNode == null) + continue; var parentId = parentNode.id; + var childId = childNode.id; var parentNodeType = parentNode.nodeDetails.node_type; var childNodeType = childNode.nodeDetails.node_type; - var childId = childNode.id; var link_type = parentNodeType.split("-")[0][0] + parentNodeType.split("-")[1][0] + '-' + childNodeType.split("-")[0][0] + @@ -539,26 +601,191 @@ define([ arrowPosition = "from"; } - var newLink = self.createLink(links[i], link_type, parentId, childId, true, arrowPosition); + var parentNodeElement = self.network.findNode(parentId); + parentNodeElement = parentNodeElement[0]; + var childNodeElement = self.network.findNode(childId); + childNodeElement = childNodeElement[0]; + var newParentNode = null, newChildNode = null; + + if(dupNodes.hasOwnProperty(parentNode.nodeDetails.name)) { + newParentNode = dupNodes[parentNode.nodeDetails.name].data; + } else { + newParentNode = self.createNode(parentNode.nodeDetails); + newParentNode.x = parentNodeElement.x-7; + newParentNode.y = parentNodeElement.y-7; + /*if(parentNodeElement.x > childNodeElement.x && + childNodeElement.y > parentNodeElement.y) { + newParentNode.x = parentNodeElement.x - 5; + newParentNode.y = parentNodeElement.y + 5; + } else if(parentNodeElement.x < childNodeElement.x && + parentNodeElement.y < childNodeElement.y) { + newParentNode.x = parentNodeElement.x + 5; + newParentNode.y = parentNodeElement.y + 5; + }*/ + newParentNode.hidden = true; + dupNodes[parentNode.nodeDetails.name] = {}; + dupNodes[parentNode.nodeDetails.name]["id"]= newParentNode.id; + dupNodes[parentNode.nodeDetails.name]["data"]= newParentNode; + underlayPathIds.nodes.push(newParentNode.id); + self.nodesDataSet.add(newParentNode); + } + + if(dupNodes.hasOwnProperty(childNode.nodeDetails.name)) { + newChildNode = dupNodes[childNode.nodeDetails.name].data; + } else { + newChildNode = self.createNode(childNode.nodeDetails); + newChildNode.x = childNodeElement.x-7; + newChildNode.y = childNodeElement.y-7; + /*if(parentNodeElement.x > childNodeElement.x && + childNodeElement.y > parentNodeElement.y) { + newChildNode.x = childNodeElement.x + 5; + newChildNode.y = childNodeElement.y - 5; + } else if(parentNodeElement.x < childNodeElement.x && + parentNodeElement.y < childNodeElement.y) { + newChildNode.x = childNodeElement.x - 5; + newChildNode.y = childNodeElement.y - 5; + }*/ + newChildNode.hidden = true; + dupNodes[childNode.nodeDetails.name] = {}; + dupNodes[childNode.nodeDetails.name]["id"]= newChildNode.id; + dupNodes[childNode.nodeDetails.name]["data"]= newChildNode; + underlayPathIds.nodes.push(newParentNode.id); + self.nodesDataSet.add(newChildNode); + } + + var newLink = self.createLink(links[i], link_type, + newParentNode.id, newChildNode.id, arrowPosition, eventName); var currentLinkId = newLink.id; self.edgesDataSet.add(newLink); - connectionWrapIds.push(currentLinkId); + underlayPathIds.links.push(currentLinkId); + dupLinks[parentNode.nodeDetails.name+ "<->" + childNode.nodeDetails.name] = currentLinkId; + dupLinks[childNode.nodeDetails.name+ "<->" + parentNode.nodeDetails.name] = currentLinkId; } - if(connectionWrapIds.length > 0) { - graphModel['underlayPathIds'] = connectionWrapIds; - } - // When the underlay path is same for earlier flow and - // current flow change events are not triggering so we need to - // reset the nodes and links to empty array once the path is plotted. - graphModel["elementMap"] = elementMap; - graphModel['adjacencyList'] = graphModel['underlayAdjacencyList']; - graphModel.flowPath.set('nodes',[], {silent: true}); - graphModel.flowPath.set('links',[], {silent: true}); - }); - + self.underlayPathIds = underlayPathIds; + } + }, + clearFlowPath: function() { + graphModel.flowPath.set('nodes',[], {silent: true}); + graphModel.flowPath.set('links',[], {silent: true}); + }, + expandNodes: function (params) { + var self = this; + var _network = self.network; + var graphModel = self.model; + self.clearFlowPath(); + self.removeUnderlayPathIds(); + var dblClickedElement = (_network.findNode(params.nodes[0]))[0]; + var nodeDetails = dblClickedElement.options.nodeDetails; + var elementType = dblClickedElement.options.type; + switch (elementType) { + case 'PhysicalRouter': + var chassis_type = nodeDetails['chassis_type']; + if (chassis_type === "tor") { + graphModel.selectedElement.set({ + 'nodeType': ctwc.PROUTER, + 'nodeDetail': nodeDetails}); + var children = graphModel.getChildren( + nodeDetails['name'], "virtual-router"); + var adjList = _.clone( + graphModel['underlayAdjacencyList']); + if (children.length > 0) { + var childrenName = []; + for (var i = 0; i < children.length; i++) { + childrenName.push(children[i]["name"]); + adjList[children[i]["name"]] = []; + } + adjList[nodeDetails['name']] = childrenName; + graphModel['adjacencyList'] = adjList; + var childElementsArray = self + .createElementsFromAdjacencyList(graphModel); + self.addElementsToGraph(childElementsArray, + graphModel); + self.markErrorNodes(); + self.addDimlightToConnectedElements(); + var thisNode = [nodeDetails]; + self.addHighlightToNodesAndLinks(thisNode, childElementsArray, graphModel); + var graphData = { + nodes: this.nodesDataSet, + edges: this.edgesDataSet + }; + } + } + break; + case 'VirtualRouter': + graphModel.selectedElement.set({ + 'nodeType': ctwc.VROUTER, + 'nodeDetail': nodeDetails}); + var parentNode = null; + if(self.underlayPathIds.nodes.length > 0 || + self.underlayPathIds.links.length > 0) { + $.each(_network.getConnectedEdges(params.nodes[0]), function(idx, edge_id) { + var connectedNodes = _network.getConnectedNodes(edge_id); + var connectedNode0 = self.nodesDataSet.get(connectedNodes[0]); + var connectedNode1 = self.nodesDataSet.get(connectedNodes[1]); + if(connectedNode0 && + connectedNode0.nodeDetails.node_type == "physical-router") { + parentNode = connectedNode0; + } else if (connectedNode1 && + connectedNode1.nodeDetails.node_type == "physical-router") { + parentNode = connectedNode1; + } + }); + } + var siblings = []; //vrouter siblings + if(parentNode != null) { + siblings = graphModel.getChildren(parentNode.nodeDetails['name'], + "virtual-router"); + } + var children = graphModel.getChildren(nodeDetails['name'], + "virtual-machine"); + var newAdjList = {}; + var oldAdjList = {}; + if(self.underlayPathIds.nodes.length > 0 || + self.underlayPathIds.links.length > 0) { + newAdjList = _.clone(graphModel['underlayAdjacencyList']); + oldAdjList = _.clone(graphModel['underlayAdjacencyList']); + } + else { + newAdjList = _.clone(graphModel['adjacencyList']); + oldAdjList = _.clone(graphModel['adjacencyList']); + } + if (siblings.length > 0) { + var siblingName = []; + for (var i = 0; i < siblings.length; i++) { + if(siblings[i]["name"] !== dblClickedElement.options.nodeDetails.name) { + siblingName.push(siblings[i]["name"]); + newAdjList[siblings[i]["name"]] = []; + } + } + newAdjList[parentNode.nodeDetails['name']] = siblingName; + oldAdjList = _.clone(newAdjList); + oldAdjList[parentNode.nodeDetails['name']] = []; + } + if (children.length > 0) { + var childrenName = []; + for (var i = 0; i < children.length; i++) { + childrenName.push(children[i]["name"]); + newAdjList[children[i]["name"]] = []; + } + newAdjList[nodeDetails['name']] = childrenName; + } else { + newAdjList = oldAdjList; + } + graphModel['adjacencyList'] = newAdjList; + var childElementsArray = self.createElementsFromAdjacencyList(graphModel); + self.addElementsToGraph(childElementsArray, graphModel); + self.addDimlightToConnectedElements(); + var thisNode = [nodeDetails]; + self.addHighlightToNodesAndLinks(thisNode, childElementsArray, graphModel); + self.markErrorNodes(); + graphModel['adjacencyList'] = oldAdjList; + self.clearFlowPath(); + self.removeUnderlayPathIds(); + break; + } }, - createLink: function(link, link_type, srcId, tgtId, shadowLink, arrowPosition, reverse) { + createLink: function(link, link_type, srcId, tgtId, arrowPosition, eventName) { var options; var linkElement; link.link_type = link_type; @@ -567,41 +794,34 @@ define([ id: id, from: srcId, to: tgtId, - linkDetails: link, - color: this.style.default.color + linkDetails: link }; - if(arrowPosition == "to") { - linkConfig.arrows = { - to : { - enabled : true, - scaleFactor: 1 - } - }; - } else if(arrowPosition == "from") { - linkConfig.arrows = { - from : { - enabled : true, - scaleFactor: 1 - } - }; - } - if(true == shadowLink) { - linkConfig.color = this.style.defaultSelected.color; - linkConfig.width = 2; - linkConfig.smooth = { - type: "curvedCW", - forceDirection: "vertical", - roundness: .2 - - }; - linkConfig.dashes = true; - /*linkConfig.shadow = - { - enabled: true, - size: 10, - x: (reverse == true) ? -5 : 5, - y: (reverse == true) ? -5 : 5 - }*/ + if(arrowPosition == "from" || + arrowPosition == "to") { + if(self.network.getSelectedNodes().length > 0 || + self.network.getSelectedEdges().length > 0) { + if(eventName == "dragEnd") + linkConfig.color = this.style.flowPathDefault.color; + else + linkConfig.color = this.style.flowPathDimlight.color; + } else { + linkConfig.color = this.style.flowPathDefault.color; + } + if(arrowPosition == "to") { + linkConfig.arrows = { + to : { + enabled : true, + scaleFactor: .6 + } + }; + } else if(arrowPosition == "from") { + linkConfig.arrows = { + from : { + enabled : true, + scaleFactor: .6 + } + }; + } } var tooltipConfig = this.getUnderlayTooltipConfig()['link']; var title = tooltipConfig.title(link); @@ -636,21 +856,17 @@ define([ var node_type = node.node_type; var chassis_type = node.chassis_type; var nodeConfig = {}; - nodeConfig.shape = 'icon'; + /*nodeConfig.shape = 'icon'; nodeConfig.font = {}; nodeConfig.font.face = 'Arial, helvetica, sans-serif'; nodeConfig.font.size = 10; nodeConfig.font.color = '#333'; //text color nodeConfig.font.strokeColor = '#333'; //text color - nodeConfig.font.strokeWidth = 0.4; //text color + nodeConfig.font.strokeWidth = 0.4; //text color*/ nodeConfig.icon = {}; - nodeConfig.icon.face = 'contrailFonts'; + /*nodeConfig.icon.face = 'contrailFonts'; nodeConfig.icon.size = 40; - nodeConfig.icon.color = this.style.default.color; //icon color - nodeConfig.color = {}; - nodeConfig.color.hover = {}; - nodeConfig.color.hover.border = "red"; - nodeConfig.color.hover.background = "red"; + nodeConfig.icon.color = this.style.default.color; //icon color*/ var labelNodeName = contrail.truncateText(nodeName, 20); switch (node_type) { case 'physical-router': @@ -708,7 +924,7 @@ define([ nodeConfig.icon.code = '\ue602'; break; } - nodeConfig.level = level; + //nodeConfig.level = level; var id = UUIDjs.create().hex; nodeConfig.id = id; nodeConfig.label = labelNodeName; @@ -905,6 +1121,59 @@ define([ continue; } } + + /*for(var i=0; i" + endpoint1; + var altLinkName = endpoint1 + "<->" + endpoint0; + var endpoint0Node = jsonPath(nodes, '$[?(@.name=="' + endpoint0 + '")]'); + if(false !== endpoint0Node && endpoint0Node.length === 1) { + endpoint0Node = endpoint0Node[0]; + } else { + continue; + } + var endpoint1Node = jsonPath(nodes, '$[?(@.name=="' + endpoint1 + '")]'); + if(false !== endpoint1Node && endpoint1Node.length === 1) { + endpoint1Node = endpoint1Node[0]; + } else { + continue; + } + + var endpoint0NodeType = endpoint0Node.node_type; + var endpoint1NodeType = endpoint1Node.node_type; + if(endpoint0NodeType == "virtual-router" || + endpoint1NodeType == "virtual-router" || + endpoint0NodeType == "virtual-machine" || + endpoint1NodeType == "virtual-machine") + continue; + var link_type = endpoint0NodeType.split("-")[0][0] + + endpoint0NodeType.split("-")[1][0] + '-' + + endpoint1NodeType.split("-")[0][0] + + endpoint1NodeType.split("-")[1][0]; + if(null == elMap["node"][endpoint0]) { + var currentEl = self.createNode(endpoint0Node); + elements.push(currentEl); + var currentElId = currentEl.id; + elMap.node[endpoint0] = currentElId; + } + if(null == elMap["node"][endpoint1]) { + var currentEl = self.createNode(endpoint1Node); + elements.push(currentEl); + var currentElId = currentEl.id; + elMap.node[endpoint1] = currentElId; + } + linkElements.push( + self.createLink(link, link_type, elMap["node"][endpoint0], elMap["node"][endpoint1])); + var currentLink = + linkElements[linkElements.length - 1]; + var currentLinkId = currentLink.id; + + elMap.link[linkName] = currentLinkId; + elMap.link[altLinkName] = currentLinkId; + }*/ underlayGraphModel.elementMap = elMap; underlayGraphModel.connectedElements = conElements; // Links must be added after all the elements. This is because when the links @@ -939,7 +1208,7 @@ define([ ajaxConfig: { url: ctwl.URL_UNDERLAY_TOPOLOGY, type: 'GET' - }, + }, failureCallback: function(response, model) { model['tree'] = {}; _this.populateModelAndAddToGraph(null); @@ -960,6 +1229,7 @@ define([ } }, failureCallback: function(response, model) { + $(".vis-network-tooltip").popover('destroy'); model['tree'] = {}; _this.populateModelAndAddToGraph(null); } @@ -977,11 +1247,28 @@ define([ var edgesDataSet = this.edgesDataSet; var nodeIds = nodesDataSet.getIds(); var edgeIds = edgesDataSet.getIds(); + var mapPositions = {}; + var newElementIds = jsonPath(elements, "$..id"); + _.each(nodeIds, function(id, idx) { - nodesDataSet.remove(id); + if(newElementIds.indexOf(id) == -1) { + nodesDataSet.remove(id); + } else { + var node = network.findNode(id); + if(node && node.length == 1) { + node = node[0]; + mapPositions[id] = {}; + mapPositions[id] = { + x: node.x, + y: node.y + } + } + } }); _.each(edgeIds, function(id, idx) { - edgesDataSet.remove(id); + if(newElementIds.indexOf(id) == -1) { + edgesDataSet.remove(id); + } }); for (var i = 0; i < elements.length; i++) { if (elements[i].hasOwnProperty('from') && @@ -1007,6 +1294,55 @@ define([ } } } + //if(mapPositions === {}) + network.setOptions(this.visOptions); + var updatedNodes = []; + _.each(mapPositions, function(position, id) { + for (var i = 0; i < elements.length; i++) { + if (elements[i].id == id) { + elements[i].x = position.x; + elements[i].y = position.y; + nodesDataSet.update(elements[i]); + updatedNodes.push(id); + continue; + } + } + }); + var newElements = []; + var newParentElement = null; + if(updatedNodes.length > 0) { + for(var i=0; i 0) { + var parentEl = + nodesDataSet.get(this.model.elementMap.node[elements[i].nodeDetails.parent[0]]); + if(parentEl) { + newParentElement = parentEl; + newElements.push(elements[i]); + } + } + } + } + } + } + if(newParentElement !== null) { + var len = newElements.length * this.visOptions.layout.hierarchical.levelSeparation; + var startPos = newParentElement.x - (len/2); + if(!isNaN(startPos)) { + for(var i=0; i 0) + node.icon.color = _this.style.defaultDimlight.color; + else + node.icon.color = _this.style.default.color; nodes.push(node); } + + _.each(mapPositions, function(position, id) { + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].id == id) { + nodes[i].x = position.x; + nodes[i].y = position.y; + continue; + } + } + }); nodesDataSet.update(nodes); var edges = []; for (var i = 0; i < edgeIds.length; i++) { var edge = edgesDataSet.get(edgeIds[i]); - edge.color = _this.style.default.color; + if($.inArray(edgeIds[i], flowPathEdges) == -1) { + edge.color = _this.style.defaultDimlight.color; + } else { + if(_this.network.getSelectedNodes().length > 0 || + _this.network.getSelectedEdges().length > 0) { + edge.color = this.style.flowPathDimlight.color; + } else { + edge.color = this.style.flowPathDefault.color; + } + } edges.push(edge); } edgesDataSet.update(edges); @@ -1430,11 +1836,13 @@ define([ }, resetTopology: function(options) { - var underlayGraphModel = options['model']; + var self = this; + var underlayGraphModel = self.model; this.removeUnderlayPathIds(); - this.clearHighlightedConnectedElements(); + this.resetConnectedElements() var adjList = _.clone(underlayGraphModel['underlayAdjacencyList']); underlayGraphModel['adjacencyList'] = adjList; + self.network.setOptions(self.visOptions); var childElementsArray = this.createElementsFromAdjacencyList(underlayGraphModel); this.addElementsToGraph(childElementsArray, underlayGraphModel); @@ -1443,6 +1851,12 @@ define([ monitorInfraUtils.removeUnderlayTabs( this.rootView.viewMap[ctwc.UNDERLAY_TABS_VIEW_ID]); } + underlayGraphModel.selectedElement.set({ + 'nodeType': '', + 'nodeDetail': {}}); + + $('.vis-button vis-zoomExtends').click(); + self.enableFreeflow(); }, clearHighlightedConnectedElements: function() { diff --git a/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayTabView.js b/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayTabView.js index 333434815..1f97b7c17 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayTabView.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayTabView.js @@ -17,24 +17,34 @@ define([ getUnderlayTabConfig(viewConfig), null, null, null, function (underlayTabView) { if(!callBackExecuted) { - var graphModel = $('#' + ctwl.UNDERLAY_GRAPH_ID).data('graphModel'); - underlayTabView.listenTo(graphModel.selectedElement, 'change', function (selectedElement) { - var nodeType = selectedElement['attributes']['nodeType']; - var nodeDetails = selectedElement['attributes']['nodeDetail']; - if(nodeType == ctwc.PROUTER) { - showPRouterTabs(nodeDetails, underlayTabView); - } else if (nodeType == ctwc.VROUTER) { - showVRouterTabs(nodeDetails, underlayTabView); - } else if (nodeType == ctwc.VIRTUALMACHINE) { - showVMTabs(nodeDetails, underlayTabView); - } else if (nodeType == ctwc.UNDERLAY_LINK) { - showLinkTrafficStatistics(nodeDetails, underlayTabView); - } - }); + self.listenToGraphModel(underlayTabView); callBackExecuted = true; } } ); + }, + listenToGraphModel : function (underlayTabView) { + var _this = this; + if($('#' + ctwl.UNDERLAY_GRAPH_ID).data('graphModel') != null) { + var graphModel = $('#' + ctwl.UNDERLAY_GRAPH_ID).data('graphModel'); + underlayTabView.listenTo(graphModel.selectedElement, 'change', function (selectedElement) { + var nodeType = selectedElement['attributes']['nodeType']; + var nodeDetails = selectedElement['attributes']['nodeDetail']; + if(nodeType == ctwc.PROUTER) { + showPRouterTabs(nodeDetails, underlayTabView); + } else if (nodeType == ctwc.VROUTER) { + showVRouterTabs(nodeDetails, underlayTabView); + } else if (nodeType == ctwc.VIRTUALMACHINE) { + showVMTabs(nodeDetails, underlayTabView); + } else if (nodeType == ctwc.UNDERLAY_LINK) { + showLinkTrafficStatistics(nodeDetails, underlayTabView); + } + }); + } else { + setTimeout(function(underlayTabView) { + _this.listenToGraphModel(_this) + }, 1000); + } } });