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