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