diff options
Diffstat (limited to 'usecaseui-portal/src/app/views/network/ccvpn-network')
3 files changed, 921 insertions, 1093 deletions
diff --git a/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.css b/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.css index 53cf02b4..e1a1d6e4 100644 --- a/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.css +++ b/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.css @@ -71,7 +71,6 @@ margin: 0; color: #3C4F8C; margin-left: 10px; - display: inline-block; } .model .title-modelshow{ color: #A0AACD; @@ -115,3 +114,4 @@ + diff --git a/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.html b/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.html index fb025d96..48d01d36 100644 --- a/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.html +++ b/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.html @@ -16,13 +16,40 @@ <nz-spin [nzSpinning]="isSpinning" nzSize="large"> <div class="model"> <!--chart--> - <button nz-button nzType="primary" *ngIf="!nonetwork" style="margin-top: 2px;display: inline-block" (click)="showForm()" - [disabled]='isVisible' [attr.disabled] ='isVisible?true:undefined'> - {{"i18nTextDefine_CreateLink" | translate}} - </button> + <div class="action ant-tabs-bar"> + <span> + <i class="icon"> + <img src="assets/images/service-type.png"alt=""> + </i> + {{"i18nTextDefine_Connectivity" | translate}} : + </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{connectivitySelected.name}}</span> <i class="anticon anticon-down"></i> + </button> + <ul nz-menu> + <li nz-menu-item (click)="choseConnectivity(item)" *ngFor="let item of connectivityList"> + <a title="{{item.name}}">{{item.name}}</a> + </li> + </ul> + </nz-dropdown> + +<!-- <button class="create" nz-button [nzType]="'primary'" (click)="createModal()"> + <i *ngIf="width>1200" class="anticon anticon-plus"></i> + <span> {{"i18nTextDefine_Create" | translate}} </span> + </button>--> + </div> + <h2 *ngIf="!nonetwork" [ngClass]="{'title-modelshow':isVisible == true}"> Please configure network links for registered devices and partner system. </h2> + <h3 [ngClass]="{'title-modelshow':isVisible == true}"> + Service Instances: + </h3> + <div id="svcContainer" style="overflow: hidden;"> + </div> + <h3 [ngClass]="{'title-modelshow':isVisible == true}"> + Network Topology: + </h3> <div id="tpContainer" style="overflow: hidden;"> <div *ngIf="nonetwork" style="padding: 20px"> <h2> diff --git a/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.ts b/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.ts index 56009546..d1e40672 100644 --- a/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.ts +++ b/usecaseui-portal/src/app/views/network/ccvpn-network/ccvpn-network.component.ts @@ -13,10 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; -import * as d3 from 'd3'; +import {Component, OnInit} from '@angular/core'; +import * as d3 from 'd3' import * as $ from 'jquery'; -import { networkHttpservice } from '../../../core/services/networkHttpservice.service'; +import {networkHttpservice} from '../../../core/services/networkHttpservice.service'; +import {EventQueueService} from "../../../core/services/eventQueue.service"; +import {AppEvent} from "@src/app/core/services/appEvent"; +import {AppEventType} from "@src/app/core/services/appEventType"; @Component({ selector: 'app-ccvpn-network', @@ -25,62 +28,64 @@ import { networkHttpservice } from '../../../core/services/networkHttpservice.se }) export class CcvpnNetworkComponent implements OnInit { - constructor(private myhttp: networkHttpservice) { + constructor(private myhttp: networkHttpservice, + private eventDispatcher: EventQueueService) { } ngOnInit() { let thisNg = this; - thisNg.getD3Data(); - - - //Local cloud TP port connection, click on the right to expand the details - $('#tpContainer').on('click', '.line-port', function () { - thisNg.isVisible = false; - thisNg.delBoxisVisible = true; - thisNg.delcloud = false; - - thisNg.delTp1 = $(this).attr('data-tp1'); - thisNg.delTp2 = $(this).attr('data-tp2'); - thisNg.delNode1 = $(this).attr('data-node1'); - thisNg.delNode2 = $(this).attr('data-node2'); - thisNg.delVersion = $(this).attr('data-version'); - thisNg.delLinkname = $(this).attr('data-link'); - thisNg.delcloudUrl = null; - thisNg.delLinkIndex = $(this); - - let dataD3 = thisNg.d3Data; - for (let p = 0; p < dataD3.length; p++) {//Determine which Domain network the two tp ports belong to - if (dataD3[p]['name'] == thisNg.delTp1) { - thisNg.network.push(dataD3[p]['source']['name']); - } - if (dataD3[p]['name'] == thisNg.delTp2) { - thisNg.network.push(dataD3[p]['source']['name']); + this.isSpinning = true; + this.myhttp.getConnectivities() + .subscribe((data) => { + if(data){ + for (let conn of data["connectivity"]) { + if (conn["vpn-type"] === "mdsc"){ + this.connectivityList.push({ "name": conn["connectivity-id"], + "id": conn["connectivity-id"], + "relationship-list" : conn["relationship-list"] + }); + } + } + if (this.connectivityList.length !== 0) { + this.connectivitySelected = this.connectivityList[0]; + this.choseConnectivity(this.connectivitySelected); + + }; } - } - thisNg.delNetwork1 = thisNg.network[0]; - thisNg.delNetwork2 = thisNg.network[1]; - }); + }, + (err) => { + console.log(err); + }); + this.myhttp.getLogicalLinksData() + .subscribe((data) => { + if (data) { + for (let ll of data["logical-link"]){ + // Filter layer1 logical link + //if (ll["relationship-list"] !== undefined && + // ll["relationship-list"]["relationship"].length) { + thisNg.logicalLinks.push(ll); + //} + } + let tpMapping = thisNg.getPnfTpMapping(thisNg.logicalLinks); - //External cloud connection, click on the right to expand the details - $('#tpContainer').on('click', '.cloudline', function () { - thisNg.isVisible = false; - thisNg.delBoxisVisible = true; - thisNg.delcloud = true; - - thisNg.delTp1 = $(this).attr('data-tp1'); - thisNg.delTp2 = $(this).attr('data-tp2'); - thisNg.delNode1 = $(this).attr('data-node1'); - thisNg.delNode2 = $(this).attr('data-node2'); - thisNg.delVersion = $(this).attr('data-version'); - thisNg.delNetwork1 = $(this).attr('data-network'); - thisNg.delNetwork2 = $(this).attr('data-cloudnetwork'); - thisNg.delcloudUrl = $(this).attr('data-url'); - thisNg.delLinkname = $(this).attr('data-link'); - thisNg.aaiId = $(this).attr('data-aaiid'); - thisNg.getCloudUrl(thisNg.aaiId); - }); + let links = thisNg.getLinks( thisNg.logicalLinks, tpMapping); + let tps = thisNg.getNodes(tpMapping); + console.log(links); + console.log(tps); + + thisNg.drawTopo(tps, links); + + } + this.isSpinning = false; + }, + (err) => { + console.log(err); + }) } + connectivityList = []; + connectivitySelected = { name: null, id: null }; + addLinkDisabled = true; nonetwork = false; isVisible = false; @@ -89,6 +94,9 @@ export class CcvpnNetworkComponent implements OnInit { delBoxisVisible = false; isSpinning = true; + pnfs = []; + layer1Tps = []; + d3Data = [];//D3Render the required data logicalLinks = [];//logicalLinks Existing connection data returned by the interface linkName = null;//Linked name link-name @@ -129,12 +137,25 @@ export class CcvpnNetworkComponent implements OnInit { winWidth = $('#tpContainer').width(); winHeight = $('#tpContainer').height(); charge = -300; + SEPERATOR = '-'; - imgmap = { - '1': 'assets/images/cloud-county1.png', - '2': 'assets/images/tp.png', - '3': 'assets/images/cloud-out.png', + + imgMap = { + 'pnf': 'assets/images/site.png', + 'tp': 'assets/images/tp.png' }; + + //### SELECTION - store the selected node ### + //### EDITING - store the drag mode (either 'drag' or 'add_link') ### + svcEditorGlobal = { + selection: null + } + svcContainerOpt = { + containerId : "svcContainer", + width: 1000, + height: this.winHeight + }; + tpoption = { container: '#tpContainer', data: '', @@ -142,1127 +163,907 @@ export class CcvpnNetworkComponent implements OnInit { height: this.winHeight }; - showForm(): void { - if (this.addLinkDisabled == false) { - this.isVisible = true; - this.delBoxisVisible = false; - } - } - - hideForm(): void { - this.isVisible = false; - this.delBoxisVisible = false; - this.linkName = null; - this.networkVal1 = null;//Initialize the default data of the network1 drop-down box - this.networkVal2 = null;//Initialize the network2 drop-down box default data - this.selectedNode1 = null;//Initialize the default data of the node1 drop-down box - this.selectedNode2 = null;//Initialize the default data of the node2 drop-down box - this.selecteTpName1 = null;//Initialize the default data of the TP1 drop-down box - this.selecteTpName2 = null;//Initialize the default data of the TP2 drop-down box - this.cloudUrl = null;//External cloud URL address - this.cloudNetwork = null;//External cloud network name - this.cloudNode = null;//External cloud Node name - this.cloudTp = null;//External cloud Tp name - } - - //Get cloud image data - getD3Data() { - this.isSpinning = true; - this.myhttp.getNetworkD3Data() - .subscribe((data) => { - this.isSpinning = false; - if (data.length == 0) { - this.addLinkDisabled = false; - this.nonetwork = true; - return; - } - this.nonetwork = false; - for (let ii = 0; ii < data.length; ii++) {//Determine if there is external cloud information in the data, and kick it out. - if (data[ii]['aaiId'] != null) { - this.dataCloud = data.splice(ii, 1); - } - } - - for (let i = 0; i < data.length; i++) { - let name1 = {}, name2 = {}; - let nodess = []; - name1['name'] = name2['network'] = data[i]['networkId']; - name1['type'] = '1'; - name1['source'] = i; - this.d3Data.push(name1); - for (let c = 0; c < data[i]["pnfs"].length; c++) { - nodess.push(data[i]['pnfs'][c]['pnfName']); - this.nodeOption1[name2['network']] = nodess; - } - this.networkOption.push(name2); + /** + * Redraw the selected L2 ethernet service. + * @param {Array<object>} treeData parsed from AAI connectivity. + */ + drawService(treeData) { + //Model of service graph + let graph = { + nodes: [ + ], + links: [ + ], + objectify: (function() { + /* resolve node IDs (not optimized at all!) + */ + var l, n, _i, _len, _ref, _results; + _ref = graph.links; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + l = _ref[_i]; + _results.push((function() { + var _j, _len2, _ref2, _results2; + _ref2 = graph.nodes; + _results2 = []; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + n = _ref2[_j]; + if (l.source === n.id) { + l.source = n; + continue; + } + if (l.target === n.id) { + l.target = n; + continue; + } else { + _results2.push(void 0); + } + } + return _results2; + })()); } - for (let i = 0; i < data.length; i++) { - let tp_length = data[i]['tps'].length; - for (let h = 0; h < tp_length; h++) { - let name2 = {}; - let interface_name = data[i]['tps'][h]['interface-name']; - name2['name'] = interface_name; - name2['type'] = '2'; - name2['source'] = i; - this.d3Data.push(name2); - } + return _results; + }), + remove: (function(condemned) { + /* remove the given node or link from the graph, also deleting dangling links if a node is removed + */ if (Array.prototype.indexOf.call(this.nodes, condemned) >= 0) { + this.nodes = this.nodes.filter(function(n) { + return n !== condemned; + }); + return this.links = this.links.filter(function(l) { + return l.source.id !== condemned.id && l.target.id !== condemned.id; + }); + } else if (Array.prototype.indexOf.call(this.links, condemned) >= 0) { + return this.links = this.links.filter(function(l) { + return l !== condemned; + }); } - for (let b = 0; b < this.d3Data.length; b++) { - this.d3Data[b]['target'] = b; + }), + last_index: 0, + add_node: (function(type) { + var n; + n = { + id: this.last_index++, + x: 960 / 2, + y: 500 / 2, + type: type + }; + this.nodes.push(n); + return n; + }), + add_link: (function(source, target) { + /* avoid links to self + */ + var l, link, _i, _len, _ref; + if (source === target) return null; + /* avoid link duplicates + */ + _ref = this.links; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + if (link.source === source && link.target === target) return null; } - this.initPosition(this.d3Data); - setTimeout(this.render(this.d3Data, this.imgmap, this.dataCloud, this.charge, data), 0); - }, (err) => { - console.log(err); - }); + l = { + source: source, + target: target + }; + this.links.push(l); + return l; + }) + }; - } + var nodeList = treeData.map(obj => { + let rObj = {}; + rObj["id"] = obj["id"]; + rObj["x"] = 500; + rObj["y"] = 500; + rObj["type"] = obj["type"]; + return rObj; + }) - //Get the initial connection status of the cloud image getlogicalLinksData - getLinksData() { - this.myhttp.getLogicalLinksData() - .subscribe((data) => { - if (data["status"] == "FAILED") { - return; + var linkList = [] ; + for (var i = 0, e = treeData.length; i < e; i++){ + for (var j = i+1, k = e; j < k; j++){ + linkList.push({ + source: treeData[i].id, + target: treeData[j].id + }); } - for (let i = 0; i < data["logical-link"].length; i++) {//Determine whether there is an external cloud connection in the obtained connection, and kick it out. - if (data['logical-link'][i]['relationship-list']['relationship'].length > 2) { - this.dataCloudLink = data['logical-link'].splice(i, 1); + } + graph.nodes = nodeList; + graph.links = linkList; + graph.objectify(); + var _this = this; + var margin = {top: 20, right: 120, bottom: 20, left: 120}, + width = 1000 - margin.right - margin.left, + height = 350 - margin.top - margin.bottom; + //clean existing element + d3.select("div#" + this.svcContainerOpt.containerId).selectAll("*").remove(); + + let svg = d3.select("div#" + this.svcContainerOpt.containerId).append("svg") + .attr("width", width + margin.right + margin.left) + .attr("height", height + margin.top + margin.bottom); + let container = svg.append("g").style("fill", "transparent"); + + let vis = container.append('g'); + container.call(d3.behavior.zoom().scaleExtent([0.5, 8]) + .on('zoom', function(){ + vis.attr('transform', "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); + })); + + vis.append('rect') + .attr('class', 'overlay') + .attr('x', -500000) + .attr('y', -500000) + .attr('width', 1000000) + .attr('height', 1000000) + .on('click', function(d) { + _this.svcEditorGlobal.selection = null; + d3.selectAll('.node').classed('selected', false); + return d3.selectAll('.link').classed('selected', false); + }); + let colorify = d3.scale.category10(); + /* initialize the force layout + */ + let force = d3.layout.force().size([width, height]).charge(-400).linkDistance(160) + .on('tick', (function(e) { + /* update nodes and links + */ + let k = 16 * e.alpha; + graph.nodes.forEach(function(o, i) { + if (o["type"] === "root"){ + o["x"] += k + //o["x"] += i & 2 ? k : -k; + + } else if (o["type"] === "leaf") { + o["x"] += -k; + //o["x"] += i & 2 ? k : -k; } - } + }); + vis.selectAll('.node').attr('transform', function(d) { + return "translate(" + d.x + "," + d.y + ")"; + }); - for (let i = 0; i < data["logical-link"].length; i++) { - let textval = []; - textval[0] = data['logical-link'][i]['relationship-list']['relationship'][0]['relationship-data'][1]['relationship-value'];//tp1 - textval[1] = data['logical-link'][i]['relationship-list']['relationship'][1]['relationship-data'][1]['relationship-value'];//tp2 - textval[2] = data['logical-link'][i]['resource-version'];//version - textval[3] = data['logical-link'][i]['relationship-list']['relationship'][0]['relationship-data'][0]['relationship-value'];//node1 - textval[4] = data['logical-link'][i]['relationship-list']['relationship'][1]['relationship-data'][0]['relationship-value'];//node2 - textval[5] = data['logical-link'][i]['operational-status']; - textval[6] = data['logical-link'][i]['link-name']; - this.logicalLinks.push(textval); - this.chose(textval); - } - if (this.dataCloudLink.length > 0) { - this.getcloudLine(this.dataCloudLink); - } - }, (err) => { - console.log(err); - }); - } - //D3Cloud rendering - render(nodes, imgmap, dataCloud, charge, dataD3) { - let thiss = this; - let _this = this.tpoption, - width = null, - height = _this.height; - if (_this.width > 800) { - width = _this.width; - } else { - width = 800; - } - if (dataD3.length <= 4) { - charge = -300; - } else if (dataD3.length > 4 && dataD3.length <= 6) { - charge = -160; - } else if (dataD3.length > 6 && dataD3.length <= 10) { - charge = -110; - } else { - charge = -100; - } - let svg = d3.select(_this.container).append('svg') - .attr('width', width) - .attr('height', height) - .attr('id', 'content-svg') - .style('pointer-events', 'all') - .style('position', 'absolute') - .style('top', '1%') - .style('right', '2%'), - graph = svg.append('g').attr('class', 'graph').attr('id', 'graph'), - - _g_nodes = graph.selectAll('g.node') - .data(nodes) - .enter() - .append('g') - .style('display', function (d) { - let display = 'block'; - switch (d.type) { - case '1': - display = 'none'; - break; - case '2': - display = 'none'; - break; - default: - break; + //#svcContainer > svg > g > g > g:nth-child(3) > text + //_this.svcEditorGlobal.selection + return vis.selectAll('.link').attr('x1', function(d) { + return d.source.x; + }).attr('y1', function(d) { + return d.source.y; + }).attr('x2', function(d) { + return d.target.x; + }).attr('y2', function(d) { + return d.target.y; + }); + })); + let nodeDragging = force.drag().on('dragstart', function (d){ + d3.event.sourceEvent.stopPropagation(); + d.fixed = true; + }) + + let topoNodeSync = _this.eventDispatcher.on(AppEventType.UserNodeDrag) + .subscribe(event => { + //console.log(event); + let pnfId: string = event.payload.id; + let pnfId_short: string = pnfId.substr(pnfId.lastIndexOf('-')+1); + vis.selectAll('.node > circle').attr('stroke-width', function(d) { + if (d.id.startsWith(pnfId_short)){ + return "4px"; } - return display; - }) - .style('cursor', 'pointer') - .attr('class', 'node'), - - _g_lines = graph.selectAll('line.line') - .data(nodes) - .enter() - .append('g') - .style('display', 'none') - .attr('class', 'line'); - - - _g_lines.append('line') - .style('stroke', '#93c62d' - ) - .style('stroke-width', 2); - - _g_nodes.append('image') - .attr('width', function (d) { - let width = 40; - switch (d.type) { - case '1': - width = 4.4 * width; - break; - case '2': - width = 0.12 * width; - break; - default: - break; - } - return width; - }) - .attr('height', function (d) { - let height = 20; - switch (d.type) { - case '1': - height = 3.5 * height; - break; - case '2': - height = 0.2 * height; - break; - default: - break; - } - return height; - }) - .attr('xlink:href', function (d) { - return imgmap[d.type]; + return "1px"; + }); }); - _g_nodes.append('text') - .text(function (d) { - return d.name; - }) - .style('transform', function (d) { - let x = null; - let y = null; - switch (d.type) { - case '1': - x = 7; - y = -7; - break; - case '2': - x = 1; - y = -2; - break; - default: - break; - } - return 'translate(' + x + '%,' + y + '%)'; - }) - .style('font-size', function (d) { - let size = 14; - switch (d.type) { - case '1': - size = 14; - break; - case '2': - size = 12; - break; - default: - break; - } - return size; - }) - .style('fill', function (d) { - let color = '#666'; - switch (d.type) { - case '1': - color = '#666'; - break; - case '2': - color = '#666'; - break; - default: - break; - } - return color; - }) - .style('font-weight', '500'); - + // DELETION - pressing DEL deletes the selection + // CREATION - pressing N creates a new node + // d3.select(window).on('keydown', function() { + // if (d3.event.keyCode === 46) { + // if (global.selection != null) { + // graph.remove(global.selection); + // global.selection = null; + // return update(); + // } + // } else if (d3.event.keyCode === 78) { + // graph.add_node(); + // return update(); + // } + // }); + + //Parameter for Editing tools + let toolbar = $("<div class='toolbar'></div>"); + $("div#" + this.svcContainerOpt.containerId).append(toolbar); + toolbar.append($("<svg\n" + + " class='active tool'\n " + + "data-tool='pointer'\n" + + " xmlns='http://www.w3.org/2000/svg'\n" + + " version='1.1'\n" + + " width='32'\n" + + " height='32'\n" + + " fill='#b52d0c'" + + " viewBox='0 0 128 128'>\n" + + " <g transform='translate(881.10358,-356.22543)'>\n" + + " <g transform='matrix(0.8660254,-0.5,0.5,0.8660254,-266.51112,-215.31898)'>\n" + + " <path\n" + + " d='m -797.14902,212.29589 a 5.6610848,8.6573169 0 0 0 -4.61823,4.3125 l -28.3428,75.0625 a 5.6610848,8.6573169 0 0 0 4.90431,13 l 56.68561,0 a 5.6610848,8.6573169 0 0 0 4.9043,-13 l -28.3428,-75.0625 a 5.6610848,8.6573169 0 0 0 -5.19039,-4.3125 z m 0.28608,25.96875 18.53419,49.09375 -37.06838,0 18.53419,-49.09375 z'\n />\n" + + " <path\n" + + " d='m -801.84375,290.40625 c -2.09434,2.1e-4 -3.99979,1.90566 -4,4 l 0,35.25 c 2.1e-4,2.09434 1.90566,3.99979 4,4 l 10,0 c 2.09434,-2.1e-4 3.99979,-1.90566 4,-4 l 0,-35.25 c -2.1e-4,-2.09434 -1.90566,-3.99979 -4,-4 z'\n />\n" + + " </g>\n" + + " </g>\n" + + "</svg>")); + toolbar.append($("<svg\n" + + " class='tool'\n" + + " data-tool='add_node'\n" + + " xmlns='http://www.w3.org/2000/svg'\n" + + " version='1.1'\n" + + " width='32'\n" + + " height='32'\n" + + " viewBox='0 0 128 128'>\n" + + " <g transform='translate(720.71649,-356.22543)'>\n" + + " <g transform='translate(-3.8571429,146.42857)'>\n" + + " <path\n d='m -658.27638,248.37149 c -1.95543,0.19978 -3.60373,2.03442 -3.59375,4 l 0,12.40625 -12.40625,0 c -2.09434,2.1e-4 -3.99979,1.90566 -4,4 l 0,10 c -0.007,0.1353 -0.007,0.27095 0,0.40625 0.19978,1.95543 2.03442,3.60373 4,3.59375 l 12.40625,0 0,12.4375 c 2.1e-4,2.09434 1.90566,3.99979 4,4 l 10,0 c 2.09434,-2.1e-4 3.99979,-1.90566 4,-4 l 0,-12.4375 12.4375,0 c 2.09434,-2.1e-4 3.99979,-1.90566 4,-4 l 0,-10 c -2.1e-4,-2.09434 -1.90566,-3.99979 -4,-4 l -12.4375,0 0,-12.40625 c -2.1e-4,-2.09434 -1.90566,-3.99979 -4,-4 l -10,0 c -0.1353,-0.007 -0.27095,-0.007 -0.40625,0 z'\n" + + " />\n" + + " <path\n" + + " d='m -652.84375,213.9375 c -32.97528,0 -59.875,26.86847 -59.875,59.84375 0,32.97528 26.89972,59.875 59.875,59.875 32.97528,0 59.84375,-26.89972 59.84375,-59.875 0,-32.97528 -26.86847,-59.84375 -59.84375,-59.84375 z m 0,14 c 25.40911,0 45.84375,20.43464 45.84375,45.84375 0,25.40911 -20.43464,45.875 -45.84375,45.875 -25.40911,0 -45.875,-20.46589 -45.875,-45.875 0,-25.40911 20.46589,-45.84375 45.875,-45.84375 z'\n" + + " />\n" + + " </g>\n" + + " </g>\n" + + "</svg>")); + toolbar.append($("<svg\n" + + " class='tool'\n" + + " data-tool='add_link'\n" + + " xmlns='http://www.w3.org/2000/svg'\n" + + " version='1.1'\n" + + " width='32'\n" + + " height='32'\n" + + " viewBox='0 0 128 128'>\n" + + "<g transform='translate(557.53125,-356.22543)'>\n" + + " <g transform='translate(20,0)'>\n" + + " <path\n" + + " d='m -480.84375,360 c -15.02602,0 -27.375,12.31773 -27.375,27.34375 0,4.24084 1.00221,8.28018 2.75,11.875 l -28.875,28.875 c -3.59505,-1.74807 -7.6338,-2.75 -11.875,-2.75 -15.02602,0 -27.34375,12.34898 -27.34375,27.375 0,15.02602 12.31773,27.34375 27.34375,27.34375 15.02602,0 27.375,-12.31773 27.375,-27.34375 0,-4.26067 -0.98685,-8.29868 -2.75,-11.90625 L -492.75,411.96875 c 3.60156,1.75589 7.65494,2.75 11.90625,2.75 15.02602,0 27.34375,-12.34898 27.34375,-27.375 C -453.5,372.31773 -465.81773,360 -480.84375,360 z m 0,14 c 7.45986,0 13.34375,5.88389 13.34375,13.34375 0,7.45986 -5.88389,13.375 -13.34375,13.375 -7.45986,0 -13.375,-5.91514 -13.375,-13.375 0,-7.45986 5.91514,-13.34375 13.375,-13.34375 z m -65.375,65.34375 c 7.45986,0 13.34375,5.91514 13.34375,13.375 0,7.45986 -5.88389,13.34375 -13.34375,13.34375 -7.45986,0 -13.34375,-5.88389 -13.34375,-13.34375 0,-7.45986 5.88389,-13.375 13.34375,-13.375 z'\n" + + " />\n <path\n" + + " d='m -484.34375,429.25 c -1.95543,0.19978 -3.60373,2.03442 -3.59375,4 l 0,12.40625 -12.40625,0 c -2.09434,2.1e-4 -3.99979,1.90566 -4,4 l 0,10 c -0.007,0.1353 -0.007,0.27095 0,0.40625 0.19978,1.95543 2.03442,3.60373 4,3.59375 l 12.40625,0 0,12.4375 c 2.1e-4,2.09434 1.90566,3.99979 4,4 l 10,0 c 2.09434,-2.1e-4 3.99979,-1.90566 4,-4 l 0,-12.4375 12.4375,0 c 2.09434,-2.1e-4 3.99979,-1.90566 4,-4 l 0,-10 c -2.1e-4,-2.09434 -1.90566,-3.99979 -4,-4 l -12.4375,0 0,-12.40625 c -2.1e-4,-2.09434 -1.90566,-3.99979 -4,-4 l -10,0 c -0.1353,-0.007 -0.27095,-0.007 -0.40625,0 z'\n" + + " />\n" + + " </g>\n" + + " </g>\n" + + "</svg>")); + let library = $("<div class='library'></div></div>"); + toolbar.append(library); + + ['PON', 'ETH'].forEach(function(type) { + var new_btn; + new_btn = $("<svg width='42' height='42'>\n" + + " <g class='node'>\n" + + " <circle\n" + + " cx='21'\n" + + " cy='21'\n" + + " r='18'\n" + + " stroke='" + (colorify(type)) + "'\n" + + " fill='" + (d3.hcl(colorify(type)).brighter(3)) + "'\n" + + " >\n" + + " <title>" + (type) + " UNI</title> \n" + + " </circle>" + + " </g>\n" + + "</svg>"); + new_btn.bind('click', function() { + graph.add_node(type); + return update(); + }); + library.append(new_btn); + return library.hide(); + }); - //Add custom attributes online - _g_lines.each(function (d, i) { - let _this = d3.select(this); - if (d.name) { - _this.attr('data-text', d.name); + let tool = 'pointer'; + let new_link_source = null; + let drag_link; + vis.on('mousemove.add_link', (function(d) { + /* check if there is a new link in creation + */ + var p; + if (new_link_source != null) { + /* update the draggable link representation + */ + p = d3.mouse(vis.node()); + return drag_link.attr('x1', new_link_source.x).attr('y1', new_link_source.y).attr('x2', p[0]).attr('y2', p[1]); + } + })).on('mouseup.add_link', (function(d) { + new_link_source = null; + /* remove the draggable link representation, if exists + */ + if (drag_link != null) return drag_link.remove(); + })); + d3.selectAll('.tool').on('click', function() { + var new_tool, nodes; + d3.selectAll('.tool').classed('active', false).style("fill", "#a3a4c3"); + d3.select(this).classed('active', true).style("fill", "#b52d0c"); + new_tool = $(this).data('tool'); + nodes = vis.selectAll('.node'); + + //mode change to add_link + if (new_tool === 'add_link' && tool !== 'add_link') { + /* remove drag handlers from nodes + */ + nodes.on('mousedown.drag', null).on('touchstart.drag', null); + /* add drag handlers for the add_link tool + */ + nodes.call(drag_add_link); + } else if (new_tool !== 'add_link' && tool === 'add_link') { + /* remove drag handlers for the add_link tool + */ + nodes.on('mousedown.add_link', null).on('mouseup.add_link', null); + /* add drag behavior to nodes + */ + nodes.call(nodeDragging); + } + if (new_tool === 'add_node') { + library.show(); + } else { + library.hide(); } + return tool = new_tool; }); - - let force = d3.layout.force() - .size([1000, this.winHeight]) - .linkDistance(5) - // .theta(0.6) - .charge(charge) - .nodes(nodes) - .links(nodes) - .start(); - - force.on('tick', function () { - if (force.alpha() <= 0.04) { - - _g_nodes.style('display', function (d) { - let display = 'block'; - switch (d.type) { - case '1': - display = 'block'; - break; - case '2': - display = 'none'; - break; - default: - break; - } - return display; + update(); + function update() { + /* update the layout + */ + var links, new_nodes, nodes; + force.nodes(graph.nodes).links(graph.links).start(); + /* create nodes and links + */ + /* (links are drawn with insert to make them appear under the nodes) + */ + /* also define a drag behavior to drag nodes + */ + /* dragged nodes become fixed + */ + nodes = vis.selectAll('.node').data(graph.nodes, function(d) { + return d.id; + }); + new_nodes = nodes.enter().append('g').attr('class', 'node'); +/* .on('click', (function(d) { + /!* SELECTION + *!/ + _this.svcEditorGlobal.selection = d; + d3.selectAll('.node').classed('selected', function(d2) { + return d2 === d; }); + return d3.selectAll('.link').classed('selected', false); + }));*/ + links = vis.selectAll('.link').data(graph.links, function(d) { + return "" + d.source.id + "->" + d.target.id; + }); - nodes.forEach(function (d, i) { - d.x = d.x - 25 < 0 ? 25 : d.x; - d.x = d.x + 25 > width ? width - 25 : d.x; - d.y = d.y - 15 < 0 ? 15 : d.y; - d.y = d.y + 15 > height ? height - 15 : d.y; + links.enter().insert('line', '.node').attr('class', 'link').on('click', (function(d) { + /* SELECTION + */ + _this.svcEditorGlobal.selection = d; + d3.selectAll('.link').classed('selected', function(d2) { + return d2 === d; }); + return d3.selectAll('.node').classed('selected', false); + })); + links + .style("stroke-width", "6px") + .style("stroke", "gray") + .style("opacity", "0.5"); + + links.exit().remove(); + /* TOOLBAR - add link tool initialization for new nodes + */ + if (tool === 'add_link') { + new_nodes.call(drag_add_link); + } else { + new_nodes.call(nodeDragging); + } + new_nodes.append('circle').attr('r', 18).attr('stroke', function(d) { + return colorify(d.type); + }).attr('fill', function(d) { + return d3.hcl(colorify(d.type)).brighter(3); + }); + /* draw the label + */ + new_nodes.append('text').text(function(d) { + return d.id; + }).attr('dy', '0.35em').attr('fill', function(d) { + return colorify(d.type); + }); + return nodes.exit().remove(); + }; + function drag_add_link (selection) { + return selection.on('mousedown.add_link', (function(d) { + var p; + new_link_source = d; + /* create the draggable link representation + */ + p = d3.mouse(vis.node()); + drag_link = vis.insert('line', '.node').attr('class', 'drag_link').attr('x1', d.x).attr('y1', d.y).attr('x2', p[0]).attr('y2', p[1]); + drag_link + .style("stroke-width", "6px") + .style("stroke", "gray") + .style("opacity", "0.5"); + /* prevent pan activation + */ + d3.event.stopPropagation(); + /* prevent text selection + */ + return d3.event.preventDefault(); + })).on('mouseup.add_link', (function(d) { + /* add link and update, but only if a link is actually added + */ if (graph.add_link(new_link_source, d) != null) return update(); + })); + }; - _g_nodes.attr('transform', function (d) { - let image = d3.select(this).select('image')[0][0], - halfWidth = parseFloat(image.attributes[0]['value']) / 2, - halfHeight = parseFloat(image.attributes[1]['value']) / 2; - let xx = d.x - halfWidth, - yy = d.y - halfHeight; - return 'translate(' + xx + ',' + yy + ')'; - }); + } - _g_lines.select('line') - .attr('x1', function (d) { - return d.source.x; - }) - .attr('y1', function (d) { - return d.source.y; - }) - .attr('x2', function (d) { - return d.target.x; - }) - .attr('y2', function (d) { - return d.target.y; - }); + /** + * Redraw the underlay network topology. + * @param {Array<object>} nodes parsed from AAI logicalLinks. + * @param {Array<object>} lines parsed from AAI logicalLinks. + */ + drawTopo(nodes: Array<object>, lines: Array<object>){ + let margin = {top: 20, right: 120, bottom: 20, left: 120}, + width = 1000 - margin.right - margin.left, + height = 350 - margin.top - margin.bottom; - _g_nodes.select('text').attr('dy', function (d) { - let image = this.previousSibling, - height = parseFloat(image.attributes[1]['value']), - fontSize = 12; - return height + 1.5 * fontSize; - }); - } + let thisNg = this; + + let nodeById = d3.map(); + + nodes.forEach(function(node) { + nodeById.set(node["id"], node); }); - force.on('end', function () { - force.stop(); - if (dataCloud.length > 0) { - thiss.getoutCloud(dataCloud, imgmap); - } - thiss.getLinksData(); - thiss.addLinkDisabled = false; + lines.forEach(function(link) { + link["source"] = nodeById.get(link["source"]); + link["target"] = nodeById.get(link["target"]); }); - }; + let svg = d3.select("div#tpContainer").append("svg") + .attr("width", width + margin.right + margin.left) + .attr("height", height + margin.top + margin.bottom) + .style("pointer-events", "all"); - //Topology drag and drop effect - getDragBehavior(force) { + let graph = svg.append("g").attr("class", "graph"); - return d3.behavior.drag() - .origin(function (d) { - return d; + let force = d3.layout.force() + .nodes(nodes) + .links(lines) + .size([width, height]) + /* .linkStrength(function(d){ + switch(d.type){ + case 1: + return 0.15; + case 2: + default: + return 0.1; + } + })*/ + //.gravity(0) + //.gravity(0) + .linkDistance(function (d) { + return 150; }) - .on('dragstart', dragstart) - .on('drag', dragging) - .on('dragend', dragend); + .charge(function(d) { + return -600; + }) + .start(); - function dragstart(d) { - d3.event.sourceEvent.stopPropagation(); - d3.select(this).classed('dragging', true); - force.start(); - } + let drag = force.drag() + .on("dragstart", dragstart) + .on("dragend", dragend); - function dragging(d) { - d.x = d3.event.x; - d.y = d3.event.y; - } + let _g_lines = graph.selectAll("line.line") + .data(lines) + .enter() + .append("g") + .attr("class", "line"); - function dragend(d) { - d3.select(this).classed('dragging', false); - } + let _g_nodes = graph.selectAll("g.node") + .data(nodes) + .enter() + .append("g") + .attr("class", "node") + .call(drag); + _g_lines.append("line") + .style('stroke', function (d) { + if(d.type === 2){ + return "#000000"; + } else { + return '#93c62d'; + } - } + }) + .style("stroke-width", 4); - //Initialize node location - initPosition(datas) { - let origin = [this.tpoption.width / 2, this.tpoption.height / 2]; - let points = this.getVertices(origin, Math.min(this.tpoption.width / 2, this.tpoption.height / 2), datas.length); - datas.forEach((item, i) => { - item.x = points[i].x; - item.y = points[i].y; - }); - } - //Get anchor points based on polygons - getVertices(origin, r, n) { - if (typeof n !== 'number') return; - let ox = origin[0]; - let oy = origin[1]; - let angle = 30 * n / n; - let i = 0; - let points = []; - let tempAngle = 0; - while (i < n) { - tempAngle = (i * angle * Math.PI) / 180; - points.push({ - x: ox - r * Math.sin(tempAngle), - y: oy - r * Math.cos(tempAngle), + _g_nodes.append("image") + .attr("width", function (d) { + switch (d.group) { + case 'pnf': + return 70; + case 'tp': + default: + return 10; + } + }) + .attr("height", function (d) { + switch (d.group) { + case 'pnf': + return 70; + case 'tp': + default: + return 10; + } + }) + .attr("xlink:href", function (d) { + return thisNg.imgMap[d.group]; }); - i++; - } - return points; - } - //Rendering an external cloud - getoutCloud(dataCloud, imgmap) { - let _this = this, - width; - let networkId = dataCloud[0]['networkId']; - if (_this.tpoption.width > 800) { - width = _this.tpoption.width; - } else { - width = 800; - } - let svg = d3.select('#content-svg'); - svg.append('g').attr('class', 'out').attr('id', 'out').style({ 'display': 'block' }).attr('transform', 'translate(' + (width - 200) + ',0)'); - let out = d3.select('#out'); - out.append('image').style('width', '200').style('height', '118').attr('xlink:href', imgmap['3']); - out.append('text').text(networkId) - .style('transform', 'translate(0,0)') - .style('font-size', '16') - .style('font-weight', '400') - .attr('dx', '40') - .attr('dy', '70') - .style('fill', '#666'); - } + _g_nodes.append("text") + .text(function (d) { + return d.id.substr( d.id.lastIndexOf('-')+1); + }) + .style('font-size', '12') + .style('fill', '#333'); + + //_g_nodes.each(function (d, i) { + var selection = d3.select(this); +/* if (d.status == '0') { + selection.append("g").attr("class", "error-tip") + .append("image").attr("xlink:href", function (d) { + return imgMap['error-tip']; + }); + }*/ + // }); - //External cloud connection - getcloudLine(dataCloudLink) { - let textval = []; - textval[0] = dataCloudLink[0]['relationship-list']['relationship'][0]['relationship-data'][1]['relationship-value'];//tp1 - textval[1] = dataCloudLink[0]['relationship-list']['relationship'][1]['relationship-data'][1]['relationship-value'];//tp2 - textval[2] = dataCloudLink[0]['resource-version'];//version - textval[3] = dataCloudLink[0]['relationship-list']['relationship'][0]['relationship-data'][0]['relationship-value'];//node1 - textval[4] = dataCloudLink[0]['relationship-list']['relationship'][1]['relationship-data'][0]['relationship-value'];//node2 - textval[5] = dataCloudLink[0]['operational-status'];//status - textval[6] = dataCloudLink[0]['relationship-list']['relationship'][2]['relationship-data'][0]['relationship-value'];//aaiId - textval[7] = this.dataCloud[0]['networkId']; - let dataD3 = this.d3Data; - let arr = [ - textval[0], - textval[1] - ]; - for (let p = 0; p < dataD3.length; p++) {//Determine which Domain network the two tp ports belong to - for (let pp = 0; pp < arr.length; pp++) {//Determine which Domain network the two tp ports belong to - if (dataD3[p]['name'] == arr[pp]) { - textval[8] = dataD3[p]['source']['name'];//network1 - } - } - } - textval[9] = dataCloudLink[0]['link-name']; - let lines_json = {}; - let _this = this, - width; - if (_this.tpoption.width > 800) { - width = _this.tpoption.width; - } else { - width = 800; - } - for (let i = 0; i < $(".node").length; i++) { - if ($('.node').eq(i).find('text').html() == textval[8]) { - //Get the x, y coordinates of the second level - let translates = $('.node').eq(i).css('transform'); - lines_json['x1'] = parseFloat(translates.substring(7).split(',')[4]); - lines_json['y1'] = parseFloat(translates.substring(7).split(',')[5]); - lines_json['x2'] = width - 100; - lines_json['y2'] = 100; + _g_lines.each(function (d, i) { + var _this = d3.select(this); + if (d.type === 1) { + _this.append("text") + .text("100GB") + .style('fill', 'rgb(255,198,22)') + .style('font-size', '11'); + + _this.append("rect") + .attr("fill", function (d) { + return '#555'; + }) + .attr("width", function (d) { + return 4; + }) + .attr("height", function (d) { + return 4; + }) + .append("animate"); + + _this.select("rect").append("animate"); + } else { + _this.append("image") + .attr("xlink:href", function () { + return thisNg.imgMap['link-cut']; + }); } - } - let x1 = lines_json['x1']; - let y1 = lines_json['y1']; - let x2 = lines_json['x2']; - let y2 = lines_json['y2']; - let color = '#14bb58'; - if (textval[5] == 'up') { - color = '#14bb58'; - } else { - color = 'red'; - } - let line = '<line class=\'line cloudline line-click\' stroke=\'' + color + '\' stroke-width=\'2\' style=\'cursor:pointer\'></line>'; - let svg = d3.select('#graph'); - $('.cloudline').remove(); - $('#graph').prepend(line); - $('.cloudline').attr({ - x1: x1 + 100, - y1: y1 + 10, - x2: x2, - y2: y2, - 'data-tp1': textval[0], - 'data-tp2': textval[1], - 'data-version': textval[2], - 'data-node1': textval[3], - 'data-node2': textval[4], - 'data-network': textval[8], - 'data-cloudnetwork': textval[7], - 'data-url': '', - 'data-aaiid': textval[6], - 'data-link': textval[9], }); - svg.html(svg.html()); - this.getCloudUrl(textval[6]); - this.getExtAAIIdVersion(textval[6]); - } - //Query external cloud host url address - getCloudUrl(aaiId) { - this.myhttp.queryCloudUrl(aaiId) - .subscribe((data) => { - this.delcloudUrl = data['service-url']; - $('.cloudline').attr({ - 'data-url': data['service-url'] + force.on("tick", function (e) { + + _g_lines.select("line").attr("x1", function (d) { + return d.source.x; + }) + .attr("y1", function (d) { + return d.source.y; + }) + .attr("x2", function (d) { + return d.target.x; + }) + .attr("y2", function (d) { + return d.target.y; + }); + _g_lines.select("image").attr("x", function (d) { + var x1 = d.source.x, + x2 = d.target.x, + x = x1 - (x1 - x2) / 2; + return x - 8; + }) + .attr("y", function (d) { + var y1 = d.source.y, + y2 = d.target.y, + y = y1 - (y1 - y2) / 2; + return y - 15; }); - }, (err) => { - console.log(err); - }); - } - //Query external cloud ext-aai-id resource-version - getExtAAIIdVersion(aaiId) { - this.myhttp.queryExtAAIIdVersion(aaiId) - .subscribe((data) => { - this.delVersion = data["resource-version"]; - $('.cloudline').attr({ - 'data-version': data["resource-version"], + _g_lines.select("text") + .attr('x', function (d) { + var x1 = d.source.x, + x2 = d.target.x, + halfX = x1 - (x1 - x2) / 2, + x3 = x1 - (x1 - halfX) / 2; + return x3; + }) + .attr('y', function (d) { + var y1 = d.source.y, + y2 = d.target.y, + halfY = y1 - (y1 - y2) / 2, + y3 = y1 - (y1 - halfY) / 2; + y3 = y3 - 5; + return y3; + }) + .attr("transform", function (d) { + var x1 = d.source.x, + x2 = d.target.x, + y1 = d.source.y, + y2 = d.target.y, + x = x1 - (x1 - x2) / 2, + y = y1 - (y1 - y2) / 2, + rightAngleSide1 = Math.abs(y2 - y1), + rightAngleSide2 = Math.abs(x2 - x1), + _asin = 0, + _rotateAngle = 0, + x3 = x1 - (x1 - x) / 2, + y3 = y1 - (y1 - y) / 2; + + if (x1 < x2) { + _asin = (y2 - y1) / Math.sqrt(Math.pow(rightAngleSide1, 2) + Math.pow( + rightAngleSide2, 2)); + _rotateAngle = Math.asin(_asin) * 180 / Math.PI; + } else { + _asin = (y1 - y2) / Math.sqrt(Math.pow(rightAngleSide1, 2) + Math.pow( + rightAngleSide2, 2)); + _rotateAngle = Math.asin(_asin) * 180 / Math.PI; + _rotateAngle = _rotateAngle < 0 ? (180 + _rotateAngle) : -(180 - + _rotateAngle); + } + return 'rotate(' + (_rotateAngle) + ',' + x3 + ' ' + y3 + ')'; }); - }, (err) => { - console.log(err); - }); - } + _g_lines.select("rect") + .attr('x', function (d) { + return d.source.x - 1; + }) + .attr('y', function (d) { + return d.source.y - 1; + }) + .selectAll('animate').each(function (d, i) { + if (i == 0) { + d3.select(this) + .attr("attributeName", function (d) { + return 'x'; + }) + .attr("from", function (d) { + return d.source.x - 1; + }) + .attr("to", function (d) { + return d.target.x; + }); + } else { + d3.select(this) + .attr("attributeName", function (d) { + return 'y'; + }) + .attr("from", function (d) { + return d.source.y - 1; + }) + .attr("to", function (d) { + return d.target.y; + }); + } - //The right form drop-down box data is filled with three levels of linkage - //Left Port - network1Change(value: string): void { - this.selectedNode1 = this.nodeOption1[value][0]; - this.getPInterfaces1(); - } + d3.select(this).attr("attributeType", "XML") + .attr("dur", function (d) { + return '1.5s'; + }) + .attr("repeatCount", "indefinite"); - node1Change(): void { - this.getPInterfaces1(); - } + }) +/* let k = 6 * e.alpha; + nodes.forEach(function(o, i) { + if (o["layer"] === "Otn"){ + o["y"] += k + //o["x"] += i & 2 ? k : -k; + + } else if (o["layer"] === "Eth") { + o["y"] += -k; + //o["x"] += i & 2 ? k : -k; + } + });*/ - //Get the TP data under the specified node - getPInterfaces1() { - let params = { - pnfName: this.selectedNode1, - }; - this.myhttp.getPInterfacesData(params) - .subscribe((data) => { - this.tpOption1 = []; - for (let i = 0; i < data.length; i++) { - let tpName = data[i]['interface-name']; - this.tpOption1.push(tpName); + _g_nodes.attr("transform", function (d) { + if(d.group === 'pnf') { + var image = d3.select(this).select("image")[0][0], + halfWidth = parseFloat("70") / 2, + halfHeight = parseFloat("70") / 2; + + return 'translate(' + (d.x - halfWidth) + ',' + (d.y - halfHeight) + ')'; + } else { + return 'translate(' + (d.x) + ',' + (d.y) + ')'; } - this.selecteTpName1 = this.tpOption1[0]; - }, (err) => { - console.log(err); - }); - } - //Right Port - network2Change(value: string): void { - this.selectedNode2 = this.nodeOption1[value][0]; - this.getPInterfaces2(); - } + }); - node2Change(): void { - this.getPInterfaces2(); - } + _g_nodes.select("text").attr('dy', function (d) { + var image = this.previousSibling, + height = parseFloat("10"), + fontSize = parseFloat(this.style.fontSize); - //Get the TP data under the specified node - getPInterfaces2() { - let params = { - pnfName: this.selectedNode2, - }; - this.myhttp.getPInterfacesData(params) - .subscribe((data) => { - this.tpOption2 = []; - for (let i = 0; i < data.length; i++) { - let tpName = data[i]['interface-name']; - this.tpOption2.push(tpName); - } - this.selecteTpName2 = this.tpOption2[0]; - }, (err) => { - console.log(err); + return height + 1.5 * fontSize; }); - } - //Submit form, connect - submitForm(): void { - //When the page ONAP is not selected, the local cloud TP connection - let _thiss = this; - if (this.inputshow == false) { - if (this.linkName == null || this.networkVal1 == null || this.selectedNode1 == null || this.selecteTpName1 == null || this.networkVal2 == null || this.selectedNode2 == null || this.selecteTpName2 == null) { - alert('The service port cannot be empty. Please select the port information.'); - return; - } else if (this.networkVal1 == this.networkVal2) { - alert('The TP port under the same cloud service cannot be connected!'); - return; - } - let tp_links = [], - tp1 = this.selecteTpName1, - tp2 = this.selecteTpName2; - for (let i = 0; i < $(".line-port").length; i++) { - let data_text1 = $('.line-port').eq(i).attr('data-tp1'); - let data_text2 = $('.line-port').eq(i).attr('data-tp2'); - tp_links.push(data_text1); - tp_links.push(data_text2); - } - if (tp_links.indexOf(tp1) != -1 || tp_links.indexOf(tp2) != -1) { - alert('This port number connection already exists!'); - return; - } - this.createTpLinks(); + _g_nodes.select(".error-tip").attr("transform", function (d) { - } else { - //When the page ONAP is selected, the external cloud is created, and the connection is made. - if (this.linkName == null || this.networkVal1 == null || this.selectedNode1 == null || this.selecteTpName1 == null || this.cloudUrl == null || this.cloudNetwork == null || this.cloudNode == null || this.cloudTp == null) { - alert('The service port cannot be empty. Please fill in the complete port information.'); - return; - } - let tp_links = [], - tp1 = this.selecteTpName1; - for (let i = 0; i < $(".line-port").length; i++) { - let data_text1 = $('.line-port').eq(i).attr('data-tp1'); - tp_links.push(data_text1); - } - if (tp_links.indexOf(tp1) != -1) { - alert('This port number connection already exists!'); - return; - } + var image = this.parentNode.firstChild, + width = parseFloat("70"); - let time = this.cloudNetwork + new Date().getTime();//Create aaiid for the external cloud, this identifier is unique and cannot be repeated - this.createCloudUrls(time) - } - } + return 'translate(' + 0.8 * width + ',0)'; - //Create tp connection call interface createLink - createTpLinks() { - let params = { - 'link-name': this.linkName, - 'link-type': 'cross-link', - 'operational-status': 'up', - 'relationship-list': { - 'relationship': [ - { - 'related-to': 'p-interface', - 'related-link': '/aai/v14/network/pnfs/pnf/' + this.selectedNode1 + '/p-interfaces/p-interface/' + this.selecteTpName1, - 'relationship-data': [ - { - 'relationship-key': 'pnf.pnf-id', - 'relationship-value': this.selectedNode1 - }, - { - 'relationship-key': 'p-interface.p-interface-id', - 'relationship-value': this.selecteTpName1, - } - ] - }, - { - 'related-to': 'p-interface', - 'related-link': '/aai/v14/network/pnfs/pnf/' + this.selectedNode2 + '/p-interfaces/p-interface/' + this.selecteTpName2, - 'relationship-data': [ - { - 'relationship-key': 'pnf.pnf-id', - 'relationship-value': this.selectedNode2 - }, - { - 'relationship-key': 'p-interface.p-interface-id', - 'relationship-value': this.selecteTpName2 - } - ] - } - ] - } - }; - this.myhttp.createLink(params) - .subscribe((data) => { - if (data['status'] == 'SUCCESS') { - this.queryAddLink(); - } - }, (err) => { - console.log(err); - console.log('Create connection interface call failed'); }); - } - //Query the newly added connection immediately after creating the tp cable - queryAddLink() { - let linkName = this.linkName, - selecteTpName1 = this.selecteTpName1, - selecteTpName2 = this.selecteTpName2, - selectedNode1 = this.selectedNode1, - selectedNode2 = this.selectedNode2; - let params = { - 'link-name': linkName, - }; - this.myhttp.querySpecificLinkInfo(params) - .subscribe((data) => { - let version = data['resource-version'], - operational_status = data['operational-status'], - linkname = data['link-name']; - let textval = [selecteTpName1, selecteTpName2, version, selectedNode1, selectedNode2, operational_status, linkname]; - this.hideForm(); - this.chose(textval); - }, (err) => { - console.log(err); - }); - } + }); - //Connection between two TP coordinates - chose(textval) { - let lines_json = {}; - lines_json['tp1'] = textval[0]; - lines_json['tp2'] = textval[1]; - lines_json['version'] = textval[2]; - lines_json['node1'] = textval[3]; - lines_json['node2'] = textval[4]; - lines_json['status'] = textval[5]; - lines_json['linkname'] = textval[6]; - for (let i = 0; i < $(".node").length; i++) { - if ($('.node').eq(i).find('text').html() == textval[0]) { - $('.node').eq(i).show(); - //Get the x, y coordinates of the second level - let translates = $('.node').eq(i).css('transform'); - lines_json['x1'] = parseFloat(translates.substring(7).split(',')[4]); - lines_json['y1'] = parseFloat(translates.substring(7).split(',')[5]); - } - if ($('.node').eq(i).find('text').html() == textval[1]) { - $('.node').eq(i).show(); - let translates = $('.node').eq(i).css('transform'); - lines_json['x2'] = parseFloat(translates.substring(7).split(',')[4]); - lines_json['y2'] = parseFloat(translates.substring(7).split(',')[5]); - } - } - this.addLine(lines_json); - } - //Connection between two TPs - addLine(lines) { - let tp1 = lines.tp1; - let tp2 = lines.tp2; - let version = lines.version; - let node1 = lines.node1; - let node2 = lines.node2; - let status = lines.status; - let linkname = lines.linkname; - let x1 = lines.x1; - let y1 = lines.y1; - let x2 = lines.x2; - let y2 = lines.y2; - let color = '#14bb58'; - if (status == 'up') { - color = '#14bb58'; - } else { - color = 'red'; + function dblclick(d) { + d3.select(this).classed("fixed", d.fixed = false); } - let line = '<line class=\'line line-port line-click\' stroke=\'' + color + '\' stroke-width=\'2\' style=\'cursor:pointer\'></line>'; - let svg = d3.select('#graph'); - $('#graph').prepend(line); - $('.line').first().attr({ - x1: x1, - y1: y1, - x2: x2, - y2: y2, - 'data-tp1': tp1, - 'data-tp2': tp2, - 'data-version': version, - 'data-node1': node1, - 'data-node2': node2, - 'data-link': linkname - }); - svg.html(svg.html()); - } - - //After creating an external cloud connection, query the connection immediately - queryOutCloudLink(time) { - let networkVal1 = this.networkVal1, - selectedNode1 = this.selectedNode1, - selecteTpName1 = this.selecteTpName1, - cloudUrl = this.cloudUrl, - cloudNetWork = this.cloudNetwork, - cloudNode = this.cloudNode, - cloudTp = this.cloudTp, - linkname = this.linkName; - let params = { - 'link-name': linkname, - }; - this.myhttp.querySpecificLinkInfo(params) - .subscribe((data) => { - let status = data['operational-status']; - let link_name = data['link-name']; - this.outCloudShow = true; - this.hideForm(); - this.outCloud(this.imgmap); - setTimeout(this.cloudLine(networkVal1, selectedNode1, selecteTpName1, cloudUrl, cloudNetWork, cloudNode, cloudTp, status, link_name, time), 0); - }, (err) => { - console.log(err); - }); - } - //Add external cloud - outCloud(imgmap) { - let _this = this, - width; - if (_this.tpoption.width > 800) { - width = _this.tpoption.width; - } else { - width = 800; + function dragstart(d) { + d3.select(this).classed("fixed", d.fixed = true); + thisNg.eventDispatcher.dispatch(new AppEvent(AppEventType.UserNodeDrag, d)); } - let svg = d3.select('#content-svg'); - svg.append('g').attr('class', 'out').attr('id', 'out').style({ 'display': 'block' }).attr('transform', 'translate(' + (width - 200) + ',0)'); - let out = d3.select('#out'); - out.append('image').style('width', '200').style('height', '118').attr('xlink:href', imgmap['3']); - out.append('text').text('Partner Network') - .style('transform', 'translate(0,0)') - .style('font-size', '16') - .style('font-weight', 'bold') - .attr('dx', '40') - .attr('dy', '70') - .style('fill', '#fff'); - } + function dragend(d) { - //Add external cloud connection - cloudLine(networkVal1, selectedNode1, selecteTpName1, cloudUrl, cloudNetWork, cloudNode, cloudTp, status, link_name, time) { - let lines_json = {}; - let _this = this, - width; - if (_this.tpoption.width > 800) { - width = _this.tpoption.width; - } else { - width = 800; } - for (let i = 0; i < $(".node").length; i++) { - if ($('.node').eq(i).find('text').html() == networkVal1) { - //Get the x, y coordinates of the second level - let translates = $('.node').eq(i).css('transform'); - lines_json['x1'] = parseFloat(translates.substring(7).split(',')[4]); - lines_json['y1'] = parseFloat(translates.substring(7).split(',')[5]); - lines_json['x2'] = width - 100; - lines_json['y2'] = 100; + + function color (d){ + const scale = d3.scaleOrdinal(d3.schemeCategory10); + switch(d.group){ + case "pnf": + return scale(1); + case "tp": + return scale(2); + default: + return scale(9); } } - let x1 = lines_json['x1']; - let y1 = lines_json['y1']; - let x2 = lines_json['x2']; - let y2 = lines_json['y2']; - let color = '#14bb58'; - if (status == 'up') { - color = '#14bb58'; - } else { - color = 'red'; - } - let line = '<line class=\'line cloudline line-click\' stroke=\'' + color + '\' stroke-width=\'2\' style=\'cursor:pointer\'></line>'; - let svg = d3.select('#graph'); - $('.cloudline').remove(); - $('#graph').prepend(line); - $('.cloudline').attr({ - x1: x1 + 100, - y1: y1 + 10, - x2: x2, - y2: y2, - 'data-tp1': selecteTpName1, - 'data-tp2': cloudTp, - 'data-node1': selectedNode1, - 'data-node2': cloudNode, - 'data-network': networkVal1, - 'data-cloudnetwork': cloudNetWork, - 'data-url': cloudUrl, - 'data-aaiid': time, - 'data-link': link_name - }); - svg.html(svg.html()); - this.getExtAAIIdVersion(time); } - //Create an external cloud, call the following 5 interfaces when connecting:createCloudNetwork,createPnfs,createCloudTp,createCloudLinks,createCloudUrls - createCloudNetwork(time) { - let _thiss = this; - let params = { - '-xmlns': 'http://org.onap.aai.inventory/v14', - 'in-maint': 'false', - "network-id": this.cloudNetwork, - "provider-id": "", - "client-id": "", - "te-topo-id": "", - "relationship-list": { - "relationship": [{ - "related-to": "ext-aai-network", - 'related-link': '/aai/v14/network/ext-aai-networks/ext-aai-network/' + time - }] - } - }; + choseConnectivity(item) { + if (this.connectivitySelected !== item) this.connectivitySelected = item; + this.drawService(this.getSvcTree()); + } - //Do some asynchronous operations - _thiss.myhttp.createNetwrok(params) - .subscribe((data) => { - if (data["status"] == "SUCCESS") { - _thiss.createPnfs(time) - } - }, (err) => { - console.log(err); - }); + getSvcTree(): Array<object> { + let tree = [] + let rel = this.connectivitySelected["relationship-list"]["relationship"] || null; + if (rel){ + tree = rel.filter(rl => rl["related-to"] === "uni") + .map(obj => { + let rObj ={}; + rObj["id"] = obj["relationship-data"][0]["relationship-value"], + rObj["type"] = "leaf"; + return rObj; + }) + } + return tree; + } + getNodes(ptMapping: Array<object>) : Array<object>{ + let nodes = []; + for (let pnf of ptMapping){ + if (pnf["layer"] === 2){ + continue; + } + let name = pnf["pnfName"]; + let newNode = { + "id" : name, + "group": "pnf", + "radius" : 2, + "layer" : pnf["layer"] === 2? "Eth" : "Otn" + } + nodes.push(newNode); + } + return nodes; } - createPnfs(time) { - let _thiss = this; - let params = { - "-xmlns": "http://org.onap.aai.inventory/v14", - "pnf-name": this.cloudNode, - "pnf-id": this.cloudNode, - "in-maint": "true", - "relationship-list": { - "relationship": [ - { - "related-to": "ext-aai-network", - "relationship-label": "org.onap.relationships.inventory.BelongsTo", - "related-link": "/aai/v14/network/ext-aai-networks/ext-aai-network/" + time, - "relationship-data": { - "relationship-key": "ext-aai-network.aai-id", - "relationship-value": time + getLinks(logicalLinks: Array<object>, ptMapping: Array<object>) : Array<object> { + let links = []; + for (let ll of logicalLinks){ + let lkName:string = ll["link-name"]; + let topoIdIdx:number = lkName.lastIndexOf("topologyId-"); + if (topoIdIdx !== -1 && lkName.charAt(topoIdIdx + 11) === '2'){ + //Ignore + continue; + } else if (typeof ll["relationship-list"] === 'undefined' || + typeof ll["relationship-list"]["relationship"] === 'undefined'){ + continue; + } + //pnf to pnf + let endpoints = []; + for (let pi of ll["relationship-list"]["relationship"]) { + if (pi["related-to"] === "p-interface"){ + for (let rd of pi["relationship-data"]){ + if (rd["relationship-key"] === "pnf.pnf-name"){ + endpoints.push(rd["relationship-value"]); } - }, - { - "related-to": "network-resource", - "relationship-label": "tosca.relationships.network.LinksTo", - "related-link": "/aai/v14/network/network-resources/network-resource/" + this.cloudNetwork } - ] - } - }; - - //Do some asynchronous operations - _thiss.myhttp.createPnf(params) - .subscribe((data) => { - if (data["status"] == "SUCCESS") { - _thiss.createCloudTp(time) } - }, (err) => { - console.log(err); - }); - } - - createCloudTp(time) { - let _thiss = this; - let params = { - "-xmlns": "http://org.onap.aai.inventory/v14", - "interface-name": this.cloudTp, - "speed-value": "1000000", - "in-maint": "true", - "network-ref": "", - "transparent": "true", - "operational-status": "up" - }; - - let cloudNodeName = this.cloudNode; - //Do some asynchronous operations - _thiss.myhttp.createTp(params, cloudNodeName) - .subscribe((data) => { - if (data["status"] == "SUCCESS") { - _thiss.createCloudLinks(time) + } + if (endpoints.length === 2){ + let newlk = { + "source": endpoints[0], + "target": endpoints[1], + "type" : 1 } - }, (err) => { - console.log(err); - }); + links.push(newlk); + } + } + return links; } - createCloudLinks(time) { - let _thiss = this; - let params = { - "-xmlns": "http://org.onap.aai.inventory/v14", - "link-name": this.linkName, - "in-maint": "false", - "link-type": "cross-link", - "speed-value": "", - "operational-status": "up", - "relationship-list": { - "relationship": [ - { - "related-to": "p-interface", - "relationship-label": "tosca.relationships.network.LinksTo", - "related-link": "/aai/v14/network/pnfs/pnf/" + this.selectedNode1 + "/p-interfaces/p-interface/" + this.selecteTpName1, - "relationship-data": [ - { - "relationship-key": "pnf.pnf-name", - "relationship-value": this.selectedNode1 - }, - { - "relationship-key": "p-interface.interface-name", - "relationship-value": this.selecteTpName1 - } - ], - "related-to-property": [{ - "property-key": "p-interface.prov-status" - }] - }, - { - "related-to": "p-interface", - "relationship-label": "tosca.relationships.network.LinksTo", - "related-link": "/aai/v14/network/pnfs/pnf/" + this.cloudNode + "/p-interfaces/p-interface/" + this.cloudTp, - "relationship-data": [ - { - "relationship-key": "pnf.pnf-name", - "relationship-value": this.cloudNode - }, - { - "relationship-key": "p-interface.interface-name", - "relationship-value": this.cloudTp - } - ], - "related-to-property": [{ - "property-key": "p-interface.prov-status" - }] - }, - { - "related-to": "ext-aai-network", - "relationship-label": "org.onap.relationships.inventory.BelongsTo", - "related-link": "/aai/v14/network/ext-aai-networks/ext-aai-network/" + time, - "relationship-data": [ - { - "relationship-key": "ext-aai-network.aai-id", - "relationship-value": time - } - ] + getPnfTpMapping(logicalLinks: Array<object>) { + let pnfs = []; + let pnfVisited = {}; + let pnfIndex: number = 0; + for (let ll of logicalLinks){ + let lkName:string = ll["link-name"]; + let topoIdIdx:number = lkName.lastIndexOf("topologyId-"); + if (topoIdIdx !== -1 && lkName.charAt(topoIdIdx + 11) === '2'){ + //Ethernet layer logical-link + let lastDashIdx:number = lkName.lastIndexOf("-"); + let pnfName: string = lkName.replace("linkId", "nodeId").substr(0, lastDashIdx); + let uniName: string = lkName.substr( lastDashIdx+1); + + if (pnfVisited[pnfName]){ + let idx: number = parseInt(pnfVisited[pnfName].substr(1)); + pnfs[idx].tps[uniName] = true; + } else { + pnfVisited[pnfName] = '#' + pnfIndex; + let newPnf = { + "pnfName" : pnfName, + "tps" : { + }, + "layer" :2 } - ] - } - }; + newPnf.tps[uniName] = true; + pnfs.push(newPnf); + pnfIndex++; - //Do some asynchronous operations - _thiss.myhttp.createCloudLink(params) - .subscribe((data) => { - // resolve(data['status']); - if (data["status"] == "SUCCESS") { - _thiss.queryOutCloudLink(time); } - }, (err) => { - console.log(err); - }); - } - - createCloudUrls(time) { - let _thiss = this; - let params = { - '-xmlns': 'http://org.onap.aai.inventory/v14', - 'aai-id': time, - 'esr-system-info': { - 'esr-system-info-id': 'example-esr-system-info-id-val-0', - 'service-url': this.cloudUrl, - 'user-name': 'demo', - 'password': 'demo123456!', - 'system-type': 'ONAP' + continue; + } else if (ll["relationship-list"] === undefined || + ll["relationship-list"]["relationship"].length === 0 ){ + continue; } - }; - _thiss.myhttp.createCloudUrl(params) - .subscribe((data) => { - if (data['status'] == 'SUCCESS') { - _thiss.createCloudNetwork(time); - } - }, (err) => { - console.log(err); - }); - } + for (let pi of ll["relationship-list"]["relationship"]) { + if (pi["related-to"] === "p-interface"){ + let pnfName:string; + let tpName:string; + for (let rd of pi["relationship-data"]){ + if (rd["relationship-key"] === "pnf.pnf-name"){ + pnfName = rd["relationship-value"]; + } else if (rd["relationship-key"] === "p-interface.interface-name"){ + tpName = rd["relationship-value"]; + } + } + if (pnfVisited[pnfName]){ + let idx: number = parseInt(pnfVisited[pnfName].substr(1)); + pnfs[idx].tps[tpName] = true; + } else { + pnfVisited[pnfName] = '#' + pnfIndex; + let newPnf = { + "pnfName" : pnfName, + "tps" : { + }, + "layer" : 1 + } + newPnf.tps[tpName] = true; + pnfs.push(newPnf); + pnfIndex++; - //Local cloud TP port Delete connection Call interface deleteLink - delLink(): void { - let deltp1 = this.delTp1, - deltp2 = this.delTp2, - version = this.delVersion, - dellinkname = this.delLinkname, - delLinkIndex = this.delLinkIndex; - let params = { - 'logical-link': dellinkname, - 'resource-version': version, - }; - this.myhttp.deleteLink(params) - .subscribe((data) => { - if (data['status'] == 'SUCCESS') { - this.delLine(deltp1, deltp2); - delLinkIndex.remove(); + } } - }, (err) => { - console.log(err); - console.log('Deleting a connection interface call failed'); - }); - } - - delLine(val1, val2) { - this.delBoxisVisible = false; - for (let i = 0; i < $(".node").length; i++) { - if ($('.node').eq(i).find('text').html() == val1) { - $('.node').eq(i).hide(); - } - if ($('.node').eq(i).find('text').html() == val2) { - $('.node').eq(i).hide(); } } + return pnfs; } - - - //External cloud Delete connection Call interface deleteCloudLink - delCloudLink(): void { - let deltp1 = this.delTp1, - deltp2 = this.delTp2, - version = this.delVersion, - aaiId = this.aaiId; - let params = { - "aaiId": aaiId, - "version": version, - }; - this.myhttp.deleteCloudLink(params) - .subscribe((data) => { - if (data['status'] == 'SUCCESS') { - this.delLine(deltp1, deltp2); - $('.cloudline').remove(); - $('#out').remove(); - } - }, (err) => { - console.log(err); - console.log('Deleting a connection interface call failed'); - }); - } - } |