687f4fa9eb3137344a255d9aeb4d029fe2345a0d
[osm/NG-UI.git] / src / app / packages / ns-packages / ns-composer / NSComposerComponent.ts
1 /*
2  Copyright 2020 TATA ELXSI
3
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
7
8   http://www.apache.org/licenses/LICENSE-2.0
9
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.
15
16  Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
17 */
18 /**
19  * @file NS Compose Component
20  */
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';
40
41 /**
42  * Creating component
43  * @Component takes NSComposerComponent.html as template url
44  */
45 @Component({
46   selector: 'app-ns-composer',
47   templateUrl: './NSComposerComponent.html',
48   styleUrls: ['./NSComposerComponent.scss'],
49   encapsulation: ViewEncapsulation.None
50 })
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 = {
63     name: '',
64     'mgmt-network': true,
65     'vim-network-name': '',
66     type: '',
67     id: ''
68   };
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
121   private svg: 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
127   private vlNode: 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
133   private cpnode: 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
145   private force: 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 */
153   private nsd: string;
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
170   private path: 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;
203
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);
214   }
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');
219     this.generateData();
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'
224     });
225   }
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);
231     } else {
232       ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
233     }
234   }
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
239     });
240   }
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();
248     } else {
249       this.svg.selectAll('*').remove();
250       this.vnfd = ev.dataTransfer.getData('text');
251       this.vnfdropComposer();
252     }
253   }
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'
260     );
261     if (this.nsData.vld !== undefined) {
262       this.nsData.vld.push({
263         'vim-network-name': 'PUBLIC',
264         name: generateId,
265         'mgmt-network': true,
266         type: 'ELAN',
267         id: generateId
268       });
269     } else {
270       Object.assign(this.nsData, {
271         vld: [{
272           'vim-network-name': 'PUBLIC',
273           name: generateId,
274           'mgmt-network': true,
275           type: 'ELAN',
276           id: generateId
277         }]
278       });
279     }
280     this.putType = 'nsdadd';
281     this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
282   }
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
289       });
290     } else {
291       Object.assign(this.nsData, {
292         'constituent-vnfd': [{
293           'vnfd-id-ref': this.vnfd,
294           'member-vnf-index': ++this.memberVnfIndexValue
295         }]
296       });
297     }
298     this.putType = 'vnfdadd';
299     this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
300   }
301   /** Events handles allow drop on D3 region @public */
302   public allowDrop(ev: DragEvent): void {
303     ev.preventDefault();
304   }
305   /** Save NSD Information @public */
306   public saveNSD(): void {
307     if (this.vnfdPackageDetails.shortName !== undefined) {
308       this.nsData['short-name'] = this.vnfdPackageDetails.shortName;
309     }
310     if (this.vnfdPackageDetails.vendor !== undefined) {
311       this.nsData.vendor = this.vnfdPackageDetails.vendor;
312     }
313     if (this.vnfdPackageDetails.description !== undefined) {
314       this.nsData.description = this.vnfdPackageDetails.description;
315     }
316     if (this.vnfdPackageDetails.version !== undefined) {
317       this.nsData.version = this.vnfdPackageDetails.version;
318     }
319     if (this.vnfdPackageDetails.id !== undefined) {
320       this.nsData.id = this.vnfdPackageDetails.id;
321     }
322     if (this.vnfdPackageDetails.name !== undefined) {
323       this.nsData.name = this.vnfdPackageDetails.name;
324     }
325     this.putType = 'nsdUpdate';
326     this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
327   }
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;
337       }
338     });
339     this.putType = 'vlUpdate';
340     this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
341   }
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';
364     }
365     /** Below hide for conflicts with light weight UI */
366     const apiURLHeader: APIURLHEADER = {
367       url: apiURL + '/' + identifier + '/nsd_content',
368       httpOptions: { headers: this.headers }
369     };
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: {}) => {
378           this.generateData();
379           this.notifierService.notify('success', this.translateService.instant(successMessage));
380           this.isLoadingResults = false;
381         }, (error: ERRORDATA) => {
382           this.generateData();
383           this.restService.handleError(error, 'put');
384           this.isLoadingResults = false;
385         });
386       }).catch((): void => {
387         this.notifierService.notify('error', this.translateService.instant('ERROR'));
388         this.isLoadingResults = false;
389       });
390   }
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) => {
397       if (result) {
398         // empty
399       }
400     }).catch();
401   }
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) {
408       this.force.stop();
409     }
410     this.forceSimulationActive = alreadyFixedIsActive;
411     this.nodes.forEach((d: COMPOSERNODES) => {
412       d.fx = (alreadyFixedIsActive) ? null : d.x;
413       d.fy = (alreadyFixedIsActive) ? null : d.y;
414     });
415     if (alreadyFixedIsActive) {
416       this.force.restart();
417     }
418   }
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);
424   }
425   /** Prepare information for node creation of VNFD @private */
426   private generateData(): void {
427     this.generateVNFData();
428     this.generateDataNSDTopology();
429     this.sideBarOpened = false;
430   }
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');
437     });
438   }
439   /** Prepare information for node creation of NSD Topology @private */
440   private generateDataNSDTopology(): void {
441     this.nodes = [];
442     this.links = [];
443     this.iConnectionPointRef = 0;
444     this.jConnectionPointRef = 0;
445     this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails) => {
446       delete nsData._admin;
447       delete nsData._id;
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);
459       }
460       if (nsData['constituent-vnfd'] !== undefined) {
461         /** Details of the VNFD */
462         this.nsDataConstituentVNFD(nsData);
463       }
464       if (nsData.vld !== undefined) {
465         this.nsDataVLDLinkCreation(nsData);
466       }
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();
471       } else {
472         this.restService.handleError(error, 'get');
473       }
474       this.isLoadingResults = false;
475       this.isShowNSDDetails = false;
476     });
477   }
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 });
482       this.nsd = res.id;
483       if (res['vnfd-connection-point-ref'] !== undefined) {
484         res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
485           this.nodes.push(
486             {
487               id: this.nsd + ++this.iConnectionPointRef + ':' + result['vnfd-connection-point-ref'],
488               reflexive: false,
489               type: 'ns',
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
493             });
494         });
495       }
496     });
497   }
498   /** nsData constituent-vnfd undefined Call this function @private */
499   private nsDataConstituentVNFD(nsData: NSDDetails): void {
500     nsData['constituent-vnfd'].forEach((res: CONSTITUENTVNFD) => {
501       this.nodes.push(
502         {
503           id: res['vnfd-id-ref'] + ':' + res['member-vnf-index'],
504           reflexive: false,
505           type: 'vnfd',
506           name: res['vnfd-id-ref'],
507           nodeIndex: res['member-vnf-index'],
508           selectorId: res['vnfd-id-ref'] + '_' + res['member-vnf-index']
509         });
510       this.vnfd = res['vnfd-id-ref'];
511       this.memberVnfIndexValue = res['member-vnf-index'];
512     });
513   }
514
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);
521       }
522     });
523   }
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);
532       this.links.push(
533         {
534           source: this.nodes[connectionPointPos],
535           target: this.nodes[nsdPos]
536         },
537         {
538           source: this.nodes[connectionPointPos],
539           target: this.nodes[vnfdPos]
540         });
541     });
542   }
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)];
548     }
549     return result;
550   }
551   /** Separate and create node @private */
552   private separateAndCreatenode(): void {
553     this.seprateNodes(this.nodes);
554     this.createnode(this.nodes);
555     this.isLoadingResults = false;
556   }
557   /** Get the default Configuration of containers @private */
558   private getGraphContainerAttr(): GRAPHDETAILS {
559     return {
560       width: 700,
561       height: 400,
562       nodeHeight: 50,
563       nodeWidth: 35,
564       textX: -35,
565       textY: 30,
566       radius: 5,
567       distance: 50,
568       strength: -500,
569       forcex: 2,
570       forcey: 2,
571       sourcePaddingYes: 17,
572       sourcePaddingNo: 12,
573       targetPaddingYes: 4,
574       targetPaddingNo: 3,
575       alphaTarget: 0.3,
576       imageX: -25,
577       imageY: -25,
578       shiftKeyCode: 17
579     };
580   }
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);
591       }
592     });
593   }
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');
618     // app starts here
619     this.restart(node);
620   }
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})`);
642   }
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();
653   }
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();
660     this.getPathNodes();
661     this.getVLNodes();
662     this.getVNFDNodes();
663     this.getCPNodes();
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);
668   }
669   /** setting the Path @private */
670   private getPathNodes(): void {
671     this.path = this.path.enter().append('svg:path');
672   }
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')
679       .style('opacity', 1)
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);
693   }
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')
700       .style('opacity', 1)
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);
714   }
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')
721       .style('opacity', 1)
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);
735   }
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]}`);
741   }
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;
749     } else {
750       findVNFName = this.mousedownNode.name;
751       findVLDID = this.mouseupNode.id;
752     }
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;
760       }
761     });
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;
772       });
773       modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
774       modalRef.result.then((result: MODALCLOSERESPONSEWITHCP) => {
775         if (result) {
776           this.nsData = getNsData;
777           this.generateCPForVNF(this.selectedVLDResult, result.connection_point, getVLDIndex);
778           this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
779         } else {
780           this.deselectPath();
781         }
782       }).catch();
783     } else {
784       this.deselectPath();
785       this.notifierService.notify('error', this.translateService.instant('ERROR'));
786     }
787   }
788
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
796       });
797     } else {
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
803         }]
804       });
805     }
806   }
807
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);
815       }
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}`);
821     }
822   }
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') {
839       this.deselectPath();
840       this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
841     } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
842       this.deselectPath();
843       this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
844     } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
845       this.deselectPath();
846       this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
847     } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
848       this.deselectPath();
849       this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
850     } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
851       this.deselectPath();
852       this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
853     } else {
854       this.deselectPath();
855       this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
856     }
857     this.resetMouseVars();
858     // select new link
859     this.currentSelectedLink = d;
860     this.currentSelectedNode = null;
861   }
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');
865   }
866   /** reset Mouse varaibles @private */
867   private resetMouseVars(): void {
868     this.mousedownNode = null;
869     this.mouseupNode = null;
870     this.mousedownLink = null;
871   }
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);
877   }
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;
884   }
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);
889   }
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;
901         }
902       });
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;
908         }
909       });
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);
918             }
919           });
920         }
921       });
922     } else {
923       this.showRightSideInfo(true, false, false, false);
924     }
925   }
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) => {
933       if (result) {
934         this.doubleClickLink(d);
935       }
936     }).catch();
937   }
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') {
942       getID = d.target.id;
943     } else if (d.source.type === 'vld') {
944       getID = d.source.id;
945     }
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'];
952             }
953           });
954         }
955       }
956     });
957     this.putType = 'linkdelete';
958     this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
959   }
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';
971     }
972     modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
973       if (result) {
974         this.doubleClick(d);
975       }
976     }).catch();
977   }
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']);
992             });
993           }
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);
1005                   }
1006                 });
1007               }
1008             });
1009           }
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);
1021                   }
1022                 });
1023               }
1024             });
1025           }
1026           this.putType = 'nsdelete';
1027         }
1028         this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1029       }
1030     });
1031   }
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)
1040       );
1041       this.gvnfdNode.call(d3.drag()
1042         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1043       );
1044       this.gcpNode.call(d3.drag()
1045         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1046       );
1047       this.svg.classed('ctrl', true);
1048     }
1049   }
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);
1059     }
1060   }
1061   /** Events handles when dragstarted @private */
1062   private dragstarted(d: COMPOSERNODES): void {
1063     d.fx = d.x;
1064     d.fy = d.y;
1065   }
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;
1070   }
1071   /** Events handles when dragended @private */
1072   private dragended(d: COMPOSERNODES): void {
1073     if (this.forceSimulationActive) {
1074       d.fx = null;
1075       d.fy = null;
1076     } else {
1077       d.fx = d.x;
1078       d.fy = d.y;
1079       this.forceSimulationActive = false;
1080     }
1081   }
1082   /** Events handles when node double click   @private */
1083   private onNodedblClickToggleSidebar(): void {
1084     this.sideBarOpened = false;
1085   }
1086   /** Events handles when node single click   @private */
1087   private onNodeClickToggleSidebar(): void {
1088     this.sideBarOpened = true;
1089   }
1090 }