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