2 Copyright 2020 TATA ELXSI
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.
16 Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
19 * @file NS Compose Component
21 // tslint:disable: no-increment-decrement
22 import { HttpHeaders } from '@angular/common/http';
23 import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
24 import { ActivatedRoute, Router } from '@angular/router';
25 import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
26 import { TranslateService } from '@ngx-translate/core';
27 import { NotifierService } from 'angular-notifier';
28 import { APIURLHEADER, CONSTANTNUMBER, ERRORDATA, MODALCLOSERESPONSEDATA, MODALCLOSERESPONSEWITHCP } from 'CommonModel';
29 import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
30 import * as d3 from 'd3';
31 import { DataService } from 'DataService';
32 import { environment } from 'environment';
33 import * as HttpStatus from 'http-status-codes';
34 import * as jsyaml from 'js-yaml';
35 import { COMPOSERNODES, CONSTITUENTVNFD, GRAPHDETAILS, NSDDetails, Tick, TickPath, VLD, VNFDCONNECTIONPOINTREF } from 'NSDModel';
36 import { RestService } from 'RestService';
37 import { SharedService } from 'SharedService';
38 import { isNullOrUndefined } from 'util';
39 import { VNFData, VNFDDetails } from 'VNFDModel';
43 * @Component takes NSComposerComponent.html as template url
46 selector: 'app-ns-composer',
47 templateUrl: './NSComposerComponent.html',
48 styleUrls: ['./NSComposerComponent.scss'],
49 encapsulation: ViewEncapsulation.None
51 /** Exporting a class @exports NSComposerComponent */
52 export class NSComposerComponent {
53 /** To inject services @public */
54 public injector: Injector;
55 /** View child contains graphContainer ref @public */
56 @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
57 /** dataService to pass the data from one component to another @public */
58 public dataService: DataService;
59 /** Contains VNFD Informations @public */
60 public vnfdPackageDetails: VNFData = { identifier: '', shortName: '', vendor: '', description: '', version: '', id: '', name: '' };
61 /** Contains VL Details @public */
62 public vlDetails: VLD = {
65 'vim-network-name': '',
69 /** Contains the information of the type of modification @public */
70 public putType: string;
71 /** Conatins mousedown action @public */
72 public mousedownNode: COMPOSERNODES = null;
73 /** Conatins mouseup action @public */
74 public mouseupNode: COMPOSERNODES = null;
75 /** Conatins mousedownLink action @public */
76 public mousedownLink: COMPOSERNODES = null;
77 /** Conatins current Selection node action @public */
78 public currentSelectedNode: COMPOSERNODES = null;
79 /** Conatins current Selection node action @public */
80 public currentSelectedLink: COMPOSERNODES = null;
81 /** Need to show the NSD Details @public */
82 public isShowNSDDetails: boolean = true;
83 /** Contains the node information of VL @public */
84 public vlNodes: {}[] = [];
85 /** Need to show the VL Details @public */
86 public isShowVLDetails: boolean = false;
87 /** Contains the node information of VNF @public */
88 public vnfNodes: {}[] = [];
89 /** contains the VNF Details @public */
90 public vnfData: CONSTITUENTVNFD;
91 /** Need to show the VNF Details @public */
92 public isShowVNFDetails: boolean = false;
93 /** Contains the node information of CP @public */
94 public cpNodes: {}[] = [];
95 /** Need to show the CP Details */
96 public cpData: VNFDCONNECTIONPOINTREF;
97 /** Need to show the VNF Details @public */
98 public isShowCPDetails: boolean = false;
99 /** random number count @public */
100 public randomNumberLength: number;
101 /** Contains the vnfd information @public */
102 public vnfList: VNFDDetails[] = [];
103 /** Add the activeclass for the selected @public */
104 public activeClass: string = 'active';
105 /** Add the fixed class for the freeze @public */
106 public fixedClass: string = 'fixed';
107 /** Check the loading results @public */
108 public isLoadingResults: boolean = true;
109 /** Give the message for the loading @public */
110 public message: string = 'PLEASEWAIT';
111 /** Get VNF selected node @public */
112 public getVNFSelectedData: VNFDDetails[];
113 /** Assign the forcesimulation active @public */
114 public forceSimulationActive: boolean = false;
115 /** Assign pinned class for the button when freezed @public */
116 public classApplied: boolean = false;
117 /** Contains sidebar open status @public */
118 public sideBarOpened: boolean = false;
119 /** Contains SVG attributes @private */
120 // tslint:disable-next-line:no-any
122 /** Contains the Drag line */
123 // tslint:disable-next-line: no-any
124 private dragLine: any;
125 /** Contains VL node @private */
126 // tslint:disable-next-line:no-any
128 /** Contains VNFD node @private */
129 // tslint:disable-next-line:no-any
130 private vnfdnode: any;
131 /** Contains CP node @private */
132 // tslint:disable-next-line:no-any
134 /** Rendered nodes represent VL @private */
135 // tslint:disable-next-line:no-any
136 private gvlNode: any;
137 /** Rendered nodes represent VL @private */
138 // tslint:disable-next-line:no-any
139 private gvnfdNode: any;
140 /** Rendered nodes represent VL @private */
141 // tslint:disable-next-line:no-any
142 private gcpNode: any;
143 /** Contains forced node animations @private */
144 // tslint:disable-next-line:no-any
146 /** Contains all the selected node @private */
147 private selectedNode: COMPOSERNODES[] = [];
148 /** variables used for CP @private */
149 private iConnectionPointRef: number = 0;
150 /** Contains the connected point @private */
151 private connectionPoint: string;
152 /** Contains all the NSD information @private */
154 /** Contains all the VNFD information @private */
155 private vnfd: string;
156 /** Contains id of the node @private */
157 private identifier: string;
158 /** Variables used for cp @private */
159 private jConnectionPointRef: number = 0;
160 /** Contains copy of NSD information @private */
161 private nsdCopy: string;
162 /** Contains the VNFD copy @private */
163 private vnfdCopy: string;
164 /** Contains name of the node @private */
165 private name: string;
166 /** Contains member vnf index value of the node @private */
167 private memberVnfIndexValue: number = 0;
168 /** Contains path information of the node */
169 // tslint:disable-next-line:no-any
171 /** Contains the node information @private */
172 private nodes: COMPOSERNODES[] = [];
173 /** Contains the link information of nodes @private */
174 private links: {}[] = [];
175 /** Contains the NS information @private */
176 private nsData: NSDDetails;
177 /** Instance of the rest service @private */
178 private restService: RestService;
179 /** Service holds the router information @private */
180 private router: Router;
181 /** Holds teh instance of AuthService class of type AuthService @private */
182 private activatedRoute: ActivatedRoute;
183 /** Notifier service to popup notification @private */
184 private notifierService: NotifierService;
185 /** Controls the header form @private */
186 private headers: HttpHeaders;
187 /** Contains tranlsate instance @private */
188 private translateService: TranslateService;
189 /** Contains lastkeypressed instance @private */
190 private lastKeyDown: number = -1;
191 /** Instance of the modal service @private */
192 private modalService: NgbModal;
193 /** Setting the Value of connection point refrence of the CP @private */
194 private setVnfdConnectionPointRef: string;
195 /** Setting the Value of VL name for confirmation @private */
196 private vlName: string;
197 /** Setting the Value of VNFD name for confirmation @private */
198 private setVnfdName: string;
199 /** Contains all methods related to shared @private */
200 private sharedService: SharedService;
201 /** Contains selected node VLD objects @private */
202 private selectedVLDResult: VLD;
204 constructor(injector: Injector) {
205 this.injector = injector;
206 this.restService = this.injector.get(RestService);
207 this.dataService = this.injector.get(DataService);
208 this.router = this.injector.get(Router);
209 this.activatedRoute = this.injector.get(ActivatedRoute);
210 this.notifierService = this.injector.get(NotifierService);
211 this.translateService = this.injector.get(TranslateService);
212 this.modalService = this.injector.get(NgbModal);
213 this.sharedService = this.injector.get(SharedService);
215 /** Lifecyle Hooks the trigger before component is instantiate @public */
216 public ngOnInit(): void {
217 // tslint:disable-next-line:no-backbone-get-set-outside-model
218 this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
220 this.headers = new HttpHeaders({
221 'Content-Type': 'application/zip',
222 Accept: 'application/json',
223 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
226 /** Events handles at drag on D3 region @public */
227 // tslint:disable-next-line:no-any
228 public drag(ev: any): void {
229 if (ev.target.id === 'vl') {
230 ev.dataTransfer.setData('text', ev.target.id);
232 ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
235 /** On clicking redirect to NS edit page @public */
236 public onEdit(): void {
237 this.router.navigate(['/packages/ns/edit/', this.identifier]).catch(() => {
238 // Catch Navigation Error
241 /** Events handles drop at D3 region @public */
242 public drop(ev: DragEvent): void {
243 event.preventDefault();
244 this.name = ev.dataTransfer.getData('text');
245 if (this.name === 'vl') {
246 this.svg.selectAll('*').remove();
247 this.vldropComposer();
249 this.svg.selectAll('*').remove();
250 this.vnfd = ev.dataTransfer.getData('text');
251 this.vnfdropComposer();
254 /** Drop VL Composer Data @public */
255 public vldropComposer(): void {
256 this.randomNumberLength = CONSTANTNUMBER.randomNumber;
257 const generateId: string = 'ns_vl_' + this.randomString(
258 this.randomNumberLength,
259 '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
261 if (this.nsData.vld !== undefined) {
262 this.nsData.vld.push({
263 'vim-network-name': 'PUBLIC',
265 'mgmt-network': true,
270 Object.assign(this.nsData, {
272 'vim-network-name': 'PUBLIC',
274 'mgmt-network': true,
280 this.putType = 'nsdadd';
281 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
283 /** Drop VNFD Composer Data @public */
284 public vnfdropComposer(): void {
285 if (this.nsData['constituent-vnfd'] !== undefined) {
286 this.nsData['constituent-vnfd'].push({
287 'vnfd-id-ref': this.vnfd,
288 'member-vnf-index': ++this.memberVnfIndexValue
291 Object.assign(this.nsData, {
292 'constituent-vnfd': [{
293 'vnfd-id-ref': this.vnfd,
294 'member-vnf-index': ++this.memberVnfIndexValue
298 this.putType = 'vnfdadd';
299 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
301 /** Events handles allow drop on D3 region @public */
302 public allowDrop(ev: DragEvent): void {
305 /** Save NSD Information @public */
306 public saveNSD(): void {
307 if (this.vnfdPackageDetails.shortName !== undefined) {
308 this.nsData['short-name'] = this.vnfdPackageDetails.shortName;
310 if (this.vnfdPackageDetails.vendor !== undefined) {
311 this.nsData.vendor = this.vnfdPackageDetails.vendor;
313 if (this.vnfdPackageDetails.description !== undefined) {
314 this.nsData.description = this.vnfdPackageDetails.description;
316 if (this.vnfdPackageDetails.version !== undefined) {
317 this.nsData.version = this.vnfdPackageDetails.version;
319 if (this.vnfdPackageDetails.id !== undefined) {
320 this.nsData.id = this.vnfdPackageDetails.id;
322 if (this.vnfdPackageDetails.name !== undefined) {
323 this.nsData.name = this.vnfdPackageDetails.name;
325 this.putType = 'nsdUpdate';
326 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
328 /** Save Virtual Link @public */
329 public saveVL(vlid: string): void {
330 this.nsData.vld.forEach((result: VLD) => {
331 if (result.id === vlid) {
332 result.name = this.vlDetails.name;
333 result['mgmt-network'] = !isNullOrUndefined(this.vlDetails['mgmt-network']) ? this.vlDetails['mgmt-network'] : true;
334 result['vim-network-name'] = !isNullOrUndefined(this.vlDetails['vim-network-name']) ? this.vlDetails['vim-network-name'] : '';
335 result.type = this.vlDetails.type;
336 result.id = this.vlDetails.id;
339 this.putType = 'vlUpdate';
340 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
342 /** Add the new Data @public */
343 public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void {
344 this.isLoadingResults = true;
345 let successMessage: string = '';
346 if (putType === 'nsdadd') {
347 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD';
348 } else if (putType === 'vnfdadd') {
349 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD';
350 } else if (putType === 'cpAdded') {
351 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS';
352 } else if (putType === 'nsdUpdate') {
353 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
354 } else if (putType === 'vlUpdate') {
355 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
356 } else if (putType === 'nsddelete') {
357 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD';
358 } else if (putType === 'vnfddelete') {
359 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD';
360 } else if (putType === 'nsdelete') {
361 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS';
362 } else if (putType === 'linkdelete') {
363 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK';
365 /** Below hide for conflicts with light weight UI */
366 const apiURLHeader: APIURLHEADER = {
367 url: apiURL + '/' + identifier + '/nsd_content',
368 httpOptions: { headers: this.headers }
370 const nsData: {} = {};
371 nsData['nsd:nsd-catalog'] = {};
372 nsData['nsd:nsd-catalog'].nsd = [];
373 nsData['nsd:nsd-catalog'].nsd.push(data);
374 const descriptorInfo: string = jsyaml.dump(nsData, {sortKeys: true});
375 this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo })
376 .then((content: ArrayBuffer): void => {
377 this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => {
379 this.notifierService.notify('success', this.translateService.instant(successMessage));
380 this.isLoadingResults = false;
381 }, (error: ERRORDATA) => {
383 this.restService.handleError(error, 'put');
384 this.isLoadingResults = false;
386 }).catch((): void => {
387 this.notifierService.notify('error', this.translateService.instant('ERROR'));
388 this.isLoadingResults = false;
391 /** Show Info @public */
392 public showInfo(): void {
393 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
394 modalRef.componentInstance.topologyType = 'Info';
395 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
396 modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
402 /** Event to freeze the animation @public */
403 public onFreeze(): void {
404 this.classApplied = !this.classApplied;
405 const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
406 d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
407 if (alreadyFixedIsActive) {
410 this.forceSimulationActive = alreadyFixedIsActive;
411 this.nodes.forEach((d: COMPOSERNODES) => {
412 d.fx = (alreadyFixedIsActive) ? null : d.x;
413 d.fy = (alreadyFixedIsActive) ? null : d.y;
415 if (alreadyFixedIsActive) {
416 this.force.restart();
419 /** Events handles when dragended @public */
420 public toggleSidebar(): void {
421 this.sideBarOpened = !this.sideBarOpened;
422 this.deselectAllNodes();
423 this.showRightSideInfo(true, false, false, false);
425 /** Prepare information for node creation of VNFD @private */
426 private generateData(): void {
427 this.generateVNFData();
428 this.generateDataNSDTopology();
429 this.sideBarOpened = false;
431 /** Prepare the information of the VNFD @private */
432 private generateVNFData(): void {
433 this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => {
434 this.vnfList = vnfdPackageData;
435 }, (error: ERRORDATA) => {
436 this.restService.handleError(error, 'get');
439 /** Prepare information for node creation of NSD Topology @private */
440 private generateDataNSDTopology(): void {
443 this.iConnectionPointRef = 0;
444 this.jConnectionPointRef = 0;
445 this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails) => {
446 delete nsData._admin;
448 delete nsData._links;
449 this.nsData = nsData;
450 this.vnfdPackageDetails.shortName = nsData['short-name'];
451 this.vnfdPackageDetails.vendor = nsData.vendor;
452 this.vnfdPackageDetails.description = nsData.description;
453 this.vnfdPackageDetails.version = nsData.version;
454 this.vnfdPackageDetails.id = nsData.id;
455 this.vnfdPackageDetails.name = nsData.name;
456 if (nsData.vld !== undefined) {
457 /** Details of the VL */
458 this.nsDataVLD(nsData);
460 if (nsData['constituent-vnfd'] !== undefined) {
461 /** Details of the VNFD */
462 this.nsDataConstituentVNFD(nsData);
464 if (nsData.vld !== undefined) {
465 this.nsDataVLDLinkCreation(nsData);
467 this.separateAndCreatenode();
468 }, (error: ERRORDATA) => {
469 if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
470 this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
472 this.restService.handleError(error, 'get');
474 this.isLoadingResults = false;
475 this.isShowNSDDetails = false;
478 /** nsData-vld undefined Call this function @private */
479 private nsDataVLD(nsData: NSDDetails): void {
480 nsData.vld.forEach((res: VLD) => {
481 this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
483 if (res['vnfd-connection-point-ref'] !== undefined) {
484 res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
487 id: this.nsd + ++this.iConnectionPointRef + ':' + result['vnfd-connection-point-ref'],
490 name: result['vnfd-connection-point-ref'],
491 nodeIndex: result['member-vnf-index-ref'],
492 selectorId: result['vnfd-connection-point-ref'] + '_' + result['member-vnf-index-ref'] + '-osm-' + this.nsd
498 /** nsData constituent-vnfd undefined Call this function @private */
499 private nsDataConstituentVNFD(nsData: NSDDetails): void {
500 nsData['constituent-vnfd'].forEach((res: CONSTITUENTVNFD) => {
503 id: res['vnfd-id-ref'] + ':' + res['member-vnf-index'],
506 name: res['vnfd-id-ref'],
507 nodeIndex: res['member-vnf-index'],
508 selectorId: res['vnfd-id-ref'] + '_' + res['member-vnf-index']
510 this.vnfd = res['vnfd-id-ref'];
511 this.memberVnfIndexValue = res['member-vnf-index'];
515 /** nsData-vld undefined Call this function @private */
516 private nsDataVLDLinkCreation(nsData: NSDDetails): void {
517 nsData.vld.forEach((res: VLD) => {
518 this.nsdCopy = res.id;
519 if (res['vnfd-connection-point-ref'] !== undefined) {
520 this.nsDataVNFDConnectionPointRefrence(res);
524 /** nsData-vnfd-connection-point-ref undefined Call this function @private */
525 private nsDataVNFDConnectionPointRefrence(res: VLD): void {
526 res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
527 this.connectionPoint = this.nsdCopy + ++this.jConnectionPointRef + ':' + result['vnfd-connection-point-ref'];
528 this.vnfdCopy = result['vnfd-id-ref'] + ':' + result['member-vnf-index-ref'];
529 const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.connectionPoint);
530 const nsdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.nsdCopy);
531 const vnfdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.vnfdCopy);
534 source: this.nodes[connectionPointPos],
535 target: this.nodes[nsdPos]
538 source: this.nodes[connectionPointPos],
539 target: this.nodes[vnfdPos]
543 /** Generate random string @private */
544 private randomString(length: number, chars: string): string {
545 let result: string = '';
546 for (let randomStringRef: number = length; randomStringRef > 0; --randomStringRef) {
547 result += chars[Math.floor(Math.random() * chars.length)];
551 /** Separate and create node @private */
552 private separateAndCreatenode(): void {
553 this.seprateNodes(this.nodes);
554 this.createnode(this.nodes);
555 this.isLoadingResults = false;
557 /** Get the default Configuration of containers @private */
558 private getGraphContainerAttr(): GRAPHDETAILS {
571 sourcePaddingYes: 17,
581 /** Separate the nodes along with its tyep @private */
582 private seprateNodes(node: COMPOSERNODES[]): void {
583 this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
584 node.forEach((nodeList: COMPOSERNODES) => {
585 if (nodeList.type === 'vld') {
586 this.vlNodes.push(nodeList);
587 } else if (nodeList.type === 'vnfd') {
588 this.vnfNodes.push(nodeList);
589 } else if (nodeList.type === 'ns') {
590 this.cpNodes.push(nodeList);
594 /** Node is created and render at D3 region @private */
595 private createnode(node: COMPOSERNODES[]): void {
596 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
597 d3.selectAll('svg#graphContainer > *').remove();
598 d3.select(window).on('keydown', () => { this.keyDown(); });
599 d3.select(window).on('keyup', () => { this.keyUp(); });
600 this.svg = d3.select('#graphContainer')
601 .attr('oncontextmenu', 'return false;')
602 .attr('width', graphContainerAttr.width)
603 .attr('height', graphContainerAttr.height)
604 .on('mousemove', () => { this.mousemove(); });
605 this.force = d3.forceSimulation()
606 .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
607 .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
608 .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
609 graphContainerAttr.height / graphContainerAttr.forcey))
610 .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
611 .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
612 .on('tick', () => { this.tick(); });
613 this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
614 this.path = this.svg.append('svg:g').selectAll('path');
615 this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
616 this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
617 this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
621 /** update force layout (called automatically each iteration) @private */
622 private tick(): void {
623 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
624 // draw directed edges with proper padding from node centers
625 this.path.attr('class', 'link').attr('d', (d: Tick) => {
626 const deltaX: number = d.target.x - d.source.x;
627 const deltaY: number = d.target.y - d.source.y;
628 const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
629 const normX: number = deltaX / dist;
630 const normY: number = deltaY / dist;
631 const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
632 const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
633 const sourceX: number = d.source.x + (sourcePadding * normX);
634 const sourceY: number = d.source.y + (sourcePadding * normY);
635 const targetX: number = d.target.x - (targetPadding * normX);
636 const targetY: number = d.target.y - (targetPadding * normY);
637 return `M${sourceX},${sourceY}L${targetX},${targetY}`;
638 }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
639 this.vlNode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
640 this.vnfdnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
641 this.cpnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
643 /** Update graph (called when needed) at D3 region @private */
644 private restart(node: COMPOSERNODES[]): void {
645 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
646 this.path = this.path.data(this.links);
647 this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES) => d.id);
648 this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES) => d.id);
649 this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES) => d.id);
650 this.resetAndCreateNodes();
651 this.force.nodes(node).force('link').links(this.links);
652 this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
654 /** Rest and create nodes @private */
655 private resetAndCreateNodes(): void {
656 this.path.exit().remove();
657 this.vlNode.exit().remove();
658 this.vnfdnode.exit().remove();
659 this.cpnode.exit().remove();
664 this.path.merge(this.path);
665 this.vlNode = this.gvlNode.merge(this.vlNode);
666 this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
667 this.cpnode = this.gcpNode.merge(this.cpnode);
669 /** setting the Path @private */
670 private getPathNodes(): void {
671 this.path = this.path.enter().append('svg:path');
673 /** Setting all the VL nodes @private */
674 private getVLNodes(): void {
675 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
676 this.gvlNode = this.vlNode.enter().append('svg:g');
677 this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
678 this.gvlNode.append('svg:image')
680 .attr('x', graphContainerAttr.imageX)
681 .attr('y', graphContainerAttr.imageY)
682 .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
683 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
684 .attr('xlink:href', 'assets/images/VL.svg')
685 .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
686 .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
687 .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
688 .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
689 this.gvlNode.append('svg:text')
690 .attr('class', 'node_text')
691 .attr('y', graphContainerAttr.textY)
692 .text((d: COMPOSERNODES) => d.id);
694 /** Setting all the VNFD nodes @private */
695 private getVNFDNodes(): void {
696 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
697 this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
698 this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
699 this.gvnfdNode.append('svg:image')
701 .attr('x', graphContainerAttr.imageX)
702 .attr('y', graphContainerAttr.imageY)
703 .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
704 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
705 .attr('xlink:href', 'assets/images/VNFD.svg')
706 .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
707 .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
708 .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
709 .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
710 this.gvnfdNode.append('svg:text')
711 .attr('class', 'node_text')
712 .attr('y', graphContainerAttr.textY)
713 .text((d: COMPOSERNODES) => d.id);
715 /** Setting all the CP nodes @private */
716 private getCPNodes(): void {
717 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
718 this.gcpNode = this.cpnode.enter().append('svg:g');
719 this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
720 this.gcpNode.append('svg:image')
722 .attr('x', graphContainerAttr.imageX)
723 .attr('y', graphContainerAttr.imageY)
724 .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
725 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
726 .attr('xlink:href', 'assets/images/CP.svg')
727 .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
728 .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
729 .on('click', (d: COMPOSERNODES) => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
730 .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
731 this.gcpNode.append('svg:text')
732 .attr('class', 'node_text')
733 .attr('y', graphContainerAttr.textY)
734 .text((d: COMPOSERNODES) => d.id);
736 /** Events handles when mousemove it will capture the selected node data @private */
737 private mousemove(): void {
738 if (!this.mousedownNode) { return; }
739 this.dragLine.attr('d',
740 `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
742 /** Get confirmation Before Deleting the Link in Topology @private */
743 private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: number): void {
744 let findVNFName: string = '';
745 let findVLDID: string = '';
746 if (mouseData.type === 'vld') {
747 findVNFName = this.mouseupNode.name;
748 findVLDID = this.mousedownNode.id;
750 findVNFName = this.mousedownNode.name;
751 findVLDID = this.mouseupNode.id;
753 getNsData.vld.forEach((result: VLD) => {
754 if (result.id === findVLDID) {
755 this.vlName = result.name;
756 this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFDDetails) => vnfList.id === findVNFName);
757 this.setVnfdConnectionPointRef = this.getVNFSelectedData[0]['mgmt-interface'].cp;
758 this.setVnfdName = this.getVNFSelectedData[0].name;
759 this.selectedVLDResult = result;
762 if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
763 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
764 modalRef.componentInstance.topologyType = 'Add';
765 modalRef.componentInstance.cpDetails = this.getVNFSelectedData[0]['connection-point'];
766 this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
767 vlname: '<b>' + this.vlName + '</b>',
768 vnfdname: '<b>' + this.setVnfdName + '</b>',
769 cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
770 }).subscribe((res: string) => {
771 modalRef.componentInstance.topologyname = res;
773 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
774 modalRef.result.then((result: MODALCLOSERESPONSEWITHCP) => {
776 this.nsData = getNsData;
777 this.generateCPForVNF(this.selectedVLDResult, result.connection_point, getVLDIndex);
778 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
785 this.notifierService.notify('error', this.translateService.instant('ERROR'));
789 /** Generate connection point for vnf using vld @private */
790 private generateCPForVNF(result: VLD, cp: string, getVLDIndex: number): void {
791 if (result['vnfd-connection-point-ref'] !== undefined) {
792 result['vnfd-connection-point-ref'].push({
793 'member-vnf-index-ref': getVLDIndex,
794 'vnfd-connection-point-ref': cp,
795 'vnfd-id-ref': this.getVNFSelectedData[0].name
798 Object.assign(result, {
799 'vnfd-connection-point-ref': [{
800 'member-vnf-index-ref': getVLDIndex,
801 'vnfd-connection-point-ref': cp,
802 'vnfd-id-ref': this.getVNFSelectedData[0].name
808 /** Events handles when mousedown click it will capture the selected node data @private */
809 private mouseDown(d: COMPOSERNODES): void {
810 event.preventDefault();
811 if (d3.event.ctrlKey) { return; }
812 if (d3.event.shiftKey) {
813 if (d.type === 'vnfd') {
814 this.selectedNode.push(d);
816 this.mousedownNode = d;
817 this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
818 this.currentSelectedLink = null;
819 this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
820 .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
823 /** Event handles when mouseup event occures @private */
824 private mouseUp(d: COMPOSERNODES): void {
825 if (!this.mousedownNode) { return; }
826 this.dragLine.classed('hidden', true).style('marker-end', '');
827 this.mouseupNode = d;
828 if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') {
829 const getOldVLDIndex: string[] = this.mouseupNode.id.split(':');
830 const setOldVLDindex: number = +getOldVLDIndex[1];
831 this.putType = 'cpAdded';
832 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
833 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') {
834 const getOldVLDIndex: string[] = this.mousedownNode.id.split(':');
835 const setOldVLDindex: number = +getOldVLDIndex[1];
836 this.putType = 'cpAdded';
837 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
838 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
840 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
841 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
843 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
844 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
846 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
847 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
849 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
850 } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
852 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
855 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
857 this.resetMouseVars();
859 this.currentSelectedLink = d;
860 this.currentSelectedNode = null;
862 /** Mosue Drag Line false if it is not satisfied @private */
863 private deselectPath(): void {
864 this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
866 /** reset Mouse varaibles @private */
867 private resetMouseVars(): void {
868 this.mousedownNode = null;
869 this.mouseupNode = null;
870 this.mousedownLink = null;
872 /** De-select all the selected nodes @private */
873 private deselectAllNodes(): void {
874 this.vlNode.select('image').classed(this.activeClass, false);
875 this.vnfdnode.select('image').classed(this.activeClass, false);
876 this.cpnode.select('image').classed(this.activeClass, false);
878 /** Show the right-side information @private */
879 private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
880 this.isShowNSDDetails = nsdDetails;
881 this.isShowVLDetails = vldDetails;
882 this.isShowVNFDetails = vnfDeails;
883 this.isShowCPDetails = cpDetails;
885 /** Events handles when Shift Click to perform create cp @private */
886 // tslint:disable-next-line: no-any
887 private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
888 this.selectNodeExclusive(nodeSelected, d);
890 /** Selected nodes @private */
891 // tslint:disable-next-line: no-any
892 private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
893 const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
894 this.deselectAllNodes();
895 nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
896 if (d.type === 'vld' && !alreadyIsActive) {
897 this.nsData.vld.forEach((result: VLD) => {
898 if (result.id === d.id) {
899 this.showRightSideInfo(false, true, false, false);
900 this.vlDetails = result;
903 } else if (d.type === 'vnfd' && !alreadyIsActive) {
904 this.nsData['constituent-vnfd'].forEach((result: CONSTITUENTVNFD) => {
905 if (result['member-vnf-index'] === d.nodeIndex && result['vnfd-id-ref'] === d.name) {
906 this.showRightSideInfo(false, false, true, false);
907 this.vnfData = result;
910 } else if (d.type === 'ns' && !alreadyIsActive) {
911 this.nsData.vld.forEach((result: VLD) => {
912 if (result['vnfd-connection-point-ref'] !== undefined) {
913 result['vnfd-connection-point-ref'].forEach((resultCP: VNFDCONNECTIONPOINTREF) => {
914 if (resultCP['member-vnf-index-ref'] === d.nodeIndex && resultCP['vnfd-connection-point-ref'] === d.name) {
915 this.cpData = resultCP;
916 this.vlDetails = result;
917 this.showRightSideInfo(false, false, false, true);
923 this.showRightSideInfo(true, false, false, false);
926 /** Get confirmation Before Deleting the Link in Topology @private */
927 private getDeleteLinkConfirmation(d: Tick): void {
928 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
929 modalRef.componentInstance.topologyType = 'Delete';
930 modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK');
931 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
932 modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
934 this.doubleClickLink(d);
938 /** Events handles when Double Click to Delete the link @private */
939 private doubleClickLink(d: Tick): void {
940 let getID: string = '';
941 if (d.target.type === 'vld') {
943 } else if (d.source.type === 'vld') {
946 this.nodes.forEach((res: COMPOSERNODES) => {
947 if (res.id === getID) {
948 if (this.nsData.vld !== undefined) {
949 this.nsData.vld.forEach((vldresult: VLD) => {
950 if (vldresult.id === getID) {
951 delete vldresult['vnfd-connection-point-ref'];
957 this.putType = 'linkdelete';
958 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
960 /** Get confirmation Before Deleting the Node in Topology @private */
961 private getDeleteConfirmation(d: COMPOSERNODES): void {
962 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
963 modalRef.componentInstance.topologyType = 'Delete';
964 modalRef.componentInstance.topologyname = d.name;
965 if (d.type === 'vld') {
966 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
967 } else if (d.type === 'vnfd') {
968 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
969 } else if (d.type === 'ns') {
970 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
972 modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
978 /** Events handles when Double Click to Delete @private */
979 private doubleClick(d: COMPOSERNODES): void {
980 const deletedNode: COMPOSERNODES = d;
981 this.nodes.forEach((res: COMPOSERNODES) => {
982 if (res.id === d.id) {
983 if (deletedNode.type === 'vld') {
984 const pos: number = this.nsData.vld.map((e: VLD) => { return e.id; }).indexOf(d.id);
985 this.nsData.vld.splice(pos, 1);
986 this.putType = 'nsddelete';
987 } else if (deletedNode.type === 'vnfd') {
988 const constituentVNFD: string[] = [];
989 if (this.nsData['constituent-vnfd'] !== undefined) {
990 this.nsData['constituent-vnfd'].forEach((ref: CONSTITUENTVNFD) => {
991 constituentVNFD.push(ref['vnfd-id-ref'] + ':' + ref['member-vnf-index']);
994 const pos: number = constituentVNFD.map((e: string) => { return e; }).indexOf(d.id);
995 this.nsData['constituent-vnfd'].splice(pos, 1);
996 const getCP: string[] = d.id.split(':');
997 const memberVnfIndexRef: number = +getCP[1];
998 const vnfdIDRef: string = getCP[0];
999 if (this.nsData.vld !== undefined) {
1000 this.nsData.vld.forEach((resf: VLD) => {
1001 if (resf['vnfd-connection-point-ref'] !== undefined) {
1002 resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
1003 if (+connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef && connectionPoint['vnfd-id-ref'] === vnfdIDRef) {
1004 resf['vnfd-connection-point-ref'].splice(index, 1);
1010 this.putType = 'vnfddelete';
1011 } else if (deletedNode.type === 'ns') {
1012 const getCP: string[] = d.selectorId.split('-osm-');
1013 const memberVnfIndexRef: number = d.nodeIndex;
1014 const vnfdIDRef: string = getCP[getCP.length - 1];
1015 if (this.nsData.vld !== undefined) {
1016 this.nsData.vld.forEach((resf: VLD) => {
1017 if (resf['vnfd-connection-point-ref'] !== undefined && resf.id === vnfdIDRef) {
1018 resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
1019 if (connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef) {
1020 resf['vnfd-connection-point-ref'].splice(index, 1);
1026 this.putType = 'nsdelete';
1028 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1032 /** Key press event @private */
1033 private keyDown(): void {
1034 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1035 if (this.lastKeyDown !== -1) { return; }
1036 this.lastKeyDown = d3.event.keyCode;
1037 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1038 this.gvlNode.call(d3.drag()
1039 .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1041 this.gvnfdNode.call(d3.drag()
1042 .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1044 this.gcpNode.call(d3.drag()
1045 .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1047 this.svg.classed('ctrl', true);
1050 /** Key realse event @private */
1051 private keyUp(): void {
1052 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1053 this.lastKeyDown = -1;
1054 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1055 this.gvlNode.on('.drag', null);
1056 this.gvnfdNode.on('.drag', null);
1057 this.gcpNode.on('.drag', null);
1058 this.svg.classed('ctrl', false);
1061 /** Events handles when dragstarted @private */
1062 private dragstarted(d: COMPOSERNODES): void {
1066 /** Events handles when dragged @private */
1067 private dragged(d: COMPOSERNODES): void {
1068 d.fx = d.x = d3.event.x;
1069 d.fy = d.y = d3.event.y;
1071 /** Events handles when dragended @private */
1072 private dragended(d: COMPOSERNODES): void {
1073 if (this.forceSimulationActive) {
1079 this.forceSimulationActive = false;
1082 /** Events handles when node double click @private */
1083 private onNodedblClickToggleSidebar(): void {
1084 this.sideBarOpened = false;
1086 /** Events handles when node single click @private */
1087 private onNodeClickToggleSidebar(): void {
1088 this.sideBarOpened = true;