From 77887e01b6c0da65bf49a489619ec3338679a2cb Mon Sep 17 00:00:00 2001 From: Vishnu Vardhan V Date: Wed, 30 Dec 2015 15:56:37 +0530 Subject: [PATCH] Migrating underlay/overlay topology from 'jointjs' to 'visjs' 1) Initial changes required to import visjs instead of jointjs for topology visualization. - UnderlayGraphView extends ContrailView - UnderlayGraphModel extends ContrailVisModel which inturn extends Backbone.Model, takes ContrailRemoteDataHandler as argument. - Save UnderlayGraphModel in the 'data' attribute of 'underlay-graph' div. 2) Removing the earlier paths of trace and map flow 3) HostName and Management IP label change in the details tab. 4) Added the node name to the title in prouter details tab 5) Showing the loading icon in topology portion. Related-Bug: #1529765 Change-Id: I2fb8a0f6fff9099bbf672e2a2b6eeb27ce143b79 --- webroot/common/ui/templates/controller.tmpl | 15 +- .../ui/js/utils/monitor.infra.parsers.js | 5 +- .../common/ui/js/utils/monitor.infra.utils.js | 34 +- .../ui/js/models/UnderlayGraphModel.js | 273 +- .../ui/js/views/SearchFlowFormView.js | 3 +- .../ui/js/views/SearchFlowResultView.js | 6 +- .../ui/js/views/TraceFlowResultView.js | 16 +- .../underlay/ui/js/views/UnderlayGraphView.js | 2341 +++++++++++------ .../underlay/ui/js/views/UnderlayTabView.js | 4 +- .../underlay/ui/js/views/UnderlayView.js | 35 +- 10 files changed, 1574 insertions(+), 1158 deletions(-) diff --git a/webroot/common/ui/templates/controller.tmpl b/webroot/common/ui/templates/controller.tmpl index 895744ef1..556d8ff2b 100644 --- a/webroot/common/ui/templates/controller.tmpl +++ b/webroot/common/ui/templates/controller.tmpl @@ -24,16 +24,11 @@ diff --git a/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.parsers.js b/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.parsers.js index 7f7eeb1a7..e891cd57f 100644 --- a/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.parsers.js +++ b/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.parsers.js @@ -1725,9 +1725,8 @@ define( } }; this.parseUnderlayFlowRecords = function (response) { - var graphView = - $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphView'); - response['vRouters'] = graphView.model.vRouters; + var graphModel = monitorInfraUtils.getUnderlayGraphModel(); + response['vRouters'] = graphModel.vRouters; var vRouters = ifNull(response['vRouters'],[]); $.each(ifNull(response['data'],[]),function (idx,obj) { var formattedVrouter,formattedOtherVrouter, diff --git a/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.utils.js b/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.utils.js index c8116e953..aa5939893 100644 --- a/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.utils.js +++ b/webroot/monitor/infrastructure/common/ui/js/utils/monitor.infra.utils.js @@ -1863,13 +1863,13 @@ define([ viewConfig: { data: viewConfig.data, templateConfig: monitorInfraUtils. - getUnderlayDetailsTabTemplateConfig(), + getUnderlayDetailsTabTemplateConfig(viewConfig.data), app: cowc.APP_CONTRAIL_CONTROLLER, }, } }; - self.getUnderlayDetailsTabTemplateConfig = function() { + self.getUnderlayDetailsTabTemplateConfig = function(data) { return { advancedViewOptions: false, templateGenerator: 'RowSectionTemplateGenerator', @@ -1883,12 +1883,15 @@ define([ class: 'span6', rows: [ { - title: ctwl.UNDERLAY_PROUTER_DETAILS, + title: contrail.format('{0} ( {1} )', + ctwl.UNDERLAY_PROUTER_DETAILS, + data.hostName), templateGenerator: 'BlockListTemplateGenerator', templateGeneratorConfig: [ { key: 'hostName', + label: 'Hostname', templateGenerator: 'TextGenerator' },{ @@ -1906,6 +1909,7 @@ define([ } },{ key: 'managementIP', + label: 'Management IP', templateGenerator: 'TextGenerator', } @@ -1924,10 +1928,8 @@ define([ self.getTrafficStatisticsTabViewConfig = function (data) { var ajaxConfig = {}; var endpoints = ifNull(data['endpoints'],[]); - var sourceType = getValueByJsonPath(data, - 'sourceElement;attributes;nodeDetails;node_type','-'); - var targetType = getValueByJsonPath(data, - 'targetElement;attributes;nodeDetails;node_type','-'); + var sourceType = getValueByJsonPath(data,'sourceElement;node_type','-'); + var targetType = getValueByJsonPath(data,'targetElement;node_type','-'); var view = 'LineWithFocusChartView', modelMap = null; var viewConfig = {}, viewPathPrefix; if(sourceType == ctwc.PROUTER && targetType == ctwc.PROUTER) { @@ -1951,8 +1953,7 @@ define([ }; } else if(sourceType == ctwc.PROUTER && targetType == ctwc.VROUTER) { var vrouter = (sourceType == ctwc.VROUTER) ? - data['sourceElement']['attributes']['nodeDetails']['name']: - data['targetElement']['attributes']['nodeDetails']['name']; + data['sourceElement']['name']: data['targetElement']['name']; var params = { minsSince: 60, sampleCnt: 120, @@ -1991,10 +1992,9 @@ define([ } } else if(sourceType == ctwc.VIRTUALMACHINE || targetType == ctwc.VIRTUALMACHINE) { - var instanceUUID = getValueByJsonPath(data, - 'targetElement;attributes;nodeDetails;name','-'); + var instanceUUID = getValueByJsonPath(data, 'targetElement;name','-'); var vmName = getValueByJsonPath(data, - 'targetElement;attributes;nodeDetails;more_attributes;vm_name','-'); + 'targetElement;more_attributes;vm_name','-'); var modelKey = ctwc.get(ctwc.UMID_INSTANCE_UVE, instanceUUID); view = 'InstanceTrafficStatsView'; viewPathPrefix = 'monitor/networking/ui/js/views/'; @@ -2094,8 +2094,8 @@ define([ }, self.getTraceFlowVrouterGridColumns = function () { - var graphView = $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphView'); - computeNodes = graphView.model.vRouters; + var graphModel = monitorInfraUtils.getUnderlayGraphModel(); + computeNodes = graphModel.vRouters; return [ { field:'peer_vrouter', @@ -2358,8 +2358,8 @@ define([ } ]; }; - self.getUnderlayGraphInstance = function () { - return $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphView'); + self.getUnderlayGraphModel = function () { + return $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphModel'); }; self.showFlowPath = function (connectionWrapIds, offsetWidth, graphView) { @@ -2619,7 +2619,7 @@ define([ }; self.showUnderlayPaths = function (data) { - var graphModel = monitorInfraUtils.getUnderlayGraphInstance().model; + var graphModel = monitorInfraUtils.getUnderlayGraphModel(); var currentUrlHashObj = layoutHandler.getURLHashObj(), currentPage = currentUrlHashObj.p, currentParams = currentUrlHashObj.q; diff --git a/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js b/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js index e0b6071fd..f2705f4e6 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/models/UnderlayGraphModel.js @@ -2,9 +2,8 @@ * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. */ -define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbone) { - var UnderlayGraphModel = ContrailGraphModel.extend({ - +define(['contrail-vis-model', 'backbone'],function(ContrailVisModel, Backbone) { + UnderlayGraphModel = ContrailVisModel.extend({ initialize: function (graphModelConfig) { this.nodes = [], this.links = [], @@ -24,6 +23,7 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon this.underlayPathReqObj = {}, this.uveMissingNodes = [], this.configMissingNodes = [], + this.firstLevelNode = "", this.selectedElement = new Backbone.Model({ nodeType: '', nodeDetail: {} @@ -32,9 +32,9 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon nodes: [], links: [] }); - ContrailGraphModel.prototype.initialize.apply(this, [graphModelConfig]); + ContrailVisModel.prototype.initialize.apply(this, [graphModelConfig]); }, - getElementsForUnderlayGraph : function (response) { + initializeUnderlayModel : function (response) { //update chasis-type in nodes var nodes = ifNull(response['nodes'], []); var configMissingNodes = getValueByJsonPath(response, @@ -47,7 +47,7 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon var nodeObj = { name:configMissingNodes[i], node_type: "physical-router", - chassis_type: "coreswitch", + chassis_type: "unknown", more_attributes: {}, errorMsg:'Configuration Unavailable' }; @@ -57,7 +57,7 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon var nodeObj = { name:uveMissingNodes[i], node_type: "physical-router", - chassis_type: "coreswitch", + chassis_type: "unknown", more_attributes: {}, errorMsg:'System Information Unavailable' }; @@ -131,17 +131,21 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon } if(cores.length > 0) { firstLevelNodes = cores; + this.firstLevelNode = "coreswitch"; } else { var spines = this.spines; if(spines.length > 0) { firstLevelNodes = spines; + this.firstLevelNode = "spine"; } else { var tors = this.tors; if(tors.length > 0) { firstLevelNodes = tors; + this.firstLevelNode = "tor"; } } } + this.firstLevelNodes = firstLevelNodes; this.parseTree(firstLevelNodes, tree, tmpTree); if(JSON.stringify(tmpTree) !== "{}") { $.each(tmpTree, function (elementKey, elementValue) { @@ -152,263 +156,14 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon var adjacencyList = this.prepareData('tor'); this.adjacencyList = adjacencyList; this.underlayAdjacencyList = adjacencyList; - var els = this.createElementsFromAdjacencyList(); + //this.addElementsToGraph(els, null); - return { - elements: els, - nodes: this.nodes, - links: this.links, - }; }, - createElementsFromAdjacencyList: function () { - var elements = []; - var linkElements = []; - var self = this; - var adjacencyList = this.adjacencyList; - var conElements = this.connectedElements; - var nodes = this.nodes; - var links = this.links; - var elMap = this.elementMap; - _.each(adjacencyList, function(edges, parentElementLabel) { - if(null !== elMap["node"][parentElementLabel] && - typeof elMap["node"][parentElementLabel] !== "undefined") { - var el = self.getCell(elMap["node"][parentElementLabel]); - if(null !== el && typeof el !== "undefined") { - elements.push(el); - return; - } else { - el = jsonPath(conElements, '$[?(@.id=="' + - elMap["node"][parentElementLabel] + '")]'); - if(typeof el === "object" && el.length === 1) { - elements.push(el[0]); - return; - } - } - } - var parentNode = jsonPath(nodes, '$[?(@.name=="' + - parentElementLabel + '")]'); - if(false !== parentNode && parentNode.length === 1) { - parentNode = parentNode[0]; - var parentName = parentNode.name; - var parentNodeType = parentNode.node_type; - elements.push(self.createNode(parentNode)); - var currentEl = elements[elements.length-1]; - conElements.push(currentEl); - var currentElId = currentEl.id; - elMap.node[parentName] = currentElId; - } - }); - - _.each(adjacencyList, function(edges, parentElementLabel) { - var parentNode = jsonPath(nodes, '$[?(@.name=="' + - parentElementLabel + '")]'); - if(false !== parentNode && parentNode.length === 1) { - parentNode = parentNode[0]; - var parentNodeType = parentNode.node_type; - var parentId = elMap.node[parentNode.name]; - _.each(edges, function(childElementLabel) { - if(null !== elMap["link"][parentElementLabel + - "<->" + childElementLabel] && - typeof elMap["link"][parentElementLabel + - "<->" + childElementLabel] !== "undefined") { - var linkEl = self.getCell(elMap["link"][parentElementLabel + - "<->" + childElementLabel]); - if(null !== linkEl && typeof linkEl !== "undefined") { - linkElements.push(linkEl); - return; - } else { - linkEl = jsonPath(conElements, '$[?(@.id=="' + - elMap["link"][parentElementLabel + - '<->' + childElementLabel] + '")]'); - if(typeof linkEl === "object" && - linkEl.length === 1) { - linkElements.push(linkEl[0]); - return; - } - } - } - var childNode = jsonPath(nodes, '$[?(@.name=="' + - childElementLabel + '")]'); - if(false !== childNode && childNode.length === 1) { - childNode = childNode[0]; - var childNodeType = childNode.node_type; - var childId = elMap.node[childNode["name"]]; - var link_type = parentNodeType.split("-")[0][0] + - parentNodeType.split("-")[1][0] + '-' + - childNodeType.split("-")[0][0] + - childNodeType.split("-")[1][0]; - for(var i=0; i" + parentElementLabel; - var altLinkName = parentElementLabel + - "<->" + childElementLabel; - if((null == elMap["link"][linkName] && - typeof elMap["link"][linkName] == "undefined") && - null == elMap["link"][altLinkName] && - typeof elMap["link"][altLinkName] == "undefined") { - linkElements.push(self.createLink( - link, link_type, parentId, childId)); - var currentLink = - linkElements[linkElements.length-1]; - var currentLinkId = currentLink.id; - conElements.push(currentLink); - elMap.link[linkName] = currentLinkId; - elMap.link[altLinkName] = currentLinkId; - break; - } - } - } - } - }); - } - }); - 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; - var link_type = endpoint0NodeType.split("-")[0][0] + - endpoint0NodeType.split("-")[1][0] + '-' + - endpoint1NodeType.split("-")[0][0] + - endpoint1NodeType.split("-")[1][0]; - if(null != elMap["node"] && typeof elMap["node"] !== "undefined") { - if(null != elMap["node"][endpoint0] && typeof elMap["node"][endpoint0] !== "undefined" && - null != elMap["node"][endpoint1] && typeof elMap["node"][endpoint1] !== "undefined") { - if(null == elMap["link"][linkName] && typeof elMap["link"][linkName] === "undefined" && - null == elMap["link"][altLinkName] && typeof elMap["link"][altLinkName] === "undefined") { - linkElements.push( - self.createLink(link, link_type, elMap["node"][endpoint0], elMap["node"][endpoint1])); - var currentLink = - linkElements[linkElements.length - 1]; - var currentLinkId = currentLink.id; - conElements.push(currentLink); - elMap.link[linkName] = currentLinkId; - elMap.link[altLinkName] = currentLinkId; - } else { - var el = jsonPath(linkElements, '$[?(@.id=="' + linkName + '")]'); - var linkEl = self.getCell(elMap.link[linkName]); - if(false == el && null != linkEl) { - linkElements.push(linkEl); - } else - continue; - } - } else { - continue; - } - } else { - continue; - } - } - this.connectedElements = conElements; - // Links must be added after all the elements. This is because when the links - // are added to the graph, link source/target - // elements must be in the graph already. - return elements.concat(linkElements.unique()); - }, getErrorNodes: function () { return this.uveMissingNodes.concat(this.configMissingNodes); }, - createLink: function (link, link_type, srcId, tgtId) { - var options; - var linkElement; - link.link_type = link_type; - options = { - direction : "bi", - linkType : link.link_type, - linkDetails : link - }; - link['connectionStroke'] = '#637939'; - - options['sourceId'] = srcId; - options['targetId'] = tgtId; - linkElement = new ContrailElement('link', options); - return linkElement; - }, - - createNode: function (node) { - var nodeName = node['name'], - type = node.node_type, - chassis_type = node.chassis_type, - width = 40, - height = 40, - imageLink, element, options, imageName; - var refX, refY; - var labelNodeName = contrail.truncateText(nodeName,20); - switch(chassis_type) { - case "coreswitch": - chassis_type = 'router'; - break; - case "spine": - chassis_type = 'router'; - break; - case "tor": - chassis_type = 'switch'; - break; - case "virtual-machine": - if(node.hasOwnProperty('more_attributes') && - node.more_attributes.hasOwnProperty('vm_name') && - node.more_attributes.vm_name.trim() !== "" && - node.more_attributes.vm_name.trim() !== "-") { - labelNodeName = contrail.truncateText( - node.more_attributes.vm_name.trim(),10); - } else { - labelNodeName = contrail.truncateText(nodeName,10); - } - refY = .9; - break; - } - imageName = getImageName(node); - imageLink = '/img/icons/' + imageName; - options = { - attrs: { - image: { - 'xlink:href': imageLink, - width: width, - height: height - }, - text: { - text: labelNodeName, - "ref-y": refY - } - }, - size: { - width: width, - height: height - }, - nodeDetails: node, - font: { - iconClass: 'icon-contrail-' + chassis_type - } - }; - element = new ContrailElement(type, options); - return element; - }, - prepareData : function (stopAt) { var treeModel = this.tree; var adjList = {}; @@ -472,6 +227,8 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon return "virtual-router"; case "virtual-router": return "virtual-machine"; + case "unknown": + return ""; } }, getChildren : function (parent, child_type) { @@ -545,4 +302,4 @@ define(['contrail-graph-model', 'backbone'],function(ContrailGraphModel, Backbon } }); return UnderlayGraphModel; -}); \ No newline at end of file +}); diff --git a/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowFormView.js b/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowFormView.js index 6f9dc367d..522581259 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowFormView.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowFormView.js @@ -20,8 +20,7 @@ define([ self.renderView4Config($(self.$el).find(queryFormId), this.model, self.getViewConfig(), null, null, null, function (searchFlowFormView) { - var graph = monitorInfraUtils.getUnderlayGraphInstance(); - var graphModel = graph.model; + var graphModel = monitorInfraUtils.getUnderlayGraphModel(); searchFlowFormView.listenTo(graphModel.selectedElement, 'change', function (selectedElement) { updateWhereClause(selectedElement, searchFlowFormView); diff --git a/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowResultView.js b/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowResultView.js index 55e15423c..cbbf7e96f 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowResultView.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/views/SearchFlowResultView.js @@ -113,9 +113,9 @@ define([ remote: { ajaxConfig: searchFlowRemoteConfig, dataParser: function(response) { - var graphView = monitorInfraUtils - .getUnderlayGraphInstance(); - response['vRouters'] = graphView.model.vRouters; + var graphModel = monitorInfraUtils + .getUnderlayGraphModel(); + response['vRouters'] = graphModel.vRouters; return monitorInfraParsers .parseUnderlayFlowRecords(response); } diff --git a/webroot/monitor/infrastructure/underlay/ui/js/views/TraceFlowResultView.js b/webroot/monitor/infrastructure/underlay/ui/js/views/TraceFlowResultView.js index ae8332abc..5e54739c5 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/views/TraceFlowResultView.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/views/TraceFlowResultView.js @@ -55,8 +55,8 @@ define([ getViewConfig: function () { var self = this, viewConfig = self.attributes.viewConfig, traceFlowGridColumns = []; - var graphView = $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphView'); - var underlayGraphModel = graphView.model, traceFlowRemoteConfig = {}; + var underlayGraphModel = $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphModel'), + traceFlowRemoteConfig = {}; if(self.model.traceflow_radiobtn_name() == 'vRouter') { var vRouterDetails = getSelectedVrouterDetails(self.model); var ip = vRouterDetails['ip']; @@ -218,8 +218,7 @@ define([ }; function getSelectedVrouterDetails (traceFlowFormModel) { - var graphView = $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphView'); - var graphModel = graphView.model; + var graphModel = $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphModel'); var vRouterMap = graphModel.vRouterMap; var vRouterData = ifNull(vRouterMap[traceFlowFormModel.vrouter_dropdown_name()], {}); @@ -238,8 +237,7 @@ define([ function doTraceFlow (rowId, formModel) { var flowGrid = $("#" +ctwc.TRACEFLOW_RESULTS_GRID_ID).data('contrailGrid'); - var graphView = monitorInfraUtils.getUnderlayGraphInstance(); - var graphModel = graphView.model; + var graphModel = monitorInfraUtils.getUnderlayGraphModel(); var contextVrouterIp; if(formModel != null && formModel.showvRouter()) contextVrouterIp = @@ -332,8 +330,7 @@ define([ function doReverseTraceFlow (rowId, formModel) { var flowGrid = $("#" +ctwc.TRACEFLOW_RESULTS_GRID_ID).data('contrailGrid'); - var graphView = monitorInfraUtils.getUnderlayGraphInstance(); - var graphModel = graphView.model; + var graphModel = monitorInfraUtils.getUnderlayGraphModel(); var dataItem = ifNull(flowGrid._grid.getDataItem(rowId),{}); var contextVrouterIp = ''; if(formModel != null && formModel.showvRouter()) @@ -464,8 +461,7 @@ define([ } } } - var graphView = monitorInfraUtils.getUnderlayGraphInstance(); - var graphModel = graphView != null ? graphView.model : null; + var graphModel = monitorInfraUtils.getUnderlayGraphModel(); if (graphModel != null) { graphModel.underlayPathReqObj = postData; graphModel.flowPath.set('links',ifNull(response['links'], [])); diff --git a/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js b/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js index fc85a599a..63208f966 100644 --- a/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js +++ b/webroot/monitor/infrastructure/underlay/ui/js/views/UnderlayGraphView.js @@ -1,34 +1,410 @@ /* * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. */ - define([ 'underscore', 'contrail-view', 'underlay-graph-model', - 'graph-view', - 'contrail-list-model', -], function (_, ContrailView, UnderlayGraphModel, GraphView, ContrailListModel) { + 'vis' +], function(_, ContrailView, UnderlayGraphModel, vis) { var UnderlayGraphView = ContrailView.extend({ - render: function () { + network: null, + model : null, + tooltipConfigWidth: 0, + tooltipConfigHeight: 0, + cursorPosition: {}, + style: { + default: { + color: "rgba(85,85,85,1)" + }, + defaultDimlight: { + color: "rgba(85,85,85,0.3)" + }, + defaultSelected: { + color: "rgba(73,138,185,1)" + }, + selectedDimlight: { + color: "rgba(73,138,185,0.3)" + }, + errorNode: { + color: "rgba(185,74,72,1)" + } + }, + cidMap: { + "coreswitch": 1, + "spine": 2, + "tor": 3, + "virtual-router": 4, + "virtual-machine": 5 + }, + nodesDataSet: null, + edgesDataSet: null, + visOptions: { + autoResize: true, + clickToUse: false, + interaction: { + navigationButtons: true, + hover: true, + keyboard: false, + selectConnectedEdges: false, + hoverConnectedEdges: false, + zoomView: false + }, + layout: { + hierarchical: { + direction: 'UD', + sortMethod: 'directed' + }, + improvedLayout: true + }, + /*edges: { + smooth: { + enabled: true, + type: "cubicBezier", + forceDirection: "horizontal", + roundness: .7 //not to be used with dynamic + } + }*/ + edges: { + smooth: { + enabled: true, + type: "dynamic", + forceDirection: "none" + } + } + }, + resetTooltip: function() { + $(".vis-network-tooltip").popover('destroy'); + this.tooltipConfigWidth = 0; + this.tooltipConfigHeight = 0; + }, + render: function() { var self = this, graphTemplate = - contrail.getTemplate4Id(ctwl.TMPL_UNDERLAY_GRAPH_VIEW), + contrail.getTemplate4Id(ctwl.TMPL_UNDERLAY_GRAPH_VIEW), selectorId = '#' + ctwl.UNDERLAY_GRAPH_ID, graphModel = - new UnderlayGraphModel(getUnderlayGraphModelConfig()); + new UnderlayGraphModel(this.getUnderlayGraphModelConfig()); + this.model = graphModel; + this.listenTo(graphModel, "change", function(updatedGraphModel) { + if (contrail.checkIfExist(updatedGraphModel.elementsDataObj)) { + self.addElementsToGraph(updatedGraphModel.elementsDataObj.elements, + updatedGraphModel); + } + }); + graphModel.fetchData(); + self.$el.html(graphTemplate()); - var graphView = new GraphView(getUnderlayGraphViewConfig( - graphModel, selectorId, self)); - $(selectorId).data('graphView', graphView); - graphView.render(); + + $("#"+ctwl.UNDERLAY_GRAPH_ID).data('graphModel', graphModel); + var graphWidth = $(selectorId).width(); + var graphHeight = $(selectorId).height(); + this.nodesDataSet = new vis.DataSet([]); + this.edgesDataSet = new vis.DataSet([]); + var container = document.getElementById(ctwl.UNDERLAY_GRAPH_ID); + this.network = new vis.Network(container, { + nodes: this.nodesDataSet, + edges: this.edgesDataSet + }, + this.visOptions); + var _network = this.network; + this.network.on("blurNode", function(node){ + self.resetTooltip(); + }); + + this.network.on("dragEnd", function(node){ + self.resetTooltip(); + }); + + $(document).bind('mousemove',function(e) { + self.cursorPosition = { + "left": e.pageX, + "top": e.pageY + }; + }); + this.network.on("showPopup", function(elementId){ + var timer = null; + var tooltipConfig = null; + var hoveredElement = _network.canvas.body.nodes[elementId]; + if(!hoveredElement) { //not a node. check if edges with elementId present. + hoveredElement = _network.body.data.edges._data[elementId]; + tooltipConfig = hoveredElement.tooltipConfig; + if(!hoveredElement || !tooltipConfig) + return; //return if not a node or edge or no tooltip config + } else { + tooltipConfig = hoveredElement.options.tooltipConfig; + } + + self.tooltipConfigWidth = tooltipConfig.dimension.width; + self.tooltipConfigHeight = tooltipConfig.dimension.height; + var ttPosition = $(".vis-network-tooltip").offset(); + var cursorPosition = self.cursorPosition; + var diffPosition = { + top: cursorPosition.top, + left: cursorPosition.left - 20 + }; + $('.popover').remove(); + $(".vis-network-tooltip").offset(diffPosition); + var tt = $(".vis-network-tooltip").popover({ + trigger: 'hover', + html: true, + animation: false, + placement: function (context, src) { //src is mouse position + var srcOffset = $(src).offset(), + srcWidth = $(src)[0].getBoundingClientRect().width, + bodyWidth = $('body').width(), + bodyHeight = $('body').height(), + tooltipWidth = self.tooltipConfigWidth; + + $(context).addClass('popover-tooltip'); + $(context).css({ + 'min-width': tooltipWidth + 'px', + 'max-width': tooltipWidth + 'px' + }); + $(context).addClass('popover-tooltip'); + + if (srcOffset.left > tooltipWidth) { + return 'left'; + } else if (bodyWidth - (srcOffset.left) - srcWidth > tooltipWidth){ + return 'right'; + } else if (srcOffset.top > bodyHeight / 2){ + return 'top'; + } else { + return 'bottom'; + } + }, + + title: function () { + return tooltipConfig.title; + }, + content: function () { + return tooltipConfig.content; + }, + container: $('body') + }) + $(".vis-network-tooltip").popover("show"); + $('.popover').find('.btn').on('click', function() { + var actionKey = $(this).data('action'), + actionsCallback = tooltipConfig.actionsCallback(tt); + actionsCallback[actionKey].callback(); + $(".vis-network-tooltip").popover("hide"); + }); + $('.popover').find('i.icon-remove').on('click', function() { + $(".vis-network-tooltip").popover("hide"); + }); + $(".vis-network-tooltip").css({ + 'width': '0px', + 'height': '0px', + 'background-color': 'transparent', + 'border': 'transparent', + 'box-shadow': '0px 0px rgba(255, 255, 255, 0)', + 'fill': 'transparent' + }); + }); + + this.network.on("click", function(params) { + var parameters = params; + timeout = setTimeout(function() { + var params = parameters; + + if (params.nodes.length == 1) { + var clickedElement = _network.canvas.body.nodes[params.nodes[0]]; + self.resetConnectedElements(); + var node = self.nodesDataSet.get(params.nodes[0]); + node.icon.color = self.style.defaultSelected.color; + self.nodesDataSet.update(node); + var elementType = clickedElement.options.type; + switch (elementType) { + case 'PhysicalRouter': + var nodeDetails = clickedElement.options.nodeDetails; + if (nodeDetails['more_attributes']['ifTable'] == '-') + nodeDetails['more_attributes']['ifTable'] = []; + graphModel.selectedElement.set({ + 'nodeType': ctwc.PROUTER, + 'nodeDetail': nodeDetails}); + graphModel.selectedElement.set({ + 'nodeType': '', + 'nodeDetail': {}},{silent:true}); + break; + case 'VirtualRouter': + var nodeDetails = clickedElement.options.nodeDetails; + graphModel.selectedElement.set({ + 'nodeType': ctwc.VROUTER, + 'nodeDetail': nodeDetails}); + graphModel.selectedElement.set({ + 'nodeType': '', + 'nodeDetail': {}},{silent:true}); + break; + case 'VirtualMachine': + var nodeDetails = clickedElement.options.nodeDetails; + graphModel.selectedElement.set({ + 'nodeType': ctwc.VIRTUALMACHINE, + 'nodeDetail': nodeDetails}); + graphModel.selectedElement.set({ + 'nodeType': '', + 'nodeDetail': {}},{silent:true}); + break; + } + } else if (params.edges.length == 1) { + var data = {}; + var linkDetails = _network.body.data.edges._data[params.edges[0]].linkDetails; + var clickedElement = _network.canvas.body.edges[params.edges[0]]; + self.resetConnectedElements(); + var edge = self.edgesDataSet.get(params.edges[0]); + edge.color = self.style.defaultSelected.color; + self.edgesDataSet.update(edge); + var targetElement = clickedElement.to; + var sourceElement = clickedElement.from; + var endpoints = [sourceElement['options']['nodeDetails']['name'], + targetElement['options']['nodeDetails']['name']]; + self.addHighlightToNodesAndLinks( + [targetElement['options']['nodeDetails'], + sourceElement['options']['nodeDetails']], + null, + graphModel); + var linkDetail = {}; + linkDetail['endpoints'] = endpoints; + linkDetail['sourceElement'] = sourceElement['options']['nodeDetails']; + linkDetail['targetElement'] = targetElement['options']['nodeDetails']; + graphModel.selectedElement.set({ + 'nodeType': ctwc.UNDERLAY_LINK, + 'nodeDetail': linkDetail}); + graphModel.selectedElement.set({ + 'nodeType': '', + 'nodeDetail': {}},{silent:true}); + + } else if (params.edges.length == 0 && params.nodes.length == 0) { + $(".vis-network-tooltip").popover("hide"); + } + timeout = null; + }, 300); + }); + + this.network.on("doubleClick", function(params) { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + if (params.nodes.length == 1) { + self.removeLinkArrows(); + 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(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(); + graphModel['underlayPathIds'] = []; + break; + } + } + }); + // Drawing the underlay path and trace flow for a given flow graphModel.flowPath.on('change:nodes', function () { + self.removeLinkArrows(); var nodes = graphModel.flowPath.get('nodes'); var links = graphModel.flowPath.get('links'); if(nodes.length <=0 || links.length <= 0){ showInfoWindow("Cannot Map the path for selected flow", "Info"); - resetTopology({ + self.resetTopology({ resetBelowTabs: false, model: graphModel }); @@ -62,7 +438,7 @@ define([ } } graphModel['adjacencyList'] = adjList; - var childElementsArray = graphModel.createElementsFromAdjacencyList(); + var childElementsArray = self.createElementsFromAdjacencyList(graphModel); var tors = graphModel['tors']; for(var i=0; i 0) + self.addDimlightToConnectedElements(); for (var i = 0; i < links.length; i++) { var endpoints = links[i].endpoints; var endpoint0 = endpoints[0]; var endpoint1 = endpoints[1]; - var link = elementMap.link[endpoint0 + "<->" + endpoint1]; - if(null == link || typeof link === "undefined") - continue; + var linkName = endpoint0 + "<->" + endpoint1; + var altLinkName = endpoint1 + "<->" + endpoint0; + var link = elementMap.link[linkName]; + var existingLink = null; + var parentNodeExists = elementMap.node[endpoint0]; + var childNodeExists = elementMap.node[endpoint1]; + var parentNode = null; + var childNode = null; + if(null !== parentNodeExists && typeof parentNodeExists !== "undefined") + parentNode = self.nodesDataSet.get(elementMap.node[endpoint0]); + else { + parentNode = jsonPath(graphModel.nodes, '$[?(@.name=="' + + endpoint0 + '")]'); + if (false !== parentNode && parentNode.length === 1) { + parentNode = parentNode[0]; + var parentName = parentNode.name; + var parentNodeType = parentNode.node_type; + var currentEl = self.createNode(parentNode); + graphmodel.conElements.push(currentEl); + var currentElId = currentEl.id; + elementMap.node[parentName] = currentElId; + } + } + if(null !== childNodeExists && typeof childNodeExists !== "undefined") + childNode = self.nodesDataSet.get(elementMap.node[endpoint1]); else { - if(typeof $("g.link[model-id='" + link + "']").find('path.connection-wrap') == "object" && - $("g.link[model-id='" + link + "']").find('path.connection-wrap').length === 1) { - connectionWrapIds.push($("g.link[model-id='" + link + "']").find('path.connection-wrap')[0].id); - $("g.link[model-id='" + link + "']") - .css("opacity", "1"); + childNode = jsonPath(graphModel.nodes, '$[?(@.name=="' + + endpoint0 + '")]'); + if (false !== childNode && childNode.length === 1) { + childNode = childNode[0]; + var childName = childNode.name; + var childNodeType = childNode.node_type; + var currentEl = self.createNode(childNode); + graphmodel.conElements.push(currentEl); + var currentElId = currentEl.id; + elementMap.node[childName] = currentElId; } } + var parentId = parentNode.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] + + childNodeType.split("-")[1][0]; + + var arrowPosition = ""; + if(graphModel.elementMap.node[endpoint0] == parentId) { + arrowPosition = "to"; + } else { + arrowPosition = "from"; + } + + var newLink = self.createLink(links[i], link_type, parentId, childId, true, arrowPosition); + var currentLinkId = newLink.id; + self.edgesDataSet.add(newLink); + elementMap.link[linkName] = currentLinkId; + elementMap.link[altLinkName] = currentLinkId; + connectionWrapIds.push(currentLinkId); } if(connectionWrapIds.length > 0) { graphModel['underlayPathIds'] = connectionWrapIds; - monitorInfraUtils.showFlowPath(connectionWrapIds, null, graphModel); } // 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['adjacencyList'] = graphModel['underlayAdjacencyList']; graphModel.flowPath.set('nodes',[], {silent: true}); graphModel.flowPath.set('links',[], {silent: true}); }); + }, - markers: function () {} - }); - function getUnderlayGraphModelConfig() { - return { - forceFit: false, - rankDir: 'TB', - generateElementsFn: function (response) { - return this.getElementsForUnderlayGraph(response); - }, - remote: { - ajaxConfig: { - url: ctwl.URL_UNDERLAY_TOPOLOGY, - type: 'GET' - }, - successCallback: function (response, underlayGraphModel) { - $('#' + ctwl.GRAPH_LOADING_ID).hide(); - if (contrail.checkIfExist(underlayGraphModel.elementsDataObj)){ - var elements = underlayGraphModel.elementsDataObj.elements; - if (elements.length > 0) { - adjustDimensions(elements); - var markers = - monitorInfraUtils.getMarkersForUnderlay(); - var defs = $('#' + ctwl.UNDERLAY_GRAPH_ID ).find('svg').find('defs'); - for(var i=0; i