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