Fix template generation for R2
[vfc/nfvo/driver/vnfm/svnfm.git] / nokiav2 / driver / src / main / java / org / onap / vfc / nfvo / driver / vnfm / svnfm / nokia / vnfm / LifecycleManager.java
index e835e35..7c8e231 100644 (file)
 package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm;
 
 
+import com.google.common.base.Joiner;
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
 import com.nokia.cbam.catalog.v1.model.CatalogAdapterVnfpackage;
-import com.nokia.cbam.lcm.v32.ApiException;
 import com.nokia.cbam.lcm.v32.model.*;
 import com.nokia.cbam.lcm.v32.model.ScaleDirection;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import javax.servlet.http.HttpServletResponse;
 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.IGrantManager;
 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VimInfoProvider;
 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.StoreLoader;
@@ -32,36 +37,42 @@ import org.onap.vnfmdriver.model.*;
 import org.onap.vnfmdriver.model.VimInfo;
 import org.onap.vnfmdriver.model.VnfInfo;
 import org.slf4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
 import org.yaml.snakeyaml.Yaml;
 
-import javax.servlet.http.HttpServletResponse;
-import java.util.*;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import static java.lang.Integer.parseInt;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.toList;
 
 import static com.google.common.base.Splitter.on;
 import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.transform;
 import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Ordering.natural;
+import static com.google.common.collect.Sets.newHashSet;
+import static com.nokia.cbam.lcm.v32.model.InstantiationState.INSTANTIATED;
+import static com.nokia.cbam.lcm.v32.model.OperationStatus.FINISHED;
 import static com.nokia.cbam.lcm.v32.model.OperationType.INSTANTIATE;
 import static com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum.*;
-import static java.lang.Integer.parseInt;
-import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.*;
 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions;
 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
-import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager.*;
+import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification.LifecycleChangeNotificationManager.NEWEST_OPERATIONS_FIRST;
 import static org.slf4j.LoggerFactory.getLogger;
 import static org.springframework.util.StringUtils.isEmpty;
 
 /**
  * Responsible for executing lifecycle operation on the VNF
  */
