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