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