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 <mateusz.goluchowski@nokia.com>
Change-Id: I5285ac2ab5e95665244ca29c6549249d9330b1ed
diff --git a/vid-webpack-master/src/app/shared/services/aaiService/aai.service.spec.ts b/vid-webpack-master/src/app/shared/services/aaiService/aai.service.spec.ts
index f563cbc..ad934cf 100644
--- a/vid-webpack-master/src/app/shared/services/aaiService/aai.service.spec.ts
+++ b/vid-webpack-master/src/app/shared/services/aaiService/aai.service.spec.ts
@@ -22,7 +22,7 @@
   let httpMock: HttpTestingController;
   let aaiService: AaiService;
   let mockFeatureFlagsService: FeatureFlagsService = mock(FeatureFlagsService);
-  let store : NgRedux<AppState>;
+  let store: NgRedux<AppState>;
 
   beforeAll(done => (async () => {
     TestBed.configureTestingModule({
@@ -42,8 +42,6 @@
 
   })().then(done).catch(done.fail));
 
-
-
   describe('#resolve tests', () => {
     test('aai service resolve should return the right object', () => {
       let serviceInstance = new ServiceInstance();
@@ -173,8 +171,37 @@
     });
   });
 
+  describe('#Pnf modelCustomizationName initialization tests', () => {
+
+    test('initializePnfModelCustomizationName should not reinitialize modelCustomizationName when it exists', () => {
+      let serviceHierarchy = {
+        "pnfs": {
+          "pnfInstance": {
+            "modelCustomizationName": "existingName"
+          }
+        }
+      }
+
+      aaiService.initializePnfModelCustomizationName(serviceHierarchy);
+
+      expect(serviceHierarchy.pnfs["pnfInstance"].modelCustomizationName).toBe("existingName");
+    });
+
+    test('initializePnfModelCustomizationName should initialize modelCustomizationName when it doesnt exist', () => {
+      let serviceHierarchy = {
+        "pnfs": {
+          "pnfInstance": {}
+        }
+      }
+
+      aaiService.initializePnfModelCustomizationName(serviceHierarchy);
+
+      expect((serviceHierarchy.pnfs["pnfInstance"] as any).modelCustomizationName).toBe("pnfInstance");
+    });
+  });
+
   function getTopology() {
-    return  {
+    return {
       "vnfs": {
         "2017-388_PASQUALE-vPE 0": {
           "vfModules": {},
@@ -398,7 +425,7 @@
     }
   }
 
-  function getMockActiveNetworks(){
+  function getMockActiveNetworks() {
     return [
       {
         networkInstanceName: "networkInstanceName",
diff --git a/vid-webpack-master/src/app/shared/services/aaiService/aai.service.ts b/vid-webpack-master/src/app/shared/services/aaiService/aai.service.ts
index adb7017..2829a89 100644
--- a/vid-webpack-master/src/app/shared/services/aaiService/aai.service.ts
+++ b/vid-webpack-master/src/app/shared/services/aaiService/aai.service.ts
@@ -58,6 +58,7 @@
 
   getServiceModelById = (serviceModelId: string): Observable<any> => {
     if (_.has(this.store.getState().service.serviceHierarchy, serviceModelId)) {
+      this.initializePnfModelCustomizationName(this.store.getState().service.serviceHierarchy[serviceModelId]);
       return of(<any> JSON.parse(JSON.stringify(this.store.getState().service.serviceHierarchy[serviceModelId])));
     }
     let pathQuery: string = Constants.Path.SERVICES_PATH + serviceModelId;
@@ -321,6 +322,15 @@
     return result;
   }
 
+  initializePnfModelCustomizationName(serviceHierarchy) : void {
+    let pnfs = serviceHierarchy.pnfs;
+    for (let pnf in pnfs) {
+      if (!pnfs[pnf].modelCustomizationName){
+        pnfs[pnf].modelCustomizationName = pnf;
+      }
+    }
+  }
+
   loadMockMembers(): any {
     return [
       {
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 56f49e7..4abffab 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 {DefaultDataGeneratorService} from './default.data.generator.service';
 import {ServiceNodeTypes} from "../../models/ServiceNodeTypes";
 import {VNFModel} from "../../models/vnfModel";
+import {AppState} from "../../store/reducers";
 
-class MockAppStore<T> {}
+class MockAppStore<T> {
+  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<AppState>;
   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 @@
           "modelVersion": "1"
         },
         volumeGroupName: "",
-        isMissingData : false,
+        isMissingData: false,
         trackById: Math.random().toString()
       }, vfModuleModel, vnfModuleUUID, false, [], "");
       expect(newVfModule.name).toEqual('&lt;Automatically Assigned&gt;');
@@ -147,7 +298,7 @@
           "modelVersion": "1"
         },
         volumeGroupName: "",
-        isMissingData : false,
+        isMissingData: false,
         trackById: Math.random().toString()
       }, vfModuleModel, vnfModuleUUID, true, [], "");
       expect(newVfModule.name).toEqual('&lt;Automatically Assigned&gt;');
@@ -159,7 +310,7 @@
     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 @@
         platformName: "platformName",
         lineOfBusiness: "lineOfBusiness",
         rollbackOnFailure: "rollbackOnFailure",
-        originalName : null,
+        originalName: null,
         vfModules: {},
         modelInfo: {
           "modelCustomizationName": "VF_vGeraldine 0",
@@ -182,7 +333,7 @@
         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 @@
     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 @@
         platformName: "platformName",
         lineOfBusiness: "lineOfBusiness",
         rollbackOnFailure: "rollbackOnFailure",
-        originalName : null,
+        originalName: null,
         vfModules: {},
         modelInfo: {
           "modelCustomizationName": "VF_vGeraldine 0",
@@ -213,12 +364,73 @@
         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 e00b259..1bf6984 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 {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 @@
           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 @@
                     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 @@
     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 @@
     }
   }
 
-  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 @@
     }
   }
 
+  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 @@
         }
 
         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 @@
     }
   }
 
-
   private getIsEcompGeneratedNaming(vnfJson) {
     const ecompGeneratedNaming = vnfJson.properties.ecomp_generated_naming;
     return ecompGeneratedNaming === "true";
@@ -269,6 +295,13 @@
     }
   }
 
+  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, <any> {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 @@
     }
   }
 
+  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,