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