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