From d74f6cc4a47f4ebe94c6143f5ffb12b7f47c8fb6 Mon Sep 17 00:00:00 2001 From: Mateusz Gołuchowski Date: Thu, 5 Nov 2020 10:11:08 +0100 Subject: Extend Modern UI for pnf usecase Implemented functionalities to manage PNFs in modern UI: - Adding, removing, editing PNFs - PNF default generation based on 'min_instances' property - FE sends proper instantiation request to BE This is still impossible to deploy service with PNFs as VID's BE logic must be adjusted to generate proper request to SO as described in VID-695. Issue-ID: VID-694 Signed-off-by: Mateusz Goluchowski Change-Id: I5285ac2ab5e95665244ca29c6549249d9330b1ed --- .../default.data.generator.service.spec.ts | 234 ++++++++++++++++++++- .../default.data.generator.service.ts | 86 ++++++-- 2 files changed, 294 insertions(+), 26 deletions(-) (limited to 'vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator') diff --git a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts index 56f49e75c..4abffabcb 100644 --- a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts +++ b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts @@ -4,25 +4,176 @@ import {NgRedux} from '@angular-redux/store'; import {DefaultDataGeneratorService} from './default.data.generator.service'; import {ServiceNodeTypes} from "../../models/ServiceNodeTypes"; import {VNFModel} from "../../models/vnfModel"; +import {AppState} from "../../store/reducers"; -class MockAppStore {} +class MockAppStore { + dispatch() { + + } + + getState() { + return { + "global": { + "flags": { + "FLAG_NETWORK_TO_ASYNC_INSTANTIATION": false, + "FLAG_SHOW_ASSIGNMENTS": true, + "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, + "FLAG_SHOW_VERIFY_SERVICE": false, + "FLAG_SERVICE_MODEL_CACHE": true, + "FLAG_ADD_MSO_TESTAPI_FIELD": true + } + }, + "service": { + "serviceHierarchy": { + "serviceId": { + "service": { + "uuid": "6e59c5de-f052-46fa-aa7e-2fca9d674c44", + "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0", + "name": "action-data", + "version": "1.0", + "toscaModelURL": null, + "category": "Emanuel", + "serviceType": "", + "serviceRole": "", + "description": "action-data", + "serviceEcompNaming": "false", + "instantiationType": "Macro", + "vidNotions": { + "instantiationType": "Macro" + }, + }, + "globalSubscriberId": "subscriberId", + "pnfs": { + "pnfInstanceV1": { + "name": "pnfName", + "pnfStoreKey": "pnfInstanceV1", + "version": "1.0", + "description": "PNF description", + "uuid": "0903e1c0-8e03-4936-b5c2-260653b96413", + "invariantUuid": "00beb8f9-6d39-452f-816d-c709b9cbb87d", + "properties": { + "min_instances": "1", + "ecomp_generated_naming": "true" + } + } + }, + "modelInfo": { + "modelInvariantId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0", + "modelVersionId": "6b528779-44a3-4472-bdff-9cd15ec93450", + "modelName": "action-data", + "modelVersion": "1.0", + "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450" + }, + "instanceName": "InstanceName", + "owningEntityId": "d61e6f2d-12fa-4cc2-91df-7c244011d6fc", + "productFamilyId": "17cc1042-527b-11e6-beb8-9e71128cae77", + "lcpCloudRegionId": "AAIAIC25", + "tenantId": "092eb9e8e4b7412e8787dd091bc58e86", + "aicZoneId": "JAG1", + "projectName": null, + "rollbackOnFailure": "true", + "aicZoneName": "YUDFJULP-JAG1", + "owningEntityName": "WayneHolland", + "testApi": "GR_API", + "tenantName": "USP-SIP-IC-24335-T-01", + "bulkSize": 1, + "isALaCarte": false, + "name": "action-data", + "version": "1.0", + "description": "", + "category": "", + "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450", + "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0", + "serviceType": "", + "serviceRole": "", + "isMultiStepDesign": false + } + }, + "serviceInstance": { + "serviceId": { + "globalSubscriberId": "subscriberId", + "pnfs": { + "pnfInstanceV1": { + "name": "pnfName", + "pnfStoreKey": "pnfInstanceV1" + }, + "pnfInstanceV2": { + "name": "pnfName2", + "pnfStoreKey": "pnfInstanceV2" + } + }, + "modelInfo": { + "modelInvariantId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0", + "modelVersionId": "6b528779-44a3-4472-bdff-9cd15ec93450", + "modelName": "action-data", + "modelVersion": "1.0", + "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450" + }, + "instanceName": "InstanceName", + "owningEntityId": "d61e6f2d-12fa-4cc2-91df-7c244011d6fc", + "productFamilyId": "17cc1042-527b-11e6-beb8-9e71128cae77", + "lcpCloudRegionId": "AAIAIC25", + "tenantId": "092eb9e8e4b7412e8787dd091bc58e86", + "aicZoneId": "JAG1", + "projectName": null, + "rollbackOnFailure": "true", + "aicZoneName": "YUDFJULP-JAG1", + "owningEntityName": "WayneHolland", + "testApi": "GR_API", + "tenantName": "USP-SIP-IC-24335-T-01", + "bulkSize": 1, + "isALaCarte": false, + "name": "action-data", + "version": "1.0", + "description": "", + "category": "", + "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450", + "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0", + "serviceType": "", + "serviceRole": "", + "isMultiStepDesign": false + } + }, + "subscribers": [ + { + "id": "someSubscriberId", + "name": "someSubscriberName", + "isPermitted": true + }, + { + "id": "subscriberId", + "name": "subscriberName", + "isPermitted": true + }, + { + "id": "subscriberId2", + "name": "subscriberName2", + "isPermitted": true + } + ] + } + } + } +} describe('Default Data Generator Service', () => { let injector; let service: DefaultDataGeneratorService; + let store: NgRedux; let httpMock: HttpTestingController; beforeAll(done => (async () => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], - providers: [DefaultDataGeneratorService, - {provide: NgRedux, useClass: MockAppStore}] + providers: [DefaultDataGeneratorService, + {provide: NgRedux, useClass: MockAppStore}] }); await TestBed.compileComponents(); injector = getTestBed(); service = injector.get(DefaultDataGeneratorService); httpMock = injector.get(HttpTestingController); + store = injector.get(NgRedux); })().then(done).catch(done.fail)); test('generateVFModule aLaCarte vf module object should missed data', () => { @@ -124,7 +275,7 @@ describe('Default Data Generator Service', () => { "modelVersion": "1" }, volumeGroupName: "", - isMissingData : false, + isMissingData: false, trackById: Math.random().toString() }, vfModuleModel, vnfModuleUUID, false, [], ""); expect(newVfModule.name).toEqual('<Automatically Assigned>'); @@ -147,7 +298,7 @@ describe('Default Data Generator Service', () => { "modelVersion": "1" }, volumeGroupName: "", - isMissingData : false, + isMissingData: false, trackById: Math.random().toString() }, vfModuleModel, vnfModuleUUID, true, [], ""); expect(newVfModule.name).toEqual('<Automatically Assigned>'); @@ -159,7 +310,7 @@ describe('Default Data Generator Service', () => { test('createNewVnfTreeNode with isEcompGeneratedNaming instance name not filled - missing data true', () => { const vnfModel = generateServiceHierarchy().vnfs['VF_vGeraldine 0']; const newVnf = service.createNewTreeNode({ - uuid : '', + uuid: '', instanceName: "", productFamilyId: "productFamilyId", lcpCloudRegionId: "lcpCloudRegionId", @@ -168,7 +319,7 @@ describe('Default Data Generator Service', () => { platformName: "platformName", lineOfBusiness: "lineOfBusiness", rollbackOnFailure: "rollbackOnFailure", - originalName : null, + originalName: null, vfModules: {}, modelInfo: { "modelCustomizationName": "VF_vGeraldine 0", @@ -182,7 +333,7 @@ describe('Default Data Generator Service', () => { isMissingData: false, trackById: Math.random().toString(), vnfStoreKey: "abc" - }, new VNFModel(vnfModel),'VF_vGeraldine 0', 'vnfs'); + }, new VNFModel(vnfModel), 'VF_vGeraldine 0', 'vnfs'); expect(newVnf.name).toEqual('VF_vGeraldine 0'); expect(newVnf.missingData).toEqual(true); }); @@ -190,7 +341,7 @@ describe('Default Data Generator Service', () => { test('createNewVnfTreeNode with isEcompGeneratedNaming instance name filled - missing data false', () => { const vnfModel = generateServiceHierarchy().vnfs['VF_vGeraldine 0']; const newVnf = service.createNewTreeNode({ - uuid : '', + uuid: '', instanceName: "instanceName", productFamilyId: "productFamilyId", lcpCloudRegionId: "lcpCloudRegionId", @@ -199,7 +350,7 @@ describe('Default Data Generator Service', () => { platformName: "platformName", lineOfBusiness: "lineOfBusiness", rollbackOnFailure: "rollbackOnFailure", - originalName : null, + originalName: null, vfModules: {}, modelInfo: { "modelCustomizationName": "VF_vGeraldine 0", @@ -213,12 +364,73 @@ describe('Default Data Generator Service', () => { isMissingData: false, trackById: Math.random().toString(), vnfStoreKey: "abc" - }, vnfModel,'VF_vGeraldine 0', 'vnfs'); + }, vnfModel, 'VF_vGeraldine 0', 'vnfs'); expect(newVnf.name).toEqual("instanceName"); expect(newVnf.missingData).toEqual(false); }); }); + describe('#updatePnfsOnFirstSet tests', () => { + + test('updatePnfsOnFirstSet should call createPNFInstanceReduxIfNotExist when pnfs exists and extended pnf flag is on', () => { + jest.spyOn(service, 'isExtendedMacroPnfConfigOn').mockReturnValue(true); + spyOn(service, 'createPNFInstanceReduxIfNotExist'); + + service.updatePnfsOnFirstSet("serviceId", {}) + + expect(service.createPNFInstanceReduxIfNotExist).toHaveBeenCalled(); + }); + + test('updatePnfsOnFirstSet should not call createPNFInstanceReduxIfNotExist when pnfs exists and extended pnf flag is off', () => { + jest.spyOn(service, 'isExtendedMacroPnfConfigOn').mockReturnValue(false); + spyOn(service, 'createPNFInstanceReduxIfNotExist'); + + service.updatePnfsOnFirstSet("serviceId", {}) + + expect(service.createPNFInstanceReduxIfNotExist).not.toHaveBeenCalled(); + }); + + test('updatePnfsOnFirstSet should not call createPNFInstanceReduxIfNotExist when min_instances == 0 and extended pnf flag is on', () => { + jest.spyOn(service, 'isExtendedMacroPnfConfigOn').mockReturnValue(true); + jest.spyOn(service, 'isMinInstancesGreaterThanZero').mockReturnValue(false); + spyOn(service, 'createPNFInstanceReduxIfNotExist'); + + service.updatePnfsOnFirstSet("serviceId", {}) + + expect(service.createPNFInstanceReduxIfNotExist).not.toHaveBeenCalled(); + }); + + test('createPNFInstanceReduxIfNotExist should dispatch proper actions when pnf doesnt exist', () => { + let pnfData = { + modelInfo: { + modelCustomizationName: "pnfName" + } + }; + spyOn(store, 'dispatch'); + + service.createPNFInstanceReduxIfNotExist("serviceId", pnfData); + + expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ + type: "CREATE_PNF_INSTANCE", + })); + expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ + type: "CHANGE_INSTANCE_COUNTER", + })); + }); + + test('createPNFInstanceReduxIfNotExist should not dispatch anything when pnf exists', () => { + let pnfData = { + modelInfo: { + modelCustomizationName: "pnfInstanceV1" + } + }; + spyOn(store, 'dispatch'); + + service.createPNFInstanceReduxIfNotExist("serviceId", pnfData); + + expect(store.dispatch).not.toHaveBeenCalled(); + }); + }); }); diff --git a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts index e00b259a0..1bf698463 100644 --- a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts +++ b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts @@ -1,6 +1,5 @@ import {Injectable} from '@angular/core'; import * as _ from 'lodash'; - import {NgRedux} from '@angular-redux/store'; import {AppState} from '../../store/reducers'; import {VnfTreeNode} from "../../models/vnfTreeNode"; @@ -21,12 +20,15 @@ import {VnfGroupTreeNode} from "../../models/vnfGroupTreeNode"; import {ModelInfo} from "../../models/modelInfo"; import {ServiceInstanceActions} from "../../models/serviceInstanceActions"; import Parameter = Constants.Parameter; +import {createPNFInstance} from "../../storeUtil/utils/pnf/pnf.actions"; +import {FeatureFlagsService, Features} from "../featureFlag/feature-flags.service"; @Injectable() export class DefaultDataGeneratorService { static controlsFieldsStatus = {}; public requiredFields = { VF: [InputType.LCP_REGION, InputType.TENANT, InputType.PLATFORM], + PNF: [InputType.PLATFORM], Network: [InputType.LCP_REGION, InputType.TENANT, InputType.PLATFORM], VL: [InputType.LCP_REGION, InputType.TENANT, InputType.PLATFORM], VFmodule: [], @@ -65,11 +67,10 @@ export class DefaultDataGeneratorService { break; } if (Utils.hasContents(inputs[key][Parameter.CONSTRAINTS]) - && ( inputs[key][Parameter.CONSTRAINTS].length > 0 )) { + && (inputs[key][Parameter.CONSTRAINTS].length > 0)) { let constraintsArray = inputs[key][Parameter.CONSTRAINTS]; this.addConstraintParameters(parameterList, constraintsArray, key, inputs, parameter); - } - else { + } else { parameterList.push(parameter); } @@ -97,8 +98,8 @@ export class DefaultDataGeneratorService { name: constraintsArray[i][Parameter.VALID_VALUES][j], isDefault: false }; - if ((Utils.hasContents(inputs[key][Parameter.DEFAULT]) ) - && (inputs[key][Parameter.DEFAULT] === constraintsArray[i][Parameter.VALID_VALUES][j] )) { + if ((Utils.hasContents(inputs[key][Parameter.DEFAULT])) + && (inputs[key][Parameter.DEFAULT] === constraintsArray[i][Parameter.VALID_VALUES][j])) { option = { name: constraintsArray[i][Parameter.VALID_VALUES][j], isDefault: true @@ -180,14 +181,13 @@ export class DefaultDataGeneratorService { return _.isEmpty(displayInputs) ? [] : this.getArbitraryInputs(displayInputs); } - updateNetworksOnFirstSet(serviceId: string, formServiceValues: any){ + updateNetworksOnFirstSet(serviceId: string, formServiceValues: any) { const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId]; if (serviceHierarchy && !_.isEmpty(serviceHierarchy.networks)) { for (let networkUUID in serviceHierarchy.networks) { const isEcompGeneratedNaming = this.getIsEcompGeneratedNaming(serviceHierarchy.networks[networkUUID]); let min_vnf_instances_greater_than_0 = serviceHierarchy.networks[networkUUID].properties['min_instances'] && serviceHierarchy.networks[networkUUID].properties['min_instances'] > 0; - if(min_vnf_instances_greater_than_0) - { + if (min_vnf_instances_greater_than_0) { this.createNetworkInstanceReduxIfNotExist( serviceId, this.generateNetworkData(serviceHierarchy, networkUUID, formServiceValues, isEcompGeneratedNaming) @@ -197,14 +197,13 @@ export class DefaultDataGeneratorService { } } - updateVnfGroupsOnFirstSet(serviceId: string, formServiceValues: any){ + updateVnfGroupsOnFirstSet(serviceId: string, formServiceValues: any) { const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId]; if (serviceHierarchy && !_.isEmpty(serviceHierarchy.vnfGroups)) { for (let vnfGroupUUID in serviceHierarchy.vnfGroups) { const isEcompGeneratedNaming = this.getIsEcompGeneratedNaming(serviceHierarchy.vnfGroups[vnfGroupUUID]); let min_vnf_group_instances_greater_than_0 = serviceHierarchy.vnfGroups[vnfGroupUUID].properties['min_instances'] && serviceHierarchy.vnfGroups[vnfGroupUUID].properties['min_instances'] > 0; - if(min_vnf_group_instances_greater_than_0) - { + if (min_vnf_group_instances_greater_than_0) { this.createVnfGroupInstanceReduxIfNotExist( serviceId, this.generateVnfGroupData(serviceHierarchy, vnfGroupUUID, formServiceValues, isEcompGeneratedNaming) @@ -214,9 +213,38 @@ export class DefaultDataGeneratorService { } } + updatePnfsOnFirstSet(serviceId: string, formServiceValues: any) { + const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId]; + if (serviceHierarchy && !_.isEmpty(serviceHierarchy.pnfs)) { + for (let pnfUUID in serviceHierarchy.pnfs) { + if (this.isMinInstancesGreaterThanZero(serviceHierarchy, pnfUUID) && this.isExtendedMacroPnfConfigOn()) { + this.createPNFInstanceReduxIfNotExist( + serviceId, + this.generatePNFData( + serviceHierarchy, + pnfUUID, + formServiceValues, + this.getIsEcompGeneratedNaming(serviceHierarchy.pnfs[pnfUUID]) + ) + ); + } + } + } + } + + isExtendedMacroPnfConfigOn(): boolean { + return FeatureFlagsService.getFlagState(Features.FLAG_EXTENDED_MACRO_PNF_CONFIG, this.store) + } + + isMinInstancesGreaterThanZero(serviceHierarchy, pnfUUID): boolean { + return serviceHierarchy.pnfs[pnfUUID].properties['min_instances'] + && serviceHierarchy.pnfs[pnfUUID].properties['min_instances'] > 0; + } + updateReduxOnFirstSet(serviceId: string, formServiceValues: any): void { this.updateNetworksOnFirstSet(serviceId, formServiceValues); this.updateVnfGroupsOnFirstSet(serviceId, formServiceValues); + this.updatePnfsOnFirstSet(serviceId, formServiceValues); const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId]; if (serviceHierarchy && !_.isEmpty(serviceHierarchy.vnfs)) { for (let vnfUUID in serviceHierarchy.vnfs) { @@ -245,8 +273,7 @@ export class DefaultDataGeneratorService { } let min_vnf_instances_greater_than_0 = serviceHierarchy.vnfs[vnfUUID].properties['min_instances'] && serviceHierarchy.vnfs[vnfUUID].properties['min_instances'] > 0; - if(min_vnf_instances_greater_than_0) - { + if (min_vnf_instances_greater_than_0) { this.createVNFInstanceReduxIfNotExist( serviceId, this.generateVNFData(serviceHierarchy, vnfUUID, formServiceValues, isEcompGeneratedNaming) @@ -256,7 +283,6 @@ export class DefaultDataGeneratorService { } } - private getIsEcompGeneratedNaming(vnfJson) { const ecompGeneratedNaming = vnfJson.properties.ecomp_generated_naming; return ecompGeneratedNaming === "true"; @@ -269,6 +295,13 @@ export class DefaultDataGeneratorService { } } + createPNFInstanceReduxIfNotExist(serviceId: string, pnfData: any): void { + if(!this.store.getState().service.serviceInstance[serviceId].pnfs[pnfData.modelInfo.modelCustomizationName]){ + this.store.dispatch(createPNFInstance(pnfData, pnfData.modelInfo.modelCustomizationName, serviceId)); + this.store.dispatch(changeInstanceCounter(pnfData.modelInfo.modelUniqueId, serviceId, 1, {data : {type : 'PNF'}})); + } + } + createNetworkInstanceReduxIfNotExist(serviceId: string, networkData: any): void { if(!this.store.getState().service.serviceInstance[serviceId].vnfs[networkData.modelInfo.modelCustomizationName]){ this.store.dispatch(createNetworkInstance(networkData, networkData.modelInfo.modelCustomizationName, serviceId)); @@ -378,6 +411,29 @@ export class DefaultDataGeneratorService { } } + generatePNFData(serviceHierarchy: any, pnfName: string, formValues: any, isEcompGeneratedNaming) { + return { + 'uuid' : serviceHierarchy.pnfs[pnfName].uuid, + 'isMissingData' :this.setIsMissingData(ServiceNodeTypes.PNF, [], isEcompGeneratedNaming), + 'productFamilyId': formValues.productFamilyId, + 'lcpCloudRegionId': null, + 'tenantId': null, + 'lineOfBusiness': null, + 'platformName': null, + 'modelInfo': { + 'modelType': 'PNF', + 'modelInvariantId': serviceHierarchy.pnfs[pnfName].invariantUuid, + 'modelVersionId': serviceHierarchy.pnfs[pnfName].uuid, + 'modelName': serviceHierarchy.pnfs[pnfName].name, + 'modelVersion': serviceHierarchy.pnfs[pnfName].version, + 'modelCustomizationId': serviceHierarchy.pnfs[pnfName].customizationUuid, + 'modelCustomizationName': serviceHierarchy.pnfs[pnfName].modelCustomizationName, + 'modelUniqueId' : serviceHierarchy.pnfs[pnfName].customizationUuid || serviceHierarchy.pnfs[pnfName].uuid, + }, + 'trackById': DefaultDataGeneratorService.createRandomTrackById(), + } + } + generateNetworkData(serviceHierarchy: any, networkName: string, formValues: any, isEcompGeneratedNaming) { return { 'uuid' : serviceHierarchy.network[networkName].uuid, -- cgit