Replaced all tabs with spaces in java and pom.xml
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / adapters / vnf / MsoVnfAdapterImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7  * ================================================================================
8  * Modifications Copyright (c) 2019 Samsung
9  * ================================================================================
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.so.adapters.vnf;
25
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Optional;
35 import java.util.concurrent.TimeUnit;
36 import javax.jws.WebService;
37 import javax.xml.ws.Holder;
38 import org.apache.commons.collections.CollectionUtils;
39 import org.onap.so.adapters.valet.GenericValetResponse;
40 import org.onap.so.adapters.valet.ValetClient;
41 import org.onap.so.adapters.valet.beans.HeatRequest;
42 import org.onap.so.adapters.valet.beans.ValetConfirmResponse;
43 import org.onap.so.adapters.valet.beans.ValetCreateResponse;
44 import org.onap.so.adapters.valet.beans.ValetDeleteResponse;
45 import org.onap.so.adapters.valet.beans.ValetRollbackResponse;
46 import org.onap.so.adapters.valet.beans.ValetStatus;
47 import org.onap.so.adapters.valet.beans.ValetUpdateResponse;
48 import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists;
49 import org.onap.so.adapters.vnf.exceptions.VnfException;
50 import org.onap.so.adapters.vnf.exceptions.VnfNotFound;
51 import org.onap.so.client.aai.AAIResourcesClient;
52 import org.onap.so.cloud.CloudConfig;
53 import org.onap.so.db.catalog.beans.CloudIdentity;
54 import org.onap.so.db.catalog.beans.CloudSite;
55 import org.onap.so.db.catalog.beans.HeatEnvironment;
56 import org.onap.so.db.catalog.beans.HeatFiles;
57 import org.onap.so.db.catalog.beans.HeatTemplate;
58 import org.onap.so.db.catalog.beans.HeatTemplateParam;
59 import org.onap.so.db.catalog.beans.VfModule;
60 import org.onap.so.db.catalog.beans.VfModuleCustomization;
61 import org.onap.so.db.catalog.beans.VnfResource;
62 import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository;
63 import org.onap.so.db.catalog.data.repository.VnfResourceRepository;
64 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
65 import org.onap.so.entity.MsoRequest;
66 import org.onap.so.logger.ErrorCode;
67 import org.onap.so.heatbridge.HeatBridgeApi;
68 import org.onap.so.heatbridge.HeatBridgeImpl;
69 import org.onap.so.heatbridge.openstack.api.OpenstackClient;
70 import org.onap.so.logger.MessageEnum;
71 import org.onap.so.openstack.beans.HeatStatus;
72 import org.onap.so.openstack.beans.StackInfo;
73 import org.onap.so.openstack.beans.VnfRollback;
74 import org.onap.so.openstack.beans.VnfStatus;
75 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
76 import org.onap.so.openstack.exceptions.MsoException;
77 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
78 import org.onap.so.openstack.exceptions.MsoHeatNotFoundException;
79 import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry;
80 import org.onap.so.openstack.utils.MsoHeatUtils;
81 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
82 import org.openstack4j.model.compute.Flavor;
83 import org.openstack4j.model.compute.Image;
84 import org.openstack4j.model.compute.Server;
85 import org.openstack4j.model.heat.Resource;
86 import org.slf4j.Logger;
87 import org.slf4j.LoggerFactory;
88 import org.springframework.beans.factory.annotation.Autowired;
89 import org.springframework.core.env.Environment;
90 import org.springframework.stereotype.Component;
91 import org.springframework.transaction.annotation.Transactional;
92 import com.fasterxml.jackson.core.JsonParseException;
93 import com.fasterxml.jackson.databind.JsonNode;
94 import com.fasterxml.jackson.databind.ObjectMapper;
95
96 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter",
97         targetNamespace = "http://org.onap.so/vnf")
98 @Component
99 @Transactional
100 public class MsoVnfAdapterImpl implements MsoVnfAdapter {
101
102     @Autowired
103     private CloudConfig cloudConfig;
104
105     @Autowired
106     private Environment environment;
107
108     private static final Logger logger = LoggerFactory.getLogger(MsoVnfAdapterImpl.class);
109
110     private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
111     private static final String VNF_ADAPTER_SERVICE_NAME = "MSO-BPMN:MSO-VnfAdapter.";
112     private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
113     private static final String ADD_GET_FILES_ON_VOLUME_REQ = "org.onap.so.adapters.vnf.addGetFilesOnVolumeReq";
114     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
115     private static final String VALET_ENABLED = "org.onap.so.adapters.vnf.valet_enabled";
116     private static final String FAIL_REQUESTS_ON_VALET_FAILURE =
117             "org.onap.so.adapters.vnf.fail_requests_on_valet_failure";
118     private static final String SUCCESS_MSG = "Successfully received response from Open Stack";
119
120     @Autowired
121     private VFModuleCustomizationRepository vfModuleCustomRepo;
122
123
124     @Autowired
125     private VnfResourceRepository vnfResourceRepo;
126
127     @Autowired
128     private MsoHeatUtilsWithUpdate heatU;
129     @Autowired
130     private MsoHeatUtils heat;
131     @Autowired
132     private ValetClient vci;
133
134     /**
135      * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
136      * 
137      * @see MsoVnfAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
138      */
139     public MsoVnfAdapterImpl() {
140         // Do nothing
141         // DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
142     }
143
144     /**
145      * Health Check web method. Does nothing but return to show the adapter is deployed.
146      */
147     @Override
148     public void healthCheck() {
149         logger.debug("Health check call in VNF Adapter");
150     }
151
152     /**
153      * This is the "Create VNF" web service implementation. It will create a new VNF of the requested type in the
154      * specified cloud and tenant. The tenant must exist before this service is called.
155      *
156      * If a VNF with the same name already exists, this can be considered a success or failure, depending on the value
157      * of the 'failIfExists' parameter.
158      *
159      * All VNF types will be defined in the MSO catalog. The caller must request one of these pre-defined types or an
160      * error will be returned. Within the catalog, each VNF type references (among other things) a Heat template which
161      * is used to deploy the required VNF artifacts (VMs, networks, etc.) to the cloud.
162      *
163      * Depending on the Heat template, a variable set of input parameters will be defined, some of which are required.
164      * The caller is responsible to pass the necessary input data for the VNF or an error will be thrown.
165      *
166      * The method returns the vnfId (the canonical name), a Map of VNF output attributes, and a VnfRollback object. This
167      * last object can be passed as-is to the rollbackVnf operation to undo everything that was created for the VNF.
168      * This is useful if a VNF is successfully created but the orchestrator fails on a subsequent operation.
169      *
170      * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
171      * @param cloudOwner cloud owner of the cloud region in which to create the VNF
172      * @param tenantId Openstack tenant identifier
173      * @param vnfType VNF type key, should match a VNF definition in catalog DB
174      * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
175      * @param vnfName Name to be assigned to the new VNF
176      * @param inputs Map of key=value inputs for VNF stack creation
177      * @param failIfExists Flag whether already existing VNF should be considered a success or failure
178      * @param msoRequest Request tracking information for logs
179      * @param vnfId Holder for output VNF Openstack ID
180      * @param outputs Holder for Map of VNF outputs from heat (assigned IPs, etc)
181      * @param rollback Holder for returning VnfRollback object
182      */
183     @Override
184     public void createVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion,
185             String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs,
186             Boolean failIfExists, Boolean backout, Boolean enableBridge, MsoRequest msoRequest, Holder<String> vnfId,
187             Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) throws VnfException {
188         // parameters used for multicloud adapter
189         String genericVnfId = "";
190         String vfModuleId = "";
191         // Create a hook here to catch shortcut createVf requests:
192         if (requestType != null && requestType.startsWith("VFMOD")) {
193             logger.debug("Calling createVfModule from createVnf -- requestType=" + requestType);
194             String newRequestType = requestType.substring(5);
195             String vfVolGroupHeatStackId = "";
196             String vfBaseHeatStackId = "";
197             try {
198                 if (volumeGroupHeatStackId != null) {
199                     vfVolGroupHeatStackId =
200                             volumeGroupHeatStackId.substring(0, volumeGroupHeatStackId.lastIndexOf('|'));
201                     vfBaseHeatStackId = volumeGroupHeatStackId.substring(volumeGroupHeatStackId.lastIndexOf('|') + 1);
202                 }
203             } catch (Exception e) {
204                 // might be ok - both are just blank
205                 logger.debug("ERROR trying to parse the volumeGroupHeatStackId {}", volumeGroupHeatStackId, e);
206             }
207             this.createVfModule(cloudSiteId, cloudOwner, tenantId, vnfType, vnfVersion, genericVnfId, vnfName,
208                     vfModuleId, newRequestType, vfVolGroupHeatStackId, vfBaseHeatStackId, null, inputs, failIfExists,
209                     backout, enableBridge, msoRequest, vnfId, outputs, rollback);
210             return;
211         }
212         // createVf will know if the requestType starts with "X" that it's the "old" way
213         StringBuilder newRequestTypeSb = new StringBuilder("X");
214         String vfVolGroupHeatStackId = "";
215         String vfBaseHeatStackId = "";
216         if (requestType != null) {
217             newRequestTypeSb.append(requestType);
218         }
219         this.createVfModule(cloudSiteId, cloudOwner, tenantId, vnfType, vnfVersion, genericVnfId, vnfName, vfModuleId,
220                 newRequestTypeSb.toString(), vfVolGroupHeatStackId, vfBaseHeatStackId, null, inputs, failIfExists,
221                 backout, enableBridge, msoRequest, vnfId, outputs, rollback);
222         return;
223         // End createVf shortcut
224     }
225
226     @Override
227     public void updateVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion,
228             String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs,
229             MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback)
230             throws VnfException {
231         // As of 1707 - this method should no longer be called
232         logger.debug("UpdateVnf called?? This should not be called any longer - update vfModule");
233     }
234
235     /**
236      * This is the "Query VNF" web service implementation. It will look up a VNF by name or ID in the specified cloud
237      * and tenant.
238      *
239      * The method returns an indicator that the VNF exists, its Openstack internal ID, its status, and the set of
240      * outputs (from when the stack was created).
241      *
242      * @param cloudSiteId CLLI code of the cloud site in which to query
243      * @param tenantId Openstack tenant identifier
244      * @param vnfName VNF Name or Openstack ID
245      * @param msoRequest Request tracking information for logs
246      * @param vnfExists Flag reporting the result of the query
247      * @param vnfId Holder for output VNF Openstack ID
248      * @param outputs Holder for Map of VNF outputs from heat (assigned IPs, etc)
249      */
250     @Override
251     public void queryVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, MsoRequest msoRequest,
252             Holder<Boolean> vnfExists, Holder<String> vnfId, Holder<VnfStatus> status,
253             Holder<Map<String, String>> outputs) throws VnfException {
254
255         logger.debug("Querying VNF {} in {}", vnfName, cloudSiteId + "/" + tenantId);
256
257         // Will capture execution time for metrics
258         long startTime = System.currentTimeMillis();
259
260         StackInfo heatStack = null;
261         long subStartTime = System.currentTimeMillis();
262         try {
263             heatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, vnfName);
264         } catch (MsoException me) {
265             me.addContext("QueryVNF");
266             // Failed to query the Stack due to an openstack exception.
267             // Convert to a generic VnfException
268             String error =
269                     "Query VNF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
270             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudSiteId,
271                     tenantId, "OpenStack", "QueryVNF", ErrorCode.DataError.getValue(), "Exception - queryStack", me);
272             logger.debug(error);
273             throw new VnfException(me);
274         }
275
276         // Populate the outputs based on the returned Stack information
277         //
278         if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) {
279             // Not Found
280             vnfExists.value = Boolean.FALSE;
281             status.value = VnfStatus.NOTFOUND;
282             vnfId.value = null;
283             outputs.value = new HashMap<>(); // Return as an empty map
284
285             logger.debug("VNF {} not found", vnfName);
286         } else {
287             vnfExists.value = Boolean.TRUE;
288             status.value = stackStatusToVnfStatus(heatStack.getStatus());
289             vnfId.value = heatStack.getCanonicalName();
290             outputs.value = copyStringOutputs(heatStack.getOutputs());
291
292             logger.debug("VNF {} found, ID = {}", vnfName, vnfId.value);
293         }
294         return;
295     }
296
297     /**
298      * This is the "Delete VNF" web service implementation. It will delete a VNF by name or ID in the specified cloud
299      * and tenant.
300      *
301      * The method has no outputs.
302      *
303      * @param cloudSiteId CLLI code of the cloud site in which to delete
304      * @param cloudOwner cloud owner of the cloud region in which to delete
305      * @param tenantId Openstack tenant identifier
306      * @param vnfName VNF Name or Openstack ID
307      * @param msoRequest Request tracking information for logs
308      */
309     @Override
310     public void deleteVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, MsoRequest msoRequest)
311             throws VnfException {
312
313         logger.debug("Deleting VNF {} in {}", vnfName, cloudSiteId + "/" + tenantId);
314         // Will capture execution time for metrics
315         long startTime = System.currentTimeMillis();
316
317         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
318         // The possible outcomes of deleteStack are a StackInfo object with status
319         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
320         // could be thrown.
321         long subStartTime = System.currentTimeMillis();
322         try {
323             heat.deleteStack(tenantId, cloudOwner, cloudSiteId, vnfName, true);
324         } catch (MsoException me) {
325             me.addContext("DeleteVNF");
326             // Failed to query the Stack due to an openstack exception.
327             // Convert to a generic VnfException
328             String error =
329                     "Delete VNF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
330             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudOwner,
331                     cloudSiteId, tenantId, "OpenStack", "DeleteVNF", ErrorCode.DataError.getValue(),
332                     "Exception - DeleteVNF", me);
333             logger.debug(error);
334             throw new VnfException(me);
335         }
336
337         // On success, nothing is returned.
338         return;
339     }
340
341     /**
342      * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
343      * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
344      * to undo the creation.
345      */
346     @Override
347     public void rollbackVnf(VnfRollback rollback) throws VnfException {
348         long startTime = System.currentTimeMillis();
349         // rollback may be null (e.g. if stack already existed when Create was called)
350         if (rollback == null) {
351             logger.info(MessageEnum.RA_ROLLBACK_NULL.toString(), "OpenStack", "rollbackVnf");
352             return;
353         }
354
355         // Get the elements of the VnfRollback object for easier access
356         String cloudSiteId = rollback.getCloudSiteId();
357         String cloudOwner = rollback.getCloudOwner();
358         String tenantId = rollback.getTenantId();
359         String vnfId = rollback.getVnfId();
360
361         logger.debug("Rolling Back VNF {} in {}", vnfId, cloudOwner + "/" + cloudSiteId + "/" + tenantId);
362
363         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
364         // The possible outcomes of deleteStack are a StackInfo object with status
365         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
366         // could be thrown.
367         try {
368             heat.deleteStack(tenantId, cloudOwner, cloudSiteId, vnfId, true);
369         } catch (MsoException me) {
370             // Failed to rollback the Stack due to an openstack exception.
371             // Convert to a generic VnfException
372             me.addContext("RollbackVNF");
373             String error =
374                     "Rollback VNF: " + vnfId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
375             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfId, cloudOwner,
376                     cloudSiteId, tenantId, "OpenStack", "DeleteStack", ErrorCode.DataError.getValue(),
377                     "Exception - DeleteStack", me);
378             logger.debug(error);
379             throw new VnfException(me);
380         }
381         return;
382     }
383
384     private VnfStatus stackStatusToVnfStatus(HeatStatus stackStatus) {
385         switch (stackStatus) {
386             case CREATED:
387                 return VnfStatus.ACTIVE;
388             case UPDATED:
389                 return VnfStatus.ACTIVE;
390             case FAILED:
391                 return VnfStatus.FAILED;
392             default:
393                 return VnfStatus.UNKNOWN;
394         }
395     }
396
397     private Map<String, String> copyStringOutputs(Map<String, Object> stackOutputs) {
398         Map<String, String> stringOutputs = new HashMap<>();
399         for (Map.Entry<String, Object> entry : stackOutputs.entrySet()) {
400             String key = entry.getKey();
401             Object value = entry.getValue();
402             try {
403                 stringOutputs.put(key, value.toString());
404             } catch (Exception e) {
405                 StringBuilder msg = new StringBuilder("Unable to add " + key + " to outputs");
406                 if (value instanceof Integer) { // nothing to add to the message
407                 } else if (value instanceof JsonNode) {
408                     msg.append(" - exception converting JsonNode");
409                 } else if (value instanceof java.util.LinkedHashMap) {
410                     msg.append(" exception converting LinkedHashMap");
411                 } else {
412                     msg.append(" - unable to call .toString() " + e.getMessage());
413                 }
414                 logger.debug(msg.toString(), e);
415             }
416         }
417         return stringOutputs;
418     }
419
420     private Map<String, Object> copyStringInputs(Map<String, Object> stringInputs) {
421         return new HashMap<>(stringInputs);
422     }
423
424     protected boolean callHeatbridge(String heatStackId) {
425         String executionDir = "/usr/local/lib/python2.7/dist-packages/heatbridge";
426         String openstackIdentityUrl = "", username = "", password = "", tenant = "", region = "", owner = "";
427         long waitTimeMs = 10000L;
428         try {
429             String[] cmdarray = {"/usr/bin/python", "HeatBridgeMain.py", openstackIdentityUrl, username, password,
430                     tenant, region, owner, heatStackId};
431             String[] envp = null;
432             File dir = new File(executionDir);
433             logger.debug("Calling HeatBridgeMain.py in {} with arguments {}", dir, Arrays.toString(cmdarray));
434             Runtime r = Runtime.getRuntime();
435             Process p = r.exec(cmdarray, envp, dir);
436             boolean wait = p.waitFor(waitTimeMs, TimeUnit.MILLISECONDS);
437
438             logger.debug(" HeatBridgeMain.py returned {} with code {}", wait, p.exitValue());
439             return wait && p.exitValue() == 0;
440         } catch (IOException e) {
441             logger.debug(" HeatBridgeMain.py failed with IO Exception! " + e);
442             return false;
443         } catch (RuntimeException e) {
444             logger.debug(" HeatBridgeMain.py failed during runtime!" + e);
445             return false;
446         } catch (Exception e) {
447             logger.debug(" HeatBridgeMain.py failed for unknown reasons! " + e);
448             return false;
449         }
450     }
451
452     private void heatbridge(StackInfo heatStack, String cloudOwner, String cloudSiteId, String tenantId,
453             String genericVnfName, String vfModuleId) {
454         try {
455             CloudSite cloudSite =
456                     cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId));
457             CloudIdentity cloudIdentity = cloudSite.getIdentityService();
458             String heatStackId = heatStack.getCanonicalName().split("/")[1];
459
460             List<String> oobMgtNetNames = new ArrayList<>();
461
462             HeatBridgeApi heatBridgeClient =
463                     new HeatBridgeImpl(new AAIResourcesClient(), cloudIdentity, cloudOwner, cloudSiteId, tenantId);
464
465             OpenstackClient openstackClient = heatBridgeClient.authenticate();
466             List<Resource> stackResources = heatBridgeClient.queryNestedHeatStackResources(heatStackId);
467
468             List<Server> osServers = heatBridgeClient.getAllOpenstackServers(stackResources);
469
470             List<Image> osImages = heatBridgeClient.extractOpenstackImagesFromServers(osServers);
471
472             List<Flavor> osFlavors = heatBridgeClient.extractOpenstackFlavorsFromServers(osServers);
473
474             logger.debug("Successfully queried heat stack{} for resources.", heatStackId);
475             // os images
476             if (osImages != null && !osImages.isEmpty()) {
477                 heatBridgeClient.buildAddImagesToAaiAction(osImages);
478                 logger.debug("Successfully built AAI actions to add images.");
479             } else {
480                 logger.debug("No images to update to AAI.");
481             }
482             // flavors
483             if (osFlavors != null && !osFlavors.isEmpty()) {
484                 heatBridgeClient.buildAddFlavorsToAaiAction(osFlavors);
485                 logger.debug("Successfully built AAI actions to add flavors.");
486             } else {
487                 logger.debug("No flavors to update to AAI.");
488             }
489
490             // compute resources
491             heatBridgeClient.buildAddVserversToAaiAction(genericVnfName, vfModuleId, osServers);
492             logger.debug("Successfully queried compute resources and built AAI vserver actions.");
493
494             // neutron resources
495             List<String> oobMgtNetIds = new ArrayList<>();
496
497             // if no network-id list is provided, however network-name list is
498             if (!CollectionUtils.isEmpty(oobMgtNetNames)) {
499                 oobMgtNetIds = heatBridgeClient.extractNetworkIds(oobMgtNetNames);
500             }
501             heatBridgeClient.buildAddVserverLInterfacesToAaiAction(stackResources, oobMgtNetIds);
502             logger.debug(
503                     "Successfully queried neutron resources and built AAI actions to add l-interfaces to vservers.");
504
505             // Update AAI
506             heatBridgeClient.submitToAai();
507         } catch (Exception ex) {
508             logger.debug("Heatbrige failed for stackId: " + heatStack.getCanonicalName(), ex);
509         }
510     }
511
512     private String convertNode(final JsonNode node) {
513         try {
514             final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
515             return JSON_MAPPER.writeValueAsString(obj);
516         } catch (JsonParseException jpe) {
517             logger.debug("Error converting json to string: {}", jpe.getMessage(), jpe);
518         } catch (Exception e) {
519             logger.debug("Error converting json to string: {}", e.getMessage(), e);
520         }
521         return "[Error converting json to string]";
522     }
523
524     private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
525         if (objectMap == null) {
526             return null;
527         }
528         Map<String, String> stringMap = new HashMap<>();
529         for (String key : objectMap.keySet()) {
530             if (!stringMap.containsKey(key)) {
531                 Object obj = objectMap.get(key);
532                 if (obj instanceof String) {
533                     stringMap.put(key, (String) objectMap.get(key));
534                 } else if (obj instanceof JsonNode) {
535                     // This is a bit of mess - but I think it's the least impacting
536                     // let's convert it BACK to a string - then it will get converted back later
537                     try {
538                         String str = this.convertNode((JsonNode) obj);
539                         stringMap.put(key, str);
540                     } catch (Exception e) {
541                         logger.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode " + key, e);
542                         // okay in this instance - only string values (fqdn) are expected to be needed
543                     }
544                 } else if (obj instanceof java.util.LinkedHashMap) {
545                     logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
546                     try {
547                         String str = JSON_MAPPER.writeValueAsString(obj);
548                         stringMap.put(key, str);
549                     } catch (Exception e) {
550                         logger.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap " + key, e);
551                     }
552                 } else if (obj instanceof Integer) {
553                     try {
554                         String str = "" + obj;
555                         stringMap.put(key, str);
556                     } catch (Exception e) {
557                         logger.debug("DANGER WILL ROBINSON: unable to convert value for Integer " + key, e);
558                     }
559                 } else {
560                     try {
561                         String str = obj.toString();
562                         stringMap.put(key, str);
563                     } catch (Exception e) {
564                         logger.debug(
565                                 "DANGER WILL ROBINSON: unable to convert value " + key + " (" + e.getMessage() + ")",
566                                 e);
567                     }
568                 }
569             }
570         }
571
572         return stringMap;
573     }
574
575     @Override
576     public void createVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfType,
577             String vnfVersion, String genericVnfName, String vnfName, String vfModuleId, String requestType,
578             String volumeGroupHeatStackId, String baseVfHeatStackId, String modelCustomizationUuid,
579             Map<String, Object> inputs, Boolean failIfExists, Boolean backout, Boolean enableBridge,
580             MsoRequest msoRequest, Holder<String> vnfId, Holder<Map<String, String>> outputs,
581             Holder<VnfRollback> rollback) throws VnfException {
582         String vfModuleName = vnfName;
583         String vfModuleType = vnfType;
584         String vfVersion = vnfVersion;
585         String mcu = modelCustomizationUuid;
586         boolean useMCUuid = false;
587         if (mcu != null && !mcu.isEmpty()) {
588             if ("null".equalsIgnoreCase(mcu)) {
589                 logger.debug("modelCustomizationUuid: passed in as the string 'null' - will ignore: "
590                         + modelCustomizationUuid);
591                 useMCUuid = false;
592                 mcu = "";
593             } else {
594                 logger.debug("Found modelCustomizationUuid! Will use that: " + mcu);
595                 useMCUuid = true;
596             }
597         }
598
599         String requestTypeString = "";
600         if (requestType != null && !"".equals(requestType)) {
601             requestTypeString = requestType;
602         }
603         String nestedStackId = null;
604         if (volumeGroupHeatStackId != null && !"".equals(volumeGroupHeatStackId)
605                 && !"null".equalsIgnoreCase(volumeGroupHeatStackId)) {
606             nestedStackId = volumeGroupHeatStackId;
607         }
608         String nestedBaseStackId = null;
609         if (baseVfHeatStackId != null && !"".equals(baseVfHeatStackId) && !"null".equalsIgnoreCase(baseVfHeatStackId)) {
610             nestedBaseStackId = baseVfHeatStackId;
611         }
612
613         // This method will also handle doing things the "old" way - i.e., just orchestrate a VNF
614         boolean oldWay = false;
615         if (requestTypeString.startsWith("X")) {
616             oldWay = true;
617             logger.debug("orchestrating a VNF - *NOT* a module!");
618             requestTypeString = requestTypeString.substring(1);
619         }
620
621         // 1607 - let's parse out the request type we're being sent
622         boolean isBaseRequest = false;
623         boolean isVolumeRequest = false;
624         if (requestTypeString.startsWith("VOLUME")) {
625             isVolumeRequest = true;
626         }
627
628         logger.debug("requestTypeString = " + requestTypeString + ", nestedStackId = " + nestedStackId
629                 + ", nestedBaseStackId = " + nestedBaseStackId);
630         // Will capture execution time for metrics
631         long startTime = System.currentTimeMillis();
632
633         // Build a default rollback object (no actions performed)
634         VnfRollback vfRollback = new VnfRollback();
635         vfRollback.setCloudSiteId(cloudSiteId);
636         vfRollback.setCloudOwner(cloudOwner);
637         vfRollback.setTenantId(tenantId);
638         vfRollback.setMsoRequest(msoRequest);
639         vfRollback.setRequestType(requestTypeString);
640         vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
641         vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
642         vfRollback.setIsBase(isBaseRequest);
643         vfRollback.setModelCustomizationUuid(mcu);
644
645         // Put data into A&AI through Heatstack
646         if (enableBridge != null && enableBridge) {
647             callHeatbridge(baseVfHeatStackId);
648         }
649
650         StackInfo heatStack = null;
651         long subStartTime1 = System.currentTimeMillis();
652         try {
653             heatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, vfModuleName);
654         } catch (MsoException me) {
655             String error = "Create VF Module: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/"
656                     + tenantId + ": " + me;
657             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
658                     cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
659                     "Exception - queryStack", me);
660             logger.debug(error);
661             // Failed to query the Stack due to an openstack exception.
662             // Convert to a generic VnfException
663             me.addContext("CreateVFModule");
664             throw new VnfException(me);
665         }
666         // New with 1607 - more precise handling/messaging if the stack already exists
667         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
668             // INIT, CREATED, NOTFOUND, FAILED, BUILDING, DELETING, UNKNOWN, UPDATING, UPDATED
669             HeatStatus status = heatStack.getStatus();
670             if (status == HeatStatus.INIT || status == HeatStatus.BUILDING || status == HeatStatus.DELETING
671                     || status == HeatStatus.UPDATING) {
672                 // fail - it's in progress - return meaningful error
673                 String error = "Create VF: Stack " + vfModuleName + " already exists and has status "
674                         + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
675                         + "; please wait for it to complete, or fix manually.";
676                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
677                         cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
678                         "Stack " + vfModuleName + " already exists");
679                 logger.debug(error);
680                 throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
681                         heatStack.getCanonicalName());
682             }
683             if (status == HeatStatus.FAILED) {
684                 // fail - it exists and is in a FAILED state
685                 String error = "Create VF: Stack " + vfModuleName + " already exists and is in FAILED state in "
686                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
687                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
688                         cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
689                         "Stack " + vfModuleName + " already exists and is " + "in FAILED state");
690                 logger.debug(error);
691                 throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
692                         heatStack.getCanonicalName());
693             }
694             if (status == HeatStatus.UNKNOWN || status == HeatStatus.UPDATED) {
695                 // fail - it exists and is in a FAILED state
696                 String error = "Create VF: Stack " + vfModuleName + " already exists and has status "
697                         + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
698                         + "; requires manual intervention.";
699                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
700                         cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
701                         "Stack " + vfModuleName + " already exists and is " + "in UPDATED or UNKNOWN state");
702                 logger.debug(error);
703                 throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
704                         heatStack.getCanonicalName());
705             }
706             if (status == HeatStatus.CREATED) {
707                 // fail - it exists
708                 if (failIfExists != null && failIfExists) {
709                     String error = "Create VF: Stack " + vfModuleName + " already exists in " + cloudOwner + "/"
710                             + cloudSiteId + "/" + tenantId;
711                     logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(),
712                             vfModuleName, cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack",
713                             ErrorCode.DataError.getValue(), "Stack " + vfModuleName + " already exists");
714                     logger.debug(error);
715                     throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
716                             heatStack.getCanonicalName());
717                 } else {
718                     logger.debug("Found Existing stack, status={}", heatStack.getStatus());
719                     // Populate the outputs from the existing stack.
720                     vnfId.value = heatStack.getCanonicalName();
721                     outputs.value = copyStringOutputs(heatStack.getOutputs());
722                     rollback.value = vfRollback; // Default rollback - no updates performed
723                 }
724             }
725             return;
726
727         }
728
729         // handle a nestedStackId if sent- this one would be for the volume - so applies to both Vf and Vnf
730         StackInfo nestedHeatStack = null;
731         long subStartTime2 = System.currentTimeMillis();
732         Map<String, Object> nestedVolumeOutputs = null;
733         if (nestedStackId != null) {
734             try {
735                 logger.debug("Querying for nestedStackId = {}", nestedStackId);
736                 nestedHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedStackId);
737             } catch (MsoException me) {
738                 // Failed to query the Stack due to an openstack exception.
739                 // Convert to a generic VnfException
740                 me.addContext("CreateVFModule");
741                 String error = "Create VFModule: Attached heatStack ID Query " + nestedStackId + " in " + cloudOwner
742                         + "/" + cloudSiteId + "/" + tenantId + ": " + me;
743                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
744                         cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack",
745                         ErrorCode.BusinessProcesssError.getValue(), "MsoException trying to query nested stack", me);
746                 logger.debug("ERROR trying to query nested stack= {}", error);
747                 throw new VnfException(me);
748             }
749             if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
750                 String error = "Create VFModule: Attached heatStack ID DOES NOT EXIST " + nestedStackId + " in "
751                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR";
752                 logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
753                         cloudOwner, cloudSiteId, tenantId, error, "OpenStack", "queryStack",
754                         ErrorCode.BusinessProcesssError.getValue(),
755                         "Create VFModule: Attached heatStack ID " + "DOES NOT EXIST");
756                 logger.debug(error);
757                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
758             } else {
759                 logger.debug("Found nested volume heat stack - copying values to inputs *later*");
760                 nestedVolumeOutputs = nestedHeatStack.getOutputs();
761             }
762         }
763
764         // handle a nestedBaseStackId if sent- this is the stack ID of the base. Should be null for VNF requests
765         StackInfo nestedBaseHeatStack = null;
766         long subStartTime3 = System.currentTimeMillis();
767         Map<String, Object> baseStackOutputs = null;
768         if (nestedBaseStackId != null) {
769             try {
770                 logger.debug("Querying for nestedBaseStackId = {}", nestedBaseStackId);
771                 nestedBaseHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedBaseStackId);
772             } catch (MsoException me) {
773                 // Failed to query the Stack due to an openstack exception.
774                 // Convert to a generic VnfException
775                 me.addContext("CreateVFModule");
776                 String error = "Create VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in "
777                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
778                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
779                         cloudOwner, cloudSiteId, tenantId, "OpenStack", "QueryStack",
780                         ErrorCode.BusinessProcesssError.getValue(), "MsoException trying to query nested base stack",
781                         me);
782                 logger.debug("ERROR trying to query nested base stack= {}", error);
783                 throw new VnfException(me);
784             }
785             if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
786                 String error = "Create VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId
787                         + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR";
788                 logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
789                         cloudOwner, cloudSiteId, tenantId, error, "OpenStack", "QueryStack",
790                         ErrorCode.BusinessProcesssError.getValue(),
791                         "Create VFModule: Attached base heatStack ID DOES NOT EXIST");
792                 logger.debug("Exception occurred", error);
793                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
794             } else {
795                 logger.debug("Found nested base heat stack - these values will be copied to inputs *later*");
796                 baseStackOutputs = nestedBaseHeatStack.getOutputs();
797             }
798         }
799
800         // Ready to deploy the new VNF
801
802
803
804         try {
805             // Retrieve the VF
806             VfModule vf = null;
807             VnfResource vnfResource = null;
808             VfModuleCustomization vfmc = null;
809             logger.debug("version: {}", vfVersion);
810             if (useMCUuid) {
811                 // 1707 - db refactoring
812                 vfmc = vfModuleCustomRepo.findFirstByModelCustomizationUUIDOrderByCreatedDesc(mcu);
813                 if (vfmc != null)
814                     vf = vfmc.getVfModule();
815                 else
816                     vf = null;
817
818                 // 1702 - this will be the new way going forward. We find the vf by mcu - otherwise, code is the same.
819                 if (vf == null) {
820                     logger.debug("Unable to find vfModuleCust with modelCustomizationUuid={}", mcu);
821                     String error =
822                             "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + mcu;
823                     logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
824                             "VF Module ModelCustomizationUuid", modelCustomizationUuid, "OpenStack",
825                             ErrorCode.DataError.getValue(),
826                             "Create VF Module: Unable to find vfModule with " + "modelCustomizationUuid=" + mcu);
827                     logger.debug(error);
828                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
829                 } else {
830                     logger.trace("Found vfModuleCust entry {}", vfmc.toString());
831                 }
832                 if (vf.getIsBase()) {
833                     isBaseRequest = true;
834                     logger.debug("This is a BASE VF request!");
835                 } else {
836                     logger.debug("This is *not* a BASE VF request!");
837                     if (!isVolumeRequest && nestedBaseStackId == null) {
838                         logger.debug(
839                                 "DANGER WILL ROBINSON! This is unexpected - no nestedBaseStackId with this non-base request");
840                     }
841                 }
842             }
843
844             else { // This is to support gamma only - get info from vnf_resource table
845                 if (vfVersion != null && !vfVersion.isEmpty()) {
846                     vnfResource = vnfResourceRepo.findByModelNameAndModelVersion(vnfType, vnfVersion);
847                 } else {
848                     vnfResource = vnfResourceRepo.findByModelName(vnfType);
849                 }
850                 if (vnfResource == null) {
851                     String error = "Create VNF: Unknown VNF Type: " + vnfType;
852                     logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VNF Type", vnfType,
853                             "OpenStack", ErrorCode.DataError.getValue(), "Create VNF: Unknown VNF Type");
854                     logger.debug(error);
855                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
856                 }
857                 logger.debug("Got VNF module definition from Catalog: {}", vnfResource.toString());
858             }
859             // By here - we have either a vf or vnfResource
860
861             // 1607 - Add version check
862             // First - see if it's in the VnfResource record
863             // if we have a vf Module - then we have to query to get the VnfResource record.
864             if (!oldWay && vf.getVnfResources() != null) {
865                 vnfResource = vf.getVnfResources();
866                 if (vnfResource == null) {
867                     logger.debug("Unable to find vnfResource will not error for now...");
868                 }
869             }
870             String minVersionVnf = null;
871             String maxVersionVnf = null;
872             if (vnfResource != null) {
873                 try {
874                     minVersionVnf = vnfResource.getAicVersionMin();
875                     maxVersionVnf = vnfResource.getAicVersionMax();
876                 } catch (Exception e) {
877                     logger.debug("Unable to pull min/max version for this VNF Resource entry", e);
878                     minVersionVnf = null;
879                     maxVersionVnf = null;
880                 }
881                 if (minVersionVnf != null && "".equals(minVersionVnf)) {
882                     minVersionVnf = null;
883                 }
884                 if (maxVersionVnf != null && "".equals(maxVersionVnf)) {
885                     maxVersionVnf = null;
886                 }
887             }
888             if (minVersionVnf != null && maxVersionVnf != null) {
889                 MavenLikeVersioning aicV = new MavenLikeVersioning();
890
891                 // double check
892                 if (this.cloudConfig != null) {
893                     Optional<CloudSite> cloudSiteOpt = this.cloudConfig.getCloudSite(cloudSiteId);
894                     if (cloudSiteOpt.isPresent()) {
895                         aicV.setVersion(cloudSiteOpt.get().getCloudVersion());
896                         // Add code to handle unexpected values in here
897                         boolean moreThanMin = true;
898                         boolean equalToMin = true;
899                         boolean moreThanMax = true;
900                         boolean equalToMax = true;
901                         boolean doNotTest = false;
902                         try {
903                             moreThanMin = aicV.isMoreRecentThan(minVersionVnf);
904                             equalToMin = aicV.isTheSameVersion(minVersionVnf);
905                             moreThanMax = aicV.isMoreRecentThan(maxVersionVnf);
906                             equalToMax = aicV.isTheSameVersion(maxVersionVnf);
907                         } catch (Exception e) {
908                             logger.debug(
909                                     "An exception occurred while trying to test AIC Version {} - will default to not check",
910                                     e.getMessage(), e);
911                             doNotTest = true;
912                         }
913                         if (!doNotTest) {
914                             if ((moreThanMin || equalToMin) // aic >= min
915                                     && (equalToMax || !(moreThanMax))) { // aic <= max
916                                 logger.debug("VNF Resource " + vnfResource.getModelName() + ", ModelUuid="
917                                         + vnfResource.getModelUUID() + " VersionMin=" + minVersionVnf + " VersionMax:"
918                                         + maxVersionVnf + " supported on Cloud: " + cloudSiteId + " with AIC_Version:"
919                                         + cloudSiteOpt.get().getCloudVersion());
920                             } else {
921                                 // ERROR
922                                 String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid="
923                                         + vnfResource.getModelUUID() + " VersionMin=" + minVersionVnf + " VersionMax:"
924                                         + maxVersionVnf + " NOT supported on Cloud: " + cloudSiteId
925                                         + " with AIC_Version:" + cloudSiteOpt.get().getCloudVersion();
926                                 logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack",
927                                         ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
928                                 logger.debug(error);
929                                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
930                             }
931                         } else {
932                             logger.debug("bypassing testing AIC version...");
933                         }
934                     } // let this error out downstream to avoid introducing uncertainty at this stage
935                 } else {
936                     logger.debug("cloudConfig is NULL - cannot check cloud site version");
937                 }
938             } else {
939                 logger.debug(
940                         "AIC Version not set in VNF_Resource - this is expected thru 1607 - do not error here - not checked"
941                                 + ".");
942             }
943             // End Version check 1607
944
945
946
947             // By the time we get here - heatTemplateId and heatEnvtId should be populated (or null)
948             HeatTemplate heatTemplate = null;
949             HeatEnvironment heatEnvironment = null;
950             if (oldWay) {
951                 // This will handle old Gamma BrocadeVCE VNF
952                 heatTemplate = vnfResource.getHeatTemplates();
953             } else {
954                 if (vf != null) {
955                     if (isVolumeRequest) {
956                         heatTemplate = vf.getVolumeHeatTemplate();
957                         heatEnvironment = vfmc.getVolumeHeatEnv();
958                     } else {
959                         heatTemplate = vf.getModuleHeatTemplate();
960                         heatEnvironment = vfmc.getHeatEnvironment();
961                     }
962                 }
963             }
964
965             if (heatTemplate == null) {
966                 String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType
967                         + ", reqType=" + requestTypeString;
968                 logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID",
969                         vfModuleType, "OpenStack", ErrorCode.DataError.getValue(), error);
970                 logger.debug(error);
971                 throw new VnfException(error, MsoExceptionCategory.INTERNAL);
972             } else {
973                 logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
974             }
975
976             if (oldWay) {
977                 // This will handle old Gamma BrocadeVCE VNF
978                 logger.debug("No environment parameter found for this Type " + vfModuleType);
979             } else {
980                 if (heatEnvironment == null) {
981                     String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
982                     logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
983                             "OpenStack", ErrorCode.DataError.getValue(), error);
984                     logger.debug(error);
985                     throw new VnfException(error, MsoExceptionCategory.INTERNAL);
986                 } else {
987                     logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
988                 }
989             }
990
991             logger.debug("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId="
992                     + heatTemplate.getArtifactUuid());
993
994
995             List<HeatTemplate> nestedTemplates = heatTemplate.getChildTemplates();
996             Map<String, Object> nestedTemplatesChecked = new HashMap<>();
997             if (nestedTemplates != null && !nestedTemplates.isEmpty()) {
998                 // for debugging print them out
999                 logger.debug("Contents of nestedTemplates - to be added to files: on stack:");
1000                 for (HeatTemplate entry : nestedTemplates) {
1001                     nestedTemplatesChecked.put(entry.getTemplateName(), entry.getTemplateBody());
1002                     logger.debug(entry.getTemplateName() + " -> " + entry.getTemplateBody());
1003                 }
1004             } else {
1005                 logger.debug("No nested templates found - nothing to do here");
1006                 nestedTemplatesChecked = null;
1007             }
1008
1009             // 1510 - Also add the files: for any get_files associated with this vnf_resource_id
1010             // *if* there are any
1011             List<HeatFiles> heatFiles = null;
1012
1013             Map<String, Object> heatFilesObjects = new HashMap<>();
1014
1015             // Add ability to turn on adding get_files with volume requests (by property).
1016             boolean addGetFilesOnVolumeReq = false;
1017             try {
1018                 String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ);
1019                 if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
1020                     addGetFilesOnVolumeReq = true;
1021                     logger.debug("AddGetFilesOnVolumeReq - setting to true! {}", propertyString);
1022                 }
1023             } catch (Exception e) {
1024                 logger.debug("An error occured trying to get property " + MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ
1025                         + " - default to false", e);
1026             }
1027
1028             if (!isVolumeRequest || addGetFilesOnVolumeReq) {
1029                 if (oldWay) {
1030                     logger.debug(
1031                             "In MsoVnfAdapterImpl createVfModule, this should not happen - old way is gamma only - no heat "
1032                                     + "files!");
1033                 } else {
1034                     // 1607 - now use VF_MODULE_TO_HEAT_FILES table
1035                     logger.debug(
1036                             "In MsoVnfAdapterImpl createVfModule, about to call db.getHeatFilesForVfModule avec vfModuleId="
1037                                     + vf.getModelUUID());
1038                     heatFiles = vf.getHeatFiles();
1039                 }
1040                 if (heatFiles != null && !heatFiles.isEmpty()) {
1041                     // add these to stack - to be done in createStack
1042                     // here, we will map them to Map<String, Object> from
1043                     // Map<String, HeatFiles>
1044                     // this will match the nested templates format
1045                     logger.debug("Contents of heatFiles - to be added to files: on stack");
1046
1047                     for (HeatFiles heatfile : heatFiles) {
1048                         logger.debug(heatfile.getFileName() + " -> " + heatfile.getFileBody());
1049                         heatFilesObjects.put(heatfile.getFileName(), heatfile.getFileBody());
1050                     }
1051                 } else {
1052                     logger.debug("No heat files found -nothing to do here");
1053                     heatFilesObjects = null;
1054                 }
1055             }
1056
1057             // Check that required parameters have been supplied
1058             String missingParams = null;
1059             List<String> paramList = new ArrayList<>();
1060
1061             // New for 1510 - consult the PARAM_ALIAS field to see if we've been
1062             // supplied an alias. Only check if we don't find it initially.
1063             // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
1064             // And also new - add parameter to turn off checking all together if we find we're blocking orders we
1065             // shouldn't
1066             boolean checkRequiredParameters = true;
1067             try {
1068                 String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1069                 if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
1070                     checkRequiredParameters = false;
1071                     logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking..."
1072                             + MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1073                 }
1074             } catch (Exception e) {
1075                 // No problem - default is true
1076                 logger.debug("An exception occured trying to get property {}", MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
1077             }
1078             // 1604 - Add enhanced environment & parameter checking
1079             // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
1080             // Part 2: only submit to openstack the parameters in the envt that are in the heat template
1081             // Note this also removes any comments
1082             MsoHeatEnvironmentEntry mhee = null;
1083             if (heatEnvironment != null && heatEnvironment.getEnvironment() != null
1084                     && heatEnvironment.getEnvironment().contains("parameters:")) {
1085
1086                 logger.debug("Enhanced environment checking enabled - 1604");
1087                 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
1088
1089                 mhee = new MsoHeatEnvironmentEntry(sb);
1090                 StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
1091                 for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1092                     sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
1093                 }
1094                 if (!mhee.isValid()) {
1095                     sb2.append("Environment says it's not valid! " + mhee.getErrorString());
1096                 } else {
1097                     sb2.append("\nEnvironment:");
1098                     sb2.append(mhee.toFullString());
1099                 }
1100                 logger.debug(sb2.toString());
1101             } else {
1102                 logger.debug("NO ENVIRONMENT for this entry");
1103             }
1104             // New with 1707 - all variables converted to their native object types
1105             Map<String, Object> goldenInputs = null;
1106
1107             logger.debug("Now handle the inputs....first convert");
1108             ArrayList<String> parameterNames = new ArrayList<>();
1109             HashMap<String, String> aliasToParam = new HashMap<>();
1110             StringBuilder sb = new StringBuilder("\nTemplate Parameters:\n");
1111             int cntr = 0;
1112             try {
1113                 for (HeatTemplateParam htp : heatTemplate.getParameters()) {
1114                     sb.append("param[" + cntr++ + "]=" + htp.getParamName());
1115                     parameterNames.add(htp.getParamName());
1116                     if (htp.getParamAlias() != null && !"".equals(htp.getParamAlias())) {
1117                         aliasToParam.put(htp.getParamAlias(), htp.getParamName());
1118                         sb.append(" ** (alias=" + htp.getParamAlias() + ")");
1119                     }
1120                     sb.append("\n");
1121                 }
1122                 logger.debug(sb.toString());
1123             } catch (Exception e) {
1124                 logger.debug("??An exception occurred trying to go through Parameter Names {}", e.getMessage(), e);
1125             }
1126             // Step 1 - convert what we got as inputs (Map<String, String>) to a
1127             // Map<String, Object> - where the object matches the param type identified in the template
1128             // This will also not copy over params that aren't identified in the template
1129             goldenInputs = heat.convertInputMap(inputs, heatTemplate);
1130             // Step 2 - now simply add the outputs as we received them - no need to convert to string
1131             logger.debug("Now add in the base stack outputs if applicable");
1132             heat.copyBaseOutputsToInputs(goldenInputs, baseStackOutputs, parameterNames, aliasToParam);
1133             // Step 3 - add the volume inputs if any
1134             logger.debug("Now add in the volume stack outputs if applicable");
1135             heat.copyBaseOutputsToInputs(goldenInputs, nestedVolumeOutputs, parameterNames, aliasToParam);
1136
1137             for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1138                 logger.debug("Parameter:'" + parm.getParamName() + "', isRequired=" + parm.isRequired() + ", alias="
1139                         + parm.getParamAlias());
1140
1141                 if (parm.isRequired() && (goldenInputs == null || !goldenInputs.containsKey(parm.getParamName()))) {
1142                     // The check for an alias was moved to the method in MsoHeatUtils - when we converted the
1143                     // Map<String, String> to Map<String, Object>
1144                     logger.debug("**Parameter " + parm.getParamName() + " is required and not in the inputs...check "
1145                             + "environment");
1146                     if (mhee != null && mhee.containsParameter(parm.getParamName())) {
1147                         logger.debug("Required parameter {} appears to be in environment - do not count as missing",
1148                                 parm.getParamName());
1149                     } else {
1150                         logger.debug("adding to missing parameters list: {}", parm.getParamName());
1151                         if (missingParams == null) {
1152                             missingParams = parm.getParamName();
1153                         } else {
1154                             missingParams += "," + parm.getParamName();
1155                         }
1156                     }
1157                 }
1158                 paramList.add(parm.getParamName());
1159             }
1160             if (missingParams != null) {
1161                 if (checkRequiredParameters) {
1162                     // Problem - missing one or more required parameters
1163                     String error = "Create VFModule: Missing Required inputs: " + missingParams;
1164                     logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, "OpenStack",
1165                             ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs");
1166                     logger.debug(error);
1167                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
1168                 } else {
1169                     logger.debug("found missing parameters - but checkRequiredParameters is false - will not block");
1170                 }
1171             } else {
1172                 logger.debug("No missing parameters found - ok to proceed");
1173             }
1174             // We can now remove the recreating of the ENV with only legit params - that check is done for us,
1175             // and it causes problems with json that has arrays
1176             String newEnvironmentString = null;
1177             if (mhee != null) {
1178                 newEnvironmentString = mhee.getRawEntry().toString();
1179             }
1180
1181             // "Fix" the template if it has CR/LF (getting this from Oracle)
1182             String template = heatTemplate.getHeatTemplate();
1183             template = template.replaceAll("\r\n", "\n");
1184
1185             // Valet - 1806
1186             boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1187             boolean failRequestOnValetFailure =
1188                     this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1189             logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1190             if (oldWay || isVolumeRequest) {
1191                 isValetEnabled = false;
1192                 logger.debug("do not send to valet for volume requests or brocade");
1193             }
1194             boolean sendResponseToValet = false;
1195             if (isValetEnabled) {
1196                 Holder<Map<String, Object>> valetModifiedParamsHolder = new Holder<>();
1197                 sendResponseToValet = this.valetCreateRequest(cloudSiteId, cloudOwner, tenantId, heatFilesObjects,
1198                         nestedTemplatesChecked, vfModuleName, backout, heatTemplate, newEnvironmentString, goldenInputs,
1199                         msoRequest, inputs, failRequestOnValetFailure, valetModifiedParamsHolder);
1200                 if (sendResponseToValet) {
1201                     goldenInputs = valetModifiedParamsHolder.value;
1202                 }
1203             }
1204
1205             // Have the tenant. Now deploy the stack itself
1206             // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1207             // because we already checked for those.
1208             long createStackStarttime = System.currentTimeMillis();
1209             try {
1210                 // heatStack = heat.createStack(cloudSiteId, tenantId, vnfName, template, inputs, true,
1211                 // heatTemplate.getTimeoutMinutes());
1212                 if (backout == null) {
1213                     backout = true;
1214                 }
1215                 if (heat != null) {
1216                     logger.debug("heat is not null!!");
1217
1218                     heatStack = heat.createStack(cloudSiteId, cloudOwner, tenantId, vfModuleName, null, template,
1219                             goldenInputs, true, heatTemplate.getTimeoutMinutes(), newEnvironmentString,
1220                             nestedTemplatesChecked, heatFilesObjects, backout.booleanValue());
1221                 } else {
1222                     logger.debug("heat is null!");
1223                     throw new MsoHeatNotFoundException();
1224                 }
1225             } catch (MsoException me) {
1226                 me.addContext("CreateVFModule");
1227                 String error = "Create VF Module " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/"
1228                         + tenantId + ": " + me;
1229                 logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType,
1230                         cloudOwner, cloudSiteId, tenantId, "OpenStack", ErrorCode.DataError.getValue(),
1231                         "MsoException - createStack", me);
1232                 logger.debug(error);
1233                 if (isValetEnabled && sendResponseToValet) {
1234                     logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
1235                     try {
1236                         GenericValetResponse<ValetRollbackResponse> gvr = this.vci
1237                                 .callValetRollbackRequest(msoRequest.getRequestId(), null, backout, me.getMessage());
1238                         // Nothing to really do here whether it succeeded or not other than log it.
1239                         logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
1240                     } catch (Exception e) {
1241                         logger.error("Exception encountered while sending Rollback to Valet ", e);
1242                     }
1243                 }
1244                 throw new VnfException(me);
1245             } catch (NullPointerException npe) {
1246                 String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/"
1247                         + tenantId + ": " + npe;
1248                 logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType,
1249                         cloudOwner, cloudSiteId, tenantId, "OpenStack", ErrorCode.DataError.getValue(),
1250                         "NullPointerException - createStack", npe);
1251                 logger.debug(error);
1252                 logger.debug("NULL POINTER EXCEPTION at heat.createStack");
1253                 // npe.addContext ("CreateVNF");
1254                 throw new VnfException("NullPointerException during heat.createStack");
1255             } catch (Exception e) {
1256                 logger.debug("unhandled exception at heat.createStack", e);
1257                 throw new VnfException("Exception during heat.createStack! " + e.getMessage());
1258             }
1259             // Reach this point if createStack is successful.
1260             // Populate remaining rollback info and response parameters.
1261             vfRollback.setVnfId(heatStack.getCanonicalName());
1262             vfRollback.setVnfCreated(true);
1263
1264             vnfId.value = heatStack.getCanonicalName();
1265             outputs.value = copyStringOutputs(heatStack.getOutputs());
1266             rollback.value = vfRollback;
1267             if (isValetEnabled && sendResponseToValet) {
1268                 logger.debug("valet is enabled, the orchestration succeeded - now send confirm to valet with stack id");
1269                 try {
1270                     GenericValetResponse<ValetConfirmResponse> gvr =
1271                             this.vci.callValetConfirmRequest(msoRequest.getRequestId(), heatStack.getCanonicalName());
1272                     // Nothing to really do here whether it succeeded or not other than log it.
1273                     logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
1274                 } catch (Exception e) {
1275                     logger.error("Exception encountered while sending Confirm to Valet ", e);
1276                 }
1277             }
1278             logger.debug("VF Module {} successfully created", vfModuleName);
1279             // call heatbridge
1280             heatbridge(heatStack, cloudOwner, cloudSiteId, tenantId, genericVnfName, vfModuleId);
1281             return;
1282         } catch (Exception e) {
1283             logger.debug("unhandled exception in create VF", e);
1284             throw new VnfException("Exception during create VF " + e.getMessage());
1285         }
1286     }
1287
1288     @Override
1289     public void deleteVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfName,
1290             MsoRequest msoRequest, Holder<Map<String, String>> outputs) throws VnfException {
1291
1292         logger.debug("Deleting VF {} in ", vnfName, cloudOwner + "/" + cloudSiteId + "/" + tenantId);
1293         // Will capture execution time for metrics
1294         long startTime = System.currentTimeMillis();
1295
1296         // 1702 capture the output parameters on a delete
1297         // so we'll need to query first
1298         Map<String, Object> stackOutputs = null;
1299         try {
1300             stackOutputs = heat.queryStackForOutputs(cloudSiteId, cloudOwner, tenantId, vnfName);
1301         } catch (MsoException me) {
1302             // Failed to query the Stack due to an openstack exception.
1303             // Convert to a generic VnfException
1304             me.addContext("DeleteVFModule");
1305             String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId
1306                     + "/" + tenantId + ": " + me;
1307             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner,
1308                     cloudSiteId, tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(),
1309                     "Exception - QueryStack", me);
1310             logger.debug(error);
1311             throw new VnfException(me);
1312         }
1313         // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected
1314         // Object types
1315         outputs.value = this.convertMapStringObjectToStringString(stackOutputs);
1316
1317         boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1318         boolean failRequestOnValetFailure =
1319                 this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1320         logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1321         boolean valetDeleteRequestSucceeded = false;
1322         if (isValetEnabled) {
1323             valetDeleteRequestSucceeded = this.valetDeleteRequest(cloudSiteId, cloudOwner, tenantId, vnfName,
1324                     msoRequest, failRequestOnValetFailure);
1325         }
1326
1327         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
1328         // The possible outcomes of deleteStack are a StackInfo object with status
1329         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
1330         // could be thrown.
1331         long subStartTime = System.currentTimeMillis();
1332         try {
1333             heat.deleteStack(tenantId, cloudOwner, cloudSiteId, vnfName, true);
1334         } catch (MsoException me) {
1335             me.addContext("DeleteVNF");
1336             // Failed to query the Stack due to an openstack exception.
1337             // Convert to a generic VnfException
1338             String error =
1339                     "Delete VF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1340             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudOwner,
1341                     cloudSiteId, tenantId, "OpenStack", "DeleteStack", ErrorCode.DataError.getValue(),
1342                     "Exception - deleteStack", me);
1343             logger.debug(error);
1344             if (isValetEnabled && valetDeleteRequestSucceeded) {
1345                 logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
1346                 try {
1347                     GenericValetResponse<ValetRollbackResponse> gvr = this.vci
1348                             .callValetRollbackRequest(msoRequest.getRequestId(), vnfName, false, me.getMessage());
1349                     // Nothing to really do here whether it succeeded or not other than log it.
1350                     logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
1351                 } catch (Exception e) {
1352                     logger.error("Exception encountered while sending Rollback to Valet ", e);
1353                 }
1354             }
1355             throw new VnfException(me);
1356         }
1357         if (isValetEnabled && valetDeleteRequestSucceeded) {
1358             // only if the original request succeeded do we send a confirm
1359             logger.debug("valet is enabled, the delete succeeded - now send confirm to valet");
1360             try {
1361                 GenericValetResponse<ValetConfirmResponse> gvr =
1362                         this.vci.callValetConfirmRequest(msoRequest.getRequestId(), vnfName);
1363                 // Nothing to really do here whether it succeeded or not other than log it.
1364                 logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
1365             } catch (Exception e) {
1366                 logger.error("Exception encountered while sending Confirm to Valet ", e);
1367             }
1368         }
1369
1370         // On success, nothing is returned.
1371         return;
1372     }
1373
1374     @Override
1375     public void updateVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfType,
1376             String vnfVersion, String vnfName, String requestType, String volumeGroupHeatStackId,
1377             String baseVfHeatStackId, String vfModuleStackId, String modelCustomizationUuid, Map<String, Object> inputs,
1378             MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback)
1379             throws VnfException {
1380         String vfModuleName = vnfName;
1381         String vfModuleType = vnfType;
1382         String methodName = "updateVfModule";
1383         String serviceName = VNF_ADAPTER_SERVICE_NAME + methodName;
1384
1385         StringBuilder sbInit = new StringBuilder();
1386         sbInit.append("updateVfModule: \n");
1387         sbInit.append("cloudOwner=" + cloudOwner + "\n");
1388         sbInit.append("cloudSiteId=" + cloudSiteId + "\n");
1389         sbInit.append("tenantId=" + tenantId + "\n");
1390         sbInit.append("vnfType=" + vnfType + "\n");
1391         sbInit.append("vnfVersion=" + vnfVersion + "\n");
1392         sbInit.append("vnfName=" + vnfName + "\n");
1393         sbInit.append("requestType=" + requestType + "\n");
1394         sbInit.append("volumeGroupHeatStackId=" + volumeGroupHeatStackId + "\n");
1395         sbInit.append("baseVfHeatStackId=" + baseVfHeatStackId + "\n");
1396         sbInit.append("vfModuleStackId=" + vfModuleStackId + "\n");
1397         sbInit.append("modelCustomizationUuid=" + modelCustomizationUuid + "\n");
1398         logger.debug(sbInit.toString());
1399
1400         String mcu = modelCustomizationUuid;
1401         boolean useMCUuid = false;
1402         if (mcu != null && !mcu.isEmpty()) {
1403             if ("null".equalsIgnoreCase(mcu)) {
1404                 logger.debug("modelCustomizationUuid: passed in as the string 'null' - will ignore: {}",
1405                         modelCustomizationUuid);
1406                 useMCUuid = false;
1407                 mcu = "";
1408             } else {
1409                 logger.debug("Found modelCustomizationUuid! Will use that: {}", mcu);
1410                 useMCUuid = true;
1411             }
1412         }
1413
1414         String requestTypeString = "";
1415         if (requestType != null && !"".equals(requestType)) {
1416             requestTypeString = requestType;
1417         }
1418
1419         String nestedStackId = null;
1420         if (volumeGroupHeatStackId != null && !"".equals(volumeGroupHeatStackId)
1421                 && !"null".equalsIgnoreCase(volumeGroupHeatStackId)) {
1422             nestedStackId = volumeGroupHeatStackId;
1423         }
1424         String nestedBaseStackId = null;
1425         if (baseVfHeatStackId != null && !"".equals(baseVfHeatStackId) && !"null".equalsIgnoreCase(baseVfHeatStackId)) {
1426             nestedBaseStackId = baseVfHeatStackId;
1427         }
1428
1429         if (inputs == null) {
1430             // Create an empty set of inputs
1431             inputs = new HashMap<>();
1432             logger.debug("inputs == null - setting to empty");
1433         }
1434
1435         boolean isBaseRequest = false;
1436         boolean isVolumeRequest = false;
1437         if (requestTypeString.startsWith("VOLUME")) {
1438             isVolumeRequest = true;
1439         }
1440         if ((vfModuleName == null || "".equals(vfModuleName.trim())) && vfModuleStackId != null) {
1441             vfModuleName = this.getVfModuleNameFromModuleStackId(vfModuleStackId);
1442         }
1443
1444         logger.debug("Updating VFModule: " + vfModuleName + " of type " + vfModuleType + "in " + cloudOwner + "/"
1445                 + cloudSiteId + "/" + tenantId);
1446         logger.debug("requestTypeString = " + requestTypeString + ", nestedVolumeStackId = " + nestedStackId
1447                 + ", nestedBaseStackId = " + nestedBaseStackId);
1448
1449         // Will capture execution time for metrics
1450         long startTime = System.currentTimeMillis();
1451
1452         // Build a default rollback object (no actions performed)
1453         VnfRollback vfRollback = new VnfRollback();
1454         vfRollback.setCloudSiteId(cloudSiteId);
1455         vfRollback.setCloudOwner(cloudOwner);
1456         vfRollback.setTenantId(tenantId);
1457         vfRollback.setMsoRequest(msoRequest);
1458         vfRollback.setRequestType(requestTypeString);
1459         vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
1460         vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
1461         vfRollback.setIsBase(isBaseRequest);
1462         vfRollback.setVfModuleStackId(vfModuleStackId);
1463         vfRollback.setModelCustomizationUuid(mcu);
1464
1465         StackInfo heatStack = null;
1466         long queryStackStarttime = System.currentTimeMillis();
1467         logger.debug("UpdateVfModule - querying for {}", vfModuleName);
1468         try {
1469             heatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, vfModuleName);
1470         } catch (MsoException me) {
1471             // Failed to query the Stack due to an openstack exception.
1472             // Convert to a generic VnfException
1473             me.addContext("UpdateVFModule");
1474             String error = "Update VFModule: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/"
1475                     + tenantId + ": " + me;
1476             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
1477                     cloudOwner, cloudSiteId, tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(),
1478                     "Exception - QueryStack", me);
1479             logger.debug(error);
1480             throw new VnfException(me);
1481         }
1482
1483         // TODO - do we need to check for the other status possibilities?
1484         if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) {
1485             // Not Found
1486             String error = "Update VF: Stack " + vfModuleName + " does not exist in " + cloudOwner + "/" + cloudSiteId
1487                     + "/" + tenantId;
1488             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_NOT_EXIST.toString(), vfModuleName,
1489                     cloudOwner, cloudSiteId, tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(),
1490                     error);
1491             throw new VnfNotFound(cloudSiteId, cloudOwner, tenantId, vfModuleName);
1492         } else {
1493             logger.debug("Found Existing stack, status={}", heatStack.getStatus());
1494             // Populate the outputs from the existing stack.
1495             outputs.value = copyStringOutputs(heatStack.getOutputs());
1496             rollback.value = vfRollback; // Default rollback - no updates performed
1497         }
1498
1499         // 1604 Cinder Volume support - handle a nestedStackId if sent (volumeGroupHeatStackId):
1500         StackInfo nestedHeatStack = null;
1501         long queryStackStarttime2 = System.currentTimeMillis();
1502         Map<String, Object> nestedVolumeOutputs = null;
1503         if (nestedStackId != null) {
1504             try {
1505                 logger.debug("Querying for nestedStackId = {}", nestedStackId);
1506                 nestedHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedStackId);
1507             } catch (MsoException me) {
1508                 // Failed to query the Stack due to an openstack exception.
1509                 // Convert to a generic VnfException
1510                 me.addContext("UpdateVFModule");
1511                 String error = "Update VF: Attached heatStack ID Query " + nestedStackId + " in " + cloudOwner + "/"
1512                         + cloudSiteId + "/" + tenantId + ": " + me;
1513                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner,
1514                         cloudSiteId, tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(),
1515                         "Exception - " + error, me);
1516                 logger.debug("ERROR trying to query nested stack= {}", error);
1517                 throw new VnfException(me);
1518             }
1519             if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
1520                 String error = "Update VFModule: Attached volume heatStack ID DOES NOT EXIST " + nestedStackId + " in "
1521                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR";
1522                 logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName,
1523                         cloudOwner, cloudSiteId, tenantId, error, "OpenStack", "QueryStack",
1524                         ErrorCode.DataError.getValue(), error);
1525                 logger.debug(error);
1526                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
1527             } else {
1528                 logger.debug("Found nested heat stack - copying values to inputs *later*");
1529                 nestedVolumeOutputs = nestedHeatStack.getOutputs();
1530                 heat.copyStringOutputsToInputs(inputs, nestedHeatStack.getOutputs(), false);
1531             }
1532         }
1533         // handle a nestedBaseStackId if sent - this is the stack ID of the base.
1534         StackInfo nestedBaseHeatStack = null;
1535         Map<String, Object> baseStackOutputs = null;
1536         if (nestedBaseStackId != null) {
1537             long queryStackStarttime3 = System.currentTimeMillis();
1538             try {
1539                 logger.debug("Querying for nestedBaseStackId = {}", nestedBaseStackId);
1540                 nestedBaseHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedBaseStackId);
1541             } catch (MsoException me) {
1542                 // Failed to query the Stack due to an openstack exception.
1543                 // Convert to a generic VnfException
1544                 me.addContext("UpdateVfModule");
1545                 String error = "Update VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in "
1546                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1547                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
1548                         cloudOwner, cloudSiteId, tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(),
1549                         "Exception - " + error, me);
1550                 logger.debug("ERROR trying to query nested base stack= {}", error);
1551                 throw new VnfException(me);
1552             }
1553             if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
1554                 String error = "Update VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId
1555                         + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR";
1556                 logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
1557                         cloudOwner, cloudSiteId, tenantId, error, "OpenStack", "QueryStack",
1558                         ErrorCode.DataError.getValue(), error);
1559                 logger.debug(error);
1560                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
1561             } else {
1562                 logger.debug("Found nested base heat stack - copying values to inputs *later*");
1563                 baseStackOutputs = nestedBaseHeatStack.getOutputs();
1564                 heat.copyStringOutputsToInputs(inputs, nestedBaseHeatStack.getOutputs(), false);
1565             }
1566         }
1567
1568         // Ready to deploy the new VNF
1569
1570
1571
1572         // Retrieve the VF definition
1573         VnfResource vnfResource = null;
1574         VfModule vf = null;
1575         VfModuleCustomization vfmc = null;
1576         if (useMCUuid) {
1577             vfmc = vfModuleCustomRepo.findFirstByModelCustomizationUUIDOrderByCreatedDesc(modelCustomizationUuid);
1578             vf = vfmc != null ? vfmc.getVfModule() : null;
1579             if (vf == null) {
1580                 logger.debug("Unable to find a vfModule matching modelCustomizationUuid={}", mcu);
1581             }
1582         } else {
1583             logger.debug("1707 and later - MUST PROVIDE Model Customization UUID!");
1584         }
1585         if (vf == null) {
1586             String error = "Update VfModule: unable to find vfModule with modelCustomizationUuid=" + mcu;
1587             logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VF Module Type",
1588                     vfModuleType, "OpenStack", ErrorCode.DataError.getValue(), error);
1589             throw new VnfException(error, MsoExceptionCategory.USERDATA);
1590         }
1591         logger.debug("Got VF module definition from Catalog: {}", vf.toString());
1592         if (vf.getIsBase()) {
1593             isBaseRequest = true;
1594             logger.debug("This a BASE update request");
1595         } else {
1596             logger.debug("This is *not* a BASE VF update request");
1597             if (!isVolumeRequest && nestedBaseStackId == null) {
1598                 logger.debug(
1599                         "DANGER WILL ROBINSON! This is unexpected - no nestedBaseStackId with this non-base request");
1600             }
1601         }
1602
1603         // 1607 - Add version check
1604         // First - see if it's in the VnfResource record
1605         // if we have a vf Module - then we have to query to get the VnfResource record.
1606         if (vf.getModelUUID() != null) {
1607             String vnfResourceModelUuid = vf.getModelUUID();
1608
1609             vnfResource = vf.getVnfResources();
1610             if (vnfResource == null) {
1611                 logger.debug("Unable to find vnfResource at ? will not error for now...", vnfResourceModelUuid);
1612             }
1613         }
1614
1615         String minVersionVnf = null;
1616         String maxVersionVnf = null;
1617         if (vnfResource != null) {
1618             try {
1619                 minVersionVnf = vnfResource.getAicVersionMin();
1620                 maxVersionVnf = vnfResource.getAicVersionMax();
1621             } catch (Exception e) {
1622                 logger.debug("Unable to pull min/max version for this VNF Resource entry", e);
1623                 minVersionVnf = null;
1624                 maxVersionVnf = null;
1625             }
1626             if (minVersionVnf != null && "".equals(minVersionVnf)) {
1627                 minVersionVnf = null;
1628             }
1629             if (maxVersionVnf != null && "".equals(maxVersionVnf)) {
1630                 maxVersionVnf = null;
1631             }
1632         }
1633         if (minVersionVnf != null && maxVersionVnf != null) {
1634             MavenLikeVersioning aicV = new MavenLikeVersioning();
1635
1636             // double check
1637             if (this.cloudConfig != null) {
1638                 Optional<CloudSite> cloudSiteOpt = this.cloudConfig.getCloudSite(cloudSiteId);
1639                 if (cloudSiteOpt.isPresent()) {
1640                     aicV.setVersion(cloudSiteOpt.get().getCloudVersion());
1641                     boolean moreThanMin = true;
1642                     boolean equalToMin = true;
1643                     boolean moreThanMax = true;
1644                     boolean equalToMax = true;
1645                     boolean doNotTest = false;
1646                     try {
1647                         moreThanMin = aicV.isMoreRecentThan(minVersionVnf);
1648                         equalToMin = aicV.isTheSameVersion(minVersionVnf);
1649                         moreThanMax = aicV.isMoreRecentThan(maxVersionVnf);
1650                         equalToMax = aicV.isTheSameVersion(maxVersionVnf);
1651                     } catch (Exception e) {
1652                         logger.debug(
1653                                 "An exception occured while trying to test AIC Version {} - will default to not check",
1654                                 e.getMessage(), e);
1655                         doNotTest = true;
1656                     }
1657                     if (!doNotTest) {
1658                         if ((moreThanMin || equalToMin) // aic >= min
1659                                 && ((equalToMax) || !(moreThanMax))) { // aic <= max
1660                             logger.debug("VNF Resource " + vnfResource.getModelName() + " VersionMin=" + minVersionVnf
1661                                     + " VersionMax:" + maxVersionVnf + " supported on Cloud: " + cloudSiteId
1662                                     + " with AIC_Version:" + aicV);
1663                         } else {
1664                             // ERROR
1665                             String error = "VNF Resource type: " + vnfResource.getModelName() + " VersionMin="
1666                                     + minVersionVnf + " VersionMax:" + maxVersionVnf + " NOT supported on Cloud: "
1667                                     + cloudSiteId + " with AIC_Version:" + aicV;
1668                             logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack",
1669                                     ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
1670                             logger.debug(error);
1671                             throw new VnfException(error, MsoExceptionCategory.USERDATA);
1672                         }
1673                     } else {
1674                         logger.debug("bypassing testing AIC version...");
1675                     }
1676                 } // let this error out downstream to avoid introducing uncertainty at this stage
1677             } else {
1678                 logger.debug("cloudConfig is NULL - cannot check cloud site version");
1679             }
1680
1681         } else {
1682             logger.debug("AIC Version not set in VNF_Resource - do not error for now - not checked.");
1683         }
1684         // End Version check 1607
1685
1686         HeatTemplate heatTemplate = null;
1687         HeatEnvironment heatEnvironment = null;
1688         if (isVolumeRequest) {
1689             heatTemplate = vf.getVolumeHeatTemplate();
1690             heatEnvironment = vfmc.getVolumeHeatEnv();
1691         } else {
1692             heatTemplate = vf.getModuleHeatTemplate();
1693             heatEnvironment = vfmc.getHeatEnvironment();
1694         }
1695
1696         if (heatTemplate == null) {
1697             String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType
1698                     + ", reqType=" + requestTypeString;
1699             logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID",
1700                     vfModuleType, "OpenStack", ErrorCode.DataError.getValue(), error);
1701             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
1702         } else {
1703             logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
1704         }
1705
1706         if (heatEnvironment == null) {
1707             String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
1708             logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
1709                     "OpenStack", ErrorCode.DataError.getValue(), error);
1710             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
1711         } else {
1712             logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
1713         }
1714
1715         logger.debug("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId={}",
1716                 heatTemplate.getArtifactUuid());
1717
1718
1719         List<HeatTemplate> nestedTemplates = heatTemplate.getChildTemplates();
1720         Map<String, Object> nestedTemplatesChecked = new HashMap<>();
1721         if (nestedTemplates != null && !nestedTemplates.isEmpty()) {
1722             // for debugging print them out
1723             logger.debug("Contents of nestedTemplates - to be added to files: on stack:");
1724             for (HeatTemplate entry : nestedTemplates) {
1725
1726                 nestedTemplatesChecked.put(entry.getTemplateName(), entry.getTemplateBody());
1727                 logger.debug(entry.getTemplateName() + " -> " + entry.getTemplateBody());
1728             }
1729         } else {
1730             logger.debug("No nested templates found - nothing to do here");
1731             nestedTemplatesChecked = null;
1732         }
1733
1734         // Also add the files: for any get_files associated with this VfModule
1735         // *if* there are any
1736         logger.debug("In MsoVnfAdapterImpl.updateVfModule, about to call db.getHeatFiles avec vfModuleId={}",
1737                 vf.getModelUUID());
1738
1739         List<HeatFiles> heatFiles = null;
1740         Map<String, Object> heatFilesObjects = new HashMap<>();
1741
1742         // Add ability to turn on adding get_files with volume requests (by property).
1743         boolean addGetFilesOnVolumeReq = false;
1744         try {
1745             String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ);
1746             if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
1747                 addGetFilesOnVolumeReq = true;
1748                 logger.debug("AddGetFilesOnVolumeReq - setting to true! {}", propertyString);
1749             }
1750         } catch (Exception e) {
1751             logger.debug("An error occured trying to get property {} - default to false",
1752                     MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ, e);
1753         }
1754         if (!isVolumeRequest || addGetFilesOnVolumeReq) {
1755             logger.debug("In MsoVnfAdapterImpl updateVfModule, about to call db.getHeatFilesForVfModule avec "
1756                     + "vfModuleId={}", vf.getModelUUID());
1757
1758             heatFiles = vf.getHeatFiles();
1759             if (heatFiles != null && !heatFiles.isEmpty()) {
1760                 // add these to stack - to be done in createStack
1761                 // here, we will map them to Map<String, Object> from Map<String, HeatFiles>
1762                 // this will match the nested templates format
1763                 logger.debug("Contents of heatFiles - to be added to files: on stack:");
1764                 for (HeatFiles heatfile : heatFiles) {
1765                     logger.debug(heatfile.getFileName() + " -> " + heatfile.getFileBody());
1766                     heatFilesObjects.put(heatfile.getFileName(), heatfile.getFileBody());
1767                 }
1768             } else {
1769                 logger.debug("No heat files found -nothing to do here");
1770                 heatFilesObjects = null;
1771             }
1772         }
1773
1774         // Check that required parameters have been supplied
1775         String missingParams = null;
1776         List<String> paramList = new ArrayList<>();
1777
1778         // New for 1510 - consult the PARAM_ALIAS field to see if we've been
1779         // supplied an alias. Only check if we don't find it initially.
1780         // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
1781         // And also new - add parameter to turn off checking all together if we find we're blocking orders we
1782         // shouldn't
1783         boolean checkRequiredParameters = true;
1784         try {
1785             String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1786             if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
1787                 checkRequiredParameters = false;
1788                 logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking...",
1789                         MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1790             }
1791         } catch (Exception e) {
1792             // No problem - default is true
1793             logger.debug("An exception occured trying to get property {}", MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
1794         }
1795         // 1604 - Add enhanced environment & parameter checking
1796         // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
1797         // Part 2: only submit to openstack the parameters in the envt that are in the heat template
1798         // Note this also removes any comments
1799         MsoHeatEnvironmentEntry mhee = null;
1800         if (heatEnvironment != null && heatEnvironment.getEnvironment().toLowerCase().contains("parameters:")) {
1801             logger.debug("Enhanced environment checking enabled - 1604");
1802             StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
1803             mhee = new MsoHeatEnvironmentEntry(sb);
1804             StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
1805             for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1806                 sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
1807             }
1808             if (!mhee.isValid()) {
1809                 sb2.append("Environment says it's not valid! " + mhee.getErrorString());
1810             } else {
1811                 sb2.append("\nEnvironment:");
1812                 sb2.append(mhee.toFullString());
1813             }
1814             logger.debug(sb2.toString());
1815         } else {
1816             logger.debug("NO ENVIRONMENT for this entry");
1817         }
1818         // New for 1607 - support params of json type
1819         HashMap<String, JsonNode> jsonParams = new HashMap<>();
1820         boolean hasJson = false;
1821
1822         for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1823             logger.debug("Parameter:'" + parm.getParamName() + "', isRequired=" + parm.isRequired() + ", alias="
1824                     + parm.getParamAlias());
1825             // handle json
1826             String parameterType = parm.getParamType();
1827             if (parameterType == null || "".equals(parameterType.trim())) {
1828                 parameterType = "String";
1829             }
1830             JsonNode jsonNode = null;
1831             if ("json".equalsIgnoreCase(parameterType) && inputs != null) {
1832                 if (inputs.containsKey(parm.getParamName())) {
1833                     hasJson = true;
1834                     String jsonString = null;
1835                     try {
1836                         jsonString = JSON_MAPPER.writeValueAsString(inputs.get(parm.getParamName()));
1837                         jsonNode = JSON_MAPPER.readTree(jsonString);
1838                     } catch (JsonParseException jpe) {
1839                         // TODO - what to do here?
1840                         // for now - send the error to debug
1841                         logger.debug("Json Error Converting {} - {}", parm.getParamName(), jpe.getMessage(), jpe);
1842                         hasJson = false;
1843                         jsonNode = null;
1844                     } catch (Exception e) {
1845                         // or here?
1846                         logger.debug("Json Error Converting {} {}", parm.getParamName(), e.getMessage(), e);
1847                         hasJson = false;
1848                         jsonNode = null;
1849                     }
1850                     if (jsonNode != null) {
1851                         jsonParams.put(parm.getParamName(), jsonNode);
1852                     }
1853                 } else if (inputs.containsKey(parm.getParamAlias())) {
1854                     hasJson = true;
1855                     String jsonString = null;
1856                     try {
1857                         jsonString = (String) inputs.get(parm.getParamAlias());
1858                         jsonNode = JSON_MAPPER.readTree(jsonString);
1859                     } catch (JsonParseException jpe) {
1860                         // TODO - what to do here?
1861                         // for now - send the error to debug, but just leave it as a String
1862                         String errorMessage = jpe.getMessage();
1863                         logger.debug("Json Error Converting " + parm.getParamName() + " - " + errorMessage, jpe);
1864                         hasJson = false;
1865                         jsonNode = null;
1866                     } catch (Exception e) {
1867                         // or here?
1868                         logger.debug("Json Error Converting " + parm.getParamName() + " " + e.getMessage(), e);
1869                         hasJson = false;
1870                         jsonNode = null;
1871                     }
1872                     if (jsonNode != null) {
1873                         // Notice here - we add it to the jsonParams hashMap with the actual name -
1874                         // then manipulate the inputs so when we check for aliases below - it will not
1875                         // get flagged.
1876                         jsonParams.put(parm.getParamName(), jsonNode);
1877                         inputs.remove(parm.getParamAlias());
1878                         inputs.put(parm.getParamName(), jsonString);
1879                     }
1880                 } // TODO add a check for the parameter in the env file
1881             }
1882
1883             if (parm.isRequired() && (inputs == null || !inputs.containsKey(parm.getParamName()))) {
1884                 if (inputs.containsKey(parm.getParamAlias())) {
1885                     // They've submitted using an alias name. Remove that from inputs, and add back using real name.
1886                     String realParamName = parm.getParamName();
1887                     String alias = parm.getParamAlias();
1888                     Object value = inputs.get(alias);
1889                     logger.debug("*Found an Alias: paramName=" + realParamName + ",alias=" + alias + ",value=" + value);
1890                     inputs.remove(alias);
1891                     inputs.put(realParamName, value);
1892                     logger.debug("{} entry removed from inputs, added back using {}", alias, realParamName);
1893                 }
1894                 // enhanced - check if it's in the Environment (note: that method
1895                 else if (mhee != null && mhee.containsParameter(parm.getParamName())) {
1896
1897                     logger.debug("Required parameter {} appears to be in environment - do not count as missing",
1898                             parm.getParamName());
1899                 } else {
1900                     logger.debug("adding to missing parameters list: {}", parm.getParamName());
1901                     if (missingParams == null) {
1902                         missingParams = parm.getParamName();
1903                     } else {
1904                         missingParams += "," + parm.getParamName();
1905                     }
1906                 }
1907             }
1908             paramList.add(parm.getParamName());
1909         }
1910
1911
1912         if (missingParams != null) {
1913             // Problem - missing one or more required parameters
1914             if (checkRequiredParameters) {
1915                 String error = "Update VNF: Missing Required inputs: " + missingParams;
1916                 logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, "OpenStack",
1917                         ErrorCode.DataError.getValue(), error);
1918                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
1919             } else {
1920                 logger.debug("found missing parameters - but checkRequiredParameters is false - will not block");
1921             }
1922         } else {
1923             logger.debug("No missing parameters found - ok to proceed");
1924         }
1925
1926         // Just submit the envt entry as received from the database
1927         String newEnvironmentString = null;
1928         if (mhee != null) {
1929             newEnvironmentString = mhee.getRawEntry().toString();
1930         }
1931         // Remove any extraneous parameters (don't throw an error)
1932         if (inputs != null) {
1933             List<String> extraParams = new ArrayList<>();
1934             extraParams.addAll(inputs.keySet());
1935             // This is not a valid parameter for this template
1936             extraParams.removeAll(paramList);
1937             if (!extraParams.isEmpty()) {
1938                 logger.warn("{} {} {} {} {} {}", MessageEnum.RA_VNF_EXTRA_PARAM.toString(), vnfType,
1939                         extraParams.toString(), "OpenStack", ErrorCode.DataError.getValue(), "Extra params");
1940                 inputs.keySet().removeAll(extraParams);
1941             }
1942         }
1943         Map<String, Object> goldenInputs = copyStringInputs(inputs);
1944         // 1607 - when we get here - we have clean inputs. Create inputsTwo in case we have json
1945         Map<String, Object> inputsTwo = null;
1946         if (hasJson && jsonParams.size() > 0) {
1947             inputsTwo = new HashMap<>();
1948             for (Map.Entry<String, Object> entry : inputs.entrySet()) {
1949                 String keyParamName = entry.getKey();
1950                 Object value = entry.getValue();
1951                 if (jsonParams.containsKey(keyParamName)) {
1952                     inputsTwo.put(keyParamName, jsonParams.get(keyParamName));
1953                 } else {
1954                     inputsTwo.put(keyParamName, value);
1955                 }
1956             }
1957             goldenInputs = inputsTwo;
1958         }
1959
1960         // "Fix" the template if it has CR/LF (getting this from Oracle)
1961         String template = heatTemplate.getHeatTemplate();
1962         template = template.replaceAll("\r\n", "\n");
1963
1964         boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1965         boolean failRequestOnValetFailure =
1966                 this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1967         logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1968         if (isVolumeRequest) {
1969             isValetEnabled = false;
1970             logger.debug("never send a volume request to valet");
1971         }
1972         boolean sendResponseToValet = false;
1973         if (isValetEnabled) {
1974             Holder<Map<String, Object>> valetModifiedParamsHolder = new Holder<>();
1975             String parsedVfModuleName = this.getVfModuleNameFromModuleStackId(vfModuleStackId);
1976             // Make sure it is set to something.
1977             if (parsedVfModuleName == null || parsedVfModuleName.isEmpty()) {
1978                 parsedVfModuleName = "unknown";
1979             }
1980             sendResponseToValet = this.valetUpdateRequest(cloudSiteId, cloudOwner, tenantId, heatFilesObjects,
1981                     nestedTemplatesChecked, parsedVfModuleName, false, heatTemplate, newEnvironmentString,
1982                     (HashMap<String, Object>) goldenInputs, msoRequest, inputs, failRequestOnValetFailure,
1983                     valetModifiedParamsHolder);
1984             if (sendResponseToValet) {
1985                 goldenInputs = valetModifiedParamsHolder.value;
1986             }
1987         }
1988
1989         // Have the tenant. Now deploy the stack itself
1990         // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1991         // because we already checked for those.
1992         long updateStackStarttime = System.currentTimeMillis();
1993         try {
1994             heatStack = heatU.updateStack(cloudSiteId, cloudOwner, tenantId, vfModuleName, template, goldenInputs, true,
1995                     heatTemplate.getTimeoutMinutes(), newEnvironmentString,
1996                     // heatEnvironmentString,
1997                     nestedTemplatesChecked, heatFilesObjects);
1998         } catch (MsoException me) {
1999             me.addContext("UpdateVFModule");
2000             String error = "Update VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
2001                     + ": " + me;
2002             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_UPDATE_VNF_ERR.toString(), vfModuleType, cloudOwner,
2003                     cloudSiteId, tenantId, "OpenStack", ErrorCode.DataError.getValue(), "Exception - " + error, me);
2004             if (isValetEnabled && sendResponseToValet) {
2005                 logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
2006                 try {
2007                     GenericValetResponse<ValetRollbackResponse> gvr =
2008                             this.vci.callValetRollbackRequest(msoRequest.getRequestId(), null, false, me.getMessage());
2009                     // Nothing to really do here whether it succeeded or not other than log it.
2010                     logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
2011                 } catch (Exception e) {
2012                     logger.error("Exception encountered while sending Rollback to Valet ", e);
2013                 }
2014             }
2015             throw new VnfException(me);
2016         }
2017
2018
2019         // Reach this point if updateStack is successful.
2020         // Populate remaining rollback info and response parameters.
2021         vfRollback.setVnfId(heatStack.getCanonicalName());
2022         vfRollback.setVnfCreated(true);
2023
2024         if (isValetEnabled && sendResponseToValet) {
2025             logger.debug("valet is enabled, the update succeeded - now send confirm to valet with stack id");
2026             try {
2027                 GenericValetResponse<ValetConfirmResponse> gvr =
2028                         this.vci.callValetConfirmRequest(msoRequest.getRequestId(), heatStack.getCanonicalName());
2029                 // Nothing to really do here whether it succeeded or not other than log it.
2030                 logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
2031             } catch (Exception e) {
2032                 logger.error("Exception encountered while sending Confirm to Valet ", e);
2033             }
2034         }
2035
2036         outputs.value = copyStringOutputs(heatStack.getOutputs());
2037         rollback.value = vfRollback;
2038         return;
2039     }
2040
2041     private String getVfModuleNameFromModuleStackId(String vfModuleStackId) {
2042         // expected format of vfModuleStackId is "MSOTEST51-vSAMP3_base_module-0/1fc1f86c-7b35-447f-99a6-c23ec176ae24"
2043         // before the "/" is the vfModuleName and after the "/" is the heat stack id in Openstack
2044         if (vfModuleStackId == null)
2045             return null;
2046         int index = vfModuleStackId.lastIndexOf('/');
2047         if (index <= 0)
2048             return null;
2049         String vfModuleName = null;
2050         try {
2051             vfModuleName = vfModuleStackId.substring(0, index);
2052         } catch (Exception e) {
2053             logger.debug("Exception", e);
2054             vfModuleName = null;
2055         }
2056         return vfModuleName;
2057     }
2058
2059     /*
2060      * Helper method to check a boolean property value - on error return provided default
2061      */
2062     private boolean checkBooleanProperty(String propertyName, boolean defaultValue) {
2063         boolean property = defaultValue;
2064         try {
2065             String propertyString = this.environment.getProperty(propertyName);
2066             if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
2067                 property = true;
2068             } else if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
2069                 property = false;
2070             }
2071         } catch (Exception e) {
2072             logger.debug("An exception occured trying to get property {} - defaulting to ", propertyName, defaultValue,
2073                     e);
2074             property = defaultValue;
2075         }
2076         return property;
2077     }
2078
2079     /*
2080      * Helper method to combine getFiles and nestedTemplates in to a single Map
2081      */
2082     private Map<String, Object> combineGetFilesAndNestedTemplates(Map<String, Object> getFiles,
2083             Map<String, Object> nestedTemplates) {
2084         boolean haveGetFiles = true;
2085         boolean haveNestedTemplates = true;
2086         Map<String, Object> files = new HashMap<String, Object>();
2087         if (getFiles == null || getFiles.isEmpty()) {
2088             haveGetFiles = false;
2089         }
2090         if (nestedTemplates == null || nestedTemplates.isEmpty()) {
2091             haveNestedTemplates = false;
2092         }
2093         if (haveGetFiles && haveNestedTemplates) {
2094             for (String keyString : getFiles.keySet()) {
2095                 files.put(keyString, getFiles.get(keyString));
2096             }
2097             for (String keyString : nestedTemplates.keySet()) {
2098                 files.put(keyString, nestedTemplates.get(keyString));
2099             }
2100         } else {
2101             // Handle if we only have one or neither:
2102             if (haveGetFiles) {
2103                 files = getFiles;
2104             }
2105             if (haveNestedTemplates) {
2106                 files = nestedTemplates;
2107             }
2108         }
2109         return files;
2110     }
2111
2112     /*
2113      * Valet Create request
2114      */
2115     private boolean valetCreateRequest(String cloudSiteId, String cloudOwner, String tenantId,
2116             Map<String, Object> heatFilesObjects, Map<String, Object> nestedTemplatesChecked, String vfModuleName,
2117             boolean backout, HeatTemplate heatTemplate, String newEnvironmentString, Map<String, Object> goldenInputs,
2118             MsoRequest msoRequest, Map<String, Object> inputs, boolean failRequestOnValetFailure,
2119             Holder<Map<String, Object>> valetModifiedParamsHolder) throws VnfException {
2120         boolean valetSucceeded = false;
2121         String valetErrorMessage = "more detail not available";
2122         try {
2123             String keystoneUrl = heat.getCloudSiteKeystoneUrl(cloudSiteId);
2124             Map<String, Object> files =
2125                     this.combineGetFilesAndNestedTemplates(heatFilesObjects, nestedTemplatesChecked);
2126             HeatRequest heatRequest = new HeatRequest(vfModuleName, backout, heatTemplate.getTimeoutMinutes(),
2127                     heatTemplate.getTemplateBody(), newEnvironmentString, files, goldenInputs);
2128             GenericValetResponse<ValetCreateResponse> createReq = this.vci.callValetCreateRequest(
2129                     msoRequest.getRequestId(), cloudSiteId, cloudOwner, tenantId, msoRequest.getServiceInstanceId(),
2130                     (String) inputs.get("vnf_id"), (String) inputs.get("vnf_name"), (String) inputs.get("vf_module_id"),
2131                     (String) inputs.get("vf_module_name"), keystoneUrl, heatRequest);
2132             ValetCreateResponse vcr = createReq.getReturnObject();
2133             if (vcr != null && createReq.getStatusCode() == 200) {
2134                 ValetStatus status = vcr.getStatus();
2135                 if (status != null) {
2136                     String statusCode = status.getStatus(); // "ok" or "failed"
2137                     if ("ok".equalsIgnoreCase(statusCode)) {
2138                         Map<String, Object> newInputs = vcr.getParameters();
2139                         if (newInputs != null) {
2140                             Map<String, Object> oldGold = goldenInputs;
2141                             logger.debug("parameters before being modified by valet:{}", oldGold.toString());
2142                             goldenInputs = new HashMap<String, Object>();
2143                             for (String key : newInputs.keySet()) {
2144                                 goldenInputs.put(key, newInputs.get(key));
2145                             }
2146                             valetModifiedParamsHolder.value = goldenInputs;
2147                             logger.debug("parameters after being modified by valet:{}", goldenInputs.toString());
2148                             valetSucceeded = true;
2149                         }
2150                     } else {
2151                         valetErrorMessage = status.getMessage();
2152                     }
2153                 }
2154             } else {
2155                 logger.debug("Got a bad response back from valet");
2156                 valetErrorMessage = "Bad response back from Valet";
2157                 valetSucceeded = false;
2158             }
2159         } catch (Exception e) {
2160             logger.error("An exception occurred trying to call valet ...", e);
2161             valetSucceeded = false;
2162             valetErrorMessage = e.getMessage();
2163         }
2164         if (failRequestOnValetFailure && !valetSucceeded) {
2165             // The valet request failed - and property says to fail the request
2166             // TODO Create a new exception class for valet?
2167             throw new VnfException("A failure occurred with Valet: " + valetErrorMessage);
2168         }
2169         return valetSucceeded;
2170     }
2171
2172     /*
2173      * Valet update request
2174      */
2175
2176     private boolean valetUpdateRequest(String cloudSiteId, String cloudOwnerId, String tenantId,
2177             Map<String, Object> heatFilesObjects, Map<String, Object> nestedTemplatesChecked, String vfModuleName,
2178             boolean backout, HeatTemplate heatTemplate, String newEnvironmentString, Map<String, Object> goldenInputs,
2179             MsoRequest msoRequest, Map<String, Object> inputs, boolean failRequestOnValetFailure,
2180             Holder<Map<String, Object>> valetModifiedParamsHolder) throws VnfException {
2181
2182         boolean valetSucceeded = false;
2183         String valetErrorMessage = "more detail not available";
2184         try {
2185             String keystoneUrl = heat.getCloudSiteKeystoneUrl(cloudSiteId);
2186             Map<String, Object> files =
2187                     this.combineGetFilesAndNestedTemplates(heatFilesObjects, nestedTemplatesChecked);
2188             HeatRequest heatRequest = new HeatRequest(vfModuleName, false, heatTemplate.getTimeoutMinutes(),
2189                     heatTemplate.getTemplateBody(), newEnvironmentString, files, goldenInputs);
2190             // vnf name is not sent to MSO on update requests - so we will set it to the vf module name for now
2191             GenericValetResponse<ValetUpdateResponse> updateReq =
2192                     this.vci.callValetUpdateRequest(msoRequest.getRequestId(), cloudSiteId, cloudOwnerId, tenantId,
2193                             msoRequest.getServiceInstanceId(), (String) inputs.get("vnf_id"), vfModuleName,
2194                             (String) inputs.get("vf_module_id"), vfModuleName, keystoneUrl, heatRequest);
2195             ValetUpdateResponse vur = updateReq.getReturnObject();
2196             if (vur != null && updateReq.getStatusCode() == 200) {
2197                 ValetStatus status = vur.getStatus();
2198                 if (status != null) {
2199                     String statusCode = status.getStatus(); // "ok" or "failed"
2200                     if ("ok".equalsIgnoreCase(statusCode)) {
2201                         Map<String, Object> newInputs = vur.getParameters();
2202                         if (newInputs != null) {
2203                             Map<String, Object> oldGold = goldenInputs;
2204                             logger.debug("parameters before being modified by valet:{}", oldGold.toString());
2205                             goldenInputs = new HashMap<String, Object>();
2206                             for (String key : newInputs.keySet()) {
2207                                 goldenInputs.put(key, newInputs.get(key));
2208                             }
2209                             valetModifiedParamsHolder.value = goldenInputs;
2210                             logger.debug("parameters after being modified by valet:{}", goldenInputs.toString());
2211                             valetSucceeded = true;
2212                         }
2213                     } else {
2214                         valetErrorMessage = status.getMessage();
2215                     }
2216                 }
2217             } else {
2218                 logger.debug("Got a bad response back from valet");
2219                 valetErrorMessage = "Got a bad response back from valet";
2220                 valetSucceeded = false;
2221             }
2222         } catch (Exception e) {
2223             logger.error("An exception occurred trying to call valet - will continue processing for now...", e);
2224             valetErrorMessage = e.getMessage();
2225             valetSucceeded = false;
2226         }
2227         if (failRequestOnValetFailure && !valetSucceeded) {
2228             // The valet request failed - and property says to fail the request
2229             // TODO Create a new exception class for valet?
2230             throw new VnfException("A failure occurred with Valet: " + valetErrorMessage);
2231         }
2232         return valetSucceeded;
2233     }
2234
2235     /*
2236      * Valet delete request
2237      */
2238     private boolean valetDeleteRequest(String cloudSiteId, String cloudOwnerId, String tenantId, String vnfName,
2239             MsoRequest msoRequest, boolean failRequestOnValetFailure) {
2240         boolean valetDeleteRequestSucceeded = false;
2241         String valetErrorMessage = "more detail not available";
2242         try {
2243             String vfModuleId = vnfName;
2244             String vfModuleName = vnfName;
2245             try {
2246                 vfModuleName = vnfName.substring(0, vnfName.indexOf('/'));
2247                 vfModuleId = vnfName.substring(vnfName.indexOf('/') + 1);
2248             } catch (Exception e) {
2249                 // do nothing - send what we got for vnfName for both to valet
2250                 logger.error("An exception occurred trying to call MsoVnfAdapterImpl.valetDeleteRequest() method", e);
2251             }
2252             GenericValetResponse<ValetDeleteResponse> deleteReq = this.vci.callValetDeleteRequest(
2253                     msoRequest.getRequestId(), cloudSiteId, cloudOwnerId, tenantId, vfModuleId, vfModuleName);
2254             ValetDeleteResponse vdr = deleteReq.getReturnObject();
2255             if (vdr != null && deleteReq.getStatusCode() == 200) {
2256                 ValetStatus status = vdr.getStatus();
2257                 if (status != null) {
2258                     String statusCode = status.getStatus(); // "ok" or "failed"
2259                     if ("ok".equalsIgnoreCase(statusCode)) {
2260                         logger.debug("delete request to valet returned success");
2261                         valetDeleteRequestSucceeded = true;
2262                     } else {
2263                         logger.debug("delete request to valet returned failure");
2264                         valetDeleteRequestSucceeded = false;
2265                         valetErrorMessage = status.getMessage();
2266                     }
2267                 }
2268             } else {
2269                 logger.debug("Got a bad response back from valet - delete request failed");
2270                 valetDeleteRequestSucceeded = false;
2271                 valetErrorMessage = "Got a bad response back from valet - delete request failed";
2272             }
2273         } catch (Exception e) {
2274             logger.error("An exception occurred trying to call valet - valetDeleteRequest failed", e);
2275             valetDeleteRequestSucceeded = false;
2276             valetErrorMessage = e.getMessage();
2277         }
2278         if (valetDeleteRequestSucceeded == false && failRequestOnValetFailure == true) {
2279             logger.error("ValetDeleteRequestFailed - del req still will be sent to openstack",
2280                     new VnfException("ValetDeleteRequestFailedError"));
2281         }
2282         return valetDeleteRequestSucceeded;
2283     }
2284 }