-@Component
 public class LifecycleManager {
     public static final String ONAP_CSAR_ID = "onapCsarId";
     public static final long OPERATION_STATUS_POLLING_INTERVAL_IN_MS = 5000L;
+    /**
+     * The key of the CBAM VNF extension for the identifier of the VNFM in ONAP
+     */
+    public static final String EXTERNAL_VNFM_ID = "externalVnfmId";
+    public static final String SCALE_OPERATION_NAME = "scale";
+    public static final String ETSI_CONFIG = "etsi_config";
+    public static final String PROPERTIES = "properties";
     private static Logger logger = getLogger(LifecycleManager.class);
     private final CatalogManager catalogManager;
     private final IGrantManager grantManager;
@@ -75,7 +86,6 @@ public class LifecycleManager {
      */
     private ExecutorService executorService = Executors.newCachedThreadPool();
 
-    @Autowired
     LifecycleManager(CatalogManager catalogManager, IGrantManager grantManager, CbamRestApiProvider restApiProvider, VimInfoProvider vimInfoProvider, JobManager jobManager, ILifecycleChangeNotificationManager notificationManager) {
         this.vimInfoProvider = vimInfoProvider;
         this.grantManager = grantManager;
@@ -85,10 +95,18 @@ public class LifecycleManager {
         this.catalogManager = catalogManager;
     }
 
+    /**
+     * @param vimId the VIM identifier
+     * @return the name of the region
+     */
     public static String getRegionName(String vimId) {
         return newArrayList(on(SEPARATOR).split(vimId)).get(1);
     }
 
+    /**
+     * @param vimId the VIM identifier
+     * @return the owner of the cloud
+     */
     public static String getCloudOwner(String vimId) {
         return newArrayList(on(SEPARATOR).split(vimId)).get(0);
     }
@@ -97,6 +115,78 @@ public class LifecycleManager {
         return find(NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions), op -> INSTANTIATE.equals(op.getOperationType()));
     }
 
+    public static String getVnfdIdFromModifyableAttributes(com.nokia.cbam.lcm.v32.model.VnfInfo vnf) {
+        return find(vnf.getExtensions(), p -> p.getName().equals(ONAP_CSAR_ID)).getValue().toString();
+    }
+
+    /**
+     * Create the VNF. It consists of the following steps
+     * <ul>
+     * <li>upload the VNF package to CBAM package (if not already there)</li>
+     * <li>create the VNF on CBAM</li>
+     * <li>modify attributes of the VNF (add onapCsarId field)</li>
+     * </ul>
+     * The rollback of the failed operation is not implemented
+     * <ul>
+     * <li>delete the VNF if error occurs before instantiation</li>
+     * <li>terminateVnf & delete VNF if error occurs after instantiation</li>
+     * </ul>
+     *
+     * @param vnfmId      the identifier of the VNFM
+     * @param csarId      the identifier of the VNF package
+     * @param vnfName     the name of the VNF
+     * @param description the description of the VNF
+     * @return the VNF creation result
+     */
+    public VnfCreationResult create(String vnfmId, String csarId, String vnfName, String description) {
+        logOperationInput("not yet specified", "creation", csarId);
+        try {
+            CatalogAdapterVnfpackage cbamPackage = catalogManager.preparePackageInCbam(vnfmId, csarId);
+            CreateVnfRequest vnfCreateRequest = new CreateVnfRequest();
+            vnfCreateRequest.setVnfdId(cbamPackage.getVnfdId());
+            vnfCreateRequest.setName(vnfName);
+            vnfCreateRequest.setDescription(description);
+            com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsPost(vnfCreateRequest, NOKIA_LCM_API_VERSION).blockingFirst();
+            addVnfdIdToVnfModifyableAttributeExtensions(vnfmId, vnfInfo.getId(), csarId);
+            return new VnfCreationResult(vnfInfo, cbamPackage.getVnfdId());
+        } catch (Exception e) {
+            throw buildFatalFailure(logger, "Unable to create the VNF", e);
+        }
+    }
+
+    private void logOperationInput(String vnfId, String operationName, Object payload) {
+        if (logger.isInfoEnabled()) {
+            logger.info("Starting {} operation on VNF with {} identifier with {} parameter", operationName, vnfId, new Gson().toJson(payload));
+        }
+    }
+
+    /**
+     * Instantiate the VNF
+     *
+     * @param vnfmId                        the identifier of the VNFM
+     * @param externalVirtualLinks          the external virtual links of the VNF
+     * @param httpResponse                  the HTTP response that corresponds to the VNF instantiation request
+     * @param additionalParameters          additional parameters
+     * @param vnfId                         the identifier of the VNF
+     * @param vnfmVnfdId                    the identifier of the VNF package in CBAM
+     * @param operationAdditionalParameters the additional parameters of the operation
+     * @param onapVnfdId                    the identifier of the VNFD in the VNFM
+     * @return the instantiation response
+     */
+    @SuppressWarnings("squid:S00107") //wrapping them into an object makes the code less readable
+    public VnfInstantiateResponse instantiate(String vnfmId, List<ExtVirtualLinkInfo> externalVirtualLinks, HttpServletResponse httpResponse, Object operationAdditionalParameters, AdditionalParameters additionalParameters, String vnfId, String onapVnfdId, String vnfmVnfdId) {
+        logOperationInput(vnfId, "instantiation", additionalParameters);
+        validateVimType(additionalParameters.getVimType());
+        VnfInstantiateResponse response = new VnfInstantiateResponse();
+        response.setVnfInstanceId(vnfId);
+        String vimId = getVimId(operationAdditionalParameters);
+        JobInfo spawnJob = scheduleExecution(vnfId, httpResponse, "instantiate", jobInfo ->
+                instantiateVnf(vnfmId, externalVirtualLinks, additionalParameters, onapVnfdId, vnfmVnfdId, vnfId, vimId, jobInfo)
+        );
+        response.setJobId(spawnJob.getJobId());
+        return response;
+    }
+
     /**
      * Instantiate (VF-C terminology) the VNF. It consists of the following steps
      * <ul>
@@ -112,7 +202,7 @@ public class LifecycleManager {
      * The rollback of the failed operation is not implemented
      * <ul>
      * <li>delete the VNF if error occurs before instantiation</li>
-     * <li>terminate & delete VNf if error occurs after instantiation</li>
+     * <li>terminateVnf & delete VNf if error occurs after instantiation</li>
      * </ul>
      *
      * @param vnfmId       the identifier of the VNFM
@@ -120,77 +210,86 @@ public class LifecycleManager {
      * @param httpResponse the HTTP response
      * @return the instantiation response
      */
-    public VnfInstantiateResponse instantiate(String vnfmId, VnfInstantiateRequest request, HttpServletResponse httpResponse) {
-        logger.info("Additional parameters for instantiation: " + new Gson().toJson(request.getAdditionalParam()));
-        AdditionalParams additionalParams = convertInstantiationAdditionalParams(request.getVnfPackageId(), request.getAdditionalParam());
-        validateVimType(additionalParams);
-        CatalogAdapterVnfpackage cbamPackage = catalogManager.preparePackageInCbam(vnfmId, request.getVnfPackageId());
-        try {
-            CreateVnfRequest vnfCreateRequest = new CreateVnfRequest();
-            vnfCreateRequest.setVnfdId(cbamPackage.getId());
-            vnfCreateRequest.setName(request.getVnfInstanceName());
-            vnfCreateRequest.setDescription(request.getVnfInstanceDescription());
-            com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsPost(vnfCreateRequest, NOKIA_LCM_API_VERSION);
-            VnfInstantiateResponse response = new VnfInstantiateResponse();
-            response.setVnfInstanceId(vnfInfo.getId());
-            //FIXME the vimId should be send during grant response (VFC-604)
-            String vimId = getVimId(request.getAdditionalParam());
-            addVnfdIdToVnfModifyableAttributeExtensions(vnfmId, vnfInfo.getId(), request.getVnfPackageId());
-            JobInfo spawnJob = scheduleExecution(vnfInfo.getId(), httpResponse, "instantiate", (jobInfo) -> {
-                String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, cbamPackage.getId());
-                GrantVNFResponseVim vim = grantManager.requestGrantForInstantiate(vnfmId, vnfInfo.getId(), vimId, request.getVnfPackageId(), additionalParams.getInstantiationLevel(), vnfdContent, jobInfo.getJobId());
-                if (vim.getVimId() == null) {
-                    fatalFailure(logger, "VF-C did not send VIM identifier in grant response");
-                }
-                VimInfo vimInfo = vimInfoProvider.getVimInfo(vim.getVimId());
-                InstantiateVnfRequest instantiationRequest = new InstantiateVnfRequest();
-                addExernalLinksToRequest(request.getExtVirtualLink(), additionalParams, instantiationRequest, vimId);
-                switch (additionalParams.getVimType()) {
-                    case OPENSTACK_V2_INFO:
-                        instantiationRequest.getVims().add(buildOpenStackV2INFO(vimId, vim, vimInfo));
-                        break;
-                    case OPENSTACK_V3_INFO:
-                        instantiationRequest.getVims().add(buildOpenStackV3INFO(vimId, additionalParams, vim, vimInfo));
-                        break;
-                    case VMWARE_VCLOUD_INFO:
-                        instantiationRequest.getVims().add(buildVcloudInfo(vimId, vim, vimInfo));
-                        break;
-                }
-                instantiationRequest.setFlavourId(getFlavorId(vnfdContent));
-                instantiationRequest.setComputeResourceFlavours(additionalParams.getComputeResourceFlavours());
-                instantiationRequest.setGrantlessMode(true);
-                instantiationRequest.setInstantiationLevelId(additionalParams.getInstantiationLevel());
-                instantiationRequest.setSoftwareImages(additionalParams.getSoftwareImages());
-                instantiationRequest.setZones(additionalParams.getZones());
-                instantiationRequest.setExtManagedVirtualLinks(additionalParams.getExtManagedVirtualLinks());
-                for (ExtVirtualLinkData extVirtualLinkData : additionalParams.getExtVirtualLinks()) {
-                    instantiationRequest.addExtVirtualLinksItem(extVirtualLinkData);
-                }
-                JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject();
-                if (additionalParams.getAdditionalParams() != null && !isEmpty(additionalParams.getAdditionalParams().toString())) {
-                    for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(additionalParams.getAdditionalParams()).getAsJsonObject().entrySet()) {
-                        root.add(item.getKey(), item.getValue());
-                    }
+    public VnfInstantiateResponse createAndInstantiate(String vnfmId, VnfInstantiateRequest request, HttpServletResponse httpResponse) {
+        AdditionalParameters additionalParameters = convertInstantiationAdditionalParams(request.getVnfPackageId(), request.getAdditionalParam());
+        validateVimType(additionalParameters.getVimType());
+        VnfCreationResult creationResult = create(vnfmId, request.getVnfDescriptorId(), request.getVnfInstanceName(), request.getVnfInstanceDescription());
+        return instantiate(vnfmId, request.getExtVirtualLink(), httpResponse, request.getAdditionalParam(), additionalParameters, creationResult.vnfInfo.getId(), request.getVnfPackageId(), creationResult.vnfdId);
+    }
+
+    @SuppressWarnings("squid:S00107") //wrapping them into an object makes the code less readable
+    private void instantiateVnf(String vnfmId, List<ExtVirtualLinkInfo> extVirtualLinkInfos, AdditionalParameters additionalParameters, String onapVnfdId, String vnfmVnfdId, String vnfId, String vimId, JobInfo jobInfo) {
+        String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, vnfmVnfdId);
+        addSpecifiedExtensions(vnfmId, vnfId, additionalParameters);
+        GrantVNFResponseVim vim = grantManager.requestGrantForInstantiate(vnfmId, vnfId, vimId, onapVnfdId, additionalParameters.getInstantiationLevel(), vnfdContent, jobInfo.getJobId());
+        handleBackwardIncompatibleApiChangesInVfc(vim);
+        VimInfo vimInfo = vimInfoProvider.getVimInfo(vim.getVimId());
+        InstantiateVnfRequest instantiationRequest = new InstantiateVnfRequest();
+        addExternalLinksToRequest(extVirtualLinkInfos, additionalParameters, instantiationRequest, vimId);
+        instantiationRequest.getVims().add(addVim(additionalParameters, vimId, vim, vimInfo));
+        instantiationRequest.setFlavourId(getFlavorId(vnfdContent));
+        instantiationRequest.setComputeResourceFlavours(additionalParameters.getComputeResourceFlavours());
+        instantiationRequest.setGrantlessMode(true);
+        instantiationRequest.setInstantiationLevelId(additionalParameters.getInstantiationLevel());
+        instantiationRequest.setSoftwareImages(additionalParameters.getSoftwareImages());
+        instantiationRequest.setZones(additionalParameters.getZones());
+        instantiationRequest.setExtManagedVirtualLinks(additionalParameters.getExtManagedVirtualLinks());
+        for (ExtVirtualLinkData extVirtualLinkData : additionalParameters.getExtVirtualLinks()) {
+            instantiationRequest.addExtVirtualLinksItem(extVirtualLinkData);
+        }
+        JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject();
+        if (additionalParameters.getAdditionalParams() != null) {
+            for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(additionalParameters.getAdditionalParams()).getAsJsonObject().entrySet()) {
+                root.add(item.getKey(), item.getValue());
+            }
+        } else {
+            logger.warn("No additional parameters were specified for the operation");
+        }
+        instantiationRequest.setAdditionalParams(root);
+        OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdInstantiatePost(vnfId, instantiationRequest, NOKIA_LCM_API_VERSION).blockingFirst();
+        waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
+    }
+
+    private void handleBackwardIncompatibleApiChangesInVfc(GrantVNFResponseVim vim) {
+        if (vim.getVimId() == null) {
+            if (vim.getVimid() == null) {
+                throw buildFatalFailure(logger, "VF-C did not send VIM identifier in grant response");
+            } else {
+                vim.setVimId(vim.getVimid());
+            }
+        }
+        if (vim.getAccessInfo() == null) {
+            if (vim.getAccessinfo() == null) {
+                throw buildFatalFailure(logger, "VF-C did not send access info in grant response");
+            } else {
+                vim.setAccessInfo(vim.getAccessinfo());
+            }
+        }
+    }
+
+    private com.nokia.cbam.lcm.v32.model.VimInfo addVim(AdditionalParameters additionalParameters, String vimId, GrantVNFResponseVim vim, VimInfo vimInfo) {
+        if (additionalParameters.getVimType() == OPENSTACK_V2_INFO) {
+            return buildOpenStackV2INFO(vimId, vim, vimInfo);
+
+        } else if (additionalParameters.getVimType() == OPENSTACK_V3_INFO) {
+            if (isEmpty(vimInfo.getDomain())) {
+                if (isEmpty(additionalParameters.getDomain())) {
+                    throw buildFatalFailure(logger, "The cloud did not supply the cloud domain (Amsterdam release) and was not supplied as additional data");
+                } else {
+                    logger.warn("Setting domain from additional parameters");
+                    vimInfo.setDomain(additionalParameters.getDomain());
                 }
-                instantiationRequest.setAdditionalParams(root);
-                OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdInstantiatePost(vnfInfo.getId(), instantiationRequest, NOKIA_LCM_API_VERSION);
-                waitForOperationToFinish(vnfmId, vnfInfo.getId(), operationExecution.getId(), jobInfo.getJobId());
-            });
-            response.setJobId(spawnJob.getJobId());
-            return response;
-        } catch (Exception e) {
-            throw fatalFailure(logger, "Unable to create the VNF", e);
+            }
+            return buildOpenStackV3INFO(vimId, vim, vimInfo);
+        } else {
+            //OTHER VIM TYPE is not possible
+            return buildVcloudInfo(vimId, vimInfo);
         }
     }
 
-    private void validateVimType(AdditionalParams additionalParams) {
-        switch (additionalParams.getVimType()) {
-            case OPENSTACK_V2_INFO:
-            case OPENSTACK_V3_INFO:
-            case VMWARE_VCLOUD_INFO:
-                break;
-            default:
-                throw fatalFailure(logger, "Only " + OPENSTACK_V2_INFO + ", " + OPENSTACK_V3_INFO + " and " + VMWARE_VCLOUD_INFO + " is the supported VIM types");
+    private void validateVimType(com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum vimType) {
+        if (com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum.OTHER_VIM_INFO.equals(vimType)) {
+            throw buildFatalFailure(logger, "Only " + OPENSTACK_V2_INFO + ", " + OPENSTACK_V3_INFO + " and " + VMWARE_VCLOUD_INFO + " is the supported VIM types");
         }
     }
 
@@ -198,30 +297,48 @@ public class LifecycleManager {
         return childElement(new Gson().toJsonTree(additionalParams).getAsJsonObject(), "vimId").getAsString();
     }
 
-    private AdditionalParams convertInstantiationAdditionalParams(String csarId, Object additionalParams) {
-        JsonObject vnfParameters = child(child(new Gson().toJsonTree(additionalParams).getAsJsonObject(), "inputs"), "vnfs");
-        if (!vnfParameters.has(csarId)) {
-            throw fatalFailure(logger, "The additional parameter section does not contain setting for VNF with " + csarId + " CSAR id");
+    private AdditionalParameters convertInstantiationAdditionalParams(String csarId, Object additionalParams) {
+        JsonObject root = new Gson().toJsonTree(additionalParams).getAsJsonObject();
+        if (root.has(PROPERTIES)) {
+            JsonObject properties = new JsonParser().parse(root.get(PROPERTIES).getAsString()).getAsJsonObject();
+            if (properties.has(ETSI_CONFIG)) {
+                JsonElement etsiConfig = properties.get(ETSI_CONFIG);
+                return new Gson().fromJson(etsiConfig.getAsString(), AdditionalParameters.class);
+            } else {
+                logger.info("The instantiation input for VNF with {} CSAR id does not have an " + ETSI_CONFIG + " section", csarId);
+            }
+        } else {
+            logger.info("The instantiation input for VNF with {} CSAR id does not have a properties section", csarId);
         }
-        JsonElement additionalParamsForVnf = vnfParameters.get(csarId);
-        return new Gson().fromJson(additionalParamsForVnf, AdditionalParams.class);
+        JsonObject inputs = child(root, "inputs");
+        if (!inputs.has(csarId)) {
+            return new Gson().fromJson(catalogManager.getEtsiConfiguration(csarId), AdditionalParameters.class);
+        }
+        JsonElement additionalParamsForVnf = new JsonParser().parse(inputs.get(csarId).getAsString());
+        return new Gson().fromJson(additionalParamsForVnf, AdditionalParameters.class);
     }
 
     private String getFlavorId(String vnfdContent) {
         JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject();
         JsonObject capabilities = child(child(child(root, "topology_template"), "substitution_mappings"), "capabilities");
-        JsonObject deploymentFlavorProperties = child(child(capabilities, "deployment_flavour"), "properties");
+        JsonObject deploymentFlavorProperties = child(child(capabilities, "deployment_flavour"), PROPERTIES);
         return childElement(deploymentFlavorProperties, "flavour_id").getAsString();
     }
 
-    private Set<String> getAcceptableOperationParameters(String vnfdContent, String categroryOfOperation, String operationName) {
+    private Set<Map.Entry<String, JsonElement>> getAcceptableOperationParameters(String vnfdContent, String operationName) {
         JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject();
         JsonObject interfaces = child(child(child(root, "topology_template"), "substitution_mappings"), "interfaces");
-        JsonObject additionalParameters = child(child(child(child(interfaces, categroryOfOperation), operationName), "inputs"), "additional_parameters");
-        return additionalParameters.keySet();
+        List<List<Map.Entry<String, JsonElement>>> operations = interfaces.entrySet().stream().map(m -> m.getValue().getAsJsonObject().entrySet().stream().collect(toList())).collect(toList());
+        for (Map.Entry<String, JsonElement> operation : operations.stream().flatMap(List::stream).collect(toList())) {
+            if (operation.getKey().equals(operationName)) {
+                return child(child(operation.getValue().getAsJsonObject(), "inputs"), "additional_parameters").entrySet();
+            }
+        }
+
+        throw buildFatalFailure(logger, "Unable to find operation named " + operationName);
     }
 
-    private void addExernalLinksToRequest(List<ExtVirtualLinkInfo> extVirtualLinks, AdditionalParams additionalParams, InstantiateVnfRequest instantiationRequest, String vimId) {
+    private void addExternalLinksToRequest(List<ExtVirtualLinkInfo> extVirtualLinks, AdditionalParameters additionalParameters, InstantiateVnfRequest instantiationRequest, String vimId) {
         for (ExtVirtualLinkInfo extVirtualLink : extVirtualLinks) {
             ExtVirtualLinkData cbamExternalVirtualLink = new ExtVirtualLinkData();
             cbamExternalVirtualLink.setVimId(vimId);
@@ -230,7 +347,7 @@ public class LifecycleManager {
             cbamExternalVirtualLink.setExtVirtualLinkId(extVirtualLink.getVlInstanceId());
             cbamExternalVirtualLink.getExtCps().add(ecp);
             ecp.setCpdId(extVirtualLink.getCpdId());
-            List<NetworkAddress> addresses = additionalParams.getExternalConnectionPointAddresses().get(extVirtualLink.getCpdId());
+            List<NetworkAddress> addresses = additionalParameters.getExternalConnectionPointAddresses().get(extVirtualLink.getCpdId());
             ecp.setAddresses(addresses);
             instantiationRequest.addExtVirtualLinksItem(cbamExternalVirtualLink);
         }
@@ -238,47 +355,50 @@ public class LifecycleManager {
 
     private void addVnfdIdToVnfModifyableAttributeExtensions(String vnfmId, String vnfId, String onapCsarId) {
         ModifyVnfInfoRequest request = new ModifyVnfInfoRequest();
+        request.setExtensions(new ArrayList<>());
         VnfProperty onapCsarIdProperty = new VnfProperty();
         onapCsarIdProperty.setName(ONAP_CSAR_ID);
         onapCsarIdProperty.setValue(onapCsarId);
-        request.setExtensions(new ArrayList<>());
         request.getExtensions().add(onapCsarIdProperty);
         VnfProperty externalVnfmIdProperty = new VnfProperty();
         externalVnfmIdProperty.setName(EXTERNAL_VNFM_ID);
         externalVnfmIdProperty.setValue(vnfmId);
         request.getExtensions().add(externalVnfmIdProperty);
-        request.setVnfConfigurableProperties(null);
+        executeModifyVnfInfo(vnfmId, vnfId, request);
+    }
+
+    private void executeModifyVnfInfo(String vnfmId, String vnfId, ModifyVnfInfoRequest request) {
         try {
-            OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdPatch(vnfId, request, NOKIA_LCM_API_VERSION);
-            waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId(), NOKIA_LCM_API_VERSION);
-        } catch (ApiException e) {
-            throw fatalFailure(logger, "Unable to set the " + ONAP_CSAR_ID + " property on the VNF", e);
+            OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdPatch(vnfId, request, NOKIA_LCM_API_VERSION).blockingFirst();
+            waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
+        } catch (Exception e) {
+            String properties = Joiner.on(",").join(natural().sortedCopy(transform(request.getExtensions(), VnfProperty::getName)));
+            throw buildFatalFailure(logger, "Unable to set the " + properties + " properties on the VNF with " + vnfId + " identifier", e);
         }
     }
 
-    private OPENSTACKV3INFO buildOpenStackV3INFO(String vimId, AdditionalParams additionalParams, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) {
+    private void addSpecifiedExtensions(String vnfmId, String vnfId, AdditionalParameters additionalParameters) {
+        if (!additionalParameters.getExtensions().isEmpty()) {
+            ModifyVnfInfoRequest request = new ModifyVnfInfoRequest();
+            request.setExtensions(new ArrayList<>());
+            request.getExtensions().addAll(additionalParameters.getExtensions());
+            executeModifyVnfInfo(vnfmId, vnfId, request);
+        } else {
+            logger.info("No extensions specified for VNF with {} identifier", vnfId);
+        }
+    }
+
+    private OPENSTACKV3INFO buildOpenStackV3INFO(String vimId, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) {
         OPENSTACKV3INFO openstackv3INFO = new OPENSTACKV3INFO();
         openstackv3INFO.setVimInfoType(OPENSTACK_V3_INFO);
         OpenStackAccessInfoV3 accessInfov3 = new OpenStackAccessInfoV3();
         openstackv3INFO.accessInfo(accessInfov3);
         accessInfov3.setPassword(vimInfo.getPassword());
-        accessInfov3.setDomain(additionalParams.getDomain());
+        accessInfov3.setDomain(vimInfo.getDomain());
         accessInfov3.setProject(vim.getAccessInfo().getTenant());
         accessInfov3.setRegion(getRegionName(vimId));
         accessInfov3.setUsername(vimInfo.getUserName());
-        EndpointInfo interfaceInfoV3 = new EndpointInfo();
-        interfaceInfoV3.setEndpoint(vimInfo.getUrl());
-        if (!isEmpty(vimInfo.getSslInsecure())) {
-            interfaceInfoV3.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure()));
-            interfaceInfoV3.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure()));
-        }
-        if (!interfaceInfoV3.isSkipCertificateVerification()) {
-            interfaceInfoV3.setTrustedCertificates(new ArrayList<>());
-            for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) {
-                interfaceInfoV3.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8));
-            }
-        }
-        openstackv3INFO.setInterfaceInfo(interfaceInfoV3);
+        openstackv3INFO.setInterfaceInfo(getEndpointInfo(vimInfo));
         openstackv3INFO.setId(vimId);
         return openstackv3INFO;
     }
@@ -292,10 +412,20 @@ public class LifecycleManager {
         accessInfo.setTenant(vim.getAccessInfo().getTenant());
         accessInfo.setUsername(vimInfo.getUserName());
         accessInfo.setRegion(getRegionName(vimId));
+        EndpointInfo interfaceEndpoint = getEndpointInfo(vimInfo);
+        openstackv2INFO.setInterfaceInfo(interfaceEndpoint);
+        openstackv2INFO.setId(vimId);
+        return openstackv2INFO;
+    }
+
+    private EndpointInfo getEndpointInfo(VimInfo vimInfo) {
         EndpointInfo interfaceEndpoint = new EndpointInfo();
         if (!isEmpty(vimInfo.getSslInsecure())) {
             interfaceEndpoint.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure()));
             interfaceEndpoint.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure()));
+        } else {
+            interfaceEndpoint.setSkipCertificateHostnameCheck(true);
+            interfaceEndpoint.setSkipCertificateVerification(true);
         }
         interfaceEndpoint.setEndpoint(vimInfo.getUrl());
         if (!interfaceEndpoint.isSkipCertificateVerification()) {
@@ -304,12 +434,10 @@ public class LifecycleManager {
                 interfaceEndpoint.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8));
             }
         }
