bba960cddf56ea672df144790fbb7bb4d3d5906c
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / adapters / vnf / MsoVnfCloudifyAdapterImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * OPENECOMP - MSO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (C) 2018 IBM.
8  * Modifications Copyright (c) 2019 Samsung
9  * ================================================================================
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.so.adapters.vnf;
25
26
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.Set;
33
34 import javax.jws.WebService;
35 import javax.xml.ws.Holder;
36
37 import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists;
38 import org.onap.so.adapters.vnf.exceptions.VnfException;
39 import org.onap.so.cloud.CloudConfig;
40 import org.onap.so.db.catalog.beans.CloudSite;
41 import org.onap.so.cloudify.beans.DeploymentInfo;
42 import org.onap.so.cloudify.beans.DeploymentStatus;
43 import org.onap.so.cloudify.exceptions.MsoCloudifyManagerNotFound;
44 import org.onap.so.cloudify.utils.MsoCloudifyUtils;
45 import org.onap.so.db.catalog.beans.HeatEnvironment;
46 import org.onap.so.db.catalog.beans.HeatFiles;
47 import org.onap.so.db.catalog.beans.HeatTemplate;
48 import org.onap.so.db.catalog.beans.HeatTemplateParam;
49 import org.onap.so.db.catalog.beans.VfModule;
50 import org.onap.so.db.catalog.beans.VfModuleCustomization;
51 import org.onap.so.db.catalog.beans.VnfResource;
52 import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository;
53 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
54 import org.onap.so.entity.MsoRequest;
55 import org.onap.so.logger.MessageEnum;
56
57 import org.onap.so.logger.MsoLogger;
58 import org.onap.so.openstack.beans.MsoTenant;
59 import org.onap.so.openstack.beans.VnfRollback;
60 import org.onap.so.openstack.beans.VnfStatus;
61 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
62 import org.onap.so.openstack.exceptions.MsoException;
63 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
64 import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry;
65 import org.onap.so.openstack.utils.MsoHeatEnvironmentParameter;
66 import org.onap.so.openstack.utils.MsoKeystoneUtils;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69 import org.springframework.beans.factory.annotation.Autowired;
70 import org.springframework.core.env.Environment;
71 import org.springframework.stereotype.Component;
72
73 import com.fasterxml.jackson.core.JsonParseException;
74 import com.fasterxml.jackson.databind.JsonNode;
75 import com.fasterxml.jackson.databind.ObjectMapper;
76 import org.springframework.transaction.annotation.Transactional;
77
78 @Component
79 @Transactional
80 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter", targetNamespace = "http://org.onap.so/vnf")
81 public class MsoVnfCloudifyAdapterImpl implements MsoVnfAdapter {
82
83     private static Logger logger = LoggerFactory.getLogger(MsoVnfCloudifyAdapterImpl.class);
84
85     private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
86     private static final String VNF_ADAPTER_SERVICE_NAME = "MSO-BPMN:MSO-VnfAdapter.";
87     private static final String LOG_REPLY_NAME = "MSO-VnfAdapter:MSO-BPMN.";
88     private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
89     private static final String ADD_GET_FILES_ON_VOLUME_REQ = "org.onap.so.adapters.vnf.addGetFilesOnVolumeReq";
90     private static final String CLOUDIFY_RESPONSE_SUCCESS="Successfully received response from Cloudify";
91     private static final String CLOUDIFY="Cloudify";
92
93     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
94
95     @Autowired
96     protected CloudConfig cloudConfig;
97
98     @Autowired
99     private VFModuleCustomizationRepository vfModuleCustomRepo;
100
101     @Autowired
102     private Environment environment;
103
104     @Autowired
105     protected MsoKeystoneUtils keystoneUtils;
106
107     @Autowired
108     protected MsoCloudifyUtils cloudifyUtils;
109     /**
110      * Health Check web method. Does nothing but return to show the adapter is deployed.
111      */
112     @Override
113     public void healthCheck () {
114         logger.debug("Health check call in VNF Cloudify Adapter");
115     }
116
117     /**
118      * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
119      * @see MsoVnfCloudifyAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
120      */
121     public MsoVnfCloudifyAdapterImpl() {
122
123     }
124
125     /**
126      * This is the "Create VNF" web service implementation.
127      * This function is now unsupported and will return an error.
128      *
129      */
130     @Override
131     public void createVnf (String cloudSiteId,
132                            String tenantId,
133                            String vnfType,
134                            String vnfVersion,
135                            String vnfName,
136                            String requestType,
137                            String volumeGroupHeatStackId,
138                            Map <String, Object> inputs,
139                            Boolean failIfExists,
140                            Boolean backout,
141                            Boolean enableBridge,
142                            MsoRequest msoRequest,
143                            Holder <String> vnfId,
144                            Holder <Map <String, String>> outputs,
145                            Holder <VnfRollback> rollback)
146         throws VnfException
147     {
148         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
149         logger.debug("CreateVNF command attempted but not supported");
150         throw new VnfException("CreateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
151     }
152
153     /**
154      * This is the "Update VNF" web service implementation.
155      * This function is now unsupported and will return an error.
156      *
157      */
158     @Override
159     public void updateVnf (String cloudSiteId,
160                            String tenantId,
161                            String vnfType,
162                            String vnfVersion,
163                            String vnfName,
164                            String requestType,
165                            String volumeGroupHeatStackId,
166                            Map <String, Object> inputs,
167                            MsoRequest msoRequest,
168                            Holder <Map <String, String>> outputs,
169                            Holder <VnfRollback> rollback)
170                 throws VnfException
171         {
172         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
173       logger.debug("UpdateVNF command attempted but not supported");
174       throw new VnfException ("UpdateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
175     }
176
177     /**
178      * This is the "Query VNF" web service implementation.
179      *
180      * This really should be QueryVfModule, but nobody ever changed it.
181      *
182      * For Cloudify, this will look up a deployment by its deployment ID, which is really the same
183      * as deployment name, since it assigned by the client when a deployment is created.
184      * Also, the input cloudSiteId is used only to identify which Cloudify instance to query,
185      * and the tenantId is ignored (since that really only applies for Openstack/Heat).
186      *
187      * The method returns an indicator that the VNF exists, along with its status and outputs.
188      * The input "vnfName" will also be reflected back as its ID.
189      *
190      * @param cloudSiteId CLLI code of the cloud site in which to query
191      * @param tenantId Openstack tenant identifier - ignored for Cloudify
192      * @param vnfName VNF Name (should match a deployment ID)
193      * @param msoRequest Request tracking information for logs
194      * @param vnfExists Flag reporting the result of the query
195      * @param vnfId Holder for output VNF ID
196      * @param outputs Holder for Map of VNF outputs from Cloudify deployment (assigned IPs, etc)
197      */
198     @Override
199     public void queryVnf (String cloudSiteId,
200                           String tenantId,
201                           String vnfName,
202                           MsoRequest msoRequest,
203                           Holder <Boolean> vnfExists,
204                           Holder <String> vnfId,
205                           Holder <VnfStatus> status,
206                           Holder <Map <String, String>> outputs)
207         throws VnfException
208     {
209         MsoLogger.setLogContext (msoRequest);
210         logger.debug ("Querying VNF {} in {}", vnfName, cloudSiteId + "/" + tenantId);
211
212         // Will capture execution time for metrics
213         long startTime = System.currentTimeMillis ();
214         long subStartTime = System.currentTimeMillis ();
215
216         DeploymentInfo deployment = null;
217
218         try {
219                 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
220         }
221         catch (MsoCloudifyManagerNotFound e) {
222                 // This site does not have a Cloudify Manager.
223                 // This isn't an error, just means we won't find the VNF here.
224                 deployment = null;
225         }
226         catch (MsoException me) {
227           // Failed to query the Deployment due to a cloudify exception.
228           // Convert to a generic VnfException
229           me.addContext("QueryVNF");
230           String error = "Query VNF (Cloudify): " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
231           logger
232               .error("{} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudSiteId, tenantId,
233                   CLOUDIFY, "QueryVNF", MsoLogger.ErrorCode.DataError.getValue(), "Exception - queryDeployment", me);
234           logger.debug(error);
235           throw new VnfException(me);
236       }
237
238         if (deployment != null && deployment.getStatus() != DeploymentStatus.NOTFOUND) {
239             vnfExists.value = Boolean.TRUE;
240             status.value = deploymentStatusToVnfStatus(deployment);
241             vnfId.value = deployment.getId();
242             outputs.value = copyStringOutputs(deployment.getOutputs());
243
244             logger.debug("VNF {} found in Cloudify, ID = {}", vnfName, vnfId.value);
245         } else {
246             vnfExists.value = Boolean.FALSE;
247             status.value = VnfStatus.NOTFOUND;
248             vnfId.value = null;
249             outputs.value = new HashMap<String, String>(); // Return as an empty map
250
251             logger.debug("VNF {} not found", vnfName);
252         }
253         return;
254     }
255
256
257     /**
258      * This is the "Delete VNF" web service implementation.
259      * This function is now unsupported and will return an error.
260      *
261      */
262     @Override
263     public void deleteVnf (String cloudSiteId,
264                            String tenantId,
265                            String vnfName,
266                            MsoRequest msoRequest) throws VnfException {
267         MsoLogger.setLogContext (msoRequest);
268
269         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
270         logger.debug("DeleteVNF command attempted but not supported");
271         throw new VnfException ("DeleteVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
272     }
273
274     /**
275      * This web service endpoint will rollback a previous Create VNF operation.
276      * A rollback object is returned to the client in a successful creation
277      * response. The client can pass that object as-is back to the rollbackVnf
278      * operation to undo the creation.
279      *
280      * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup,
281      * but APIs were apparently never updated.
282      */
283     @Override
284     public void rollbackVnf (VnfRollback rollback) throws VnfException {
285         long startTime = System.currentTimeMillis ();
286         // rollback may be null (e.g. if stack already existed when Create was called)
287         if (rollback == null) {
288             logger.info ("{} {} {} {}", MessageEnum.RA_ROLLBACK_NULL.toString(), "OpenStack", "rollbackVnf", MsoLogger
289                 .getServiceName());
290             return;
291         }
292
293         // Don't rollback if nothing was done originally
294         if (!rollback.getVnfCreated()) {
295             return;
296         }
297
298         // Get the elements of the VnfRollback object for easier access
299         String cloudSiteId = rollback.getCloudSiteId ();
300         String tenantId = rollback.getTenantId ();
301         String vfModuleId = rollback.getVfModuleStackId ();
302
303         MsoLogger.setLogContext (rollback.getMsoRequest());
304
305         logger.debug("Rolling Back VF Module {} in {}", vfModuleId, cloudSiteId + "/" + tenantId);
306
307         DeploymentInfo deployment = null;
308
309         // Use the MsoCloudifyUtils to delete the deployment. Set the polling flag to true.
310         // The possible outcomes of deleteStack are a StackInfo object with status
311         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
312         // could be thrown.
313         long subStartTime = System.currentTimeMillis ();
314         try {
315                 // KLUDGE - Cloudify requires Tenant Name for Openstack.  We have the ID.
316                 //          Go directly to Keystone until APIs could be updated to supply the name.
317                 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
318                 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
319
320                 // TODO: Get a reasonable timeout.  Use a global property, or store the creation timeout in rollback object and use that.
321             deployment = cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantName, vfModuleId, 5);
322             logger.debug("Rolled back deployment: {}", deployment.getId());
323         } catch (MsoException me) {
324             // Failed to rollback the VNF due to a cloudify exception.
325             // Convert to a generic VnfException
326             me.addContext ("RollbackVNF");
327             String error = "Rollback VF Module: " + vfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + me;
328             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vfModuleId, cloudSiteId,
329                 tenantId, CLOUDIFY, "DeleteDeployment", MsoLogger.ErrorCode.DataError.getValue(),
330                 "Exception - DeleteDeployment", me);
331             logger.debug(error);
332             throw new VnfException (me);
333         }
334         return;
335     }
336
337
338     private VnfStatus deploymentStatusToVnfStatus (DeploymentInfo deployment) {
339         // Determine the status based on last action & status
340         // DeploymentInfo object should be enhanced to report a better status internally.
341         DeploymentStatus status = deployment.getStatus();
342         String lastAction = deployment.getLastAction();
343
344         if (status == null  ||  lastAction == null) {
345                 return VnfStatus.UNKNOWN;
346         }
347         else if (status == DeploymentStatus.NOTFOUND) {
348                         return VnfStatus.NOTFOUND;
349         }
350         else if (status == DeploymentStatus.INSTALLED) {
351                         return VnfStatus.ACTIVE;
352         }
353         else if (status == DeploymentStatus.CREATED) {
354                 // Should have an INACTIVE status for this case.  Shouldn't really happen, but
355                 // Install was never run, or Uninstall was done but deployment didn't get deleted.
356                 return VnfStatus.UNKNOWN;
357         }
358         else if (status == DeploymentStatus.FAILED) {
359                 return VnfStatus.FAILED;
360         }
361
362         return VnfStatus.UNKNOWN;
363     }
364
365     private Map <String, String> copyStringOutputs (Map <String, Object> stackOutputs) {
366         Map <String, String> stringOutputs = new HashMap <String, String> ();
367         for (String key : stackOutputs.keySet ()) {
368             if (stackOutputs.get (key) instanceof String) {
369                 stringOutputs.put (key, (String) stackOutputs.get (key));
370             } else if (stackOutputs.get(key) instanceof Integer)  {
371                 try {
372                         String str = "" + stackOutputs.get(key);
373                         stringOutputs.put(key, str);
374                 } catch (Exception e) {
375                         logger.debug("Unable to add " + key + " to outputs");
376                 }
377             } else if (stackOutputs.get(key) instanceof JsonNode) {
378                 try {
379                         String str = this.convertNode((JsonNode) stackOutputs.get(key));
380                         stringOutputs.put(key, str);
381                 } catch (Exception e) {
382                   logger.debug("Unable to add " + key + " to outputs - exception converting JsonNode");
383               }
384             } else if (stackOutputs.get(key) instanceof java.util.LinkedHashMap) {
385                 try {
386                                         String str = JSON_MAPPER.writeValueAsString(stackOutputs.get(key));
387                         stringOutputs.put(key, str);
388                 } catch (Exception e) {
389                         logger.debug("Unable to add " + key + " to outputs - exception converting LinkedHashMap");
390                 }
391             } else {
392                 try {
393                         String str = stackOutputs.get(key).toString();
394                         stringOutputs.put(key, str);
395                 } catch (Exception e) {
396                         logger.debug("Unable to add " + key + " to outputs - unable to call .toString() " + e.getMessage());
397                 }
398             }
399         }
400         return stringOutputs;
401     }
402
403
404     private void sendMapToDebug(Map<String, Object> inputs, String optionalName) {
405         int i = 0;
406         StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName);
407         if (inputs == null) {
408                 sb.append("\tNULL");
409         }
410         else if (inputs.size() < 1) {
411                 sb.append("\tEMPTY");
412         } else {
413                 for (String str : inputs.keySet()) {
414                         String outputString;
415                         try {
416                                 outputString = inputs.get(str).toString();
417                         } catch (Exception e) {
418                                 outputString = "Unable to call toString() on the value for " + str;
419                         }
420                         sb.append("\t\nitem " + i++ + ": '" + str + "'='" + outputString + "'");
421                 }
422         }
423         logger.debug(sb.toString());
424         return;
425     }
426
427     private void sendMapToDebug(Map<String, Object> inputs) {
428         int i = 0;
429         StringBuilder sb = new StringBuilder("inputs:");
430         if (inputs == null) {
431                 sb.append("\tNULL");
432         }
433         else if (inputs.size() < 1) {
434                 sb.append("\tEMPTY");
435         } else {
436                 for (String str : inputs.keySet()) {
437                         sb.append("\titem " + i++ + ": " + str + "=" + inputs.get(str));
438                 }
439         }
440         logger.debug(sb.toString());
441         return;
442     }
443
444     private String convertNode(final JsonNode node) {
445         try {
446             final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
447             final String json = JSON_MAPPER.writeValueAsString(obj);
448             return json;
449         } catch (JsonParseException jpe) {
450             logger.debug("Error converting json to string " + jpe.getMessage());
451         } catch (Exception e) {
452             logger.debug("Error converting json to string " + e.getMessage());
453         }
454         return "[Error converting json to string]";
455     }
456
457     private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
458         if (objectMap == null) {
459             return null;
460         }
461         Map<String, String> stringMap = new HashMap<String, String>();
462         for (String key : objectMap.keySet()) {
463             if (!stringMap.containsKey(key)) {
464                 Object obj = objectMap.get(key);
465                 if (obj instanceof String) {
466                     stringMap.put(key, (String) objectMap.get(key));
467                 } else if (obj instanceof JsonNode ){
468                     // This is a bit of mess - but I think it's the least impacting
469                     // let's convert it BACK to a string - then it will get converted back later
470                     try {
471                         String str = this.convertNode((JsonNode) obj);
472                         stringMap.put(key, str);
473                     } catch (Exception e) {
474                                                 logger.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode "+ key);
475                         //okay in this instance - only string values (fqdn) are expected to be needed
476                     }
477                 } else if (obj instanceof java.util.LinkedHashMap) {
478                     logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
479                     try {
480                         String str = JSON_MAPPER.writeValueAsString(obj);
481                         stringMap.put(key, str);
482                     } catch (Exception e) {
483                         logger.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap "+ key);
484                                         }
485                                 }  else if (obj instanceof Integer) {
486                                         try {
487                                                 String str = "" + obj;
488                                                 stringMap.put(key, str);
489                                         } catch (Exception e) {
490               logger.debug("DANGER WILL ROBINSON: unable to convert value for Integer "+ key);
491                     }
492                 } else {
493                     try {
494                                                 String str = obj.toString();
495                         stringMap.put(key, str);
496                     } catch (Exception e) {
497                         logger.debug("DANGER WILL ROBINSON: unable to convert value "+ key + " (" + e.getMessage() + ")");
498                     }
499                 }
500             }
501         }
502
503         return stringMap;
504     }
505
506     /**
507      * This is the "Create VF Module" web service implementation.
508      * It will instantiate a new VF Module of the requested type in the specified cloud
509      * and tenant. The tenant must exist before this service is called.
510      *
511      * If a VF Module with the same name already exists, this can be considered a
512      * success or failure, depending on the value of the 'failIfExists' parameter.
513      *
514      * All VF Modules are defined in the MSO catalog. The caller must request
515      * one of the pre-defined module types or an error will be returned. Within the
516      * catalog, each VF Module references (among other things) a cloud template
517      * which is used to deploy the required  artifacts (VMs, networks, etc.)
518      * to the cloud.  In this adapter implementation, that artifact is expected
519      * to be a Cloudify blueprint.
520      *
521      * Depending on the blueprint, a variable set of input parameters will
522      * be defined, some of which are required. The caller is responsible to
523      * pass the necessary input data for the module or an error will be thrown.
524      *
525      * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback
526      * object. This last object can be passed as-is to the rollbackVnf operation to
527      * undo everything that was created for the Module. This is useful if a VF module
528      * is successfully created but the orchestration fails on a subsequent step.
529      *
530      * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
531      * @param tenantId Openstack tenant identifier
532      * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB.
533      *        Deprecated - should use modelCustomizationUuid
534      * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
535      *        Deprecated - VF Module versions also captured by modelCustomizationUuid
536      * @param genericVnfId Generic VNF ID
537      * @param vfModuleName Name to be assigned to the new VF Module
538      * @param vfModuleId Id of the new VF Module
539      * @param requestType Indicates if this is a Volume Group or Module request
540      * @param volumeGroupId Identifier (i.e. deployment ID) for a Volume Group
541      *        to attach to a VF Module
542      * @param baseVfModuleId Identifier (i.e. deployment ID) of the Base Module if
543      *        this is an Add-on module
544      * @param modelCustomizationUuid Unique ID for the VF Module's model.  Replaces
545      *        the use of vfModuleType.
546      * @param inputs Map of key=value inputs for VNF stack creation
547      * @param failIfExists Flag whether already existing VNF should be considered
548      * @param backout Flag whether to suppress automatic backout (for testing)
549      * @param msoRequest Request tracking information for logs
550      * @param vnfId Holder for output VNF Cloudify Deployment ID
551      * @param outputs Holder for Map of VNF outputs from Deployment (assigned IPs, etc)
552      * @param rollback Holder for returning VnfRollback object
553      */
554     @Override
555     public void createVfModule(String cloudSiteId,
556             String tenantId,
557             String vfModuleType,
558             String vnfVersion,
559             String genericVnfId,
560             String vfModuleName,
561             String vfModuleId,
562             String requestType,
563             String volumeGroupId,
564             String baseVfModuleId,
565             String modelCustomizationUuid,
566             Map <String, Object> inputs,
567             Boolean failIfExists,
568             Boolean backout,
569             Boolean enableBridge,
570             MsoRequest msoRequest,
571             Holder <String> vnfId,
572             Holder <Map <String, String>> outputs,
573             Holder <VnfRollback> rollback)
574         throws VnfException
575     {
576         // Will capture execution time for metrics
577         long startTime = System.currentTimeMillis ();
578
579         MsoLogger.setLogContext (msoRequest);
580
581         // Require a model customization ID.  Every VF Module definition must have one.
582         if (modelCustomizationUuid == null || modelCustomizationUuid.isEmpty()) {
583             logger.debug("Missing required input: modelCustomizationUuid");
584             String error = "Create vfModule error: Missing required input: modelCustomizationUuid";
585             logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
586                 "VF Module ModelCustomizationUuid", CLOUDIFY, MsoLogger.ErrorCode.DataError.getValue(),
587                 "Create VF Module: Missing required input: modelCustomizationUuid");
588             logger.debug(error);
589             throw new VnfException(error, MsoExceptionCategory.USERDATA);
590         }
591
592         // Clean up some inputs to make comparisons easier
593         if (requestType == null)
594                 requestType = "";
595
596         if ("".equals(volumeGroupId) || "null".equals(volumeGroupId))
597                 volumeGroupId = null;
598
599         if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId))
600                 baseVfModuleId = null;
601
602         if (inputs == null) {
603                 // Create an empty set of inputs
604                 inputs = new HashMap<>();
605                 logger.debug("inputs == null - setting to empty");
606         } else {
607                 this.sendMapToDebug(inputs);
608         }
609
610         // Check if this is for a "Volume" module
611         boolean isVolumeRequest = false;
612         if (requestType.startsWith("VOLUME")) {
613                 isVolumeRequest = true;
614         }
615
616         logger.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = " +
617             baseVfModuleId);
618
619         // Build a default rollback object (no actions performed)
620         VnfRollback vfRollback = new VnfRollback();
621         vfRollback.setCloudSiteId(cloudSiteId);
622         vfRollback.setTenantId(tenantId);
623         vfRollback.setMsoRequest(msoRequest);
624         vfRollback.setRequestType(requestType);
625         vfRollback.setIsBase(false);    // Until we know better
626         vfRollback.setVolumeGroupHeatStackId(volumeGroupId);
627         vfRollback.setBaseGroupHeatStackId(baseVfModuleId);
628         vfRollback.setModelCustomizationUuid(modelCustomizationUuid);
629         vfRollback.setMode("CFY");
630
631                 rollback.value = vfRollback; // Default rollback - no updates performed
632
633         // Get the VNF/VF Module definition from the Catalog DB first.
634         // There are three relevant records:  VfModule, VfModuleCustomization, VnfResource
635
636         VfModule vf = null;
637         VnfResource vnfResource = null;
638         VfModuleCustomization vfmc = null;
639
640         try {
641             vfmc = vfModuleCustomRepo.findByModelCustomizationUUID(modelCustomizationUuid);
642
643             if (vfmc == null) {
644                 String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid="
645                     + modelCustomizationUuid;
646                 logger.debug(error);
647                 logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VF Module "
648                         + "ModelCustomizationUuid",
649                     modelCustomizationUuid, "CatalogDb", MsoLogger.ErrorCode.DataError.getValue(), error);
650                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
651             } else {
652                 logger.debug("Found vfModuleCust entry " + vfmc.toString());
653             }
654
655             // Get the vfModule and vnfResource records
656                 vf = vfmc.getVfModule();
657                 vnfResource = vfmc.getVfModule().getVnfResources();
658         }
659         catch (Exception e) {
660
661                 logger.debug("unhandled exception in create VF - [Query]" + e.getMessage());
662                 throw new VnfException("Exception during create VF " + e.getMessage());
663         }
664
665         //  Perform a version check against cloudSite
666         // Obtain the cloud site information where we will create the VF Module
667         Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite (cloudSiteId);
668         if (!cloudSiteOp.isPresent()) {
669             throw new VnfException (new MsoCloudSiteNotFound (cloudSiteId));
670         }
671         CloudSite cloudSite = cloudSiteOp.get();
672                 MavenLikeVersioning aicV = new MavenLikeVersioning();
673                 aicV.setVersion(cloudSite.getCloudVersion());
674
675                 String vnfMin = vnfResource.getAicVersionMin();
676                 String vnfMax = vnfResource.getAicVersionMax();
677
678                 if ( (vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) ||
679                      (vnfMax != null && aicV.isMoreRecentThan(vnfMax)))
680                 {
681                         // ERROR
682         String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUUID()
683             + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax + " NOT supported on Cloud: " + cloudSiteId
684             + " with AIC_Version:" + cloudSite.getCloudVersion();
685         logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack",
686             MsoLogger.ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
687         logger.debug(error);
688         throw new VnfException(error, MsoExceptionCategory.USERDATA);
689                 }
690                 // End Version check
691
692
693         DeploymentInfo cloudifyDeployment = null;
694
695         // First, look up to see if the VF already exists.
696
697         long subStartTime1 = System.currentTimeMillis ();
698         try {
699             cloudifyDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, vfModuleName);
700         }
701         catch (MsoException me) {
702             // Failed to query the Deployment due to a cloudify exception.
703             String error = "Create VF Module: Query " + vfModuleName + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
704             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudSiteId,
705                 tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError.getValue(),
706                 "Exception - queryDeployment", me);
707             logger.debug(error);
708
709             // Convert to a generic VnfException
710             me.addContext ("CreateVFModule");
711             throw new VnfException (me);
712         }
713
714         // More precise handling/messaging if the Module already exists
715         if (cloudifyDeployment != null && !(cloudifyDeployment.getStatus () == DeploymentStatus.NOTFOUND)) {
716                 // CREATED, INSTALLED, INSTALLING, FAILED, UNINSTALLING, UNKNOWN
717                 DeploymentStatus status = cloudifyDeployment.getStatus();
718                         logger.debug ("Found Existing Deployment, status=" + status);
719
720                 if (status == DeploymentStatus.INSTALLED) {
721                         // fail - it exists
722                         if (failIfExists != null && failIfExists) {
723                                 String error = "Create VF: Deployment " + vfModuleName + " already exists in " + cloudSiteId + "/" + tenantId;
724                 logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
725                     cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError.getValue(),
726                     "Deployment " + vfModuleName + " already exists");
727                 logger.debug(error);
728                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
729                         } else {
730                                 // Found existing deployment and client has not requested "failIfExists".
731                                 // Populate the outputs from the existing deployment.
732
733                                 vnfId.value = cloudifyDeployment.getId();
734                                 outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
735                     return;
736                         }
737                 }
738                 // Check through various detailed error cases
739                 if (status == DeploymentStatus.INSTALLING || status == DeploymentStatus.UNINSTALLING) {
740                         // fail - it's in progress - return meaningful error
741               String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; please wait for it to complete, or fix manually.";
742               logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
743                   cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError.getValue(),
744                   "Deployment " + vfModuleName + " already exists");
745               logger.debug(error);
746                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
747                 }
748                 else if (status == DeploymentStatus.FAILED) {
749                         // fail - it exists and is in a FAILED state
750               String error = "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
751               logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
752                   cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError.getValue(),
753                   "Deployment " + vfModuleName + " already " + "exists and is in FAILED state");
754               logger.debug(error);
755                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
756                 }
757                 else if (status == DeploymentStatus.UNKNOWN || status == DeploymentStatus.CREATED) {
758                         // fail - it exists and is in a UNKNOWN state
759               String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
760               logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
761                   cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError.getValue(),
762                   "Deployment " + vfModuleName + " already " + "exists and is in " + status.toString() + " state");
763               logger.debug(error);
764                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
765                 }
766                 else {
767                         // Unexpected, since all known status values have been tested for
768               String error =
769                   "Create VF: Deployment " + vfModuleName + " already exists with unexpected status " + status
770                       .toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
771               logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
772                   cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError.getValue(),
773                   "Deployment " + vfModuleName + " already " + "exists and is in an unknown state");
774               logger.debug(error);
775               throw new VnfAlreadyExists(vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
776           }
777         }
778
779
780         // Collect outputs from Base Modules and Volume Modules
781         Map<String, Object> baseModuleOutputs = null;
782         Map<String, Object> volumeGroupOutputs = null;
783
784         // If a Volume Group was provided, query its outputs for inclusion in Module input parameters
785         if (volumeGroupId != null) {
786             long subStartTime2 = System.currentTimeMillis ();
787             DeploymentInfo volumeDeployment = null;
788             try {
789                 volumeDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, volumeGroupId);
790             }
791             catch (MsoException me) {
792                 // Failed to query the Volume GroupDeployment due to a cloudify exception.
793                 String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
794                 logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId,
795                     cloudSiteId, tenantId, CLOUDIFY, "queryDeployment(volume)",
796                     MsoLogger.ErrorCode.DataError.getValue(), "Exception - queryDeployment(volume)", me);
797                 logger.debug(error);
798                 // Convert to a generic VnfException
799                 me.addContext ("CreateVFModule(QueryVolume)");
800                 throw new VnfException (me);
801             }
802
803                 if (volumeDeployment == null || volumeDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
804               String error =
805                   "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in " + cloudSiteId + "/"
806                       + tenantId + " USER ERROR";
807               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId,
808                   cloudSiteId, tenantId, error, CLOUDIFY, "queryDeployment(volume)",
809                   MsoLogger.ErrorCode.BusinessProcesssError.getValue(),
810                   "Create VFModule: Attached Volume Group DOES NOT EXIST");
811               logger.debug(error);
812               throw new VnfException(error, MsoExceptionCategory.USERDATA);
813           } else {
814                         logger.debug("Found nested volume group");
815                         volumeGroupOutputs = volumeDeployment.getOutputs();
816                         this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs");
817                 }
818         }
819
820         // If this is an Add-On Module, query the Base Module outputs
821         // Note: This will be performed whether or not the current request is for an
822         //       Add-On Volume Group or Add-On VF Module
823
824         if (vf.getIsBase()) {
825             logger.debug("This is a BASE Module request");
826             vfRollback.setIsBase(true);
827         } else {
828             logger.debug("This is an Add-On Module request");
829
830             // Add-On Modules should always have a Base, but just treat as a warning if not provided.
831             // Add-on Volume requests may or may not specify a base.
832             if (!isVolumeRequest && baseVfModuleId == null) {
833                 logger.debug ("WARNING:  Add-on Module request - no Base Module ID provided");
834             }
835
836             if (baseVfModuleId != null) {
837                     long subStartTime2 = System.currentTimeMillis ();
838                     DeploymentInfo baseDeployment = null;
839                     try {
840                         baseDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, baseVfModuleId);
841                     }
842                     catch (MsoException me) {
843                         // Failed to query the Volume GroupDeployment due to a cloudify exception.
844                   String error =
845                       "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudSiteId + "/" + tenantId + ": "
846                           + me;
847                   logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId,
848                       cloudSiteId, tenantId, CLOUDIFY, "queryDeployment(Base)",
849                       MsoLogger.ErrorCode.DataError.getValue(), "Exception - queryDeployment(Base)", me);
850                   logger.debug(error);
851                   // Convert to a generic VnfException
852                   me.addContext("CreateVFModule(QueryBase)");
853                   throw new VnfException (me);
854                     }
855
856                 if (baseDeployment == null || baseDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
857                     String error =
858                         "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in " + cloudSiteId + "/"
859                             + tenantId + " USER ERROR";
860                     logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId,
861                         cloudSiteId, tenantId, error, CLOUDIFY, "queryDeployment(Base)",
862                         MsoLogger.ErrorCode.BusinessProcesssError.getValue(),
863                         "Create VFModule: Base " + "Module DOES NOT EXIST");
864                     logger.debug(error);
865                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
866                 } else {
867                     logger.debug("Found base module");
868                     baseModuleOutputs = baseDeployment.getOutputs();
869                     this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs");
870                 }
871             }
872         }
873
874
875         // Ready to deploy the new VNF
876
877         // NOTE:  For this section, heatTemplate is used for both HEAT templates and Cloudify blueprints.
878         // In final implementation (post-POC), the template object would either be generic or there would
879         // be a separate DB Table/Object for Blueprints.
880
881
882                 // NOTE: The template is fixed for the VF Module.  The environment is part of the customization.
883         HeatTemplate heatTemplate = null;
884         HeatEnvironment heatEnvironment = null;
885         if (isVolumeRequest) {
886                         heatTemplate = vf.getVolumeHeatTemplate();
887                         heatEnvironment = vfmc.getVolumeHeatEnv();
888                 } else {
889                         heatTemplate = vf.getModuleHeatTemplate();
890                         heatEnvironment = vfmc.getHeatEnvironment();
891                 }
892
893                 if (heatTemplate == null) {
894         String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType="
895             + requestType;
896         logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID", vfModuleType,
897             "OpenStack", MsoLogger.ErrorCode.DataError.getValue(), error);
898         throw new VnfException(error, MsoExceptionCategory.INTERNAL);
899     } else {
900                         logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
901                 }
902
903         if (heatEnvironment == null) {
904             String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
905             logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
906                 "OpenStack", MsoLogger.ErrorCode.DataError.getValue(), error);
907             // Alarm on this error, configuration must be fixed
908             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
909         } else {
910             logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
911         }
912
913
914         try {
915             // All variables converted to their native object types
916             HashMap<String, Object> goldenInputs = new HashMap<String,Object>();
917             List<String> extraInputs = new ArrayList<String>();
918
919             // NOTE: SKIP THIS FOR CLOUDIFY for now.  Just use what was passed in.
920             //  This whole section needs to be rewritten.
921                         Boolean skipInputChecks = false;
922
923                         if (skipInputChecks) {
924                                 goldenInputs = new HashMap<String,Object>();
925                                 for (String key : inputs.keySet()) {
926                                         goldenInputs.put(key, inputs.get(key));
927                                 }
928                         }
929                         else {
930                                 // Build maps for the parameters (including aliases) to simplify checks
931                                 HashMap<String, HeatTemplateParam> params = new HashMap<String, HeatTemplateParam>();
932
933                                 Set<HeatTemplateParam> paramSet = heatTemplate.getParameters();
934                                 logger.debug("paramSet has {} entries", paramSet.size());
935
936                                 for (HeatTemplateParam htp : paramSet) {
937                                         params.put(htp.getParamName(), htp);
938
939                                         // Include aliases.
940                                         String alias = htp.getParamAlias();
941                                         if (alias != null && !alias.equals("") && !params.containsKey(alias)) {
942                                                 params.put(alias, htp);
943                                         }
944                                 }
945
946                                 // First, convert all inputs to their "template" type
947                                 for (String key : inputs.keySet()) {
948                                         if (params.containsKey(key)) {
949                                                 Object value = cloudifyUtils.convertInputValue(inputs.get(key), params.get(key));
950                                                 if (value != null) {
951                                                         goldenInputs.put(key, value);
952                                                 }
953                                                 else {
954                                                         logger.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to " + params.get(key)
955                   .getParamType());
956                                                 }
957                                         } else {
958                                                 extraInputs.add(key);
959                                         }
960                                 }
961
962                                 if (!extraInputs.isEmpty()) {
963                                         logger.debug("Ignoring extra inputs: " + extraInputs);
964                                 }
965
966                                 // Next add in Volume Group Outputs if there are any.  Copy directly without conversions.
967                                 if (volumeGroupOutputs != null  &&  !volumeGroupOutputs.isEmpty()) {
968                                         for (String key : volumeGroupOutputs.keySet()) {
969                                                 if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
970                                                         goldenInputs.put(key, volumeGroupOutputs.get(key));
971                                                 }
972                                         }
973                                 }
974
975                                 // Next add in Base Module Outputs if there are any.  Copy directly without conversions.
976                                 if (baseModuleOutputs != null  &&  !baseModuleOutputs.isEmpty()) {
977                                         for (String key : baseModuleOutputs.keySet()) {
978                                                 if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
979                                                         goldenInputs.put(key, baseModuleOutputs.get(key));
980                                                 }
981                                         }
982                                 }
983
984                                 // Last, add in values from the "environment" file.
985                                 // These are added to the inputs, since Cloudify doesn't pass an environment file like Heat.
986
987                                 // TODO: This may take a different form for Cloudify, but for now process it
988                                 //       with Heat environment file syntax
989                 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
990                                 MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry (sb);
991
992                                 if (mhee.getParameters() != null) {
993                                         for (MsoHeatEnvironmentParameter envParam : mhee.getParameters()) {
994                                                 // If this is a template input, copy to golden inputs
995                                                 String envKey = envParam.getName();
996                                                 if (params.containsKey(envKey)  &&  !goldenInputs.containsKey(envKey)) {
997                                                         Object value = cloudifyUtils.convertInputValue(envParam.getValue(), params.get(envKey));
998                                                         if (value != null) {
999                                                                 goldenInputs.put(envKey, value);
1000                                                         }
1001                                                         else {
1002                                                                 logger.debug("Failed to convert environment parameter " + envKey + "='" + envParam.getValue() + "' to " +
1003                     params.get(envKey).getParamType());
1004                                                         }
1005                                                 }
1006                                         }
1007                                 }
1008
1009                     this.sendMapToDebug(goldenInputs, "Final inputs sent to Cloudify");
1010
1011
1012                     // Check that required parameters have been supplied from any of the sources
1013                     String missingParams = null;
1014                     boolean checkRequiredParameters = true;
1015                     try {
1016                         String propertyString = this.environment.getProperty(MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
1017                         if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
1018                             checkRequiredParameters = false;
1019                       logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking... {}",
1020                           MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
1021                   }
1022                     } catch (Exception e) {
1023                         // No problem - default is true
1024                   logger.debug("An exception occured trying to get property {}",
1025                       MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS, e);
1026               }
1027
1028
1029                     for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
1030                         if (parm.isRequired () && (!goldenInputs.containsKey (parm.getParamName ()))) {
1031                       logger.debug("adding to missing parameters list: {}", parm.getParamName());
1032                       if (missingParams == null) {
1033                           missingParams = parm.getParamName();
1034                       } else {
1035                           missingParams += "," + parm.getParamName();
1036                       }
1037                   }
1038                     }
1039
1040           if (missingParams != null) {
1041               if (checkRequiredParameters) {
1042                   // Problem - missing one or more required parameters
1043                   String error = "Create VFModule: Missing Required inputs: " + missingParams;
1044                   logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, CLOUDIFY,
1045                       MsoLogger.ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs");
1046                   logger.debug(error);
1047                   throw new VnfException(error, MsoExceptionCategory.USERDATA);
1048               } else {
1049                   logger.debug(
1050                       "found missing parameters [" + missingParams + "] - but checkRequiredParameters is false -"
1051                           + " will not block");
1052               }
1053           } else {
1054               logger.debug("No missing parameters found - ok to proceed");
1055           }
1056
1057                         } // NOTE: END PARAMETER CHECKING
1058
1059                         // Ready to deploy the VF Module.
1060                         // *First step - make sure the blueprint is loaded into Cloudify.
1061                         String blueprintName = heatTemplate.getTemplateName();
1062                         String blueprint = heatTemplate.getTemplateBody();
1063                         String blueprintId = blueprintName;
1064
1065                         // Use the main blueprint name as the blueprint ID (strip yaml extensions).
1066             if (blueprintId.endsWith(".yaml"))
1067                 blueprintId = blueprintId.substring(0,blueprintId.lastIndexOf(".yaml"));
1068
1069                         try {
1070                                 if (! cloudifyUtils.isBlueprintLoaded (cloudSiteId, blueprintId)) {
1071           logger.debug("Blueprint " + blueprintId + " is not loaded.  Will upload it now.");
1072
1073                                         Map<String,byte[]> blueprintFiles = new HashMap<String,byte[]>();
1074
1075                                         blueprintFiles.put(blueprintName, blueprint.getBytes());
1076
1077                             // TODO:  Implement nested blueprint logic based on Cloudify structures.
1078                                         //        For now, just use the Heat structures.
1079                                         //        The query returns a map of String->Object, where the map keys provide one layer of
1080                                         //        indirection from the Heat template names.  For this case, assume the map key matches
1081                                         //        the nested blueprint name.
1082                             List<HeatTemplate> nestedBlueprints = heatTemplate.getChildTemplates();
1083                             if (nestedBlueprints != null) {
1084                                     for (HeatTemplate nestedBlueprint: nestedBlueprints) {
1085                                         blueprintFiles.put(nestedBlueprint.getTemplateName(), nestedBlueprint.getTemplateBody().getBytes());
1086                                     }
1087                             }
1088
1089                             // TODO:  Implement file artifact logic based on Cloudify structures.
1090                             //        For now, just use the Heat structures.
1091                             List<HeatFiles> heatFiles = vf.getHeatFiles();
1092                             if (heatFiles != null) {
1093                                     for (HeatFiles heatFile: heatFiles) {
1094                                         blueprintFiles.put(heatFile.getFileName(), heatFile.getFileBody().getBytes());
1095                                     }
1096                             }
1097
1098                             // Upload the blueprint package
1099                                         cloudifyUtils.uploadBlueprint(cloudSiteId, blueprintId, blueprintName, blueprintFiles, false);
1100
1101                                 }
1102                         }
1103
1104                         catch (MsoException me) {
1105           me.addContext("CreateVFModule");
1106           String error = "Create VF Module: Upload blueprint failed.  Blueprint=" + blueprintName + ": " + me;
1107           logger.error("{} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudSiteId,
1108               tenantId, CLOUDIFY, MsoLogger.ErrorCode.DataError.getValue(), "MsoException - uploadBlueprint", me);
1109           logger.debug(error);
1110           throw new VnfException(me);
1111                         }
1112
1113             // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1114             // because we already checked for those.
1115             long createDeploymentStarttime = System.currentTimeMillis ();
1116             try {
1117                 // KLUDGE - Cloudify requires Tenant Name for Openstack.  We have the ID.
1118                 //          Go directly to Keystone until APIs could be updated to supply the name.
1119                 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
1120                 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
1121
1122                 if (backout == null) {
1123                         backout = true;
1124                 }
1125
1126                 cloudifyDeployment = cloudifyUtils.createAndInstallDeployment (cloudSiteId,
1127                                               tenantName,
1128                                               vfModuleName,
1129                                               blueprintId,
1130                                               goldenInputs,
1131                                               true,
1132                                               heatTemplate.getTimeoutMinutes (),
1133                                               backout.booleanValue());
1134
1135             } catch (MsoException me) {
1136                 me.addContext ("CreateVFModule");
1137                 String error = "Create VF Module " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1138                 logger
1139                     .error("{} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudSiteId,
1140                         tenantId, CLOUDIFY, MsoLogger.ErrorCode.DataError.getValue(), "MsoException - createDeployment",
1141                         me);
1142                 logger.debug(error);
1143                 throw new VnfException (me);
1144             } catch (NullPointerException npe) {
1145                 String error = "Create VFModule " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + npe;
1146                 logger
1147                     .error("{} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudSiteId,
1148                         tenantId, CLOUDIFY, MsoLogger.ErrorCode.DataError.getValue(),
1149                         "NullPointerException - createDeployment", npe);
1150                 logger.debug(error);
1151                 logger.debug("NULL POINTER EXCEPTION at cloudify.createAndInstallDeployment");
1152                 //npe.addContext ("CreateVNF");
1153                 throw new VnfException ("NullPointerException during cloudify.createAndInstallDeployment");
1154             } catch (Exception e) {
1155                 logger.debug("unhandled exception at cloudify.createAndInstallDeployment");
1156                 throw new VnfException("Exception during cloudify.createAndInstallDeployment! " + e.getMessage());
1157             }
1158         } catch (Exception e) {
1159                 logger.debug("unhandled exception in create VF");
1160                 throw new VnfException("Exception during create VF " + e.getMessage());
1161
1162         }
1163
1164         // Reach this point if create is successful.
1165         // Populate remaining rollback info and response parameters.
1166         vfRollback.setVnfCreated (true);
1167         vfRollback.setVnfId (cloudifyDeployment.getId());
1168         vnfId.value = cloudifyDeployment.getId();
1169         outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
1170
1171         rollback.value = vfRollback;
1172
1173         logger.debug("VF Module successfully created", vfModuleName);
1174         return;
1175     }
1176
1177     public void deleteVfModule (String cloudSiteId,
1178                            String tenantId,
1179                            String vnfName,
1180                            MsoRequest msoRequest,
1181                            Holder <Map <String, String>> outputs) throws VnfException {
1182         MsoLogger.setLogContext (msoRequest);
1183         logger.debug ("Deleting VF " + vnfName + " in " + cloudSiteId + "/" + tenantId);
1184         // Will capture execution time for metrics
1185         long startTime = System.currentTimeMillis ();
1186
1187         // 1702 capture the output parameters on a delete
1188         // so we'll need to query first
1189         DeploymentInfo deployment = null;
1190         try {
1191                 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
1192         } catch (MsoException me) {
1193             // Failed to query the deployment.  Convert to a generic VnfException
1194             me.addContext ("DeleteVFModule");
1195             String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1196             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudSiteId,
1197                 tenantId, CLOUDIFY, "QueryDeployment", MsoLogger.ErrorCode.DataError.getValue(),
1198                 "Exception - QueryDeployment", me);
1199             logger.debug(error);
1200             throw new VnfException (me);
1201         }
1202         // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected Object types
1203         outputs.value = convertMapStringObjectToStringString(deployment.getOutputs());
1204
1205         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
1206         // The possible outcomes of deleteStack are a StackInfo object with status
1207         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
1208         // could be thrown.
1209         long subStartTime = System.currentTimeMillis ();
1210         try {
1211             cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantId, vnfName, 5);
1212         } catch (MsoException me) {
1213             me.addContext("DeleteVfModule");
1214             // Convert to a generic VnfException
1215             String error = "Delete VF: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1216             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudSiteId,
1217                 tenantId, "DeleteDeployment", "DeleteDeployment", MsoLogger.ErrorCode.DataError.getValue(),
1218                 "Exception - DeleteDeployment: " + me.getMessage());
1219             logger.debug(error);
1220             throw new VnfException(me);
1221         }
1222
1223         // On success, nothing is returned.
1224         return;
1225     }
1226
1227     // TODO:  Should Update be supported for Cloudify?  What would this look like?
1228     @Override
1229     public void updateVfModule (String cloudSiteId,
1230                            String tenantId,
1231                            String vnfType,
1232                            String vnfVersion,
1233                            String vnfName,
1234                            String requestType,
1235                            String volumeGroupHeatStackId,
1236                            String baseVfHeatStackId,
1237                            String vfModuleStackId,
1238                            String modelCustomizationUuid,
1239                            Map <String, Object> inputs,
1240                            MsoRequest msoRequest,
1241                            Holder <Map <String, String>> outputs,
1242                            Holder <VnfRollback> rollback) throws VnfException
1243         {
1244                 // This operation is not currently supported for Cloudify-orchestrated VF Modules.
1245                 logger.debug("Update VF Module command attempted but not supported");
1246                 throw new VnfException ("UpdateVfModule:  Unsupported command", MsoExceptionCategory.USERDATA);
1247         }
1248
1249 }