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