-        openstackv2INFO.setInterfaceInfo(interfaceEndpoint);
-        openstackv2INFO.setId(vimId);
-        return openstackv2INFO;
+        return interfaceEndpoint;
     }
 
-    private VMWAREVCLOUDINFO buildVcloudInfo(String vimId, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) {
+    private VMWAREVCLOUDINFO buildVcloudInfo(String vimId, org.onap.vnfmdriver.model.VimInfo vimInfo) {
         VMWAREVCLOUDINFO vcloudInfo = new VMWAREVCLOUDINFO();
         vcloudInfo.setVimInfoType(VMWARE_VCLOUD_INFO);
         VCloudAccessInfo accessInfo = new VCloudAccessInfo();
@@ -317,19 +445,7 @@ public class LifecycleManager {
         accessInfo.setPassword(vimInfo.getPassword());
         accessInfo.setUsername(vimInfo.getUserName());
         accessInfo.setOrganization(getRegionName(vimId));
-        EndpointInfo interfaceEndpoint = new EndpointInfo();
-        if (!isEmpty(vimInfo.getSslInsecure())) {
-            interfaceEndpoint.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure()));
-            interfaceEndpoint.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure()));
-        }
-        interfaceEndpoint.setEndpoint(vimInfo.getUrl());
-        if (!interfaceEndpoint.isSkipCertificateVerification()) {
-            interfaceEndpoint.setTrustedCertificates(new ArrayList<>());
-            for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) {
-                interfaceEndpoint.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8));
-            }
-        }
-        vcloudInfo.setInterfaceInfo(interfaceEndpoint);
+        vcloudInfo.setInterfaceInfo(getEndpointInfo(vimInfo));
         vcloudInfo.setId(vimId);
         return vcloudInfo;
     }
