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