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