@@ -348,56 +464,85 @@ public class LifecycleManager {
      * @param httpResponse the HTTP response
      * @return the job for polling the progress of the termination
      */
-    public JobInfo terminateVnf(String vnfmId, String vnfId, VnfTerminateRequest request, HttpServletResponse httpResponse) {
-        return scheduleExecution(vnfId, httpResponse, "terminate", (jobInfo) -> {
-            TerminateVnfRequest cbamRequest = new TerminateVnfRequest();
-            cbamRequest.setAdditionalParams(jobInfo);
-            if (request.getTerminationType() == null) {
-                cbamRequest.setTerminationType(TerminationType.FORCEFUL);
+    public JobInfo terminateAndDelete(String vnfmId, String vnfId, VnfTerminateRequest request, HttpServletResponse httpResponse) {
+        logOperationInput(vnfId, "termination", request);
+        return scheduleExecution(vnfId, httpResponse, "terminateVnf", jobInfo -> {
+            terminateVnf(vnfmId, vnfId, request, jobInfo);
+            deleteVnf(vnfmId, vnfId);
+        });
+    }
+
+    /**
+     * Terminates the VNF
+     * <ul>
+     * <li>fails if the VNF does not exist</li>
+     * <li>terminates if instantiated</li>
+     * <li>deletes the VNF</li>
+     * </ul>
+     *
+     * @param vnfmId       the identifier of the VNFM
+     * @param vnfId        the identifier of the VNF
+     * @param request      the termination request
+     * @param httpResponse the HTTP response
+     * @return the job for polling the progress of the termination
+     */
+    public JobInfo terminate(String vnfmId, String vnfId, VnfTerminateRequest request, HttpServletResponse httpResponse) {
+        logOperationInput(vnfId, "termination", request);
+        return scheduleExecution(vnfId, httpResponse, "terminate", jobInfo -> terminateVnf(vnfmId, vnfId, request, jobInfo));
+    }
+
+    private void terminateVnf(String vnfmId, String vnfId, VnfTerminateRequest request, JobInfo jobInfo) {
+        TerminateVnfRequest cbamRequest = new TerminateVnfRequest();
+        setState(request, cbamRequest);
+        cbamRequest.setAdditionalParams(new Gson().toJsonTree(jobInfo).getAsJsonObject());
+        com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
+        if (vnf.getInstantiationState() == INSTANTIATED) {
+            String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf);
+            grantManager.requestGrantForTerminate(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), vnf, jobInfo.getJobId());
+            OperationExecution terminationOperation = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdTerminatePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION).blockingFirst();
+            OperationExecution finishedOperation = waitForOperationToFinish(vnfmId, vnfId, terminationOperation.getId());
+            if (finishedOperation.getStatus() == FINISHED) {
+                notificationManager.waitForTerminationToBeProcessed(finishedOperation.getId());
             } else {
-                if (request.getTerminationType().equals(VnfTerminationType.GRACEFUL)) {
-                    cbamRequest.setTerminationType(TerminationType.GRACEFUL);
-                    cbamRequest.setGracefulTerminationTimeout(parseInt(request.getGracefulTerminationTimeout()));
-                } else {
-                    cbamRequest.setTerminationType(TerminationType.FORCEFUL);
-                }
+                throw buildFatalFailure(logger, "Unable to terminate VNF the operation did not finish with success");
             }
-            com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
-            switch (vnf.getInstantiationState()) {
-                case INSTANTIATED:
-                    String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf);
-                    grantManager.requestGrantForTerminate(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), vnf, jobInfo.getJobId());
-                    OperationExecution terminationOperation = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdTerminatePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION);
-                    OperationExecution finishedOperation = waitForOperationToFinish(vnfmId, vnfId, terminationOperation.getId(), jobInfo.getJobId());
-                    switch (finishedOperation.getStatus()) {
-                        case FINISHED:
-                            notificationManager.waitForTerminationToBeProcessed(finishedOperation.getId());
-                            logger.info("Deleting VNF with " + vnfId);
-                            cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION);
-                            logger.info("VNF with " + vnfId + " has been deleted");
-                            break;
-                        default:
-                            logger.error("Unable to terminate VNF the operation did not finish with success");
-                    }
-                    break;
-                case NOT_INSTANTIATED:
-                    cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION);
-                    break;
+        } else {
+            logger.warn("The VNF with {} identifier is not instantiated no termination is required", vnfId);
+        }
+    }
+
+    private void setState(VnfTerminateRequest request, TerminateVnfRequest cbamRequest) {
+        if (request.getTerminationType() == null) {
+            cbamRequest.setTerminationType(TerminationType.FORCEFUL);
+        } else {
+            if (request.getTerminationType().equals(VnfTerminationType.GRACEFUL)) {
+                cbamRequest.setTerminationType(TerminationType.GRACEFUL);
+                cbamRequest.setGracefulTerminationTimeout(parseInt(request.getGracefulTerminationTimeout()));
+            } else {
+                cbamRequest.setTerminationType(TerminationType.FORCEFUL);
             }
-        });
+        }
+    }
+
+    /**
+     * Delete the VNF
+     *
+     * @param vnfmId the identifier of the VNFM
+     * @param vnfId  the identifier fo the VNF
+     */
+    public void deleteVnf(String vnfmId, String vnfId) {
+        logger.info("Deleting VNF with {} identifier", vnfId);
+        cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION).blockingFirst(null);
+        logger.info("The VNF with {} identifier has been deleted", vnfId);
     }
 
