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