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