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