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