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 this.nsData = nsData;
449 this.vnfdPackageDetails.shortName = nsData['short-name'];
450 this.vnfdPackageDetails.vendor = nsData.vendor;
451 this.vnfdPackageDetails.description = nsData.description;
452 this.vnfdPackageDetails.version = nsData.version;
453 this.vnfdPackageDetails.id = nsData.id;
454 this.vnfdPackageDetails.name = nsData.name;
455 if (nsData.vld !== undefined) {
456 /** Details of the VL */
457 this.nsDataVLD(nsData);
459 if (nsData['constituent-vnfd'] !== undefined) {
460 /** Details of the VNFD */
461 this.nsDataConstituentVNFD(nsData);
463 if (nsData.vld !== undefined) {
464 this.nsDataVLDLinkCreation(nsData);
466 this.separateAndCreatenode();
467 }, (error: ERRORDATA) => {
468 if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
469 this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
471 this.restService.handleError(error, 'get');
473 this.isLoadingResults = false;
474 this.isShowNSDDetails = false;
477 /** nsData-vld undefined Call this function @private */
478 private nsDataVLD(nsData: NSDDetails): void {
479 nsData.vld.forEach((res: VLD) => {
480 this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
482 if (res['vnfd-connection-point-ref'] !== undefined) {
483 res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
486 id: this.nsd + ++this.iConnectionPointRef + ':' + result['vnfd-connection-point-ref'],
489 name: result['vnfd-connection-point-ref'],
490 nodeIndex: result['member-vnf-index-ref'],
491 selectorId: result['vnfd-connection-point-ref'] + '_' + result['member-vnf-index-ref'] + '-osm-' + this.nsd
497 /** nsData constituent-vnfd undefined Call this function @private */
498 private nsDataConstituentVNFD(nsData: NSDDetails): void {
499 nsData['constituent-vnfd'].forEach((res: CONSTITUENTVNFD) => {
502 id: res['vnfd-id-ref'] + ':' + res['member-vnf-index'],
505 name: res['vnfd-id-ref'],
506 nodeIndex: res['member-vnf-index'],
507 selectorId: res['vnfd-id-ref'] + '_' + res['member-vnf-index']
509 this.vnfd = res['vnfd-id-ref'];
510 this.memberVnfIndexValue = res['member-vnf-index'];
514 /** nsData-vld undefined Call this function @private */
515 private nsDataVLDLinkCreation(nsData: NSDDetails): void {
516 nsData.vld.forEach((res: VLD) => {
517 this.nsdCopy = res.id;
518 if (res['vnfd-connection-point-ref'] !== undefined) {
519 this.nsDataVNFDConnectionPointRefrence(res);
523 /** nsData-vnfd-connection-point-ref undefined Call this function @private */
524 private nsDataVNFDConnectionPointRefrence(res: VLD): void {
525 res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
526 this.connectionPoint = this.nsdCopy + ++this.jConnectionPointRef + ':' + result['vnfd-connection-point-ref'];
527 this.vnfdCopy = result['vnfd-id-ref'] + ':' + result['member-vnf-index-ref'];
528 const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.connectionPoint);
529 const nsdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.nsdCopy);
530 const vnfdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.vnfdCopy);
533 source: this.nodes[connectionPointPos],
534 target: this.nodes[nsdPos]
537 source: this.nodes[connectionPointPos],
538 target: this.nodes[vnfdPos]
542 /** Generate random string @private */
543 private randomString(length: number, chars: string): string {
544 let result: string = '';
545 for (let randomStringRef: number = length; randomStringRef > 0; --randomStringRef) {
546 result += chars[Math.floor(Math.random() * chars.length)];
550 /** Separate and create node @private */
551 private separateAndCreatenode(): void {
552 this.seprateNodes(this.nodes);
553 this.createnode(this.nodes);
554 this.isLoadingResults = false;
556 /** Get the default Configuration of containers @private */
557 private getGraphContainerAttr(): GRAPHDETAILS {
570 sourcePaddingYes: 17,
580 /** Separate the nodes along with its tyep @private */
581 private seprateNodes(node: COMPOSERNODES[]): void {
582 this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
583 node.forEach((nodeList: COMPOSERNODES) => {
584 if (nodeList.type === 'vld') {
585 this.vlNodes.push(nodeList);
586 } else if (nodeList.type === 'vnfd') {
587 this.vnfNodes.push(nodeList);
588 } else if (nodeList.type === 'ns') {
589 this.cpNodes.push(nodeList);
593 /** Node is created and render at D3 region @private */
594 private createnode(node: COMPOSERNODES[]): void {
595 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
596 d3.selectAll('svg#graphContainer > *').remove();
597 d3.select(window).on('keydown', () => { this.keyDown(); });
598 d3.select(window).on('keyup', () => { this.keyUp(); });
599 this.svg = d3.select('#graphContainer')
600 .attr('oncontextmenu', 'return false;')
601 .attr('width', graphContainerAttr.width)
602 .attr('height', graphContainerAttr.height)
603 .on('mousemove', () => { this.mousemove(); });
604 this.force = d3.forceSimulation()
605 .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
606 .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
607 .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
608 graphContainerAttr.height / graphContainerAttr.forcey))
609 .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
610 .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
611 .on('tick', () => { this.tick(); });
612 this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
613 this.path = this.svg.append('svg:g').selectAll('path');
614 this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
615 this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
616 this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
620 /** update force layout (called automatically each iteration) @private */
621 private tick(): void {
622 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
623 // draw directed edges with proper padding from node centers
624 this.path.attr('class', 'link').attr('d', (d: Tick) => {
625 const deltaX: number = d.target.x - d.source.x;
626 const deltaY: number = d.target.y - d.source.y;
627 const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
628 const normX: number = deltaX / dist;
629 const normY: number = deltaY / dist;
630 const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
631 const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
632 const sourceX: number = d.source.x + (sourcePadding * normX);
633 const sourceY: number = d.source.y + (sourcePadding * normY);
634 const targetX: number = d.target.x - (targetPadding * normX);
635 const targetY: number = d.target.y - (targetPadding * normY);
636 return `M${sourceX},${sourceY}L${targetX},${targetY}`;
637 }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
638 this.vlNode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
639 this.vnfdnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
640 this.cpnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
642 /** Update graph (called when needed) at D3 region @private */
643 private restart(node: COMPOSERNODES[]): void {
644 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
645 this.path = this.path.data(this.links);
646 this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES) => d.id);
647 this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES) => d.id);
648 this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES) => d.id);
649 this.resetAndCreateNodes();
650 this.force.nodes(node).force('link').links(this.links);
651 this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
653 /** Rest and create nodes @private */
654 private resetAndCreateNodes(): void {
655 this.path.exit().remove();
656 this.vlNode.exit().remove();
657 this.vnfdnode.exit().remove();
658 this.cpnode.exit().remove();
663 this.path.merge(this.path);
664 this.vlNode = this.gvlNode.merge(this.vlNode);
665 this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
666 this.cpnode = this.gcpNode.merge(this.cpnode);
668 /** setting the Path @private */
669 private getPathNodes(): void {
670 this.path = this.path.enter().append('svg:path');
672 /** Setting all the VL nodes @private */
673 private getVLNodes(): void {
674 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
675 this.gvlNode = this.vlNode.enter().append('svg:g');
676 this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
677 this.gvlNode.append('svg:image')
679 .attr('x', graphContainerAttr.imageX)
680 .attr('y', graphContainerAttr.imageY)
681 .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
682 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
683 .attr('xlink:href', 'assets/images/VL.svg')
684 .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
685 .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
686 .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
687 .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
688 this.gvlNode.append('svg:text')
689 .attr('class', 'node_text')
690 .attr('y', graphContainerAttr.textY)
691 .text((d: COMPOSERNODES) => d.id);
693 /** Setting all the VNFD nodes @private */
694 private getVNFDNodes(): void {
695 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
696 this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
697 this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
698 this.gvnfdNode.append('svg:image')
700 .attr('x', graphContainerAttr.imageX)
701 .attr('y', graphContainerAttr.imageY)
702 .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
703 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
704 .attr('xlink:href', 'assets/images/VNFD.svg')
705 .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
706 .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
707 .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
708 .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
709 this.gvnfdNode.append('svg:text')
710 .attr('class', 'node_text')
711 .attr('y', graphContainerAttr.textY)
712 .text((d: COMPOSERNODES) => d.id);
714 /** Setting all the CP nodes @private */
715 private getCPNodes(): void {
716 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
717 this.gcpNode = this.cpnode.enter().append('svg:g');
718 this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
719 this.gcpNode.append('svg:image')
721 .attr('x', graphContainerAttr.imageX)
722 .attr('y', graphContainerAttr.imageY)
723 .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
724 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
725 .attr('xlink:href', 'assets/images/CP.svg')
726 .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
727 .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
728 .on('click', (d: COMPOSERNODES) => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
729 .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
730 this.gcpNode.append('svg:text')
731 .attr('class', 'node_text')
732 .attr('y', graphContainerAttr.textY)
733 .text((d: COMPOSERNODES) => d.id);
735 /** Events handles when mousemove it will capture the selected node data @private */
736 private mousemove(): void {
737 if (!this.mousedownNode) { return; }
738 this.dragLine.attr('d',
739 `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
741 /** Get confirmation Before Deleting the Link in Topology @private */
742 private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: number): void {
743 let findVNFName: string = '';
744 let findVLDID: string = '';
745 if (mouseData.type === 'vld') {
746 findVNFName = this.mouseupNode.name;
747 findVLDID = this.mousedownNode.id;
749 findVNFName = this.mousedownNode.name;
750 findVLDID = this.mouseupNode.id;
752 getNsData.vld.forEach((result: VLD) => {
753 if (result.id === findVLDID) {
754 this.vlName = result.name;
755 this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFDDetails) => vnfList.id === findVNFName);
756 this.setVnfdConnectionPointRef = this.getVNFSelectedData[0]['mgmt-interface'].cp;
757 this.setVnfdName = this.getVNFSelectedData[0].name;
758 this.selectedVLDResult = result;
761 if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
762 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
763 modalRef.componentInstance.topologyType = 'Add';
764 modalRef.componentInstance.cpDetails = this.getVNFSelectedData[0]['connection-point'];
765 this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
766 vlname: '<b>' + this.vlName + '</b>',
767 vnfdname: '<b>' + this.setVnfdName + '</b>',
768 cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
769 }).subscribe((res: string) => {
770 modalRef.componentInstance.topologyname = res;
772 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
773 modalRef.result.then((result: MODALCLOSERESPONSEWITHCP) => {
775 this.nsData = getNsData;
776 this.generateCPForVNF(this.selectedVLDResult, result.connection_point, getVLDIndex);
777 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
784 this.notifierService.notify('error', this.translateService.instant('ERROR'));
788 /** Generate connection point for vnf using vld @private */
789 private generateCPForVNF(result: VLD, cp: string, getVLDIndex: number): void {
790 if (result['vnfd-connection-point-ref'] !== undefined) {
791 result['vnfd-connection-point-ref'].push({
792 'member-vnf-index-ref': getVLDIndex,
793 'vnfd-connection-point-ref': cp,
794 'vnfd-id-ref': this.getVNFSelectedData[0].name
797 Object.assign(result, {
798 'vnfd-connection-point-ref': [{
799 'member-vnf-index-ref': getVLDIndex,
800 'vnfd-connection-point-ref': cp,
801 'vnfd-id-ref': this.getVNFSelectedData[0].name
807 /** Events handles when mousedown click it will capture the selected node data @private */
808 private mouseDown(d: COMPOSERNODES): void {
809 event.preventDefault();
810 if (d3.event.ctrlKey) { return; }
811 if (d3.event.shiftKey) {
812 if (d.type === 'vnfd') {
813 this.selectedNode.push(d);
815 this.mousedownNode = d;
816 this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
817 this.currentSelectedLink = null;
818 this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
819 .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
822 /** Event handles when mouseup event occures @private */
823 private mouseUp(d: COMPOSERNODES): void {
824 if (!this.mousedownNode) { return; }
825 this.dragLine.classed('hidden', true).style('marker-end', '');
826 this.mouseupNode = d;
827 if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') {
828 const getOldVLDIndex: string[] = this.mouseupNode.id.split(':');
829 const setOldVLDindex: number = +getOldVLDIndex[1];
830 this.putType = 'cpAdded';
831 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
832 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') {
833 const getOldVLDIndex: string[] = this.mousedownNode.id.split(':');
834 const setOldVLDindex: number = +getOldVLDIndex[1];
835 this.putType = 'cpAdded';
836 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
837 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
839 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
840 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
842 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
843 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
845 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
846 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
848 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
849 } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
851 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
854 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
856 this.resetMouseVars();
858 this.currentSelectedLink = d;
859 this.currentSelectedNode = null;
861 /** Mosue Drag Line false if it is not satisfied @private */
862 private deselectPath(): void {
863 this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
865 /** reset Mouse varaibles @private */
866 private resetMouseVars(): void {
867 this.mousedownNode = null;
868 this.mouseupNode = null;
869 this.mousedownLink = null;
871 /** De-select all the selected nodes @private */
872 private deselectAllNodes(): void {
873 this.vlNode.select('image').classed(this.activeClass, false);
874 this.vnfdnode.select('image').classed(this.activeClass, false);
875 this.cpnode.select('image').classed(this.activeClass, false);
877 /** Show the right-side information @private */
878 private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
879 this.isShowNSDDetails = nsdDetails;
880 this.isShowVLDetails = vldDetails;
881 this.isShowVNFDetails = vnfDeails;
882 this.isShowCPDetails = cpDetails;
884 /** Events handles when Shift Click to perform create cp @private */
885 // tslint:disable-next-line: no-any
886 private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
887 this.selectNodeExclusive(nodeSelected, d);
889 /** Selected nodes @private */
890 // tslint:disable-next-line: no-any
891 private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
892 const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
893 this.deselectAllNodes();
894 nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
895 if (d.type === 'vld' && !alreadyIsActive) {
896 this.nsData.vld.forEach((result: VLD) => {
897 if (result.id === d.id) {
898 this.showRightSideInfo(false, true, false, false);
899 this.vlDetails = result;
902 } else if (d.type === 'vnfd' && !alreadyIsActive) {
903 this.nsData['constituent-vnfd'].forEach((result: CONSTITUENTVNFD) => {
904 if (result['member-vnf-index'] === d.nodeIndex && result['vnfd-id-ref'] === d.name) {
905 this.showRightSideInfo(false, false, true, false);
906 this.vnfData = result;
909 } else if (d.type === 'ns' && !alreadyIsActive) {
910 this.nsData.vld.forEach((result: VLD) => {
911 if (result['vnfd-connection-point-ref'] !== undefined) {
912 result['vnfd-connection-point-ref'].forEach((resultCP: VNFDCONNECTIONPOINTREF) => {
913 if (resultCP['member-vnf-index-ref'] === d.nodeIndex && resultCP['vnfd-connection-point-ref'] === d.name) {
914 this.cpData = resultCP;
915 this.vlDetails = result;
916 this.showRightSideInfo(false, false, false, true);
922 this.showRightSideInfo(true, false, false, false);
925 /** Get confirmation Before Deleting the Link in Topology @private */
926 private getDeleteLinkConfirmation(d: Tick): void {
927 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
928 modalRef.componentInstance.topologyType = 'Delete';
929 modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK');
930 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
931 modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
933 this.doubleClickLink(d);
937 /** Events handles when Double Click to Delete the link @private */
938 private doubleClickLink(d: Tick): void {
939 let getID: string = '';
940 if (d.target.type === 'vld') {
942 } else if (d.source.type === 'vld') {
945 this.nodes.forEach((res: COMPOSERNODES) => {
946 if (res.id === getID) {
947 if (this.nsData.vld !== undefined) {
948 this.nsData.vld.forEach((vldresult: VLD) => {
949 if (vldresult.id === getID) {
950 delete vldresult['vnfd-connection-point-ref'];
956 this.putType = 'linkdelete';
957 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
959 /** Get confirmation Before Deleting the Node in Topology @private */
960 private getDeleteConfirmation(d: COMPOSERNODES): void {
961 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
962 modalRef.componentInstance.topologyType = 'Delete';
963 modalRef.componentInstance.topologyname = d.name;
964 if (d.type === 'vld') {
965 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
966 } else if (d.type === 'vnfd') {
967 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
968 } else if (d.type === 'ns') {
969 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
971 modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
977 /** Events handles when Double Click to Delete @private */
978 private doubleClick(d: COMPOSERNODES): void {
979 const deletedNode: COMPOSERNODES = d;
980 this.nodes.forEach((res: COMPOSERNODES) => {
981 if (res.id === d.id) {
982 if (deletedNode.type === 'vld') {
983 const pos: number = this.nsData.vld.map((e: VLD) => { return e.id; }).indexOf(d.id);
984 this.nsData.vld.splice(pos, 1);
985 this.putType = 'nsddelete';
986 } else if (deletedNode.type === 'vnfd') {
987 const constituentVNFD: string[] = [];
988 if (this.nsData['constituent-vnfd'] !== undefined) {
989 this.nsData['constituent-vnfd'].forEach((ref: CONSTITUENTVNFD) => {
990 constituentVNFD.push(ref['vnfd-id-ref'] + ':' + ref['member-vnf-index']);
993 const pos: number = constituentVNFD.map((e: string) => { return e; }).indexOf(d.id);
994 this.nsData['constituent-vnfd'].splice(pos, 1);
995 const getCP: string[] = d.id.split(':');
996 const memberVnfIndexRef: number = +getCP[1];
997 const vnfdIDRef: string = getCP[0];
998 if (this.nsData.vld !== undefined) {
999 this.nsData.vld.forEach((resf: VLD) => {
1000 if (resf['vnfd-connection-point-ref'] !== undefined) {
1001 resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
1002 if (+connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef && connectionPoint['vnfd-id-ref'] === vnfdIDRef) {
1003 resf['vnfd-connection-point-ref'].splice(index, 1);
1009 this.putType = 'vnfddelete';
1010 } else if (deletedNode.type === 'ns') {
1011 const getCP: string[] = d.selectorId.split('-osm-');
1012 const memberVnfIndexRef: number = d.nodeIndex;
1013 const vnfdIDRef: string = getCP[getCP.length - 1];
1014 if (this.nsData.vld !== undefined) {
1015 this.nsData.vld.forEach((resf: VLD) => {
1016 if (resf['vnfd-connection-point-ref'] !== undefined && resf.id === vnfdIDRef) {
1017 resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
1018 if (connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef) {
1019 resf['vnfd-connection-point-ref'].splice(index, 1);
1025 this.putType = 'nsdelete';
1027 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1031 /** Key press event @private */
1032 private keyDown(): void {
1033 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1034 if (this.lastKeyDown !== -1) { return; }
1035 this.lastKeyDown = d3.event.keyCode;
1036 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1037 this.gvlNode.call(d3.drag()
1038 .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1040 this.gvnfdNode.call(d3.drag()
1041 .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1043 this.gcpNode.call(d3.drag()
1044 .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
1046 this.svg.classed('ctrl', true);
1049 /** Key realse event @private */
1050 private keyUp(): void {
1051 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1052 this.lastKeyDown = -1;
1053 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1054 this.gvlNode.on('.drag', null);
1055 this.gvnfdNode.on('.drag', null);
1056 this.gcpNode.on('.drag', null);
1057 this.svg.classed('ctrl', false);
1060 /** Events handles when dragstarted @private */
1061 private dragstarted(d: COMPOSERNODES): void {
1065 /** Events handles when dragged @private */
1066 private dragged(d: COMPOSERNODES): void {
1067 d.fx = d.x = d3.event.x;
1068 d.fy = d.y = d3.event.y;
1070 /** Events handles when dragended @private */
1071 private dragended(d: COMPOSERNODES): void {
1072 if (this.forceSimulationActive) {
1078 this.forceSimulationActive = false;
1081 /** Events handles when node double click @private */
1082 private onNodedblClickToggleSidebar(): void {
1083 this.sideBarOpened = false;
1085 /** Events handles when node single click @private */
1086 private onNodeClickToggleSidebar(): void {
1087 this.sideBarOpened = true;