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