2 * Copyright 2016-2017 ZTE Corporation.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 topoUtil.topoDatas=[];
18 topoUtil.svgOffsetWidth = 0;
21 * recursive generate tree structure of the topology graph data
22 * @param {[type]} rootName [description]
23 * @param {[type]} rootNode [description]
24 * @return {[type]} [description]
26 topoUtil.generateSortData = function(rootName,rootNode) {
27 for(var i=0;i<topoUtil.topoDatas.length;i++) {
28 if(topoUtil.topoDatas[i].containIn == rootName){
29 rootNode["children"].push(topoUtil.topoDatas[i]);
30 var currentNum = rootNode["children"].length-1;
31 topoUtil.generateSortData(topoUtil.topoDatas[i].id, rootNode["children"][currentNum])
37 * generate CP data, CP is inserted into the VDU or VNF child nodes
38 * @param {[type]} cpNode [description]
39 * @param {[type]} rootNode [description]
40 * @return {[type]} [description]
42 topoUtil.generateCpData = function(cpNode, rootNode) {
43 for(var i=0;i<cpNode.length;i++){
44 for(var j=0;j<rootNode.length;j++) {
45 var node = rootNode[j];
46 if(cpNode[i].virtualbindsto == node.id) {
47 rootNode[j].cp.push(cpNode[i]);
55 * generate NETWORK and VL data, VL is inserted into the NETWORK child nodes
56 * @param {[type]} rootNode [description]
57 * @return {[type]} [description]
59 topoUtil.generateNetworkData = function(vlanNode, networkNode) {
60 if(networkNode.length == 0) {
65 networkNode.push(network);
68 for(var i=0;i<networkNode.length;i++) {
69 networkNode[i].subnets = [];
70 for(var j=0;j<vlanNode.length;j++){
71 var network = vlanNode[j].virtuallinksto;
72 if(network == networkNode[i].id) {
73 networkNode[i].subnets.push(vlanNode[j]);
81 * initialize topology graph data
82 * @param {[type]} resp [description]
83 * @param {[type]} nodeInstanceData [description]
84 * @return {[type]} [description]
86 topoUtil.initTopoData = function(resp, nodeInstanceData) {
87 if(resp && resp.length > 0) {
93 for(var i=0;i<datas.length;i++){
94 if (datas[i]["containIn"] == "") {
95 datas[i]["containIn"] = "--";
97 //add the property of children for all nodes
98 datas[i]["children"] = [];
100 //count instances number
101 if (nodeInstanceData) {
102 datas[i]["num"] = topoUtil.getInstanceNum(datas[i], nodeInstanceData);
104 //empty currentLinkNum
105 datas[i]["currentLinkNum"] = 0;
106 //distinguish VL, CP, NETWORK, VNF, VDU, VNFC from nodes to display topology graph
107 var type = datas[i]["type"];
108 if (type.toUpperCase().indexOf(".VL") > -1) {
109 vlanData.push(datas[i]);
110 } else if (type.toUpperCase().indexOf(".CP") > -1) {
111 cpData.push(datas[i]);
112 } else if(type.toUpperCase().indexOf(".NETWORK") > -1) {
113 networks.push(datas[i]);
114 } else if ((type.toUpperCase().indexOf(".VNF") > -1) || (type.toUpperCase().indexOf(".VDU") > -1)
115 || (type.toUpperCase().indexOf(".VNFC") > -1)) {
116 boxData.push(datas[i]);
118 boxData.push(datas[i]);
123 topoUtil.generateCpData(cpData, boxData);
124 //generate VNF/NS tree data
125 var rootNode = {"children":[]};
126 topoUtil.topoDatas = boxData;
127 topoUtil.generateSortData("--", rootNode);
128 vm.topologyTab.boxTopoDatas = rootNode.children;
129 //generate NETWORK and VL nodes
130 topoUtil.generateNetworkData(vlanData, networks);
131 vm.topologyTab.networkTopoDatas = networks;
133 //draw topology graph
134 topoUtil.topoDatas = datas;
135 setTimeout("topoUtil.generateLine()", 100);
136 //bind window object events
137 topoUtil.initWindowEvent();
142 * get node instances number
143 * @param {[type]} nodeTemplate [description]
144 * @param {[type]} nodeInstanceData [description]
145 * @return {[type]} [description]
147 topoUtil.getInstanceNum = function(nodeTemplate, nodeInstanceData) {
150 if(nodeTemplate.properties && nodeTemplate.properties.vnfdid) {
151 id = nodeTemplate.properties.vnfdid;
153 id = nodeTemplate.id;
156 if(nodeInstanceData && nodeInstanceData.length > 0) {
157 for (var j=0;j<nodeInstanceData.length;j++) {
158 if(nodeInstanceData[j].nodeTemplateId == id) {
166 topoUtil.getLineOffset = function(index) {
170 * get node y coordinate offset, it is based on the total number of connections and the number of connections to
171 * calculate the Y coordinate offset current connection
172 * here's the connection refers connectsto relationship between VNC and VNC
173 * @param {[type]} node current node object
174 * @param {[type]} height current DOM object cliengtHeight
175 * @return {[type]} Y coordinate offset
177 topoUtil.getNodeOffset = function(node, height) {
178 var toNodeLinkNum = ++node.currentLinkNum;
179 var totalLinkNum = node.inLinks.length + node.outLinks.length;
181 return (height/totalLinkNum)*toNodeLinkNum;
184 * get node object by name
185 * @param {[type]} name node name
186 * @return {[type]} node object data
188 topoUtil.getTopoDataById = function(id) {
190 for(var i=0;i<topoUtil.topoDatas.length;i++) {
191 if(id == topoUtil.topoDatas[i].id) {
192 node = topoUtil.topoDatas[i];
198 topoUtil.pageX = function(elem) {
199 return elem.offsetParent ? (elem.offsetLeft + topoUtil.pageX(elem.offsetParent)) : elem.offsetLeft;
202 topoUtil.pageY = function(elem) {
203 return elem.offsetParent ? (elem.offsetTop + topoUtil.pageY(elem.offsetParent)) : elem.offsetTop;
206 topoUtil.getHorizontalOffset = function(elem, elemArray) {
207 var horizontalOffset = 0;
208 for(var i=0;i<elemArray.length;i++) {
209 var nodeTop = topoUtil.pageY(elemArray[i]);
210 var fromTop = topoUtil.pageY(elem);
211 if(fromTop == nodeTop) {
212 horizontalOffset = topoUtil.getLineOffset(++horizontalIndex);
215 return horizontalOffset;
218 topoUtil.getParentNode = function(elem) {
219 return elem.className == "app" ? topoUtil.getParentNode(elem.offsetParent) : elem.offsetParent;
222 topoUtil.initElementSize = function() {
223 var height=$(".bpContainer").height();
224 $(".vlan").height() < height ? $(".vlan").height(height) : height;
226 var networkWidth = $("#networks").width();
227 var topoWidth = $("#topo").width();
228 var bodyWidth = $("body").width();
229 (networkWidth+topoWidth+50) > bodyWidth ? $("body").width(networkWidth+topoWidth+topoUtil.svgOffsetWidth+10) : $("body").width($("html").width());
231 var containerHeight=$(".container-fluid").height();
232 $(".coordinates").height(containerHeight).width($("body").width());
236 * get the widest VDU or VNF node to generate connect lines
237 * @return {[type]} [description]
239 topoUtil.getMaxNodeRight = function() {
240 var maxNode = {offsetWidth : 0};
241 for(var i=0;i<topoUtil.topoDatas.length;i++) {
242 var node = document.getElementById(topoUtil.topoDatas[i].id);
243 if(node && (maxNode.offsetWidth < node.offsetWidth)) {
247 return topoUtil.pageX(maxNode) + maxNode.offsetWidth;
250 topoUtil.initWindowEvent = function() {
251 $(window.frameElement).attr('scrolling', 'auto');
252 $('body').css('overflow', 'scroll');
253 $(window).scroll(function(){
254 $("#right-menu").css("top",$(window).scrollTop()); //vertical scroll
255 $("#right-menu").css("right",-1*$(window).scrollLeft()); //horizontal scroll
256 }).unload(function(){
257 $(window.frameElement).attr('scrolling', 'no');
259 //$(window).resize(topoUtil.generateLine);
263 * generate topology attachment
264 * connectedto represent the connection between the VNFC and VNFC, virtuallinksto represent the connection between the VLAN and VDU
265 * @return {[type]} [description]
267 topoUtil.generateLine = function() {
268 topoUtil.initElementSize();
273 var fromNodeArray = [];
274 var horizontalIndex = 0;
275 var maxNodeParentRight = topoUtil.getMaxNodeRight();
276 for(var i=0;i<topoUtil.topoDatas.length;i++) {
278 if(topoUtil.topoDatas[i].connectedto !=""){
279 var fromNode = document.getElementById(topoUtil.topoDatas[i].id);
280 var horizontalOffset = 0;
281 for(var k=0;k<fromNodeArray.length;k++) {
282 //VNFC node in the same VDU, coordinate offset
283 var nodeTop = topoUtil.pageY(fromNodeArray[k]);
284 var fromTop = topoUtil.pageY(fromNode);
285 if(fromTop == nodeTop) {
286 horizontalOffset = topoUtil.getLineOffset(++horizontalIndex);
289 fromNodeArray.push(fromNode);
290 var fromNodeParent = topoUtil.getParentNode(fromNode);
291 var toArray = topoUtil.topoDatas[i].connectedto.split(",");
292 for (var j=0;j<toArray.length;j++) {
293 var toNode = document.getElementById(toArray[j]);
294 var toNodeParent = topoUtil.getParentNode(toNode);
295 //Computing connection point and the connection point Y coordinate offset
296 var fromNodeOffset = topoUtil.getNodeOffset(topoUtil.topoDatas[i], fromNode.clientHeight);
297 var toNodeTopoData = topoUtil.getTopoDataById(toArray[j]);
298 var toNodeOffset = topoUtil.getNodeOffset(toNodeTopoData, toNode.clientHeight);
299 //X coordinate offset calculation link
300 var xLineOffset = topoUtil.getLineOffset(++vduIndex);
301 //Get the largest X coordinate offset is used to set the width of the body
302 topoUtil.svgOffsetWidth = Math.max(xLineOffset, topoUtil.svgOffsetWidth);
304 var fromNodeLeft = topoUtil.pageX(fromNode);
305 var fromNodeRight = topoUtil.pageX(fromNode) + fromNode.offsetWidth;
306 var fromNodeTop = topoUtil.pageY(fromNode);
308 var toNodeLeft = topoUtil.pageX(toNode);
309 var toNodeRight = topoUtil.pageX(toNode) + toNode.offsetWidth;
310 var toNodeTop = topoUtil.pageY(toNode);
313 if(fromNodeTop == toNodeTop) {
314 if(fromNodeLeft < toNodeLeft) {
315 coord = "M"+fromNodeRight+","+(fromNodeTop+horizontalOffset+fromNodeOffset)
316 +" L"+toNodeLeft+","+(fromNodeTop+horizontalOffset+fromNodeOffset)
318 coord = "M"+fromNodeLeft+","+(fromNodeTop+horizontalOffset+fromNodeOffset)
319 +" L"+toNodeRight+","+(fromNodeTop+horizontalOffset+fromNodeOffset);
322 var nodeRight = maxNodeParentRight + xLineOffset;
323 coord = "M"+fromNodeRight+","+(fromNodeTop+horizontalOffset+fromNodeOffset)
324 +" L"+nodeRight+","+(fromNodeTop+horizontalOffset+fromNodeOffset)
325 +" L"+nodeRight+","+(toNodeTop+toNodeOffset)
326 +" L"+toNodeRight+","+(toNodeTop+toNodeOffset);
328 vduPath +='<path d="'+coord+'" marker-end="url(#arrowhead)" fill="none" stroke-dasharray="5,5" stroke="#7A7A7A" stroke-width="3px" shape-rendering="geometricPrecision"></path>';
333 if(topoUtil.topoDatas[i].virtuallinksto !=""){
334 var fromNode = document.getElementById(topoUtil.topoDatas[i].id);
335 var toArray = topoUtil.topoDatas[i].virtuallinksto.split(",");
336 for (var j=0;j<toArray.length;j++) {
337 var toNode = document.getElementById(toArray[j]);
339 var yLineOffset = topoUtil.getLineOffset(j);
340 var xLineOffset = topoUtil.getLineOffset(++vlIndex);
342 var fromNodeLeft = topoUtil.pageX(fromNode);
343 var fromNodeTop = topoUtil.pageY(fromNode);
345 var toNodeRight = topoUtil.pageX(toNode) + toNode.offsetWidth;
346 var toNodeTop = topoUtil.pageY(toNode);
349 if(fromNodeTop == toNodeTop) {
350 coord = "M"+fromNodeLeft+","+(fromNodeTop+fromNode.clientHeight/2+xLineOffset)
351 +" L"+toNodeRight+","+(fromNodeTop+fromNode.clientHeight/2+xLineOffset);
353 coord = "M"+fromNodeLeft+","+(fromNodeTop+fromNode.clientHeight/2+yLineOffset)
354 +" L"+toNodeRight+","+(fromNodeTop+fromNode.clientHeight/2+yLineOffset);
356 vlPath +='<path d="'+coord+'" fill="none" stroke="'+toNode.style.backgroundColor+'" stroke-width="4px"></path>';
362 $("#svg_vdu g").html(vduPath);
363 $("#svg_vl g").html(vlPath);
367 * generate node table data
368 * @param {[type]} data [description]
369 * @return {[type]} [description]
371 topoUtil.generateNodeTemplate = function(data) {
372 var nodeTemplate = {};
373 nodeTemplate.id = data.id;
374 nodeTemplate.name = data.name;
375 nodeTemplate.type = data.type;
376 nodeTemplate.parentType = data.parentType;
377 nodeTemplate.vnfdid = ""; //only nested VNF node has value
378 nodeTemplate.properties = data.properties;
379 nodeTemplate.flavors = data.flavors;
380 nodeTemplate.containIn = ""; //containIn relation which the front-end custom is used to display the topo relations of the graph
381 nodeTemplate.containedin = ""; //the relation between VNF and VNFC
382 nodeTemplate.deployedon = ""; //the relation between VDU and VNFC
383 nodeTemplate.connectedto = ""; //the relation between VNFC and VNFC
384 nodeTemplate.virtuallinksto = ""; //the relation between VL and CP or between VL and VDU
385 nodeTemplate.virtualbindsto = ""; //the relation between CP and VDU
386 nodeTemplate.outLinks = []; //a collection of connected nodes connectedto
387 nodeTemplate.inLinks = []; //nodes are connected connectedto relationship collection
388 nodeTemplate.currentLinkNum = 0;
389 var relationShips = data.relationShips || []; //some nodes may not have relationships
390 $.each(relationShips, function(index, obj){
391 if (obj.sourceNodeId == data.name) {
394 case "tosca.relationships.nfv.ContainedIn" :
395 case "tosca.relationships.nfv.BelongTo" :
396 nodeTemplate.containedin = obj.targetNodeId;
399 case "tosca.relationships.nfv.DeployedOn" :
400 nodeTemplate.deployedon = obj.targetNodeId;
403 case "tosca.relationships.nfv.ConnectsTo" :
404 nodeTemplate.connectedto += "," + obj.targetNodeId;
405 nodeTemplate.outLinks.push(obj.targetNodeId);
407 case "virtualLinksTo" :
408 case "tosca.relationships.nfv.VirtualLinksTo" :
409 nodeTemplate.virtuallinksto += "," + obj.targetNodeId;
411 case "virtualBindsTo" :
412 case "tosca.relationships.nfv.VirtualBindsTo" :
413 nodeTemplate.virtualbindsto += "," + obj.targetNodeId;
417 if (obj.targetNodeId == data.name) {
420 case "tosca.relationships.nfv.ConnectsTo" :
421 nodeTemplate.inLinks.push(obj.sourceNodeId);
426 nodeTemplate.connectedto = nodeTemplate.connectedto.substring(1);
427 nodeTemplate.virtuallinksto = nodeTemplate.virtuallinksto.substring(1);
428 nodeTemplate.virtualbindsto = nodeTemplate.virtualbindsto.substring(1);
430 if(topoUtil.isVNFType(data.type)) {
431 $.each(data.properties, function(key, value) {
432 if(key == "vnfdid" && value) {
433 nodeTemplate.vnfdid = value;
441 * generate topology data
442 * deployedon is used to display the relation between VNFC and VDU
443 * containedin is used to display the relation between VNFC and VNF
444 * transform relations between VDU and VNF, containIn is used to display the relation between VDU and VNF
445 * @param {[type]} data [description]
446 * @return {[type]} [description]
448 topoUtil.generateTopoTemplate = function(data) {
449 for(var i=0;i<data.length;i++) {
450 if(data[i].containedin){
451 //assignment is designed to compatible with no VDU, only VNF and VNFC situations
452 data[i].containIn = data[i].containedin;
453 for(var j=0;j<data.length;j++) {
454 if(data[i].deployedon == data[j].id) {
455 data[j].containIn = data[i].containedin;
460 //the relationship between VNFC and VDU deployedon replace with containIn
461 if(data[i].deployedon){
462 data[i].containIn = data[i].deployedon;
469 * generate nodetemplate detail
470 * @param {[type]} data [description]
471 * @return {[type]} [description]
473 topoUtil.generateNodeTemplateDetail = function(data) {
474 var nodeTemplateDetail = {};
475 nodeTemplateDetail.properties = [];
476 var properties = data.properties;
477 for(var key in properties) {
480 property.value = properties[key];
481 nodeTemplateDetail.properties.push(property);
483 //add flavor to nodetempalte properties
484 var flavors = data.flavors;
485 if(flavors && flavors.length) {
486 var flavor = flavors[0];
487 for(var key in flavor) {
490 property.value = flavor[key];
491 nodeTemplateDetail.properties.push(property);
495 nodeTemplateDetail.relationShips = data.relationShips;
497 nodeTemplateDetail.general = [];
499 general.key = "name";
500 general.value = data.name;
501 nodeTemplateDetail.general.push(general);
503 general.key = "type";
504 general.value = data.type;
505 nodeTemplateDetail.general.push(general);
507 return nodeTemplateDetail;
510 topoUtil.getCurrentDetailData = function(detailDatas, nodetemplateid) {
512 for(var i=0; i<detailDatas.length; i++) {
513 if (detailDatas[i].id == nodetemplateid) {
514 data = topoUtil.generateNodeTemplateDetail(detailDatas[i]);
522 * generate node instance detail
523 * a node template may correspond to multiple node instances, their properties are not the same
524 * @param {[type]} data [description]
525 * @return {[type]} [description]
527 topoUtil.generateNodeInstanceDetail = function(data) {
528 var nodeInstanceDetail = [];
529 nodeInstanceDetail.properties = [];
530 nodeInstanceDetail.general = [];
532 var properties = data.properties;
533 for(var i=0;i<properties.length;i++) {
535 var name = data.name;
536 for(var key in properties[i]) {
539 property.value = properties[i][key];
540 nodeDetail.properties.push(property);
543 name = properties[i][key];
547 general.key = "name";
548 general.value = name;
549 nodeDetail.general.push(general);
551 general.key = "type";
552 general.value = data.type;
553 nodeDetail.general.push(general);
555 nodeDetail.relationShips = data.relationShips;
556 nodeInstanceDetail.push(nodeDetail);
558 return nodeInstanceDetail;
561 topoUtil.getCurrentNodeInstanceDetail = function(detailDatas, nodetemplateid) {
563 for(var i=0; i<detailDatas.length; i++) {
564 if (detailDatas[i].id == nodetemplateid) {
565 data = topoUtil.generateNodeInstanceDetail(detailDatas[i]);
572 topoUtil.getCidr = function(properties) {
573 for(var key in properties) {
575 return properties[key];
580 topoUtil.getColor = function(index) {
581 var colors = ['#1F77B4','#FF7F0E','#2CA02C','#D62728','#9467BD','#8C564B','#4b6c8b','#550000','#dc322f','#FF6600'];
582 return colors[index%10];
585 topoUtil.getCpTop = function(index, parentBoxId) {
589 var circle_top = $(".circle").css("top");
590 var circle_height = $(".circle").css("height");
591 var top = circle_top.substring(0, circle_top.length-2) - 0;
592 height = circle_height.substring(0, circle_height.length-2) - 0;
593 newTop = (top+height+10);
595 var circle_top = $(".smallCircle").css("top");
596 var circle_height = $(".smallCircle").css("height");
597 var top = circle_top.substring(0, circle_top.length-2) - 0;
598 height = circle_height.substring(0, circle_height.length-2) - 0;
599 newTop = (top+height*(index));
601 //if the length of cp over the box which cp is virtualbindsto, set the box min-heght attribute
602 var $box = $("#" + parentBoxId);
603 var min_height = $box.css("min-height");
604 var box_min_height = min_height.substring(0, min_height.length-2) - 0;
605 var cp_height = newTop + height;
606 if(cp_height > box_min_height) {
607 $box.css("min-height", cp_height);
610 return newTop + "px";
613 topoUtil.isVNFType = function(type) {
614 if((type.toUpperCase().indexOf(".VNF") > -1) && (type.toUpperCase().indexOf(".VNFC") < 0)) {