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