Merge "Fix most sonar issues"
[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.nokia.cbam.catalog.v1.model.CatalogAdapterVnfpackage;
24 import com.nokia.cbam.lcm.v32.ApiException;
25 import com.nokia.cbam.lcm.v32.model.*;
26 import com.nokia.cbam.lcm.v32.model.ScaleDirection;
27 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.IGrantManager;
28 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VimInfoProvider;
29 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.StoreLoader;
30 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.UserVisibleError;
31 import org.onap.vnfmdriver.model.ExtVirtualLinkInfo;
32 import org.onap.vnfmdriver.model.*;
33 import org.onap.vnfmdriver.model.VimInfo;
34 import org.onap.vnfmdriver.model.VnfInfo;
35 import org.slf4j.Logger;
36 import org.springframework.beans.factory.annotation.Autowired;
37 import org.springframework.stereotype.Component;
38 import org.yaml.snakeyaml.Yaml;
39
40 import javax.servlet.http.HttpServletResponse;
41 import java.util.*;
42 import java.util.concurrent.ExecutorService;
43 import java.util.concurrent.Executors;
44
45 import static com.google.common.base.Splitter.on;
46 import static com.google.common.collect.Iterables.find;
47 import static com.google.common.collect.Lists.newArrayList;
48 import static com.google.common.collect.Sets.newHashSet;
49 import static com.nokia.cbam.lcm.v32.model.InstantiationState.INSTANTIATED;
50 import static com.nokia.cbam.lcm.v32.model.OperationStatus.FINISHED;
51 import static com.nokia.cbam.lcm.v32.model.OperationType.INSTANTIATE;
52 import static com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum.*;
53 import static java.lang.Integer.parseInt;
54 import static java.nio.charset.StandardCharsets.UTF_8;
55 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.*;
56 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions;
57 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
58 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager.NEWEST_OPERATIONS_FIRST;
59 import static org.slf4j.LoggerFactory.getLogger;
60 import static org.springframework.util.StringUtils.isEmpty;
61
62 /**
63  * Responsible for executing lifecycle operation on the VNF
64  */
65 @Component
66 public class LifecycleManager {
67     public static final String ONAP_CSAR_ID = "onapCsarId";
68     public static final long OPERATION_STATUS_POLLING_INTERVAL_IN_MS = 5000L;
69     /**
70      * The key of the CBAM VNF extension for the identifier of the VNFM in ONAP
71      */
72     public static final String EXTERNAL_VNFM_ID = "externalVnfmId";
73     private static Logger logger = getLogger(LifecycleManager.class);
74     private final CatalogManager catalogManager;
75     private final IGrantManager grantManager;
76     private final JobManager jobManager;
77     private final ILifecycleChangeNotificationManager notificationManager;
78     private final CbamRestApiProvider cbamRestApiProvider;
79     private final VimInfoProvider vimInfoProvider;
80
81     /**
82      * Runs asynchronous operations in the background
83      */
84     private ExecutorService executorService = Executors.newCachedThreadPool();
85
86     @Autowired
87     LifecycleManager(CatalogManager catalogManager, IGrantManager grantManager, CbamRestApiProvider restApiProvider, VimInfoProvider vimInfoProvider, JobManager jobManager, ILifecycleChangeNotificationManager notificationManager) {
88         this.vimInfoProvider = vimInfoProvider;
89         this.grantManager = grantManager;
90         this.cbamRestApiProvider = restApiProvider;
91         this.jobManager = jobManager;
92         this.notificationManager = notificationManager;
93         this.catalogManager = catalogManager;
94     }
95
96     public static String getRegionName(String vimId) {
97         return newArrayList(on(SEPARATOR).split(vimId)).get(1);
98     }
99
100     public static String getCloudOwner(String vimId) {
101         return newArrayList(on(SEPARATOR).split(vimId)).get(0);
102     }
103
104     private static OperationExecution findLastInstantiation(List<OperationExecution> operationExecutions) {
105         return find(NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions), op -> INSTANTIATE.equals(op.getOperationType()));
106     }
107
108     /**
109      * Instantiate (VF-C terminology) the VNF. It consists of the following steps
110      * <ul>
111      * <li>upload the VNF package to CBAM package (if not already there)</li>
112      * <li>create the VNF on CBAM</li>
113      * <li>modify attributes of the VNF (add onapCsarId field)</li>
114      * <li>asynchronously</li>
115      * <li>request grant from VF-C</li>
116      * <li>instantiate VNF on CBAM</li>
117      * <li>return VNF & job id (after create VNF on CBAM)</li>
118      * <li></li>
119      * </ul>
120      * The rollback of the failed operation is not implemented
121      * <ul>
122      * <li>delete the VNF if error occurs before instantiation</li>
123      * <li>terminate & delete VNf if error occurs after instantiation</li>
124      * </ul>
125      *
126      * @param vnfmId       the identifier of the VNFM
127      * @param request      the instantiation request
128      * @param httpResponse the HTTP response
129      * @return the instantiation response
130      */
131     public VnfInstantiateResponse instantiate(String vnfmId, VnfInstantiateRequest request, HttpServletResponse httpResponse) {
132         if (logger.isDebugEnabled()) {
133             logger.debug("Additional parameters for instantiation: {}", new Gson().toJson(request.getAdditionalParam()));
134         }
135         AdditionalParams additionalParams = convertInstantiationAdditionalParams(request.getVnfPackageId(), request.getAdditionalParam());
136         validateVimType(additionalParams);
137         CatalogAdapterVnfpackage cbamPackage = catalogManager.preparePackageInCbam(vnfmId, request.getVnfPackageId());
138         try {
139             CreateVnfRequest vnfCreateRequest = new CreateVnfRequest();
140             vnfCreateRequest.setVnfdId(cbamPackage.getId());
141             vnfCreateRequest.setName(request.getVnfInstanceName());
142             vnfCreateRequest.setDescription(request.getVnfInstanceDescription());
143             com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsPost(vnfCreateRequest, NOKIA_LCM_API_VERSION);
144             VnfInstantiateResponse response = new VnfInstantiateResponse();
145             response.setVnfInstanceId(vnfInfo.getId());
146             String vimId = getVimId(request.getAdditionalParam());
147             addVnfdIdToVnfModifyableAttributeExtensions(vnfmId, vnfInfo.getId(), request.getVnfPackageId());
148             JobInfo spawnJob = scheduleExecution(vnfInfo.getId(), httpResponse, "instantiate", jobInfo ->
149                     instantiateVnf(vnfmId, request, additionalParams, cbamPackage, vnfInfo, vimId, jobInfo)
150             );
151             response.setJobId(spawnJob.getJobId());
152             return response;
153         } catch (Exception e) {
154             throw fatalFailure(logger, "Unable to create the VNF", e);
155         }
156     }
157
158     private void instantiateVnf(String vnfmId, VnfInstantiateRequest request, AdditionalParams additionalParams, CatalogAdapterVnfpackage cbamPackage, com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo, String vimId, JobInfo jobInfo) throws ApiException {
159         String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, cbamPackage.getId());
160         GrantVNFResponseVim vim = grantManager.requestGrantForInstantiate(vnfmId, vnfInfo.getId(), vimId, request.getVnfPackageId(), additionalParams.getInstantiationLevel(), vnfdContent, jobInfo.getJobId());
161         if (vim.getVimId() == null) {
162             fatalFailure(logger, "VF-C did not send VIM identifier in grant response");
163         }
164         VimInfo vimInfo = vimInfoProvider.getVimInfo(vim.getVimId());
165         InstantiateVnfRequest instantiationRequest = new InstantiateVnfRequest();
166         addExernalLinksToRequest(request.getExtVirtualLink(), additionalParams, instantiationRequest, vimId);
167         if (additionalParams.getVimType() == OPENSTACK_V2_INFO) {
168             instantiationRequest.getVims().add(buildOpenStackV2INFO(vimId, vim, vimInfo));
169
170         } else if (additionalParams.getVimType() == OPENSTACK_V3_INFO) {
171             instantiationRequest.getVims().add(buildOpenStackV3INFO(vimId, additionalParams, vim, vimInfo));
172
173         } else if (additionalParams.getVimType() == VMWARE_VCLOUD_INFO) {
174             instantiationRequest.getVims().add(buildVcloudInfo(vimId, vimInfo));
175
176         }
177         instantiationRequest.setFlavourId(getFlavorId(vnfdContent));
178         instantiationRequest.setComputeResourceFlavours(additionalParams.getComputeResourceFlavours());
179         instantiationRequest.setGrantlessMode(true);
180         instantiationRequest.setInstantiationLevelId(additionalParams.getInstantiationLevel());
181         instantiationRequest.setSoftwareImages(additionalParams.getSoftwareImages());
182         instantiationRequest.setZones(additionalParams.getZones());
183         instantiationRequest.setExtManagedVirtualLinks(additionalParams.getExtManagedVirtualLinks());
184         for (ExtVirtualLinkData extVirtualLinkData : additionalParams.getExtVirtualLinks()) {
185             instantiationRequest.addExtVirtualLinksItem(extVirtualLinkData);
186         }
187         JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject();
188         if (additionalParams.getAdditionalParams() != null && !isEmpty(additionalParams.getAdditionalParams().toString())) {
189             for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(additionalParams.getAdditionalParams()).getAsJsonObject().entrySet()) {
190                 root.add(item.getKey(), item.getValue());
191             }
192         }
193         instantiationRequest.setAdditionalParams(root);
194         OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdInstantiatePost(vnfInfo.getId(), instantiationRequest, NOKIA_LCM_API_VERSION);
195         waitForOperationToFinish(vnfmId, vnfInfo.getId(), operationExecution.getId());
196     }
197
198     private void validateVimType(AdditionalParams additionalParams) {
199         switch (additionalParams.getVimType()) {
200             case OPENSTACK_V2_INFO:
201             case OPENSTACK_V3_INFO:
202             case VMWARE_VCLOUD_INFO:
203                 break;
204             default:
205                 throw fatalFailure(logger, "Only " + OPENSTACK_V2_INFO + ", " + OPENSTACK_V3_INFO + " and " + VMWARE_VCLOUD_INFO + " is the supported VIM types");
206         }
207     }
208
209     private String getVimId(Object additionalParams) {
210         return childElement(new Gson().toJsonTree(additionalParams).getAsJsonObject(), "vimId").getAsString();
211     }
212
213     private AdditionalParams convertInstantiationAdditionalParams(String csarId, Object additionalParams) {
214         JsonObject vnfParameters = child(child(new Gson().toJsonTree(additionalParams).getAsJsonObject(), "inputs"), "vnfs");
215         if (!vnfParameters.has(csarId)) {
216             throw fatalFailure(logger, "The additional parameter section does not contain setting for VNF with " + csarId + " CSAR id");
217         }
218         JsonElement additionalParamsForVnf = vnfParameters.get(csarId);
219         return new Gson().fromJson(additionalParamsForVnf, AdditionalParams.class);
220     }
221
222     private String getFlavorId(String vnfdContent) {
223         JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject();
224         JsonObject capabilities = child(child(child(root, "topology_template"), "substitution_mappings"), "capabilities");
225         JsonObject deploymentFlavorProperties = child(child(capabilities, "deployment_flavour"), "properties");
226         return childElement(deploymentFlavorProperties, "flavour_id").getAsString();
227     }
228
229     private Set<String> getAcceptableOperationParameters(String vnfdContent, String categroryOfOperation, String operationName) {
230         JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject();
231         JsonObject interfaces = child(child(child(root, "topology_template"), "substitution_mappings"), "interfaces");
232         JsonObject additionalParameters = child(child(child(child(interfaces, categroryOfOperation), operationName), "inputs"), "additional_parameters");
233         return additionalParameters.keySet();
234     }
235
236     private void addExernalLinksToRequest(List<ExtVirtualLinkInfo> extVirtualLinks, AdditionalParams additionalParams, InstantiateVnfRequest instantiationRequest, String vimId) {
237         for (ExtVirtualLinkInfo extVirtualLink : extVirtualLinks) {
238             ExtVirtualLinkData cbamExternalVirtualLink = new ExtVirtualLinkData();
239             cbamExternalVirtualLink.setVimId(vimId);
240             cbamExternalVirtualLink.setResourceId(extVirtualLink.getResourceId());
241             VnfExtCpData ecp = new VnfExtCpData();
242             cbamExternalVirtualLink.setExtVirtualLinkId(extVirtualLink.getVlInstanceId());
243             cbamExternalVirtualLink.getExtCps().add(ecp);
244             ecp.setCpdId(extVirtualLink.getCpdId());
245             List<NetworkAddress> addresses = additionalParams.getExternalConnectionPointAddresses().get(extVirtualLink.getCpdId());
246             ecp.setAddresses(addresses);
247             instantiationRequest.addExtVirtualLinksItem(cbamExternalVirtualLink);
248         }
249     }
250
251     private void addVnfdIdToVnfModifyableAttributeExtensions(String vnfmId, String vnfId, String onapCsarId) {
252         ModifyVnfInfoRequest request = new ModifyVnfInfoRequest();
253         VnfProperty onapCsarIdProperty = new VnfProperty();
254         onapCsarIdProperty.setName(ONAP_CSAR_ID);
255         onapCsarIdProperty.setValue(onapCsarId);
256         request.setExtensions(new ArrayList<>());
257         request.getExtensions().add(onapCsarIdProperty);
258         VnfProperty externalVnfmIdProperty = new VnfProperty();
259         externalVnfmIdProperty.setName(EXTERNAL_VNFM_ID);
260         externalVnfmIdProperty.setValue(vnfmId);
261         request.getExtensions().add(externalVnfmIdProperty);
262         request.setVnfConfigurableProperties(null);
263         try {
264             OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdPatch(vnfId, request, NOKIA_LCM_API_VERSION);
265             waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
266         } catch (ApiException e) {
267             fatalFailure(logger, "Unable to set the " + ONAP_CSAR_ID + " property on the VNF", e);
268         }
269     }
270
271     private OPENSTACKV3INFO buildOpenStackV3INFO(String vimId, AdditionalParams additionalParams, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) {
272         OPENSTACKV3INFO openstackv3INFO = new OPENSTACKV3INFO();
273         openstackv3INFO.setVimInfoType(OPENSTACK_V3_INFO);
274         OpenStackAccessInfoV3 accessInfov3 = new OpenStackAccessInfoV3();
275         openstackv3INFO.accessInfo(accessInfov3);
276         accessInfov3.setPassword(vimInfo.getPassword());
277         accessInfov3.setDomain(additionalParams.getDomain());
278         accessInfov3.setProject(vim.getAccessInfo().getTenant());
279         accessInfov3.setRegion(getRegionName(vimId));
280         accessInfov3.setUsername(vimInfo.getUserName());
281         EndpointInfo interfaceInfoV3 = new EndpointInfo();
282         interfaceInfoV3.setEndpoint(vimInfo.getUrl());
283         if (!isEmpty(vimInfo.getSslInsecure())) {
284             interfaceInfoV3.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure()));
285             interfaceInfoV3.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure()));
286         } else {
287             interfaceInfoV3.setSkipCertificateHostnameCheck(true);
288             interfaceInfoV3.setSkipCertificateVerification(true);
289         }
290         if (!interfaceInfoV3.isSkipCertificateVerification()) {
291             interfaceInfoV3.setTrustedCertificates(new ArrayList<>());
292             for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) {
293                 interfaceInfoV3.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8));
294             }
295         }
296         openstackv3INFO.setInterfaceInfo(interfaceInfoV3);
297         openstackv3INFO.setId(vimId);
298         return openstackv3INFO;
299     }
300
301     private OPENSTACKV2INFO buildOpenStackV2INFO(String vimId, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) {
302         OPENSTACKV2INFO openstackv2INFO = new OPENSTACKV2INFO();
303         openstackv2INFO.setVimInfoType(OPENSTACK_V2_INFO);
304         OpenStackAccessInfoV2 accessInfo = new OpenStackAccessInfoV2();
305         openstackv2INFO.setAccessInfo(accessInfo);
306         accessInfo.setPassword(vimInfo.getPassword());
307         accessInfo.setTenant(vim.getAccessInfo().getTenant());
308         accessInfo.setUsername(vimInfo.getUserName());
309         accessInfo.setRegion(getRegionName(vimId));
310         EndpointInfo interfaceEndpoint = new EndpointInfo();
311         if (!isEmpty(vimInfo.getSslInsecure())) {
312             interfaceEndpoint.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure()));
313             interfaceEndpoint.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure()));
314         } else {
315             interfaceEndpoint.setSkipCertificateHostnameCheck(true);
316             interfaceEndpoint.setSkipCertificateVerification(true);
317         }
318         interfaceEndpoint.setEndpoint(vimInfo.getUrl());
319         if (!interfaceEndpoint.isSkipCertificateVerification()) {
320             interfaceEndpoint.setTrustedCertificates(new ArrayList<>());
321             for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) {
322                 interfaceEndpoint.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8));
323             }
324         }
325         openstackv2INFO.setInterfaceInfo(interfaceEndpoint);
326         openstackv2INFO.setId(vimId);
327         return openstackv2INFO;
328     }
329
330     private VMWAREVCLOUDINFO buildVcloudInfo(String vimId, org.onap.vnfmdriver.model.VimInfo vimInfo) {
331         VMWAREVCLOUDINFO vcloudInfo = new VMWAREVCLOUDINFO();
332         vcloudInfo.setVimInfoType(VMWARE_VCLOUD_INFO);
333         VCloudAccessInfo accessInfo = new VCloudAccessInfo();
334         vcloudInfo.setAccessInfo(accessInfo);
335         accessInfo.setPassword(vimInfo.getPassword());
336         accessInfo.setUsername(vimInfo.getUserName());
337         accessInfo.setOrganization(getRegionName(vimId));
338         EndpointInfo interfaceEndpoint = new EndpointInfo();
339         if (!isEmpty(vimInfo.getSslInsecure())) {
340             interfaceEndpoint.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure()));
341             interfaceEndpoint.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure()));
342         } else {
343             interfaceEndpoint.setSkipCertificateHostnameCheck(true);
344             interfaceEndpoint.setSkipCertificateVerification(true);
345         }
346         interfaceEndpoint.setEndpoint(vimInfo.getUrl());
347         if (!interfaceEndpoint.isSkipCertificateVerification()) {
348             interfaceEndpoint.setTrustedCertificates(new ArrayList<>());
349             for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) {
350                 interfaceEndpoint.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8));
351             }
352         }
353         vcloudInfo.setInterfaceInfo(interfaceEndpoint);
354         vcloudInfo.setId(vimId);
355         return vcloudInfo;
356     }
357
358     /**
359      * Terminates and deletes the VNF
360      * <ul>
361      * <li>fails if the VNF does not exist</li>
362      * <li>terminates if instantiated</li>
363      * <li>deletes the VNF</li>
364      * </ul>
365      *
366      * @param vnfmId       the identifier of the VNFM
367      * @param vnfId        the identifier of the VNF
368      * @param request      the termination request
369      * @param httpResponse the HTTP response
370      * @return the job for polling the progress of the termination
371      */
372     public JobInfo terminateVnf(String vnfmId, String vnfId, VnfTerminateRequest request, HttpServletResponse httpResponse) {
373         return scheduleExecution(vnfId, httpResponse, "terminate", jobInfo -> {
374             TerminateVnfRequest cbamRequest = new TerminateVnfRequest();
375             cbamRequest.setAdditionalParams(jobInfo);
376             if (request.getTerminationType() == null) {
377                 cbamRequest.setTerminationType(TerminationType.FORCEFUL);
378             } else {
379                 if (request.getTerminationType().equals(VnfTerminationType.GRACEFUL)) {
380                     cbamRequest.setTerminationType(TerminationType.GRACEFUL);
381                     cbamRequest.setGracefulTerminationTimeout(parseInt(request.getGracefulTerminationTimeout()));
382                 } else {
383                     cbamRequest.setTerminationType(TerminationType.FORCEFUL);
384                 }
385             }
386             com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
387             if (vnf.getInstantiationState() == INSTANTIATED) {
388                 terminateVnf(vnfmId, vnfId, jobInfo, cbamRequest, vnf);
389
390             } else {
391                 cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION);
392
393             }
394         });
395     }
396
397     private void terminateVnf(String vnfmId, String vnfId, JobInfo jobInfo, TerminateVnfRequest cbamRequest, com.nokia.cbam.lcm.v32.model.VnfInfo vnf) throws ApiException {
398         String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf);
399         grantManager.requestGrantForTerminate(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), vnf, jobInfo.getJobId());
400         OperationExecution terminationOperation = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdTerminatePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION);
401         OperationExecution finishedOperation = waitForOperationToFinish(vnfmId, vnfId, terminationOperation.getId());
402         if (finishedOperation.getStatus() == FINISHED) {
403             notificationManager.waitForTerminationToBeProcessed(finishedOperation.getId());
404             logger.info("Deleting VNF with {}", vnfId);
405             cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION);
406             logger.info("VNF with {} has been deleted", vnfId);
407
408         } else {
409             logger.error("Unable to terminate VNF the operation did not finish with success");
410         }
411     }
412
413     private String getVimIdFromInstantiationRequest(String vnfmId, com.nokia.cbam.lcm.v32.model.VnfInfo vnf) throws ApiException {
414         OperationExecution lastInstantiation = findLastInstantiation(vnf.getOperationExecutions());
415         Object operationParameters = cbamRestApiProvider.getCbamOperationExecutionApi(vnfmId).operationExecutionsOperationExecutionIdOperationParamsGet(lastInstantiation.getId(), NOKIA_LCM_API_VERSION);
416         JsonObject root = new Gson().toJsonTree(operationParameters).getAsJsonObject();
417         return childElement(childElement(root, "vims").getAsJsonArray().get(0).getAsJsonObject(), "id").getAsString();
418     }
419
420     private String getVnfdIdFromModifyableAttributes(com.nokia.cbam.lcm.v32.model.VnfInfo vnf) {
421         return find(vnf.getExtensions(), p -> p.getName().equals(ONAP_CSAR_ID)).getValue().toString();
422     }
423
424     /**
425      * @param vnfmId the identifier of the VNFM
426      * @param vnfId  the identifier of the VNF
427      * @return the current state of the VNF
428      */
429     public VnfInfo queryVnf(String vnfmId, String vnfId) {
430         try {
431             com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
432             VnfInfo vnfInfo = new VnfInfo();
433             vnfInfo.setVersion(cbamVnfInfo.getVnfSoftwareVersion());
434             vnfInfo.setVnfInstanceId(vnfId);
435             String onapCsarId = getVnfdIdFromModifyableAttributes(cbamVnfInfo);
436             vnfInfo.setVnfdId(onapCsarId);
437             vnfInfo.setVnfPackageId(onapCsarId);
438             vnfInfo.setVnfInstanceDescription(cbamVnfInfo.getDescription());
439             vnfInfo.setVnfInstanceName(cbamVnfInfo.getName());
440             vnfInfo.setVnfProvider(cbamVnfInfo.getVnfProvider());
441             vnfInfo.setVnfStatus("ACTIVE");
442             vnfInfo.setVnfType("Kuku");
443             return vnfInfo;
444         } catch (ApiException e) {
445             throw fatalFailure(logger, "Unable to query VNF (" + vnfId + ")", e);
446         }
447     }
448
449     private ScaleDirection convert(org.onap.vnfmdriver.model.ScaleDirection direction) {
450         if (org.onap.vnfmdriver.model.ScaleDirection.IN.equals(direction)) {
451             return ScaleDirection.IN;
452         } else {
453             return ScaleDirection.OUT;
454         }
455     }
456
457     /**
458      * Scale the VNF
459      *
460      * @param vnfmId       the identifier of the VNFM
461      * @param vnfId        the identifier of the VNF
462      * @param request      the scale request
463      * @param httpResponse the HTTP response
464      * @return the job for tracking the scale
465      */
466     public JobInfo scaleVnf(String vnfmId, String vnfId, VnfScaleRequest request, HttpServletResponse httpResponse) {
467         if (logger.isInfoEnabled()) {
468             logger.info("Scale VNF with {} identifier REST: {}", vnfId, new Gson().toJson(request));
469         }
470         return scheduleExecution(vnfId, httpResponse, "scale", jobInfo -> {
471             ScaleVnfRequest cbamRequest = new ScaleVnfRequest();
472             cbamRequest.setAspectId(request.getAspectId());
473             cbamRequest.setNumberOfSteps(Integer.valueOf(request.getNumberOfSteps()));
474             cbamRequest.setType(convert(request.getType()));
475             com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
476             JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject();
477             com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
478             String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, cbamVnfInfo.getVnfdId());
479             Set<String> acceptableOperationParameters = getAcceptableOperationParameters(vnfdContent, "Basic", "scale");
480             if (request.getAdditionalParam() != null) {
481                 for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(request.getAdditionalParam()).getAsJsonObject().entrySet()) {
482                     if (acceptableOperationParameters.contains(item.getKey())) {
483                         root.add(item.getKey(), item.getValue());
484                     }
485                 }
486             } else {
487                 logger.warn("No additional parameters were passed for scaling");
488             }
489             cbamRequest.setAdditionalParams(root);
490             grantManager.requestGrantForScale(vnfmId, vnfId, getVimIdFromInstantiationRequest(vnfmId, vnf), getVnfdIdFromModifyableAttributes(vnf), request, jobInfo.getJobId());
491             OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdScalePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION);
492             waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
493         });
494     }
495
496     /**
497      * Heal the VNF
498      *
499      * @param vnfmId       the identifier of the VNFM
500      * @param vnfId        the identifier of the VNF
501      * @param request      the heal request
502      * @param httpResponse the HTTP response
503      * @return the job for tracking the heal
504      */
505     public JobInfo healVnf(String vnfmId, String vnfId, VnfHealRequest request, HttpServletResponse httpResponse) {
506         return scheduleExecution(vnfId, httpResponse, "heal", job -> {
507             HealVnfRequest cbamHealRequest = new HealVnfRequest();
508             Map<String, String> additionalParams = new HashMap<>();
509             additionalParams.put("vmName", request.getAffectedvm().getVmname());
510             additionalParams.put("action", request.getAction());
511             additionalParams.put("jobId", job.getJobId());
512             cbamHealRequest.setAdditionalParams(additionalParams);
513             com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION);
514             String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf);
515             grantManager.requestGrantForHeal(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), request, job.getJobId());
516             OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdHealPost(vnfId, cbamHealRequest, NOKIA_LCM_API_VERSION);
517             waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId());
518         });
519     }
520
521     private JobInfo scheduleExecution(String vnfId, HttpServletResponse httpResponse, String operation, AsynchronousExecution asynchronExecution) {
522         JobInfo jobInfo = new JobInfo();
523         jobInfo.setJobId(jobManager.spawnJob(vnfId, httpResponse));
524         executorService.submit(() -> {
525             try {
526                 asynchronExecution.execute(jobInfo);
527             } catch (RuntimeException e) {
528                 logger.error("Unable to " + operation + " VNF with " + vnfId + " identifier", e);
529                 jobManager.jobFinished(jobInfo.getJobId());
530                 throw e;
531             } catch (Exception e) {
532                 String msg = "Unable to " + operation + " VNF with " + vnfId + " identifier";
533                 logger.error(msg, e);
534                 //the job can only be signaled to be finished after the error is logged
535                 jobManager.jobFinished(jobInfo.getJobId());
536                 throw new UserVisibleError(msg, e);
537             }
538             jobManager.jobFinished(jobInfo.getJobId());
539         });
540         return jobInfo;
541     }
542
543     private OperationExecution waitForOperationToFinish(String vnfmId, String vnfId, String operationExecutionId) {
544         while (true) {
545             try {
546                 OperationExecution operationExecution = find(cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdOperationExecutionsGet(vnfId, NOKIA_LCM_API_VERSION), opEx -> operationExecutionId.equals(opEx.getId()));
547                 if (hasOperationFinished(operationExecution)) {
548                     logger.debug("Operation finished with " + operationExecution.getId());
549                     return operationExecution;
550                 }
551             } catch (Exception e) {
552                 //swallow exception and retry
553                 logger.warn("Unable to retrieve operations details", e);
554             }
555             systemFunctions().sleep(OPERATION_STATUS_POLLING_INTERVAL_IN_MS);
556         }
557     }
558
559     private boolean hasOperationFinished(OperationExecution operationExecution) {
560         return newHashSet(FINISHED, OperationStatus.FAILED).contains(operationExecution.getStatus());
561     }
562
563     @FunctionalInterface
564     private interface AsynchronousExecution {
565         void execute(JobInfo job) throws ApiException;
566     }
567 }