-    private String getVimIdFromInstantiationRequest(String vnfmId, com.nokia.cbam.lcm.v32.model.VnfInfo vnf) throws ApiException {
+    private String getVimIdFromInstantiationRequest(String vnfmId, com.nokia.cbam.lcm.v32.model.VnfInfo vnf) {
         OperationExecution lastInstantiation = findLastInstantiation(vnf.getOperationExecutions());
-        Object operationParameters = cbamRestApiProvider.getCbamOperationExecutionApi(vnfmId).operationExecutionsOperationExecutionIdOperationParamsGet(lastInstantiation.getId(), NOKIA_LCM_API_VERSION);
+        Object operationParameters = cbamRestApiProvider.getCbamOperationExecutionApi(vnfmId).operationExecutionsOperationExecutionIdOperationParamsGet(lastInstantiation.getId(), NOKIA_LCM_API_VERSION).blockingFirst();
         JsonObject root = new Gson().toJsonTree(operationParameters).getAsJsonObject();
         return childElement(childElement(root, "vims").getAsJsonArray().get(0).getAsJsonObject(), "id").getAsString();
     }
 
-    private String getVnfdIdFromModifyableAttributes(com.nokia.cbam.lcm.v32.model.VnfInfo vnf) {
-        return find(vnf.getExtensions(), p -> p.getName().equals(ONAP_CSAR_ID)).getValue().toString();
-    }
-
     /**
      * @param vnfmId the identifier of the VNFM
      * @param vnfId  the identifier of the VNF
@@ -405,24 +550,28 @@ public class LifecycleManager {
      */
     public VnfInfo queryVnf(String vnfmId, String vnfId) {
         try {
-            com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
-            VnfInfo vnfInfo = new VnfInfo();
-            vnfInfo.setVersion(cbamVnfInfo.getVnfSoftwareVersion());
-            vnfInfo.setVnfInstanceId(vnfId);
-            String onapCsarId = getVnfdIdFromModifyableAttributes(cbamVnfInfo);
-            vnfInfo.setVnfdId(onapCsarId);
-            vnfInfo.setVnfPackageId(onapCsarId);
-            vnfInfo.setVnfInstanceDescription(cbamVnfInfo.getDescription());
-            vnfInfo.setVnfInstanceName(cbamVnfInfo.getName());
-            vnfInfo.setVnfProvider(cbamVnfInfo.getVnfProvider());
-            vnfInfo.setVnfStatus("ACTIVE");
-            vnfInfo.setVnfType("Kuku");
-            return vnfInfo;
-        } catch (ApiException e) {
-            throw fatalFailure(logger, "Unable to query VNF (" + vnfId + ")", e);
+            com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
+            return convertVnfInfo(vnfId, cbamVnfInfo);
+        } catch (Exception e) {
+            throw buildFatalFailure(logger, "Unable to query VNF (" + vnfId + ")", e);
         }
     }
 
+    private VnfInfo convertVnfInfo(String vnfId, com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo) {
+        VnfInfo vnfInfo = new VnfInfo();
+        vnfInfo.setVersion(cbamVnfInfo.getVnfSoftwareVersion());
+        vnfInfo.setVnfInstanceId(vnfId);
+        String onapCsarId = getVnfdIdFromModifyableAttributes(cbamVnfInfo);
+        vnfInfo.setVnfdId(onapCsarId);
+        vnfInfo.setVnfPackageId(onapCsarId);
+        vnfInfo.setVnfInstanceDescription(cbamVnfInfo.getDescription());
+        vnfInfo.setVnfInstanceName(cbamVnfInfo.getName());
+        vnfInfo.setVnfProvider(cbamVnfInfo.getVnfProvider());
+        vnfInfo.setVnfStatus("ACTIVE");
+        vnfInfo.setVnfType("Kuku");
+        return vnfInfo;
+    }
+
     private ScaleDirection convert(org.onap.vnfmdriver.model.ScaleDirection direction) {
         if (org.onap.vnfmdriver.model.ScaleDirection.IN.equals(direction)) {
             return ScaleDirection.IN;
@@ -441,31 +590,47 @@ public class LifecycleManager {
      * @return the job for tracking the scale
      */
     public JobInfo scaleVnf(String vnfmId, String vnfId, VnfScaleRequest request, HttpServletResponse httpResponse) {
-        logger.info("Scale VNF " + vnfId + " " + new Gson().toJson(request));
-        return scheduleExecution(vnfId, httpResponse, "scale", (jobInfo) -> {
+        logOperationInput(vnfId, SCALE_OPERATION_NAME, request);
+        return scheduleExecution(vnfId, httpResponse, SCALE_OPERATION_NAME, jobInfo -> {
             ScaleVnfRequest cbamRequest = new ScaleVnfRequest();
             cbamRequest.setAspectId(request.getAspectId());
             cbamRequest.setNumberOfSteps(Integer.valueOf(request.getNumberOfSteps()));
             cbamRequest.setType(convert(request.getType()));
-            com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
+            com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
             JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject();
-            com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
+            com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
             String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, cbamVnfInfo.getVnfdId());
-            Set<String> acceptableOperationParameters = getAcceptableOperationParameters(vnfdContent, "Basic", "scale");
-            if (request.getAdditionalParam() != null) {
-                for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(request.getAdditionalParam()).getAsJsonObject().entrySet()) {
-                    if (acceptableOperationParameters.contains(item.getKey())) {
-                        root.add(item.getKey(), item.getValue());
-                    }
-                }
-            }
+            Set<Map.Entry<String, JsonElement>> acceptableOperationParameters = getAcceptableOperationParameters(vnfdContent, SCALE_OPERATION_NAME);
+            buildAdditionalParameters(request, root, acceptableOperationParameters);
             cbamRequest.setAdditionalParams(root);
             grantManager.requestGrantForScale(vnfmId, vnfId, getVimIdFromInstantiationRequest(vnfmId, vnf), getVnfdIdFromModifyableAttributes(vnf), request, jobInfo.getJobId());
-            OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdScalePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION);
-            waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId(), jobInfo.getJobId());
+            OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdScalePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION).blockingFirst();
+            waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
         });
     }
 
+    private void buildAdditionalParameters(VnfScaleRequest request, JsonObject root, Set<Map.Entry<String, JsonElement>> acceptableOperationParameters) {
+        if (request.getAdditionalParam() != null) {
+            for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(request.getAdditionalParam()).getAsJsonObject().entrySet()) {
+                if (isParameterAccepted(acceptableOperationParameters, item)) {
+                    root.add(item.getKey(), item.getValue());
+                }
+            }
+        } else {
+            logger.warn("No additional parameters were passed for scaling");
+        }
+    }
+
+    private boolean isParameterAccepted(Set<Map.Entry<String, JsonElement>> acceptableOperationParameters, Map.Entry<String, JsonElement> item) {
+        boolean found = false;
+        for (Map.Entry<String, JsonElement> acceptableOperationParameter : acceptableOperationParameters) {
+            if (acceptableOperationParameter.getKey().equals(item.getKey())) {
+                found = true;
+            }
+        }
+        return found;
+    }
+
     /**
      * Heal the VNF
      *
@@ -473,21 +638,34 @@ public class LifecycleManager {
      * @param vnfId        the identifier of the VNF
      * @param request      the heal request
      * @param httpResponse the HTTP response
+     * @param vnfcId       the identifer of thr VNFC to be healed
      * @return the job for tracking the heal
      */
