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