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