Process VNF property passed instantiation input
[vfc/nfvo/driver/vnfm/svnfm.git] / nokiav2 / driver / src / main / java / org / onap / vfc / nfvo / driver / vnfm / svnfm / nokia / vnfm / LifecycleManager.java
1 /*
2  * Copyright 2016-2017, Nokia Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm;
18
19
20 import com.google.gson.Gson;
21 import com.google.gson.JsonElement;
22 import com.google.gson.JsonObject;
23 import com.google.gson.JsonParser;
24 import com.nokia.cbam.catalog.v1.model.CatalogAdapterVnfpackage;
25 import com.nokia.cbam.lcm.v32.model.*;
26 import com.nokia.cbam.lcm.v32.model.ScaleDirection;
27 import java.util.*;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import javax.servlet.http.HttpServletResponse;
31 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.IGrantManager;
32 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VimInfoProvider;
33 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.StoreLoader;
34 import org.onap.vnfmdriver.model.ExtVirtualLinkInfo;
35 import org.onap.vnfmdriver.model.*;
36 import org.onap.vnfmdriver.model.VimInfo;
37 import org.onap.vnfmdriver.model.VnfInfo;
38 import org.slf4j.Logger;
39 import org.yaml.snakeyaml.Yaml;
40
41 import static java.lang.Integer.parseInt;
42 import static java.nio.charset.StandardCharsets.UTF_8;
43
44 import static com.google.common.base.Splitter.on;
45 import static com.google.common.collect.Iterables.find;
46 import static com.google.common.collect.Lists.newArrayList;
47 import static com.google.common.collect.Sets.newHashSet;
48 import static com.nokia.cbam.lcm.v32.model.InstantiationState.INSTANTIATED;
49 import static com.nokia.cbam.lcm.v32.model.OperationStatus.FINISHED;
50 import static com.nokia.cbam.lcm.v32.model.OperationType.INSTANTIATE;
51 import static com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum.*;
52 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.*;
53 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions;
54 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
55 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification.LifecycleChangeNotificationManager.NEWEST_OPERATIONS_FIRST;
56 import static org.slf4j.LoggerFactory.getLogger;
57 import static org.springframework.util.StringUtils.isEmpty;
58
59 /**
60  * Responsible for executing lifecycle operation on the VNF
61  */
62 public class LifecycleManager {
63     public static final String ONAP_CSAR_ID = "onapCsarId";
64     public static final long OPERATION_STATUS_POLLING_INTERVAL_IN_MS = 5000L;
65     /**
66      * The key of the CBAM VNF extension for the identifier of the VNFM in ONAP
67      */
68     public static final String EXTERNAL_VNFM_ID = "externalVnfmId";
69     public static final String SCALE_OPERATION_NAME = "scale";
70     public static final String ETSI_CONFIG = "etsi_config";
71     private static Logger logger = getLogger(LifecycleManager.class);
72     private final CatalogManager catalogManager;
73     private final IGrantManager grantManager;
74     private final JobManager jobManager;
75     private final ILifecycleChangeNotificationManager notificationManager;
76     private final CbamRestApiProvider cbamRestApiProvider;
77     private final VimInfoProvider vimInfoProvider;
78
79     /**
80      * Runs asynchronous operations in the background
81      */
82     private ExecutorService executorService = Executors.newCachedThreadPool();
83
84     LifecycleManager(CatalogManager catalogManager, IGrantManager grantManager, CbamRestApiProvider restApiProvider, VimInfoProvider vimInfoProvider, JobManager jobManager, ILifecycleChangeNotificationManager notificationManager) {
85         this.vimInfoProvider = vimInfoProvider;
86         this.grantManager = grantManager;
87         this.cbamRestApiProvider = restApiProvider;
88         this.jobManager = jobManager;
89         this.notificationManager = notificationManager;
90         this.catalogManager = catalogManager;
91     }
92
93     /**
94      * @param vimId the VIM identifier
95      * @return the name of the region
96      */
97     public static String getRegionName(String vimId) {
98         return newArrayList(on(SEPARATOR).split(vimId)).get(1);
99     }
100
101     /**
102      * @param vimId the VIM identifier
103      * @return the owner of the cloud
104      */
105     public static String getCloudOwner(String vimId) {
106         return newArrayList(on(SEPARATOR).split(vimId)).get(0);
107     }
108
109     private static OperationExecution findLastInstantiation(List<OperationExecution> operationExecutions) {
110         return find(NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions), op -> INSTANTIATE.equals(op.getOperationType()));
111     }
112
113     public static String getVnfdIdFromModifyableAttributes(com.nokia.cbam.lcm.v32.model.VnfInfo vnf) {
114         return find(vnf.getExtensions(), p -> p.getName().equals(ONAP_CSAR_ID)).getValue().toString();
115     }
116
117     /**
118      * Create the VNF. It consists of the following steps
119      * <ul>
120      * <li>upload the VNF package to CBAM package (if not already there)</li>
121      * <li>create the VNF on CBAM</li>
122      * <li>modify attributes of the VNF (add onapCsarId field)</li>
123      * </ul>
124      * The rollback of the failed operation is not implemented
125      * <ul>
126      * <li>delete the VNF if error occurs before instantiation</li>
127      * <li>terminateVnf & delete VNF if error occurs after instantiation</li>
128      * </ul>
129      *
130      * @param vnfmId      the identifier of the VNFM
131      * @param csarId      the identifier of the VNF package
132      * @param vnfName     the name of the VNF
133      * @param description the description of the VNF
134      * @return the VNF creation result
135      */
136     public VnfCreationResult create(String vnfmId, String csarId, String vnfName, String description) {
137         logOperationInput("not yet specified", "creation", csarId);
138         try {
139             CatalogAdapterVnfpackage cbamPackage = catalogManager.preparePackageInCbam(vnfmId, csarId);
140             CreateVnfRequest vnfCreateRequest = new CreateVnfRequest();
141             vnfCreateRequest.setVnfdId(cbamPackage.getVnfdId());
142             vnfCreateRequest.setName(vnfName);
143             vnfCreateRequest.setDescription(description);
144             com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsPost(vnfCreateRequest, NOKIA_LCM_API_VERSION).blockingFirst();
145             addVnfdIdToVnfModifyableAttributeExtensions(vnfmId, vnfInfo.getId(), csarId);
146             return new VnfCreationResult(vnfInfo, cbamPackage.getVnfdId());
147         } catch (Exception e) {
148             throw buildFatalFailure(logger, "Unable to create the VNF", e);
149         }
150     }
151
152     private void logOperationInput(String vnfId, String operationName, Object payload) {
153         if (logger.isInfoEnabled()) {
154             logger.info("Starting {} operation on VNF with {} identifier with {} parameter", operationName, vnfId, new Gson().toJson(payload));
155         }
156     }
157
158     /**
159      * Instantiate the VNF
160      *
161      * @param vnfmId                        the identifier of the VNFM
162      * @param externalVirtualLinks          the external virtual links of the VNF
163      * @param httpResponse                  the HTTP response that corresponds to the VNF instantiation request
164      * @param additionalParameters          additional parameters
165      * @param vnfId                         the identifier of the VNF
166      * @param vnfmVnfdId                    the identifier of the VNF package in CBAM
167      * @param operationAdditionalParameters the additional parameters of the operation
168      * @param onapVnfdId                    the identifier of the VNFD in the VNFM
169      * @return the instantiation response
170      */
171     @SuppressWarnings("squid:S00107") //wrapping them into an object makes the code less readable
172     public VnfInstantiateResponse instantiate(String vnfmId, List<ExtVirtualLinkInfo> externalVirtualLinks, HttpServletResponse httpResponse, Object operationAdditionalParameters, AdditionalParameters additionalParameters, String vnfId, String onapVnfdId, String vnfmVnfdId) {
173         logOperationInput(vnfId, "instantiation", additionalParameters);
174         validateVimType(additionalParameters.getVimType());
175         VnfInstantiateResponse response = new VnfInstantiateResponse();
176         response.setVnfInstanceId(vnfId);
177         String vimId = getVimId(operationAdditionalParameters);
178         JobInfo spawnJob = scheduleExecution(vnfId, httpResponse, "instantiate", jobInfo ->
179                 instantiateVnf(vnfmId, externalVirtualLinks, additionalParameters, onapVnfdId, vnfmVnfdId, vnfId, vimId, jobInfo)
180         );
181         response.setJobId(spawnJob.getJobId());
182         return response;
183     }
184
185     /**
186      * Instantiate (VF-C terminology) the VNF. It consists of the following steps
187      * <ul>
188      * <li>upload the VNF package to CBAM package (if not already there)</li>
189      * <li>create the VNF on CBAM</li>
190      * <li>modify attributes of the VNF (add onapCsarId field)</li>
191      * <li>asynchronously</li>
192      * <li>request grant from VF-C</li>
193      * <li>instantiate VNF on CBAM</li>
194      * <li>return VNF & job id (after create VNF on CBAM)</li>
195      * <li></li>
196      * </ul>
197      * The rollback of the failed operation is not implemented
198      * <ul>
199      * <li>delete the VNF if error occurs before instantiation</li>
200      * <li>terminateVnf & delete VNf if error occurs after instantiation</li>
201      * </ul>
202      *
203      * @param vnfmId       the identifier of the VNFM
204      * @param request      the instantiation request
205      * @param httpResponse the HTTP response
206      * @return the instantiation response
207      */
208     public VnfInstantiateResponse createAndInstantiate(String vnfmId, VnfInstantiateRequest request, HttpServletResponse httpResponse) {
209         AdditionalParameters additionalParameters = convertInstantiationAdditionalParams(request.getVnfPackageId(), request.getAdditionalParam());
210         validateVimType(additionalParameters.getVimType());
211         VnfCreationResult creationResult = create(vnfmId, request.getVnfDescriptorId(), request.getVnfInstanceName(), request.getVnfInstanceDescription());
212         return instantiate(vnfmId, request.getExtVirtualLink(), httpResponse, request.getAdditionalParam(), additionalParameters, creationResult.vnfInfo.getId(), request.getVnfPackageId(), creationResult.vnfdId);
213     }
214
215     @SuppressWarnings("squid:S00107") //wrapping them into an object makes the code less readable
216     private void instantiateVnf(String vnfmId, List<ExtVirtualLinkInfo> extVirtualLinkInfos, AdditionalParameters additionalParameters, String onapVnfdId, String vnfmVnfdId, String vnfId, String vimId, JobInfo jobInfo) {
217         String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, vnfmVnfdId);
218         GrantVNFResponseVim vim = grantManager.requestGrantForInstantiate(vnfmId, vnfId, vimId, onapVnfdId, additionalParameters.getInstantiationLevel(), vnfdContent, jobInfo.getJobId());
219         handleBackwardIncompatibleApiChangesInVfc(vim);
220         VimInfo vimInfo = vimInfoProvider.getVimInfo(vim.getVimId());
221         InstantiateVnfRequest instantiationRequest = new InstantiateVnfRequest();
222         addExternalLinksToRequest(extVirtualLinkInfos, additionalParameters, instantiationRequest, vimId);
223         instantiationRequest.getVims().add(addVim(additionalParameters, vimId, vim, vimInfo));
224         instantiationRequest.setFlavourId(getFlavorId(vnfdContent));
225         instantiationRequest.setComputeResourceFlavours(additionalParameters.getComputeResourceFlavours());
226         instantiationRequest.setGrantlessMode(true);
227         instantiationRequest.setInstantiationLevelId(additionalParameters.getInstantiationLevel());
228         instantiationRequest.setSoftwareImages(additionalParameters.getSoftwareImages());
229         instantiationRequest.setZones(additionalParameters.getZones());
230         instantiationRequest.setExtManagedVirtualLinks(additionalParameters.getExtManagedVirtualLinks());
231         for (ExtVirtualLinkData extVirtualLinkData : additionalParameters.getExtVirtualLinks()) {
232             instantiationRequest.addExtVirtualLinksItem(extVirtualLinkData);
233         }
234         JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject();
235         if (additionalParameters.getAdditionalParams() != null) {
236             for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(additionalParameters.getAdditionalParams()).getAsJsonObject().entrySet()) {
237                 root.add(item.getKey(), item.getValue());
238             }
239         } else {
240             logger.warn("No additional parameters were specified for the operation");
241         }
242         instantiationRequest.setAdditionalParams(root);
243         OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdInstantiatePost(vnfId, instantiationRequest, NOKIA_LCM_API_VERSION).blockingFirst();
244         waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
245     }
246
247     private void handleBackwardIncompatibleApiChangesInVfc(GrantVNFResponseVim vim) {
248         if (vim.getVimId() == null) {
249             if (vim.getVimid() == null) {
250                 throw buildFatalFailure(logger, "VF-C did not send VIM identifier in grant response");
251             } else {
252                 vim.setVimId(vim.getVimid());
253             }
254         }
255         if (vim.getAccessInfo() == null) {
256             if (vim.getAccessinfo() == null) {
257                 throw buildFatalFailure(logger, "VF-C did not send access info in grant response");
258             } else {
259                 vim.setAccessInfo(vim.getAccessinfo());
260             }
261         }
262     }
263
264     private com.nokia.cbam.lcm.v32.model.VimInfo addVim(AdditionalParameters additionalParameters, String vimId, GrantVNFResponseVim vim, VimInfo vimInfo) {
265         if (additionalParameters.getVimType() == OPENSTACK_V2_INFO) {
266             return buildOpenStackV2INFO(vimId, vim, vimInfo);
267
268         } else if (additionalParameters.getVimType() == OPENSTACK_V3_INFO) {
269             if (isEmpty(vimInfo.getDomain())) {
270                 if (isEmpty(additionalParameters.getDomain())) {
271                     throw buildFatalFailure(logger, "The cloud did not supply the cloud domain (Amsterdam release) and was not supplied as additional data");
272                 } else {
273                     logger.warn("Setting domain from additional parameters");
274                     vimInfo.setDomain(additionalParameters.getDomain());
275                 }
276             }
277             return buildOpenStackV3INFO(vimId, vim, vimInfo);
278         } else {
279             //OTHER VIM TYPE is not possible
280             return buildVcloudInfo(vimId, vimInfo);
281         }
282     }
283
284     private void validateVimType(com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum vimType) {
285         if (com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum.OTHER_VIM_INFO.equals(vimType)) {
286             throw buildFatalFailure(logger, "Only " + OPENSTACK_V2_INFO + ", " + OPENSTACK_V3_INFO + " and " + VMWARE_VCLOUD_INFO + " is the supported VIM types");
287         }
288     }
289
290     private String getVimId(Object additionalParams) {
291         return childElement(new Gson().toJsonTree(additionalParams).getAsJsonObject(), "vimId").getAsString();
292     }
293
294     private AdditionalParameters convertInstantiationAdditionalParams(String csarId, Object additionalParams) {
295         JsonObject root = new Gson().toJsonTree(additionalParams).getAsJsonObject();
296         if(root.has("properties")){
297             JsonObject properties = new JsonParser().parse(root.get("properties").getAsString()).getAsJsonObject();
298             if(properties.has(ETSI_CONFIG)){
299                 JsonElement etsi_config = properties.get(ETSI_CONFIG);
300                 return new Gson().fromJson(etsi_config.getAsString(), AdditionalParameters.class);
301             }
302             else{
303                 logger.info("The instantiation input for VNF with {} CSAR id does not have an " + ETSI_CONFIG +" section", csarId);
304             }
305         }
306         else{
307             logger.info("The instantiation input for VNF with {} CSAR id does not have a properties section", csarId);
308         }
309         JsonObject inputs = child(root, "inputs");
310         if (!inputs.has(csarId)) {
311             throw buildFatalFailure(logger, "The additional parameter section does not contain settings for VNF with " + csarId + " CSAR id");
312         }
313         JsonElement additionalParamsForVnf = new JsonParser().parse(inputs.get(csarId).getAsString());
314         return new Gson().fromJson(additionalParamsForVnf, AdditionalParameters.class);
315     }
316
317     private String getFlavorId(String vnfdContent) {
318         JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject();
319         JsonObject capabilities = child(child(child(root, "topology_template"), "substitution_mappings"), "capabilities");
320         JsonObject deploymentFlavorProperties = child(child(capabilities, "deployment_flavour"), "properties");
321         return childElement(deploymentFlavorProperties, "flavour_id").getAsString();
322     }
323
324     private Set<Map.Entry<String, JsonElement>> getAcceptableOperationParameters(String vnfdContent, String categoryOfOperation, String operationName) {
325         JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject();
326         JsonObject interfaces = child(child(child(root, "topology_template"), "substitution_mappings"), "interfaces");
327         JsonObject additionalParameters = child(child(child(child(interfaces, categoryOfOperation), operationName), "inputs"), "additional_parameters");
328         return additionalParameters.entrySet();
329     }
330
331     private void addExternalLinksToRequest(List<ExtVirtualLinkInfo> extVirtualLinks, AdditionalParameters additionalParameters, InstantiateVnfRequest instantiationRequest, String vimId) {
332         for (ExtVirtualLinkInfo extVirtualLink : extVirtualLinks) {
333             ExtVirtualLinkData cbamExternalVirtualLink = new ExtVirtualLinkData();
334             cbamExternalVirtualLink.setVimId(vimId);
335             cbamExternalVirtualLink.setResourceId(extVirtualLink.getResourceId());
336             VnfExtCpData ecp = new VnfExtCpData();
337             cbamExternalVirtualLink.setExtVirtualLinkId(extVirtualLink.getVlInstanceId());
338             cbamExternalVirtualLink.getExtCps().add(ecp);
339             ecp.setCpdId(extVirtualLink.getCpdId());
340             List<NetworkAddress> addresses = additionalParameters.getExternalConnectionPointAddresses().get(extVirtualLink.getCpdId());
341             ecp.setAddresses(addresses);
342             instantiationRequest.addExtVirtualLinksItem(cbamExternalVirtualLink);
343         }
344     }
345
346     private void addVnfdIdToVnfModifyableAttributeExtensions(String vnfmId, String vnfId, String onapCsarId) {
347         ModifyVnfInfoRequest request = new ModifyVnfInfoRequest();
348         VnfProperty onapCsarIdProperty = new VnfProperty();
349         onapCsarIdProperty.setName(ONAP_CSAR_ID);
350         onapCsarIdProperty.setValue(onapCsarId);
351         request.setExtensions(new ArrayList<>());
352         request.getExtensions().add(onapCsarIdProperty);
353         VnfProperty externalVnfmIdProperty = new VnfProperty();
354         externalVnfmIdProperty.setName(EXTERNAL_VNFM_ID);
355         externalVnfmIdProperty.setValue(vnfmId);
356         request.getExtensions().add(externalVnfmIdProperty);
357         request.setVnfConfigurableProperties(null);
358         try {
359             OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdPatch(vnfId, request, NOKIA_LCM_API_VERSION).blockingFirst();
360             waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
361         } catch (Exception e) {
362             throw buildFatalFailure(logger, "Unable to set the " + ONAP_CSAR_ID + " property on the VNF", e);
363         }
364     }
365
366     private OPENSTACKV3INFO buildOpenStackV3INFO(String vimId, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) {
367         OPENSTACKV3INFO openstackv3INFO = new OPENSTACKV3INFO();
368         openstackv3INFO.setVimInfoType(OPENSTACK_V3_INFO);
369         OpenStackAccessInfoV3 accessInfov3 = new OpenStackAccessInfoV3();
370         openstackv3INFO.accessInfo(accessInfov3);
371         accessInfov3.setPassword(vimInfo.getPassword());
372         accessInfov3.setDomain(vimInfo.getDomain());
373         accessInfov3.setProject(vim.getAccessInfo().getTenant());
374         accessInfov3.setRegion(getRegionName(vimId));
375         accessInfov3.setUsername(vimInfo.getUserName());
376         openstackv3INFO.setInterfaceInfo(getEndpointInfo(vimInfo));
377         openstackv3INFO.setId(vimId);
378         return openstackv3INFO;
379     }
380
381     private OPENSTACKV2INFO buildOpenStackV2INFO(String vimId, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) {
382         OPENSTACKV2INFO openstackv2INFO = new OPENSTACKV2INFO();
383         openstackv2INFO.setVimInfoType(OPENSTACK_V2_INFO);
384         OpenStackAccessInfoV2 accessInfo = new OpenStackAccessInfoV2();
385         openstackv2INFO.setAccessInfo(accessInfo);
386         accessInfo.setPassword(vimInfo.getPassword());
387         accessInfo.setTenant(vim.getAccessInfo().getTenant());
388         accessInfo.setUsername(vimInfo.getUserName());
389         accessInfo.setRegion(getRegionName(vimId));
390         EndpointInfo interfaceEndpoint = getEndpointInfo(vimInfo);
391         openstackv2INFO.setInterfaceInfo(interfaceEndpoint);
392         openstackv2INFO.setId(vimId);
393         return openstackv2INFO;
394     }
395
396     private EndpointInfo getEndpointInfo(VimInfo vimInfo) {
397         EndpointInfo interfaceEndpoint = new EndpointInfo();
398         if (!isEmpty(vimInfo.getSslInsecure())) {
399             interfaceEndpoint.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure()));
400             interfaceEndpoint.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure()));
401         } else {
402             interfaceEndpoint.setSkipCertificateHostnameCheck(true);
403             interfaceEndpoint.setSkipCertificateVerification(true);
404         }
405         interfaceEndpoint.setEndpoint(vimInfo.getUrl());
406         if (!interfaceEndpoint.isSkipCertificateVerification()) {
407             interfaceEndpoint.setTrustedCertificates(new ArrayList<>());
408             for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) {
409                 interfaceEndpoint.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8));
410             }
411         }
412         return interfaceEndpoint;
413     }
414
415     private VMWAREVCLOUDINFO buildVcloudInfo(String vimId, org.onap.vnfmdriver.model.VimInfo vimInfo) {
416         VMWAREVCLOUDINFO vcloudInfo = new VMWAREVCLOUDINFO();
417         vcloudInfo.setVimInfoType(VMWARE_VCLOUD_INFO);
418         VCloudAccessInfo accessInfo = new VCloudAccessInfo();
419         vcloudInfo.setAccessInfo(accessInfo);
420         accessInfo.setPassword(vimInfo.getPassword());
421         accessInfo.setUsername(vimInfo.getUserName());
422         accessInfo.setOrganization(getRegionName(vimId));
423         vcloudInfo.setInterfaceInfo(getEndpointInfo(vimInfo));
424         vcloudInfo.setId(vimId);
425         return vcloudInfo;
426     }
427
428     /**
429      * Terminates and deletes the VNF
430      * <ul>
431      * <li>fails if the VNF does not exist</li>
432      * <li>terminates if instantiated</li>
433      * <li>deletes the VNF</li>
434      * </ul>
435      *
436      * @param vnfmId       the identifier of the VNFM
437      * @param vnfId        the identifier of the VNF
438      * @param request      the termination request
439      * @param httpResponse the HTTP response
440      * @return the job for polling the progress of the termination
441      */
442     public JobInfo terminateAndDelete(String vnfmId, String vnfId, VnfTerminateRequest request, HttpServletResponse httpResponse) {
443         logOperationInput(vnfId, "termination", request);
444         return scheduleExecution(vnfId, httpResponse, "terminateVnf", jobInfo -> {
445             terminateVnf(vnfmId, vnfId, request, jobInfo);
446             deleteVnf(vnfmId, vnfId);
447         });
448     }
449
450     /**
451      * Terminates the VNF
452      * <ul>
453      * <li>fails if the VNF does not exist</li>
454      * <li>terminates if instantiated</li>
455      * <li>deletes the VNF</li>
456      * </ul>
457      *
458      * @param vnfmId       the identifier of the VNFM
459      * @param vnfId        the identifier of the VNF
460      * @param request      the termination request
461      * @param httpResponse the HTTP response
462      * @return the job for polling the progress of the termination
463      */
464     public JobInfo terminate(String vnfmId, String vnfId, VnfTerminateRequest request, HttpServletResponse httpResponse) {
465         logOperationInput(vnfId, "termination", request);
466         return scheduleExecution(vnfId, httpResponse, "terminate", jobInfo -> terminateVnf(vnfmId, vnfId, request, jobInfo));
467     }
468
469     private void terminateVnf(String vnfmId, String vnfId, VnfTerminateRequest request, JobInfo jobInfo) {
470         TerminateVnfRequest cbamRequest = new TerminateVnfRequest();
471         setState(request, cbamRequest);
472         cbamRequest.setAdditionalParams(new Gson().toJsonTree(jobInfo).getAsJsonObject());
473         com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
474         if (vnf.getInstantiationState() == INSTANTIATED) {
475             String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf);
476             grantManager.requestGrantForTerminate(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), vnf, jobInfo.getJobId());
477             OperationExecution terminationOperation = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdTerminatePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION).blockingFirst();
478             OperationExecution finishedOperation = waitForOperationToFinish(vnfmId, vnfId, terminationOperation.getId());
479             if (finishedOperation.getStatus() == FINISHED) {
480                 notificationManager.waitForTerminationToBeProcessed(finishedOperation.getId());
481             } else {
482                 throw buildFatalFailure(logger, "Unable to terminate VNF the operation did not finish with success");
483             }
484         } else {
485             logger.warn("The VNF with {} identifier is not instantiated no termination is required", vnfId);
486         }
487     }
488
489     private void setState(VnfTerminateRequest request, TerminateVnfRequest cbamRequest) {
490         if (request.getTerminationType() == null) {
491             cbamRequest.setTerminationType(TerminationType.FORCEFUL);
492         } else {
493             if (request.getTerminationType().equals(VnfTerminationType.GRACEFUL)) {
494                 cbamRequest.setTerminationType(TerminationType.GRACEFUL);
495                 cbamRequest.setGracefulTerminationTimeout(parseInt(request.getGracefulTerminationTimeout()));
496             } else {
497                 cbamRequest.setTerminationType(TerminationType.FORCEFUL);
498             }
499         }
500     }
501
502     /**
503      * Delete the VNF
504      *
505      * @param vnfmId the identifier of the VNFM
506      * @param vnfId  the identifier fo the VNF
507      */
508     public void deleteVnf(String vnfmId, String vnfId) {
509         logger.info("Deleting VNF with {} identifier", vnfId);
510         cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION).blockingFirst(null);
511         logger.info("The VNF with {} identifier has been deleted", vnfId);
512     }
513
514     private String getVimIdFromInstantiationRequest(String vnfmId, com.nokia.cbam.lcm.v32.model.VnfInfo vnf) {
515         OperationExecution lastInstantiation = findLastInstantiation(vnf.getOperationExecutions());
516         Object operationParameters = cbamRestApiProvider.getCbamOperationExecutionApi(vnfmId).operationExecutionsOperationExecutionIdOperationParamsGet(lastInstantiation.getId(), NOKIA_LCM_API_VERSION).blockingFirst();
517         JsonObject root = new Gson().toJsonTree(operationParameters).getAsJsonObject();
518         return childElement(childElement(root, "vims").getAsJsonArray().get(0).getAsJsonObject(), "id").getAsString();
519     }
520
521     /**
522      * @param vnfmId the identifier of the VNFM
523      * @param vnfId  the identifier of the VNF
524      * @return the current state of the VNF
525      */
526     public VnfInfo queryVnf(String vnfmId, String vnfId) {
527         try {
528             com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
529             return convertVnfInfo(vnfId, cbamVnfInfo);
530         } catch (Exception e) {
531             throw buildFatalFailure(logger, "Unable to query VNF (" + vnfId + ")", e);
532         }
533     }
534
535     private VnfInfo convertVnfInfo(String vnfId, com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo) {
536         VnfInfo vnfInfo = new VnfInfo();
537         vnfInfo.setVersion(cbamVnfInfo.getVnfSoftwareVersion());
538         vnfInfo.setVnfInstanceId(vnfId);
539         String onapCsarId = getVnfdIdFromModifyableAttributes(cbamVnfInfo);
540         vnfInfo.setVnfdId(onapCsarId);
541         vnfInfo.setVnfPackageId(onapCsarId);
542         vnfInfo.setVnfInstanceDescription(cbamVnfInfo.getDescription());
543         vnfInfo.setVnfInstanceName(cbamVnfInfo.getName());
544         vnfInfo.setVnfProvider(cbamVnfInfo.getVnfProvider());
545         vnfInfo.setVnfStatus("ACTIVE");
546         vnfInfo.setVnfType("Kuku");
547         return vnfInfo;
548     }
549
550     private ScaleDirection convert(org.onap.vnfmdriver.model.ScaleDirection direction) {
551         if (org.onap.vnfmdriver.model.ScaleDirection.IN.equals(direction)) {
552             return ScaleDirection.IN;
553         } else {
554             return ScaleDirection.OUT;
555         }
556     }
557
558     /**
559      * Scale the VNF
560      *
561      * @param vnfmId       the identifier of the VNFM
562      * @param vnfId        the identifier of the VNF
563      * @param request      the scale request
564      * @param httpResponse the HTTP response
565      * @return the job for tracking the scale
566      */
567     public JobInfo scaleVnf(String vnfmId, String vnfId, VnfScaleRequest request, HttpServletResponse httpResponse) {
568         logOperationInput(vnfId, SCALE_OPERATION_NAME, request);
569         return scheduleExecution(vnfId, httpResponse, SCALE_OPERATION_NAME, jobInfo -> {
570             ScaleVnfRequest cbamRequest = new ScaleVnfRequest();
571             cbamRequest.setAspectId(request.getAspectId());
572             cbamRequest.setNumberOfSteps(Integer.valueOf(request.getNumberOfSteps()));
573             cbamRequest.setType(convert(request.getType()));
574             com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
575             JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject();
576             com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
577             String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, cbamVnfInfo.getVnfdId());
578             Set<Map.Entry<String, JsonElement>> acceptableOperationParameters = getAcceptableOperationParameters(vnfdContent, "Basic", SCALE_OPERATION_NAME);
579             buildAdditionalParameters(request, root, acceptableOperationParameters);
580             cbamRequest.setAdditionalParams(root);
581             grantManager.requestGrantForScale(vnfmId, vnfId, getVimIdFromInstantiationRequest(vnfmId, vnf), getVnfdIdFromModifyableAttributes(vnf), request, jobInfo.getJobId());
582             OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdScalePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION).blockingFirst();
583             waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
584         });
585     }
586
587     private void buildAdditionalParameters(VnfScaleRequest request, JsonObject root, Set<Map.Entry<String, JsonElement>> acceptableOperationParameters) {
588         if (request.getAdditionalParam() != null) {
589             for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(request.getAdditionalParam()).getAsJsonObject().entrySet()) {
590                 if (isParameterAccepted(acceptableOperationParameters, item)) {
591                     root.add(item.getKey(), item.getValue());
592                 }
593             }
594         } else {
595             logger.warn("No additional parameters were passed for scaling");
596         }
597     }
598
599     private boolean isParameterAccepted(Set<Map.Entry<String, JsonElement>> acceptableOperationParameters, Map.Entry<String, JsonElement> item) {
600         boolean found = false;
601         for (Map.Entry<String, JsonElement> acceptableOperationParameter : acceptableOperationParameters) {
602             if (acceptableOperationParameter.getKey().equals(item.getKey())) {
603                 found = true;
604             }
605         }
606         return found;
607     }
608
609     /**
610      * Heal the VNF
611      *
612      * @param vnfmId       the identifier of the VNFM
613      * @param vnfId        the identifier of the VNF
614      * @param request      the heal request
615      * @param httpResponse the HTTP response
616      * @param vnfcId       the identifer of thr VNFC to be healed
617      * @return the job for tracking the heal
618      */
619     public JobInfo healVnf(String vnfmId, String vnfId, VnfHealRequest request, Optional<String> vnfcId, HttpServletResponse httpResponse) {
620         logOperationInput(vnfId, "heal", request);
621         return scheduleExecution(vnfId, httpResponse, "heal", job -> {
622             HealVnfRequest cbamHealRequest = new HealVnfRequest();
623             Map<String, String> additionalParams = new HashMap<>();
624             additionalParams.put("vmName", request.getAffectedvm().getVmname());
625             additionalParams.put("action", request.getAction());
626             additionalParams.put("jobId", job.getJobId());
627             additionalParams.put("vnfcId", vnfcId.orElse("unknown"));
628             cbamHealRequest.setAdditionalParams(additionalParams);
629             com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst();
630             String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf);
631             grantManager.requestGrantForHeal(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), request, job.getJobId());
632             OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdHealPost(vnfId, cbamHealRequest, NOKIA_LCM_API_VERSION).blockingFirst();
633             waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
634         });
635     }
636
637     public JobInfo customOperation(String vnfmId, String vnfId, String operationId, Object additionalParams, HttpServletResponse httpResponse) {
638         logOperationInput(vnfId, "custom", additionalParams);
639         return scheduleExecution(vnfId, httpResponse, "custom", job -> {
640             CustomOperationRequest cbamRequest = new CustomOperationRequest();
641             cbamRequest.setAdditionalParams(additionalParams);
642             OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdCustomCustomOperationNamePost(vnfId, operationId, cbamRequest, NOKIA_LCM_API_VERSION).blockingFirst();
643             waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
644         });
645     }
646
647     private JobInfo scheduleExecution(String vnfId, HttpServletResponse httpResponse, String operation, AsynchronousExecution asynchronExecution) {
648         JobInfo jobInfo = new JobInfo();
649         jobInfo.setJobId(jobManager.spawnJob(vnfId, httpResponse));
650         executorService.submit(() -> {
651             try {
652                 asynchronExecution.execute(jobInfo);
653             } catch (RuntimeException e) {
654                 logger.error("Unable to " + operation + " VNF with " + vnfId + " identifier", e);
655                 jobManager.jobFinished(jobInfo.getJobId());
656                 throw e;
657             }
658             jobManager.jobFinished(jobInfo.getJobId());
659         });
660         return jobInfo;
661     }
662
663     private OperationExecution waitForOperationToFinish(String vnfmId, String vnfId, String operationExecutionId) {
664         while (true) {
665             try {
666                 OperationExecution operationExecution = find(cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdOperationExecutionsGet(vnfId, NOKIA_LCM_API_VERSION).blockingFirst(), opEx -> operationExecutionId.equals(opEx.getId()));
667                 if (hasOperationFinished(operationExecution)) {
668                     logger.debug("Operation finished with " + operationExecution.getId());
669                     return operationExecution;
670                 }
671             } catch (Exception e) {
672                 //swallow exception and retry
673                 logger.warn("Unable to retrieve operations details", e);
674             }
675             systemFunctions().sleep(OPERATION_STATUS_POLLING_INTERVAL_IN_MS);
676         }
677     }
678
679     private boolean hasOperationFinished(OperationExecution operationExecution) {
680         return newHashSet(FINISHED, OperationStatus.FAILED).contains(operationExecution.getStatus());
681     }
682
683     @FunctionalInterface
684     private interface AsynchronousExecution {
685         void execute(JobInfo job);
686     }
687
688     public static class VnfCreationResult {
689         private final com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo;
690
691         private final String vnfdId;
692
693         public VnfCreationResult(com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo, String vnfdId) {
694             this.vnfInfo = vnfInfo;
695             this.vnfdId = vnfdId;
696         }
697
698         public com.nokia.cbam.lcm.v32.model.VnfInfo getVnfInfo() {
699             return vnfInfo;
700         }
701     }
702 }