-    public JobInfo healVnf(String vnfmId, String vnfId, VnfHealRequest request, HttpServletResponse httpResponse) {
-        return scheduleExecution(vnfId, httpResponse, "heal", (job) -> {
+    public JobInfo healVnf(String vnfmId, String vnfId, VnfHealRequest request, Optional<String> vnfcId, HttpServletResponse httpResponse) {
+        logOperationInput(vnfId, "heal", request);
+        return scheduleExecution(vnfId, httpResponse, "heal", job -> {
             HealVnfRequest cbamHealRequest = new HealVnfRequest();
             Map<String, String> additionalParams = new HashMap<>();
             additionalParams.put("vmName", request.getAffectedvm().getVmname());
             additionalParams.put("action", request.getAction());
             additionalParams.put("jobId", job.getJobId());
+            additionalParams.put("vnfcId", vnfcId.orElse("unknown"));
             cbamHealRequest.setAdditionalParams(additionalParams);
-            com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
+            com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
             String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf);
             grantManager.requestGrantForHeal(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), request, job.getJobId());
-            OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdHealPost(vnfId, cbamHealRequest, NOKIA_LCM_API_VERSION);
-            waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId(), job.getJobId());
+            OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdHealPost(vnfId, cbamHealRequest, NOKIA_LCM_API_VERSION).blockingFirst();
+            waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
+        });
+    }
+
+    public JobInfo customOperation(String vnfmId, String vnfId, String operationId, Object additionalParams, HttpServletResponse httpResponse) {
+        logOperationInput(vnfId, "custom", additionalParams);
+        return scheduleExecution(vnfId, httpResponse, "custom", job -> {
+            CustomOperationRequest cbamRequest = new CustomOperationRequest();
+            cbamRequest.setAdditionalParams(additionalParams);
+            OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdCustomCustomOperationNamePost(vnfId, operationId, cbamRequest, NOKIA_LCM_API_VERSION).blockingFirst();
+            waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
         });
     }
 
