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