--- /dev/null
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file NS Compose Component
+ */
+// tslint:disable: no-increment-decrement
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, CONSTANTNUMBER, ERRORDATA, MODALCLOSERESPONSEDATA, MODALCLOSERESPONSEWITHCP } from 'CommonModel';
+import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
+import * as d3 from 'd3';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import * as jsyaml from 'js-yaml';
+import { COMPOSERNODES, CONSTITUENTVNFD, GRAPHDETAILS, NSDDetails, Tick, TickPath, VLD, VNFDCONNECTIONPOINTREF } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VNFData, VNFDDetails } from 'VNFDModel';
+
+/**
+ * Creating component
+ * @Component takes NSComposerComponent.html as template url
+ */
+@Component({
+ selector: 'app-ns-composer',
+ templateUrl: './NSComposerComponent.html',
+ styleUrls: ['./NSComposerComponent.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+/** Exporting a class @exports NSComposerComponent */
+export class NSComposerComponent {
+ /** To inject services @public */
+ public injector: Injector;
+ /** View child contains graphContainer ref @public */
+ @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+ /** Contains VNFD Informations @public */
+ public vnfdPackageDetails: VNFData = { identifier: '', shortName: '', vendor: '', description: '', version: '', id: '', name: '' };
+ /** Contains VL Details @public */
+ public vlDetails: VLD = {
+ name: '',
+ 'mgmt-network': true,
+ 'vim-network-name': '',
+ type: '',
+ id: ''
+ };
+ /** Contains the information of the type of modification @public */
+ public putType: string;
+ /** Conatins mousedown action @public */
+ public mousedownNode: COMPOSERNODES = null;
+ /** Conatins mouseup action @public */
+ public mouseupNode: COMPOSERNODES = null;
+ /** Conatins mousedownLink action @public */
+ public mousedownLink: COMPOSERNODES = null;
+ /** Conatins current Selection node action @public */
+ public currentSelectedNode: COMPOSERNODES = null;
+ /** Conatins current Selection node action @public */
+ public currentSelectedLink: COMPOSERNODES = null;
+ /** Need to show the NSD Details @public */
+ public isShowNSDDetails: boolean = true;
+ /** Contains the node information of VL @public */
+ public vlNodes: {}[] = [];
+ /** Need to show the VL Details @public */
+ public isShowVLDetails: boolean = false;
+ /** Contains the node information of VNF @public */
+ public vnfNodes: {}[] = [];
+ /** contains the VNF Details @public */
+ public vnfData: CONSTITUENTVNFD;
+ /** Need to show the VNF Details @public */
+ public isShowVNFDetails: boolean = false;
+ /** Contains the node information of CP @public */
+ public cpNodes: {}[] = [];
+ /** Need to show the CP Details */
+ public cpData: VNFDCONNECTIONPOINTREF;
+ /** Need to show the VNF Details @public */
+ public isShowCPDetails: boolean = false;
+ /** random number count @public */
+ public randomNumberLength: number;
+ /** Contains the vnfd information @public */
+ public vnfList: VNFDDetails[] = [];
+ /** Add the activeclass for the selected @public */
+ public activeClass: string = 'active';
+ /** Add the fixed class for the freeze @public */
+ public fixedClass: string = 'fixed';
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+ /** Get VNF selected node @public */
+ public getVNFSelectedData: VNFDDetails[];
+ /** Assign the forcesimulation active @public */
+ public forceSimulationActive: boolean = false;
+ /** Assign pinned class for the button when freezed @public */
+ public classApplied: boolean = false;
+ /** Contains sidebar open status @public */
+ public sideBarOpened: boolean = false;
+ /** Contains SVG attributes @private */
+ // tslint:disable-next-line:no-any
+ private svg: any;
+ /** Contains the Drag line */
+ // tslint:disable-next-line: no-any
+ private dragLine: any;
+ /** Contains VL node @private */
+ // tslint:disable-next-line:no-any
+ private vlNode: any;
+ /** Contains VNFD node @private */
+ // tslint:disable-next-line:no-any
+ private vnfdnode: any;
+ /** Contains CP node @private */
+ // tslint:disable-next-line:no-any
+ private cpnode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gvlNode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gvnfdNode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gcpNode: any;
+ /** Contains forced node animations @private */
+ // tslint:disable-next-line:no-any
+ private force: any;
+ /** Contains all the selected node @private */
+ private selectedNode: COMPOSERNODES[] = [];
+ /** variables used for CP @private */
+ private iConnectionPointRef: number = 0;
+ /** Contains the connected point @private */
+ private connectionPoint: string;
+ /** Contains all the NSD information @private */
+ private nsd: string;
+ /** Contains all the VNFD information @private */
+ private vnfd: string;
+ /** Contains id of the node @private */
+ private identifier: string;
+ /** Variables used for cp @private */
+ private jConnectionPointRef: number = 0;
+ /** Contains copy of NSD information @private */
+ private nsdCopy: string;
+ /** Contains the VNFD copy @private */
+ private vnfdCopy: string;
+ /** Contains name of the node @private */
+ private name: string;
+ /** Contains member vnf index value of the node @private */
+ private memberVnfIndexValue: number = 0;
+ /** Contains path information of the node */
+ // tslint:disable-next-line:no-any
+ private path: any;
+ /** Contains the node information @private */
+ private nodes: COMPOSERNODES[] = [];
+ /** Contains the link information of nodes @private */
+ private links: {}[] = [];
+ /** Contains the NS information @private */
+ private nsData: NSDDetails;
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** Service holds the router information @private */
+ private router: Router;
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+ /** Contains lastkeypressed instance @private */
+ private lastKeyDown: number = -1;
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+ /** Setting the Value of connection point refrence of the CP @private */
+ private setVnfdConnectionPointRef: string;
+ /** Setting the Value of VL name for confirmation @private */
+ private vlName: string;
+ /** Setting the Value of VNFD name for confirmation @private */
+ private setVnfdName: string;
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+ /** Contains selected node VLD objects @private */
+ private selectedVLDResult: VLD;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.router = this.injector.get(Router);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/zip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+ /** Events handles at drag on D3 region @public */
+ // tslint:disable-next-line:no-any
+ public drag(ev: any): void {
+ if (ev.target.id === 'vl') {
+ ev.dataTransfer.setData('text', ev.target.id);
+ } else {
+ ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
+ }
+ }
+ /** On clicking redirect to NS edit page @public */
+ public onEdit(): void {
+ this.router.navigate(['/packages/ns/edit/', this.identifier]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+ /** Events handles drop at D3 region @public */
+ public drop(ev: DragEvent): void {
+ event.preventDefault();
+ this.name = ev.dataTransfer.getData('text');
+ if (this.name === 'vl') {
+ this.svg.selectAll('*').remove();
+ this.vldropComposer();
+ } else {
+ this.svg.selectAll('*').remove();
+ this.vnfd = ev.dataTransfer.getData('text');
+ this.vnfdropComposer();
+ }
+ }
+ /** Drop VL Composer Data @public */
+ public vldropComposer(): void {
+ this.randomNumberLength = CONSTANTNUMBER.randomNumber;
+ const generateId: string = 'ns_vl_' + this.randomString(
+ this.randomNumberLength,
+ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ );
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.push({
+ 'vim-network-name': 'PUBLIC',
+ name: generateId,
+ 'mgmt-network': true,
+ type: 'ELAN',
+ id: generateId
+ });
+ } else {
+ Object.assign(this.nsData, {
+ vld: [{
+ 'vim-network-name': 'PUBLIC',
+ name: generateId,
+ 'mgmt-network': true,
+ type: 'ELAN',
+ id: generateId
+ }]
+ });
+ }
+ this.putType = 'nsdadd';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Drop VNFD Composer Data @public */
+ public vnfdropComposer(): void {
+ if (this.nsData['constituent-vnfd'] !== undefined) {
+ this.nsData['constituent-vnfd'].push({
+ 'vnfd-id-ref': this.vnfd,
+ 'member-vnf-index': ++this.memberVnfIndexValue
+ });
+ } else {
+ Object.assign(this.nsData, {
+ 'constituent-vnfd': [{
+ 'vnfd-id-ref': this.vnfd,
+ 'member-vnf-index': ++this.memberVnfIndexValue
+ }]
+ });
+ }
+ this.putType = 'vnfdadd';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Events handles allow drop on D3 region @public */
+ public allowDrop(ev: DragEvent): void {
+ ev.preventDefault();
+ }
+ /** Save NSD Information @public */
+ public saveNSD(): void {
+ if (this.vnfdPackageDetails.shortName !== undefined) {
+ this.nsData['short-name'] = this.vnfdPackageDetails.shortName;
+ }
+ if (this.vnfdPackageDetails.vendor !== undefined) {
+ this.nsData.vendor = this.vnfdPackageDetails.vendor;
+ }
+ if (this.vnfdPackageDetails.description !== undefined) {
+ this.nsData.description = this.vnfdPackageDetails.description;
+ }
+ if (this.vnfdPackageDetails.version !== undefined) {
+ this.nsData.version = this.vnfdPackageDetails.version;
+ }
+ if (this.vnfdPackageDetails.id !== undefined) {
+ this.nsData.id = this.vnfdPackageDetails.id;
+ }
+ if (this.vnfdPackageDetails.name !== undefined) {
+ this.nsData.name = this.vnfdPackageDetails.name;
+ }
+ this.putType = 'nsdUpdate';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Save Virtual Link @public */
+ public saveVL(vlid: string): void {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result.id === vlid) {
+ result.name = this.vlDetails.name;
+ result['mgmt-network'] = !isNullOrUndefined(this.vlDetails['mgmt-network']) ? this.vlDetails['mgmt-network'] : true;
+ result['vim-network-name'] = !isNullOrUndefined(this.vlDetails['vim-network-name']) ? this.vlDetails['vim-network-name'] : '';
+ result.type = this.vlDetails.type;
+ result.id = this.vlDetails.id;
+ }
+ });
+ this.putType = 'vlUpdate';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Add the new Data @public */
+ public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void {
+ this.isLoadingResults = true;
+ let successMessage: string = '';
+ if (putType === 'nsdadd') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD';
+ } else if (putType === 'vnfdadd') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD';
+ } else if (putType === 'cpAdded') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS';
+ } else if (putType === 'nsdUpdate') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
+ } else if (putType === 'vlUpdate') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
+ } else if (putType === 'nsddelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD';
+ } else if (putType === 'vnfddelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD';
+ } else if (putType === 'nsdelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS';
+ } else if (putType === 'linkdelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK';
+ }
+ /** Below hide for conflicts with light weight UI */
+ const apiURLHeader: APIURLHEADER = {
+ url: apiURL + '/' + identifier + '/nsd_content',
+ httpOptions: { headers: this.headers }
+ };
+ const nsData: {} = {};
+ nsData['nsd:nsd-catalog'] = {};
+ nsData['nsd:nsd-catalog'].nsd = [];
+ nsData['nsd:nsd-catalog'].nsd.push(data);
+ const descriptorInfo: string = jsyaml.dump(nsData, {sortKeys: true});
+ this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo })
+ .then((content: ArrayBuffer): void => {
+ this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => {
+ this.generateData();
+ this.notifierService.notify('success', this.translateService.instant(successMessage));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.generateData();
+ this.restService.handleError(error, 'put');
+ this.isLoadingResults = false;
+ });
+ }).catch((): void => {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ this.isLoadingResults = false;
+ });
+ }
+ /** Show Info @public */
+ public showInfo(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Info';
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ // empty
+ }
+ }).catch();
+ }
+ /** Event to freeze the animation @public */
+ public onFreeze(): void {
+ this.classApplied = !this.classApplied;
+ const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
+ d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
+ if (alreadyFixedIsActive) {
+ this.force.stop();
+ }
+ this.forceSimulationActive = alreadyFixedIsActive;
+ this.nodes.forEach((d: COMPOSERNODES) => {
+ d.fx = (alreadyFixedIsActive) ? null : d.x;
+ d.fy = (alreadyFixedIsActive) ? null : d.y;
+ });
+ if (alreadyFixedIsActive) {
+ this.force.restart();
+ }
+ }
+ /** Events handles when dragended @public */
+ public toggleSidebar(): void {
+ this.sideBarOpened = !this.sideBarOpened;
+ this.deselectAllNodes();
+ this.showRightSideInfo(true, false, false, false);
+ }
+ /** Prepare information for node creation of VNFD @private */
+ private generateData(): void {
+ this.generateVNFData();
+ this.generateDataNSDTopology();
+ this.sideBarOpened = false;
+ }
+ /** Prepare the information of the VNFD @private */
+ private generateVNFData(): void {
+ this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => {
+ this.vnfList = vnfdPackageData;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+ /** Prepare information for node creation of NSD Topology @private */
+ private generateDataNSDTopology(): void {
+ this.nodes = [];
+ this.links = [];
+ this.iConnectionPointRef = 0;
+ this.jConnectionPointRef = 0;
+ this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails) => {
+ delete nsData._admin;
+ delete nsData._id;
+ this.nsData = nsData;
+ this.vnfdPackageDetails.shortName = nsData['short-name'];
+ this.vnfdPackageDetails.vendor = nsData.vendor;
+ this.vnfdPackageDetails.description = nsData.description;
+ this.vnfdPackageDetails.version = nsData.version;
+ this.vnfdPackageDetails.id = nsData.id;
+ this.vnfdPackageDetails.name = nsData.name;
+ if (nsData.vld !== undefined) {
+ /** Details of the VL */
+ this.nsDataVLD(nsData);
+ }
+ if (nsData['constituent-vnfd'] !== undefined) {
+ /** Details of the VNFD */
+ this.nsDataConstituentVNFD(nsData);
+ }
+ if (nsData.vld !== undefined) {
+ this.nsDataVLDLinkCreation(nsData);
+ }
+ this.separateAndCreatenode();
+ }, (error: ERRORDATA) => {
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ this.isLoadingResults = false;
+ this.isShowNSDDetails = false;
+ });
+ }
+ /** nsData-vld undefined Call this function @private */
+ private nsDataVLD(nsData: NSDDetails): void {
+ nsData.vld.forEach((res: VLD) => {
+ this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
+ this.nsd = res.id;
+ if (res['vnfd-connection-point-ref'] !== undefined) {
+ res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
+ this.nodes.push(
+ {
+ id: this.nsd + ++this.iConnectionPointRef + ':' + result['vnfd-connection-point-ref'],
+ reflexive: false,
+ type: 'ns',
+ name: result['vnfd-connection-point-ref'],
+ nodeIndex: result['member-vnf-index-ref'],
+ selectorId: result['vnfd-connection-point-ref'] + '_' + result['member-vnf-index-ref'] + '-osm-' + this.nsd
+ });
+ });
+ }
+ });
+ }
+ /** nsData constituent-vnfd undefined Call this function @private */
+ private nsDataConstituentVNFD(nsData: NSDDetails): void {
+ nsData['constituent-vnfd'].forEach((res: CONSTITUENTVNFD) => {
+ this.nodes.push(
+ {
+ id: res['vnfd-id-ref'] + ':' + res['member-vnf-index'],
+ reflexive: false,
+ type: 'vnfd',
+ name: res['vnfd-id-ref'],
+ nodeIndex: res['member-vnf-index'],
+ selectorId: res['vnfd-id-ref'] + '_' + res['member-vnf-index']
+ });
+ this.vnfd = res['vnfd-id-ref'];
+ this.memberVnfIndexValue = res['member-vnf-index'];
+ });
+ }
+
+ /** nsData-vld undefined Call this function @private */
+ private nsDataVLDLinkCreation(nsData: NSDDetails): void {
+ nsData.vld.forEach((res: VLD) => {
+ this.nsdCopy = res.id;
+ if (res['vnfd-connection-point-ref'] !== undefined) {
+ this.nsDataVNFDConnectionPointRefrence(res);
+ }
+ });
+ }
+ /** nsData-vnfd-connection-point-ref undefined Call this function @private */
+ private nsDataVNFDConnectionPointRefrence(res: VLD): void {
+ res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
+ this.connectionPoint = this.nsdCopy + ++this.jConnectionPointRef + ':' + result['vnfd-connection-point-ref'];
+ this.vnfdCopy = result['vnfd-id-ref'] + ':' + result['member-vnf-index-ref'];
+ const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.connectionPoint);
+ const nsdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.nsdCopy);
+ const vnfdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.vnfdCopy);
+ this.links.push(
+ {
+ source: this.nodes[connectionPointPos],
+ target: this.nodes[nsdPos]
+ },
+ {
+ source: this.nodes[connectionPointPos],
+ target: this.nodes[vnfdPos]
+ });
+ });
+ }
+ /** Generate random string @private */
+ private randomString(length: number, chars: string): string {
+ let result: string = '';
+ for (let randomStringRef: number = length; randomStringRef > 0; --randomStringRef) {
+ result += chars[Math.floor(Math.random() * chars.length)];
+ }
+ return result;
+ }
+ /** Separate and create node @private */
+ private separateAndCreatenode(): void {
+ this.seprateNodes(this.nodes);
+ this.createnode(this.nodes);
+ this.isLoadingResults = false;
+ }
+ /** Get the default Configuration of containers @private */
+ private getGraphContainerAttr(): GRAPHDETAILS {
+ return {
+ width: 700,
+ height: 400,
+ nodeHeight: 50,
+ nodeWidth: 35,
+ textX: -35,
+ textY: 30,
+ radius: 5,
+ distance: 50,
+ strength: -500,
+ forcex: 2,
+ forcey: 2,
+ sourcePaddingYes: 17,
+ sourcePaddingNo: 12,
+ targetPaddingYes: 4,
+ targetPaddingNo: 3,
+ alphaTarget: 0.3,
+ imageX: -25,
+ imageY: -25,
+ shiftKeyCode: 17
+ };
+ }
+ /** Separate the nodes along with its tyep @private */
+ private seprateNodes(node: COMPOSERNODES[]): void {
+ this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
+ node.forEach((nodeList: COMPOSERNODES) => {
+ if (nodeList.type === 'vld') {
+ this.vlNodes.push(nodeList);
+ } else if (nodeList.type === 'vnfd') {
+ this.vnfNodes.push(nodeList);
+ } else if (nodeList.type === 'ns') {
+ this.cpNodes.push(nodeList);
+ }
+ });
+ }
+ /** Node is created and render at D3 region @private */
+ private createnode(node: COMPOSERNODES[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ d3.selectAll('svg#graphContainer > *').remove();
+ d3.select(window).on('keydown', () => { this.keyDown(); });
+ d3.select(window).on('keyup', () => { this.keyUp(); });
+ this.svg = d3.select('#graphContainer')
+ .attr('oncontextmenu', 'return false;')
+ .attr('width', graphContainerAttr.width)
+ .attr('height', graphContainerAttr.height)
+ .on('mousemove', () => { this.mousemove(); });
+ this.force = d3.forceSimulation()
+ .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
+ .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
+ .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
+ graphContainerAttr.height / graphContainerAttr.forcey))
+ .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
+ .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
+ .on('tick', () => { this.tick(); });
+ this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
+ this.path = this.svg.append('svg:g').selectAll('path');
+ this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
+ this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
+ this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
+ // app starts here
+ this.restart(node);
+ }
+ /** update force layout (called automatically each iteration) @private */
+ private tick(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ // draw directed edges with proper padding from node centers
+ this.path.attr('class', 'link').attr('d', (d: Tick) => {
+ const deltaX: number = d.target.x - d.source.x;
+ const deltaY: number = d.target.y - d.source.y;
+ const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ const normX: number = deltaX / dist;
+ const normY: number = deltaY / dist;
+ const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
+ const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
+ const sourceX: number = d.source.x + (sourcePadding * normX);
+ const sourceY: number = d.source.y + (sourcePadding * normY);
+ const targetX: number = d.target.x - (targetPadding * normX);
+ const targetY: number = d.target.y - (targetPadding * normY);
+ return `M${sourceX},${sourceY}L${targetX},${targetY}`;
+ }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
+ this.vlNode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.vnfdnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.cpnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ }
+ /** Update graph (called when needed) at D3 region @private */
+ private restart(node: COMPOSERNODES[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path = this.path.data(this.links);
+ this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES) => d.id);
+ this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES) => d.id);
+ this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES) => d.id);
+ this.resetAndCreateNodes();
+ this.force.nodes(node).force('link').links(this.links);
+ this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
+ }
+ /** Rest and create nodes @private */
+ private resetAndCreateNodes(): void {
+ this.path.exit().remove();
+ this.vlNode.exit().remove();
+ this.vnfdnode.exit().remove();
+ this.cpnode.exit().remove();
+ this.getPathNodes();
+ this.getVLNodes();
+ this.getVNFDNodes();
+ this.getCPNodes();
+ this.path.merge(this.path);
+ this.vlNode = this.gvlNode.merge(this.vlNode);
+ this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
+ this.cpnode = this.gcpNode.merge(this.cpnode);
+ }
+ /** setting the Path @private */
+ private getPathNodes(): void {
+ this.path = this.path.enter().append('svg:path');
+ }
+ /** Setting all the VL nodes @private */
+ private getVLNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gvlNode = this.vlNode.enter().append('svg:g');
+ this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gvlNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VL.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gvlNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Setting all the VNFD nodes @private */
+ private getVNFDNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
+ this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gvnfdNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VNFD.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gvnfdNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Setting all the CP nodes @private */
+ private getCPNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gcpNode = this.cpnode.enter().append('svg:g');
+ this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gcpNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/CP.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gcpNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Events handles when mousemove it will capture the selected node data @private */
+ private mousemove(): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.attr('d',
+ `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
+ }
+ /** Get confirmation Before Deleting the Link in Topology @private */
+ private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: number): void {
+ let findVNFName: string = '';
+ let findVLDID: string = '';
+ if (mouseData.type === 'vld') {
+ findVNFName = this.mouseupNode.name;
+ findVLDID = this.mousedownNode.id;
+ } else {
+ findVNFName = this.mousedownNode.name;
+ findVLDID = this.mouseupNode.id;
+ }
+ getNsData.vld.forEach((result: VLD) => {
+ if (result.id === findVLDID) {
+ this.vlName = result.name;
+ this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFDDetails) => vnfList.id === findVNFName);
+ this.setVnfdConnectionPointRef = this.getVNFSelectedData[0]['mgmt-interface'].cp;
+ this.setVnfdName = this.getVNFSelectedData[0].name;
+ this.selectedVLDResult = result;
+ }
+ });
+ if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Add';
+ modalRef.componentInstance.cpDetails = this.getVNFSelectedData[0]['connection-point'];
+ this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
+ vlname: '<b>' + this.vlName + '</b>',
+ vnfdname: '<b>' + this.setVnfdName + '</b>',
+ cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
+ }).subscribe((res: string) => {
+ modalRef.componentInstance.topologyname = res;
+ });
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
+ modalRef.result.then((result: MODALCLOSERESPONSEWITHCP) => {
+ if (result) {
+ this.nsData = getNsData;
+ this.generateCPForVNF(this.selectedVLDResult, result.connection_point, getVLDIndex);
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
+ } else {
+ this.deselectPath();
+ }
+ }).catch();
+ } else {
+ this.deselectPath();
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ }
+
+ /** Generate connection point for vnf using vld @private */
+ private generateCPForVNF(result: VLD, cp: string, getVLDIndex: number): void {
+ if (result['vnfd-connection-point-ref'] !== undefined) {
+ result['vnfd-connection-point-ref'].push({
+ 'member-vnf-index-ref': getVLDIndex,
+ 'vnfd-connection-point-ref': cp,
+ 'vnfd-id-ref': this.getVNFSelectedData[0].name
+ });
+ } else {
+ Object.assign(result, {
+ 'vnfd-connection-point-ref': [{
+ 'member-vnf-index-ref': getVLDIndex,
+ 'vnfd-connection-point-ref': cp,
+ 'vnfd-id-ref': this.getVNFSelectedData[0].name
+ }]
+ });
+ }
+ }
+
+ /** Events handles when mousedown click it will capture the selected node data @private */
+ private mouseDown(d: COMPOSERNODES): void {
+ event.preventDefault();
+ if (d3.event.ctrlKey) { return; }
+ if (d3.event.shiftKey) {
+ if (d.type === 'vnfd') {
+ this.selectedNode.push(d);
+ }
+ this.mousedownNode = d;
+ this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
+ this.currentSelectedLink = null;
+ this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
+ .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
+ }
+ }
+ /** Event handles when mouseup event occures @private */
+ private mouseUp(d: COMPOSERNODES): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.classed('hidden', true).style('marker-end', '');
+ this.mouseupNode = d;
+ if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') {
+ const getOldVLDIndex: string[] = this.mouseupNode.id.split(':');
+ const setOldVLDindex: number = +getOldVLDIndex[1];
+ this.putType = 'cpAdded';
+ this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') {
+ const getOldVLDIndex: string[] = this.mousedownNode.id.split(':');
+ const setOldVLDindex: number = +getOldVLDIndex[1];
+ this.putType = 'cpAdded';
+ this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
+ } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
+ } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
+ } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
+ } else {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
+ }
+ this.resetMouseVars();
+ // select new link
+ this.currentSelectedLink = d;
+ this.currentSelectedNode = null;
+ }
+ /** Mosue Drag Line false if it is not satisfied @private */
+ private deselectPath(): void {
+ this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
+ }
+ /** reset Mouse varaibles @private */
+ private resetMouseVars(): void {
+ this.mousedownNode = null;
+ this.mouseupNode = null;
+ this.mousedownLink = null;
+ }
+ /** De-select all the selected nodes @private */
+ private deselectAllNodes(): void {
+ this.vlNode.select('image').classed(this.activeClass, false);
+ this.vnfdnode.select('image').classed(this.activeClass, false);
+ this.cpnode.select('image').classed(this.activeClass, false);
+ }
+ /** Show the right-side information @private */
+ private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
+ this.isShowNSDDetails = nsdDetails;
+ this.isShowVLDetails = vldDetails;
+ this.isShowVNFDetails = vnfDeails;
+ this.isShowCPDetails = cpDetails;
+ }
+ /** Events handles when Shift Click to perform create cp @private */
+ // tslint:disable-next-line: no-any
+ private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
+ this.selectNodeExclusive(nodeSelected, d);
+ }
+ /** Selected nodes @private */
+ // tslint:disable-next-line: no-any
+ private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
+ const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
+ this.deselectAllNodes();
+ nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
+ if (d.type === 'vld' && !alreadyIsActive) {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result.id === d.id) {
+ this.showRightSideInfo(false, true, false, false);
+ this.vlDetails = result;
+ }
+ });
+ } else if (d.type === 'vnfd' && !alreadyIsActive) {
+ this.nsData['constituent-vnfd'].forEach((result: CONSTITUENTVNFD) => {
+ if (result['member-vnf-index'] === d.nodeIndex && result['vnfd-id-ref'] === d.name) {
+ this.showRightSideInfo(false, false, true, false);
+ this.vnfData = result;
+ }
+ });
+ } else if (d.type === 'ns' && !alreadyIsActive) {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result['vnfd-connection-point-ref'] !== undefined) {
+ result['vnfd-connection-point-ref'].forEach((resultCP: VNFDCONNECTIONPOINTREF) => {
+ if (resultCP['member-vnf-index-ref'] === d.nodeIndex && resultCP['vnfd-connection-point-ref'] === d.name) {
+ this.cpData = resultCP;
+ this.vlDetails = result;
+ this.showRightSideInfo(false, false, false, true);
+ }
+ });
+ }
+ });
+ } else {
+ this.showRightSideInfo(true, false, false, false);
+ }
+ }
+ /** Get confirmation Before Deleting the Link in Topology @private */
+ private getDeleteLinkConfirmation(d: Tick): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK');
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.doubleClickLink(d);
+ }
+ }).catch();
+ }
+ /** Events handles when Double Click to Delete the link @private */
+ private doubleClickLink(d: Tick): void {
+ let getID: string = '';
+ if (d.target.type === 'vld') {
+ getID = d.target.id;
+ } else if (d.source.type === 'vld') {
+ getID = d.source.id;
+ }
+ this.nodes.forEach((res: COMPOSERNODES) => {
+ if (res.id === getID) {
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((vldresult: VLD) => {
+ if (vldresult.id === getID) {
+ delete vldresult['vnfd-connection-point-ref'];
+ }
+ });
+ }
+ }
+ });
+ this.putType = 'linkdelete';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Get confirmation Before Deleting the Node in Topology @private */
+ private getDeleteConfirmation(d: COMPOSERNODES): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = d.name;
+ if (d.type === 'vld') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
+ } else if (d.type === 'vnfd') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
+ } else if (d.type === 'ns') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
+ }
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.doubleClick(d);
+ }
+ }).catch();
+ }
+ /** Events handles when Double Click to Delete @private */
+ private doubleClick(d: COMPOSERNODES): void {
+ const deletedNode: COMPOSERNODES = d;
+ this.nodes.forEach((res: COMPOSERNODES) => {
+ if (res.id === d.id) {
+ if (deletedNode.type === 'vld') {
+ const pos: number = this.nsData.vld.map((e: VLD) => { return e.id; }).indexOf(d.id);
+ this.nsData.vld.splice(pos, 1);
+ this.putType = 'nsddelete';
+ } else if (deletedNode.type === 'vnfd') {
+ const constituentVNFD: string[] = [];
+ if (this.nsData['constituent-vnfd'] !== undefined) {
+ this.nsData['constituent-vnfd'].forEach((ref: CONSTITUENTVNFD) => {
+ constituentVNFD.push(ref['vnfd-id-ref'] + ':' + ref['member-vnf-index']);
+ });
+ }
+ const pos: number = constituentVNFD.map((e: string) => { return e; }).indexOf(d.id);
+ this.nsData['constituent-vnfd'].splice(pos, 1);
+ const getCP: string[] = d.id.split(':');
+ const memberVnfIndexRef: number = +getCP[1];
+ const vnfdIDRef: string = getCP[0];
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((resf: VLD) => {
+ if (resf['vnfd-connection-point-ref'] !== undefined) {
+ resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
+ if (+connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef && connectionPoint['vnfd-id-ref'] === vnfdIDRef) {
+ resf['vnfd-connection-point-ref'].splice(index, 1);
+ }
+ });
+ }
+ });
+ }
+ this.putType = 'vnfddelete';
+ } else if (deletedNode.type === 'ns') {
+ const getCP: string[] = d.selectorId.split('-osm-');
+ const memberVnfIndexRef: number = d.nodeIndex;
+ const vnfdIDRef: string = getCP[getCP.length - 1];
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((resf: VLD) => {
+ if (resf['vnfd-connection-point-ref'] !== undefined && resf.id === vnfdIDRef) {
+ resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
+ if (connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef) {
+ resf['vnfd-connection-point-ref'].splice(index, 1);
+ }
+ });
+ }
+ });
+ }
+ this.putType = 'nsdelete';
+ }
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ });
+ }
+ /** Key press event @private */
+ private keyDown(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ if (this.lastKeyDown !== -1) { return; }
+ this.lastKeyDown = d3.event.keyCode;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gvlNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gvnfdNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gcpNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.svg.classed('ctrl', true);
+ }
+ }
+ /** Key realse event @private */
+ private keyUp(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.lastKeyDown = -1;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gvlNode.on('.drag', null);
+ this.gvnfdNode.on('.drag', null);
+ this.gcpNode.on('.drag', null);
+ this.svg.classed('ctrl', false);
+ }
+ }
+ /** Events handles when dragstarted @private */
+ private dragstarted(d: COMPOSERNODES): void {
+ d.fx = d.x;
+ d.fy = d.y;
+ }
+ /** Events handles when dragged @private */
+ private dragged(d: COMPOSERNODES): void {
+ d.fx = d.x = d3.event.x;
+ d.fy = d.y = d3.event.y;
+ }
+ /** Events handles when dragended @private */
+ private dragended(d: COMPOSERNODES): void {
+ if (this.forceSimulationActive) {
+ d.fx = null;
+ d.fy = null;
+ } else {
+ d.fx = d.x;
+ d.fy = d.y;
+ this.forceSimulationActive = false;
+ }
+ }
+ /** Events handles when node double click @private */
+ private onNodedblClickToggleSidebar(): void {
+ this.sideBarOpened = false;
+ }
+ /** Events handles when node single click @private */
+ private onNodeClickToggleSidebar(): void {
+ this.sideBarOpened = true;
+ }
+}