@@ -501,27 +679,19 @@ public class LifecycleManager {
                 logger.error("Unable to " + operation + " VNF with " + vnfId + " identifier", e);
                 jobManager.jobFinished(jobInfo.getJobId());
                 throw e;
-            } catch (Exception e) {
-                String msg = "Unable to " + operation + " VNF with " + vnfId + " identifier";
-                logger.error(msg, e);
-                //the job can only be signaled to be finished after the error is logged
-                jobManager.jobFinished(jobInfo.getJobId());
-                throw new RuntimeException(msg, e);
             }
             jobManager.jobFinished(jobInfo.getJobId());
         });
         return jobInfo;
     }
 
-    private OperationExecution waitForOperationToFinish(String vnfmId, String vnfId, String operationExecutionId, String jobId) {
+    private OperationExecution waitForOperationToFinish(String vnfmId, String vnfId, String operationExecutionId) {
         while (true) {
             try {
-                OperationExecution operationExecution = find(cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdOperationExecutionsGet(vnfId, NOKIA_LCM_API_VERSION), opEx -> operationExecutionId.equals(opEx.getId()));
-                switch (operationExecution.getStatus()) {
-                    case FINISHED:
-                    case FAILED:
-                        logger.debug("Operation finished with " + operationExecution.getId());
-                        return operationExecution;
+                OperationExecution operationExecution = find(cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdOperationExecutionsGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst(), opEx -> operationExecutionId.equals(opEx.getId()));
+                if (hasOperationFinished(operationExecution)) {
+                    logger.debug("Operation finished with " + operationExecution.getId());
+                    return operationExecution;
                 }
             } catch (Exception e) {
                 //swallow exception and retry
@@ -531,7 +701,27 @@ public class LifecycleManager {
         }
     }
 
+    private boolean hasOperationFinished(OperationExecution operationExecution) {
+        return newHashSet(FINISHED, OperationStatus.FAILED).contains(operationExecution.getStatus());
+    }
+
+    @FunctionalInterface
     private interface AsynchronousExecution {
-        void execute(JobInfo job) throws Exception;
+        void execute(JobInfo job);
+    }
+
+    public static class VnfCreationResult {
+        private final com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo;
+
+        private final String vnfdId;
+
+        public VnfCreationResult(com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo, String vnfdId) {
+            this.vnfInfo = vnfInfo;
+            this.vnfdId = vnfdId;
+        }
+
+        public com.nokia.cbam.lcm.v32.model.VnfInfo getVnfInfo() {
+            return vnfInfo;
+        }
     }
 }