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