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