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