diff options
Diffstat (limited to 'vid-webpack-master/src/app/drawingBoard/service-planning/duplicate')
5 files changed, 691 insertions, 0 deletions
diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.html b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.html new file mode 100644 index 000000000..c5f43b9c8 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.html @@ -0,0 +1,19 @@ +<div> + <div> + <span style="font-weight: bold;"> + Please select the number of additional instances you want to add of this node. + </span> + </div> + <div> + <span style="font-weight: bold;"> + Important: VID will duplicate this node including its children. + </span> + </div> + + <div class="duplicate-number"> + <select class="quantity-select" [(ngModel)]="duplicateNumber" name="duplicate" [attr.data-tests-id]="'duplicate-amount-vfmodules'" id="duplicate-select" (change)="onDuplicateNumberChange()"> + <option *ngFor="let item of duplicateOptions" [value]="item">{{item}}</option> + </select> + </div> + <!--TODO max --> +</div> diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.scss b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.scss new file mode 100644 index 000000000..f5b566d0b --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.scss @@ -0,0 +1,9 @@ +.quantity-select { + width: 100%; + font-family: inherit; + font-size: inherit; + border: 1px solid #009FDB; + margin-top: 10px; + padding-left: 7px; + height: 30px; +} diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.ts new file mode 100644 index 000000000..e1c5d7296 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate-vnf.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import * as _ from 'lodash'; +import {DuplicateService} from "./duplicate.service"; + +@Component({ + selector: 'duplicate-vnf', + templateUrl : './duplicate-vnf.component.html', + styleUrls : ['./duplicate-vnf.component.scss'] +}) +export class DuplicateVnfComponent { + duplicateNumber : number = 1; + duplicateOptions : number[] = []; + duplicateService:DuplicateService; + constructor( private _duplicateService: DuplicateService ){ + this.duplicateService = _duplicateService; + this.duplicateOptions = _.range(1, this._duplicateService.maxNumberOfDuplicate + 1); + this.onDuplicateNumberChange(); + } + + onDuplicateNumberChange() { + this.duplicateService.setNumberOfDuplicates(+this.duplicateNumber); + } + + +} diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.spec.ts new file mode 100644 index 000000000..81251cb1a --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.spec.ts @@ -0,0 +1,449 @@ +import { DuplicateService } from './duplicate.service'; +import { LogService } from '../../../shared/utils/log/log.service'; +import { NgRedux } from '@angular-redux/store'; +import {ITreeNode} from "angular-tree-component/dist/defs/api"; +import { SdcUiServices} from "onap-ui-angular"; +import {IModalConfig} from "onap-ui-angular/dist/components/common"; +import {AppState} from "../../../shared/store/reducers"; +import {getTestBed, TestBed} from "@angular/core/testing"; + +class MockAppStore<T> { + getState(){ + return { + getState() { + return { + service : { + serviceHierarchy: { + "serviceId" : { + vnfs : { + "vnfModelName" : { + properties : { + max_instances : 2 + } + }, + "vnfModelName2" : { + properties : { + max_instances : 2 + } + }, + "vnfModelName3" : { + properties : { + } + } + } + } + }, + serviceInstance : { + "serviceId" : { + existingVNFCounterMap : { + "vnfModelId" : 1, + "vnfModelId2" : 2, + "vnfModelId3" : 0 + } + } + + } + } + } + } + } + } +} + +class MockModalService<T> {} + +describe('Drawing board tree service', () => { + let injector; + let service: DuplicateService; + let store : NgRedux<AppState>; + beforeAll(done => (async () => { + TestBed.configureTestingModule({ + providers : [ + DuplicateService, + LogService, + {provide: NgRedux, useClass: MockAppStore}, + {provide: SdcUiServices.ModalService, useClass: MockModalService} + ] + }); + await TestBed.compileComponents(); + + injector = getTestBed(); + service = injector.get(DuplicateService); + store = injector.get(NgRedux); + + })().then(done).catch(done.fail)); + + + test('setNumberOfDuplicates should set number of duplicates', ()=>{ + service.setNumberOfDuplicates(10); + expect(service.numberOfDuplicates).toEqual(10); + }); + + test('isEnabled should return false if type is VNF and has missing data', ()=>{ + let node = { + data : { + type : 'VF', + menuActions : { + duplicate : () => { + + } + } + } + }; + spyOn(node.data.menuActions, 'duplicate').and.returnValue(true); + spyOn(service, 'hasMissingData').and.returnValue(true); + let result : boolean = service.isEnabled(<any>node, null, null); + expect(result).toBeFalsy(); + }); + + test('openDuplicateModal', ()=>{ + spyOn(service, 'getRemainsInstance').and.returnValue(1); + let result : IModalConfig = service.openDuplicateModal( + 'currentServiceId', + 'currentServiceId', + 'currentId', + 'storeKey', + 2, + null,null); + expect(result.title).toEqual('Duplicate Node'); + }); + + test('openDuplicateModal should call getRemainsInstance with correct parameters', ()=>{ + spyOn(service, 'getRemainsInstance'); + service.openDuplicateModal( + 'currentServiceId', + 'currentServiceId', + 'currentId', + 'storeKey', + 2, + null,null); + expect(service.getRemainsInstance).toHaveBeenCalledWith('currentServiceId', 'currentId', 'currentServiceId', null, null); + }); + + test('canDuplicate VNF should return true', () => { + sessionStorage.setItem('reduxState' , JSON.stringify({global : { flags : {FLAG_DUPLICATE_VNF : true}}})); + let node : ITreeNode = <any> {data : {type : 'VF'}}; + + let result = service.canDuplicate(node); + expect(result).toBeTruthy(); + }); + + test('canDuplicate Network should return true', () => { + sessionStorage.setItem('reduxState' , JSON.stringify({global : { flags : {FLAG_DUPLICATE_VNF : true}}})); + let node : ITreeNode = <any> {data : {type : 'VL'}}; + + let result = service.canDuplicate(node); + expect(result).toBeTruthy(); + }); + + test('canDuplicate VFModule should return false', () => { + sessionStorage.setItem('reduxState' , JSON.stringify({global : { flags : {FLAG_DUPLICATE_VNF : true}}})); + let node : ITreeNode = <any> {data : {type : 'VFModule'}}; + + let result = service.canDuplicate(node); + expect(result).toBeFalsy(); + }); + + test('Drawing board tree service should be defined', () => { + expect(service).toBeDefined(); + }); + + test('DrawingBoardDuplicateService should be defined', () => { + expect(service).toBeDefined(); + }); + + test('Duplicate multi vnfs should save multi vnfs in redux', () => { + service.existingNames = { + "2017488_adiodvpe": "", + "rrr": "", + "ttt": "" + }; + let newVnfs = service.cloneVnf(<any>{ + "vfModules": { + "2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2": { + "2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2mtlfi": { + "instanceName": "rrr", + "volumeGroupName": "ttt" + } + } + }, + "originalName": null, + "trackById": "pfs1f0len3", + "instanceName": "2017488_ADIODvPE" + }, "2017-488_ADIOD-vPE 0"); + + expect(newVnfs.instanceName).toBe("2017488_ADIODvPE_001"); + expect(newVnfs.vfModules['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2']['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2mtlfi'].instanceName).toBe("rrr_001"); + expect(newVnfs.vfModules['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2']['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2mtlfi'].volumeGroupName).toBe("ttt_001"); + + newVnfs = service.cloneVnf(<any>{ + "vfModules": { + "2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2": { + "2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2mtlfi": { + "instanceName": "rrr", + "volumeGroupName": "ttt" + } + } + }, + "originalName": null, + "trackById": "pfs1f0len3", + "instanceName": "2017488_ADIODvPE" + }, "2017-488_ADIOD-vPE 0"); + + expect(newVnfs.instanceName).toBe("2017488_ADIODvPE_002"); + expect(newVnfs.vfModules['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2']['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2mtlfi'].instanceName).toBe("rrr_002"); + expect(newVnfs.vfModules['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2']['2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2mtlfi'].volumeGroupName).toBe("ttt_002"); + }); + + test('ensure name is unique - send new name - shouldn\'t change name', () => { + service.existingNames = { + "2017488_adiodvpe": "", + "uniqueinstancename": "" + }; + const name = "uniqueInstanceName-1"; + let uniqueName: string = service.ensureUniqueNameOrGenerateOne(name); + expect(uniqueName).toEqual(name); + }); + + test('ensure name is unique send existing name should change name', () => { + service.existingNames = { + "2017488_adiodvpe": "", + "uniqueinstancename-1": "" + }; + const name = "uniqueInstanceName-1"; + let uniqueName: string = service.ensureUniqueNameOrGenerateOne(name); + expect(uniqueName).toEqual(name + "_001"); + }); + + test('isAlreadyExist - send new name should return false', () => { + service.existingNames = { + "2017488_adiodvpe": "", + "uniqueinstancename": "" + }; + const name = "uniqueinstancename-1"; + let isExist: boolean = service.isAlreadyExists(name, service.existingNames); + expect(isExist).toBeFalsy(); + }); + + test('isAlreadyExist - send existing name should return true', () => { + service.existingNames = { + "2017488_adiodvpe": "", + "uniqueinstancename-1": "" + }; + const name = "uniqueinstancename-1"; + let isExist: boolean = service.isAlreadyExists(name, service.existingNames); + expect(isExist).toBeTruthy(); + }); + + test('isAlreadyExist - send existing name case insensitive should return true', () => { + service.existingNames = { + "2017488_adiodvpe": "", + "uniqueinstancename-1": "" + }; + const name = "uniqueInstanceName-1"; + let isExist: boolean = service.isAlreadyExists(name, service.existingNames); + expect(isExist).toBeTruthy(); + }); + + test('getNumberAsPaddingString should return 001 if padding is 000 and val is 1 string', () => { + let paddingNumber: string = service.getNumberAsPaddingString(1, '0000'); + expect(paddingNumber).toEqual('0001'); + }); + + test('getNumberAsPaddingString should return 010 if padding is 000 and val is 10 string', () => { + let paddingNumber: string = service.getNumberAsPaddingString(10, '0000'); + expect(paddingNumber).toEqual('0010'); + }); + + test('generateVNFUniqueName should return the next free number', () => { + const vnfName: string = "VF_vMee 0"; + let result: string = service.generateUniqueStoreKey( + "6e59c5de-f052-46fa-aa7e-2fca9d674c44", + vnfName, + getExistingVNFs(), + {} + ); + + expect(result).toEqual(vnfName + ":0003"); + }); + + test('generateVNFUniqueName on duplicate multi vnfs should return the next free number', () => { + spyOn(service, 'openDuplicateModal'); + service.openDuplicateModal(null, null, null,null, 2, null, null); + + expect(service.openDuplicateModal).toHaveBeenCalledWith(null, null, null,null, 2, null, null); + }); + + + test('isEnabled should return false if node has missing data', () => { + let node = { + data : { + missingData: true, + menuActions : { + duplicate : () => { + + } + } + } + }; + let result : boolean = service.isEnabled(<any>node, null, null); + expect(result).toBeFalsy(); + }); + + test('isEnabled should return true if not reach to max', () => { + let node = { + data : { + missingData: false, modelName : "vnfModelName" , modelId : "vnfModelId", type : "VF", children: [], modelCustomizationId : "vnfModelId",modelUniqueId: "vnfModelId", + menuActions : { + duplicate : () => { + + } + } + } + }; + let result : boolean = service.isEnabled(<any>node, <any>getStoreState(), "serviceId"); + expect(result).toBeTruthy(); + }); + + test('isEnabled should return false if reach to max', () => { + let node = { + data : { + missingData: false, modelName : "vnfModelName2" , modelId : "vnfModelId2", type : "VF" ,children: [], + menuActions : { + duplicate : () => { + + } + } + } + }; + let result : boolean = service.isEnabled(<any>node, <any>getStoreState(), "serviceId"); + expect(result).toBeFalsy(); + }); + + test('isEnabled should return true if max is null', () => { + let node = { + data : { + missingData: false, modelName : "vnfModelName3" , modelId : "vnfModelId3",type : "VF", children: [], modelUniqueId: "vnfModelId3", + menuActions : { + duplicate : () => { + + } + } + } + }; + let result : boolean = service.isEnabled(<any>node, <any>getStoreState(), "serviceId"); + expect(result).toBeTruthy(); + }); + + test('isEnabled should return false if type is vf module', () => { + let node = { + data : { + missingData: false, modelName : "vnfModelName3" , modelId : "vnfModelId3",type : "VFModule", children: [], + menuActions : { + } + } + }; + let result : boolean = service.isEnabled(<any>node, <any>getStoreState(), "serviceId"); + expect(result).toBeFalsy(); + }); + + test('getRemainsVNFinstance should return the remains 1 VNF', () => { + let result : number = service.getRemainsInstance("vnfModelId", "vnfModelName" , "serviceId" , <any>getStoreState(), <any>{data : {type : 'VF'}}); + expect(result).toBe(1); + }); + + test('getRemainsVNFinstance should return the remains 0 VNF', () => { + let result : number = service.getRemainsInstance("vnfModelId2", "vnfModelName2" , "serviceId" , <any>getStoreState(),<any>{data : {type : 'VF'}}); + expect(result).toBe(0); + }); + + test('isVNFChildrensHasMissingData should return true if vnf missing data = true', () => { + let result : boolean = service.hasMissingData(<any>getTreeNode(true,false)); + expect(result).toBeTruthy(); + }); + + test('isVNFChildrensHasMissingData return should false if vnf missing data = false', () => { + let result : boolean = service.hasMissingData(<any>getTreeNode(false,false)); + expect(result).toBeFalsy(); + }); + + test('isVNFChildrensHasMissingData should return true if vfModule missing data = true', () => { + let result : boolean = service.hasMissingData(<any>getTreeNode(false,true)); + expect(result).toBeTruthy(); + }); + + test('isVNFChildrensHasMissingData should return false if no children and vfModule missing data = false', () => { + let node = <any>getTreeNode(false,false); + node.data.children = []; + let result : boolean = service.hasMissingData(node); + expect(result).toBeFalsy(); + }); + + function getExistingVNFs(){ + return {"VF_vMee 0":{"rollbackOnFailure":"true","vfModules":{"vf_vmee0..VfVmee..vmme_vlc..module-1":{"vf_vmee0..VfVmee..vmme_vlc..module-1dgbxq":{"modelInfo":{"modelInvariantId":"98a7c88b-b577-476a-90e4-e25a5871e02b","modelVersionId":"522159d5-d6e0-4c2a-aa44-5a542a12a830","modelName":"VfVmee..vmme_vlc..module-1","modelVersion":"2","modelCustomizationId":"55b1be94-671a-403e-a26c-667e9c47d091","modelCustomizationName":"VfVmee..vmme_vlc..module-1"},"instanceParams":[{}]}}},"productFamilyId":"17cc1042-527b-11e6-beb8-9e71128cae77","lcpCloudRegionId":"hvf6","tenantId":"bae71557c5bb4d5aac6743a4e5f1d054","lineOfBusiness":"ONAP","platformName":"platform","modelInfo":{"modelInvariantId":"4160458e-f648-4b30-a176-43881ffffe9e","modelVersionId":"d6557200-ecf2-4641-8094-5393ae3aae60","modelName":"VF_vMee","modelVersion":"2.0","modelCustomizationId":"91415b44-753d-494c-926a-456a9172bbb9","modelCustomizationName":"VF_vMee 0"}},"VF_vMee 0:0001":{"rollbackOnFailure":"true","vfModules":{"vf_vmee0..VfVmee..vmme_vlc..module-1":{"vf_vmee0..VfVmee..vmme_vlc..module-1dgbxq":{"modelInfo":{"modelInvariantId":"98a7c88b-b577-476a-90e4-e25a5871e02b","modelVersionId":"522159d5-d6e0-4c2a-aa44-5a542a12a830","modelName":"VfVmee..vmme_vlc..module-1","modelVersion":"2","modelCustomizationId":"55b1be94-671a-403e-a26c-667e9c47d091","modelCustomizationName":"VfVmee..vmme_vlc..module-1"},"instanceParams":[{}]}}},"productFamilyId":"17cc1042-527b-11e6-beb8-9e71128cae77","lcpCloudRegionId":"hvf6","tenantId":"bae71557c5bb4d5aac6743a4e5f1d054","lineOfBusiness":"ONAP","platformName":"platform","modelInfo":{"modelInvariantId":"4160458e-f648-4b30-a176-43881ffffe9e","modelVersionId":"d6557200-ecf2-4641-8094-5393ae3aae60","modelName":"VF_vMee","modelVersion":"2.0","modelCustomizationId":"91415b44-753d-494c-926a-456a9172bbb9","modelCustomizationName":"VF_vMee 0"},"originalName":"VF_vMee 0"},"VF_vMee 0:0002":{"rollbackOnFailure":"true","vfModules":{"vf_vmee0..VfVmee..vmme_vlc..module-1":{"vf_vmee0..VfVmee..vmme_vlc..module-1dgbxq":{"modelInfo":{"modelInvariantId":"98a7c88b-b577-476a-90e4-e25a5871e02b","modelVersionId":"522159d5-d6e0-4c2a-aa44-5a542a12a830","modelName":"VfVmee..vmme_vlc..module-1","modelVersion":"2","modelCustomizationId":"55b1be94-671a-403e-a26c-667e9c47d091","modelCustomizationName":"VfVmee..vmme_vlc..module-1"},"instanceParams":[{}]}}},"productFamilyId":"17cc1042-527b-11e6-beb8-9e71128cae77","lcpCloudRegionId":"hvf6","tenantId":"bae71557c5bb4d5aac6743a4e5f1d054","lineOfBusiness":"ONAP","platformName":"platform","modelInfo":{"modelInvariantId":"4160458e-f648-4b30-a176-43881ffffe9e","modelVersionId":"d6557200-ecf2-4641-8094-5393ae3aae60","modelName":"VF_vMee","modelVersion":"2.0","modelCustomizationId":"91415b44-753d-494c-926a-456a9172bbb9","modelCustomizationName":"VF_vMee 0"},"originalName":"VF_vMee 0"}} + } + + function getStoreState(){ + return { + getState() { + return { + service : { + serviceHierarchy: { + "serviceId" : { + vnfs : { + "vnfModelName" : { + properties : { + ecomp_generated_naming: "false", + max_instances : 2 + } + }, + "vnfModelName2" : { + properties : { + ecomp_generated_naming: "false", + max_instances : 2 + } + }, + "vnfModelName3" : { + properties : { + ecomp_generated_naming: "false", + } + } + } + } + }, + serviceInstance : { + "serviceId" : { + existingVNFCounterMap : { + "vnfModelId" : 1, + "vnfModelId2" : 2, + "vnfModelId3" : 0 + } + } + + } + } + } + } + } + } + + + function getTreeNode(VNfMissingData : boolean, vfModuleMissingData : boolean){ + return { + data : { + children : vfModuleMissingData ? + [{ + missingData : true + }] : + [{ + missingData : false + } + ], + missingData : VNfMissingData + } + } + } +}); + + + diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.ts new file mode 100644 index 000000000..e695edf86 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.ts @@ -0,0 +1,189 @@ +import { Injectable } from '@angular/core'; +import { ITreeNode } from 'angular-tree-component/dist/defs/api'; +import { AppState } from '../../../shared/store/reducers'; +import { LogService } from '../../../shared/utils/log/log.service'; +import { NgRedux } from '@angular-redux/store'; +import {VnfInstance} from "../../../shared/models/vnfInstance"; +import {VfModuleMap} from "../../../shared/models/vfModulesMap"; +import * as _ from "lodash"; +import {DefaultDataGeneratorService} from "../../../shared/services/defaultDataServiceGenerator/default.data.generator.service"; +import {TypeNodeInformation} from "../typeNodeInformation.model"; +import { SdcUiServices} from "onap-ui-angular"; +import { SdcUiCommon} from "onap-ui-angular"; +import {changeInstanceCounter, duplicateBulkInstances} from "../../../shared/storeUtil/utils/general/general.actions"; +import {IModalConfig} from "onap-ui-angular/dist/modals/models/modal-config"; + +@Injectable() +export class DuplicateService { + + constructor(private _logService : LogService, private _store: NgRedux<AppState>, modalService: SdcUiServices.ModalService) { + this.modalService = modalService; + } + + numberOfDuplicates:number; + setNumberOfDuplicates(numberOfDuplicates: number) { + this.numberOfDuplicates = numberOfDuplicates; + } + + currentInstanceId: string = null; + currentServiceId: string = null; + maxNumberOfDuplicate: number = 0; + storeKey: string = null; + padding = '0000'; + modalService: SdcUiServices.ModalService; + store : NgRedux<AppState>; + existingNames : {[key: string] : any}; + currentNode : ITreeNode = null; + + + + canDuplicate(node: ITreeNode): boolean { + let reduxState = <AppState>JSON.parse(sessionStorage.getItem('reduxState')); + return reduxState.global.flags['FLAG_DUPLICATE_VNF'] && (node.data.type === 'VF' || node.data.type === 'VL'); + } + + isEnabled(node: ITreeNode, store: NgRedux<AppState>, serviceId : string): boolean { + if(!_.isNil(node) && !_.isNil(node.data.menuActions['duplicate'])){ + if(this.hasMissingData(node)) return false; + const typeNodeInformation : TypeNodeInformation = new TypeNodeInformation(node); + const max : number = store.getState().service.serviceHierarchy[serviceId][typeNodeInformation.hierarchyName][node.data.modelName].properties['max_instances'] || 1; + const currentExisting: number = store.getState().service.serviceInstance[serviceId][typeNodeInformation.existingMappingCounterName][node.data.modelUniqueId]; + + return max - currentExisting > 0; + }else { + return false; + } + } + + hasMissingData(node : ITreeNode): boolean { + if(!_.isNil(node)){ + if(node.data.missingData) return true; + if(!_.isNil(node.data.children)){ + for(let child of node.data.children) { + if(child.missingData){ + return true; + } + } + } + + } + return false; + } + + getRemainsInstance(modelId : string, modelName : string, serviceId : string, store: NgRedux<AppState>, node : ITreeNode) : number { + const typeNodeInformation : TypeNodeInformation = new TypeNodeInformation(node); + const properties = store.getState().service.serviceHierarchy[serviceId][typeNodeInformation.hierarchyName][modelName].properties; + const currentExisting : number = store.getState().service.serviceInstance[serviceId][typeNodeInformation.existingMappingCounterName][modelId]; + return (!_.isNil(properties) && !_.isNil(properties['max_instances'])) ? properties['max_instances'] - currentExisting : null; + } + + + + openDuplicateModal(currentServiceId: string, currentUuid: string, currentId: string, storeKey : string, numberOfDuplicate: number, _store : NgRedux<AppState>, node: ITreeNode): IModalConfig { + this.currentInstanceId = currentId; + this.currentServiceId = currentServiceId; + this.maxNumberOfDuplicate = this.getRemainsInstance(currentUuid, currentId, currentServiceId, _store, node); + this.storeKey = storeKey; + this.store = _store; + this.currentNode = node; + + + return { + size: SdcUiCommon.ModalSize.medium, + title: 'Duplicate Node', + type: SdcUiCommon.ModalType.custom, + buttons: [ + {text: 'Duplicate', callback: this.duplicate.bind(this, this.currentNode), closeModal: true}, + {text: 'Cancel', closeModal: true} + ] + }; + } + + duplicate(node : ITreeNode): void { + const typeNodeInformation : TypeNodeInformation = new TypeNodeInformation(node); + this.existingNames = this.store.getState().service.serviceInstance[this.currentServiceId].existingNames; + const toClone = this.store.getState().service.serviceInstance[this.currentServiceId][typeNodeInformation.hierarchyName][this.storeKey]; + let newObjects = {}; + for(let i = 0; i < this.numberOfDuplicates; i++) { + const uniqueStoreKey = this.generateUniqueStoreKey(this.currentServiceId, this.currentInstanceId, this.store.getState().service.serviceInstance[this.currentServiceId][typeNodeInformation.hierarchyName], newObjects); + const clone = this.cloneVnf(toClone, this.currentInstanceId); + newObjects[uniqueStoreKey] = clone; + } + this.store.dispatch(duplicateBulkInstances(this.currentServiceId, newObjects, this.existingNames, node)); + this.store.dispatch(changeInstanceCounter(toClone.modelInfo.modelUniqueId, this.currentServiceId, this.numberOfDuplicates, node)); + this._logService.info("Duplicate " + this.storeKey + " serviceId: " + this.currentServiceId + "number of duplicate: " + this.numberOfDuplicates, toClone); + } + + + cloneVnf(vnf : VnfInstance, originalName: string): VnfInstance { + let newUniqueVnf : VnfInstance = _.cloneDeep(vnf); + + newUniqueVnf.originalName = originalName; + newUniqueVnf.trackById = DefaultDataGeneratorService.createRandomTrackById(); + if (!_.isNil(vnf.instanceName)){ + newUniqueVnf.instanceName = this.ensureUniqueNameOrGenerateOne(vnf.instanceName); + } + + for (let vf_module_model_name in vnf.vfModules) { + const vfModuleModel: VfModuleMap = vnf.vfModules[vf_module_model_name]; + for (let vfModule in vfModuleModel) { + newUniqueVnf.vfModules[vf_module_model_name][vfModule].trackById = DefaultDataGeneratorService.createRandomTrackById(); + if (!_.isNil(vfModuleModel[vfModule].instanceName)){ + newUniqueVnf.vfModules[vf_module_model_name][vfModule].instanceName = this.ensureUniqueNameOrGenerateOne(vfModuleModel[vfModule].instanceName); + } + if (!_.isNil(vfModuleModel[vfModule].volumeGroupName)){ + newUniqueVnf.vfModules[vf_module_model_name][vfModule].volumeGroupName = this.ensureUniqueNameOrGenerateOne(vfModuleModel[vfModule].volumeGroupName); + } + } + } + return newUniqueVnf; + } + + ensureUniqueNameOrGenerateOne(instanceName){ + let uniqueInstanceName = instanceName; + if (this.isAlreadyExists(instanceName, this.existingNames)) { + uniqueInstanceName = this.generateNextUniqueName(instanceName, this.existingNames); + this.existingNames[uniqueInstanceName.toLowerCase()] = ""; + } + return uniqueInstanceName; + } + + + isAlreadyExists(name : string, existingNames : {[key: string] : any}){ + return _.has(existingNames, name.toLowerCase()); + } + + generateNextUniqueName(name : string, existingNames : {[key: string] : any}) :string{ + let suffix = "000"; + let counter = 1; + if (name.match(/^.*_[\d]{3}$/)){ + name = name.substring(0, name.length - 4); + } + + while(true){ + let paddingNumber : string = this.getNumberAsPaddingString(counter, suffix); + let candidateUniqueName = name + '_' + paddingNumber; + if(!this.isAlreadyExists(candidateUniqueName, existingNames)){ + return candidateUniqueName; + } + counter++; + } + } + + generateUniqueStoreKey(serviceId : string, objectName : string, existing : any, newObjects: any) : string { + let counter = 1; + while(true){ + let paddingNumber : string = this.getNumberAsPaddingString(counter, this.padding); + const name = objectName + ':' + paddingNumber; + if(_.isNil(existing[name]) && _.isNil(newObjects[name])){ + return name; + } + counter++; + } + } + + getNumberAsPaddingString(val: number, padding: string): string { + const str = "" + val; + return padding.substring(0, padding.length - str.length) + str; + } +} |