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