2  * ============LICENSE_START=======================================================
 
   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
 
  14  *      http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  24 package org.onap.so.adapters.vnf;
 
  27 import java.util.ArrayList;
 
  28 import java.util.HashMap;
 
  29 import java.util.List;
 
  31 import java.util.Optional;
 
  33 import javax.jws.WebService;
 
  34 import javax.xml.ws.Holder;
 
  35 import org.onap.so.logger.LoggingAnchor;
 
  36 import com.woorea.openstack.heat.Heat;
 
  37 import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists;
 
  38 import org.onap.so.adapters.vnf.exceptions.VnfException;
 
  39 import org.onap.so.cloud.CloudConfig;
 
  40 import org.onap.so.db.catalog.beans.CloudSite;
 
  41 import org.onap.so.cloudify.beans.DeploymentInfo;
 
  42 import org.onap.so.cloudify.beans.DeploymentStatus;
 
  43 import org.onap.so.cloudify.exceptions.MsoCloudifyManagerNotFound;
 
  44 import org.onap.so.cloudify.utils.MsoCloudifyUtils;
 
  45 import org.onap.so.db.catalog.beans.HeatEnvironment;
 
  46 import org.onap.so.db.catalog.beans.HeatFiles;
 
  47 import org.onap.so.db.catalog.beans.HeatTemplate;
 
  48 import org.onap.so.db.catalog.beans.HeatTemplateParam;
 
  49 import org.onap.so.db.catalog.beans.VfModule;
 
  50 import org.onap.so.db.catalog.beans.VfModuleCustomization;
 
  51 import org.onap.so.db.catalog.beans.VnfResource;
 
  52 import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository;
 
  53 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
 
  54 import org.onap.so.entity.MsoRequest;
 
  55 import org.onap.so.logger.ErrorCode;
 
  56 import org.onap.so.logger.MessageEnum;
 
  57 import org.onap.so.openstack.beans.MsoTenant;
 
  58 import org.onap.so.openstack.beans.VnfRollback;
 
  59 import org.onap.so.openstack.beans.VnfStatus;
 
  60 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
 
  61 import org.onap.so.openstack.exceptions.MsoException;
 
  62 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
 
  63 import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry;
 
  64 import org.onap.so.openstack.utils.MsoHeatEnvironmentParameter;
 
  65 import org.onap.so.openstack.utils.MsoKeystoneUtils;
 
  66 import org.slf4j.Logger;
 
  67 import org.slf4j.LoggerFactory;
 
  68 import org.springframework.beans.factory.annotation.Autowired;
 
  69 import org.springframework.core.env.Environment;
 
  70 import org.springframework.stereotype.Component;
 
  71 import com.fasterxml.jackson.core.JsonParseException;
 
  72 import com.fasterxml.jackson.databind.JsonNode;
 
  73 import com.fasterxml.jackson.databind.ObjectMapper;
 
  74 import org.springframework.transaction.annotation.Transactional;
 
  78 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter",
 
  79         targetNamespace = "http://org.onap.so/vnf")
 
  80 public class MsoVnfCloudifyAdapterImpl implements MsoVnfAdapter {
 
  82     private static Logger logger = LoggerFactory.getLogger(MsoVnfCloudifyAdapterImpl.class);
 
  84     private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
 
  85     private static final String CLOUDIFY = "Cloudify";
 
  87     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
 
  88     private static final String BRACKETS = LoggingAnchor.NINE;
 
  89     private static final String OPENSTACK = "OpenStack";
 
  92     protected CloudConfig cloudConfig;
 
  95     private VFModuleCustomizationRepository vfModuleCustomRepo;
 
  98     private Environment environment;
 
 101     protected MsoKeystoneUtils keystoneUtils;
 
 104     protected MsoCloudifyUtils cloudifyUtils;
 
 107      * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
 
 109      * @see MsoVnfCloudifyAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
 
 111     public MsoVnfCloudifyAdapterImpl() {
 
 116      * Health Check web method. Does nothing but return to show the adapter is deployed.
 
 119     public void healthCheck() {
 
 120         logger.debug("Health check call in VNF Cloudify Adapter");
 
 124      * This is the "Create VNF" web service implementation. This function is now unsupported and will return an error.
 
 128     public void createVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion,
 
 129             String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs,
 
 130             Boolean failIfExists, Boolean backout, Boolean enableBridge, MsoRequest msoRequest, Holder<String> vnfId,
 
 131             Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) throws VnfException {
 
 132         // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
 
 133         logger.debug("CreateVNF command attempted but not supported");
 
 134         throw new VnfException("CreateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
 
 138      * This is the "Update VNF" web service implementation. This function is now unsupported and will return an error.
 
 142     public void updateVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion,
 
 143             String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs,
 
 144             MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback)
 
 145             throws VnfException {
 
 146         // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
 
 147         logger.debug("UpdateVNF command attempted but not supported");
 
 148         throw new VnfException("UpdateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
 
 152      * This is the "Query VNF" web service implementation.
 
 154      * This really should be QueryVfModule, but nobody ever changed it.
 
 156      * For Cloudify, this will look up a deployment by its deployment ID, which is really the same as deployment name,
 
 157      * since it assigned by the client when a deployment is created. Also, the input cloudSiteId is used only to
 
 158      * identify which Cloudify instance to query, and the tenantId is ignored (since that really only applies for
 
 161      * The method returns an indicator that the VNF exists, along with its status and outputs. The input "vnfName" will
 
 162      * also be reflected back as its ID.
 
 164      * @param cloudSiteId CLLI code of the cloud site in which to query
 
 165      * @param cloudOwner cloud owner of the cloud site in which to query
 
 166      * @param tenantId Openstack tenant identifier - ignored for Cloudify
 
 167      * @param vnfName VNF Name (should match a deployment ID)
 
 168      * @param msoRequest Request tracking information for logs
 
 169      * @param vnfExists Flag reporting the result of the query
 
 170      * @param vnfId Holder for output VNF ID
 
 171      * @param outputs Holder for Map of VNF outputs from Cloudify deployment (assigned IPs, etc)
 
 174     public void queryVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, MsoRequest msoRequest,
 
 175             Holder<Boolean> vnfExists, Holder<String> vnfId, Holder<VnfStatus> status,
 
 176             Holder<Map<String, String>> outputs) throws VnfException {
 
 177         logger.debug("Querying VNF {} in {}", vnfName, cloudSiteId + "/" + tenantId);
 
 179         DeploymentInfo deployment = null;
 
 182             deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
 
 183         } catch (MsoCloudifyManagerNotFound e) {
 
 184             // This site does not have a Cloudify Manager.
 
 185             // This isn't an error, just means we won't find the VNF here.
 
 187         } catch (MsoException me) {
 
 188             // Failed to query the Deployment due to a cloudify exception.
 
 189             logger.debug("Failed to query the Deployment due to a cloudify exception");
 
 190             // Convert to a generic VnfException
 
 191             me.addContext("QueryVNF");
 
 192             String error = "Query VNF (Cloudify): " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
 
 194             logger.error(BRACKETS, MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId, tenantId,
 
 195                     CLOUDIFY, "QueryVNF", ErrorCode.DataError.getValue(), "Exception - queryDeployment", me);
 
 197             throw new VnfException(me);
 
 200         if (deployment != null && deployment.getStatus() != DeploymentStatus.NOTFOUND) {
 
 201             vnfExists.value = Boolean.TRUE;
 
 202             status.value = deploymentStatusToVnfStatus(deployment);
 
 203             vnfId.value = deployment.getId();
 
 204             outputs.value = copyStringOutputs(deployment.getOutputs());
 
 206             logger.debug("VNF {} found in Cloudify, ID = {}", vnfName, vnfId.value);
 
 208             vnfExists.value = Boolean.FALSE;
 
 209             status.value = VnfStatus.NOTFOUND;
 
 211             outputs.value = new HashMap<String, String>(); // Return as an empty map
 
 213             logger.debug("VNF {} not found", vnfName);
 
 219      * This is the "Delete VNF" web service implementation. This function is now unsupported and will return an error.
 
 223     public void deleteVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, MsoRequest msoRequest)
 
 224             throws VnfException {
 
 226         // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
 
 227         logger.debug("DeleteVNF command attempted but not supported");
 
 228         throw new VnfException("DeleteVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
 
 232      * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
 
 233      * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
 
 234      * to undo the creation.
 
 236      * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup, but APIs were apparently never updated.
 
 239     public void rollbackVnf(VnfRollback rollback) throws VnfException {
 
 240         // rollback may be null (e.g. if stack already existed when Create was called)
 
 241         if (rollback == null) {
 
 242             logger.info(LoggingAnchor.THREE, MessageEnum.RA_ROLLBACK_NULL.toString(), OPENSTACK, "rollbackVnf");
 
 246         // Don't rollback if nothing was done originally
 
 247         if (!rollback.getVnfCreated()) {
 
 251         // Get the elements of the VnfRollback object for easier access
 
 252         String cloudSiteId = rollback.getCloudSiteId();
 
 253         String cloudOwner = rollback.getCloudOwner();
 
 254         String tenantId = rollback.getTenantId();
 
 255         String vfModuleId = rollback.getVfModuleStackId();
 
 257         logger.debug("Rolling Back VF Module {} in {}", vfModuleId, cloudOwner + "/" + cloudSiteId + "/" + tenantId);
 
 259         DeploymentInfo deployment = null;
 
 261         // Use the MsoCloudifyUtils to delete the deployment. Set the polling flag to true.
 
 262         // The possible outcomes of deleteStack are a StackInfo object with status
 
 263         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
 
 266             // KLUDGE - Cloudify requires Tenant Name for Openstack. We have the ID.
 
 267             // Go directly to Keystone until APIs could be updated to supply the name.
 
 268             MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
 
 269             String tenantName = (msoTenant != null ? msoTenant.getTenantName() : tenantId);
 
 271             // TODO: Get a reasonable timeout. Use a global property, or store the creation timeout in rollback object
 
 273             deployment = cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantName, vfModuleId, 5);
 
 274             logger.debug("Rolled back deployment: {}", deployment.getId());
 
 275         } catch (MsoException me) {
 
 276             // Failed to rollback the VNF due to a cloudify exception.
 
 277             // Convert to a generic VnfException
 
 278             me.addContext("RollbackVNF");
 
 279             String error = "Rollback VF Module: " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/"
 
 280                     + tenantId + ": " + me;
 
 281             logger.error(BRACKETS, MessageEnum.RA_DELETE_VNF_ERR.toString(), vfModuleId, cloudOwner, cloudSiteId,
 
 282                     tenantId, CLOUDIFY, "DeleteDeployment", ErrorCode.DataError.getValue(),
 
 283                     "Exception - DeleteDeployment", me);
 
 285             throw new VnfException(me);
 
 290     private VnfStatus deploymentStatusToVnfStatus(DeploymentInfo deployment) {
 
 291         // Determine the status based on last action & status
 
 292         // DeploymentInfo object should be enhanced to report a better status internally.
 
 293         DeploymentStatus status = deployment.getStatus();
 
 294         String lastAction = deployment.getLastAction();
 
 296         if (status == null || lastAction == null) {
 
 297             return VnfStatus.UNKNOWN;
 
 298         } else if (status == DeploymentStatus.NOTFOUND) {
 
 299             return VnfStatus.NOTFOUND;
 
 300         } else if (status == DeploymentStatus.INSTALLED) {
 
 301             return VnfStatus.ACTIVE;
 
 302         } else if (status == DeploymentStatus.CREATED) {
 
 303             // Should have an INACTIVE status for this case. Shouldn't really happen, but
 
 304             // Install was never run, or Uninstall was done but deployment didn't get deleted.
 
 305             return VnfStatus.UNKNOWN;
 
 306         } else if (status == DeploymentStatus.FAILED) {
 
 307             return VnfStatus.FAILED;
 
 310         return VnfStatus.UNKNOWN;
 
 313     private Map<String, String> copyStringOutputs(Map<String, Object> stackOutputs) {
 
 314         Map<String, String> stringOutputs = new HashMap<>();
 
 315         for (Map.Entry<String, Object> entry : stackOutputs.entrySet()) {
 
 316             if (entry.getValue() instanceof String) {
 
 317                 stringOutputs.put(entry.getKey(), (String) entry.getValue());
 
 318             } else if (entry.getValue() instanceof Integer) {
 
 320                     String str = "" + entry.getValue();
 
 321                     stringOutputs.put(entry.getKey(), str);
 
 322                 } catch (Exception e) {
 
 323                     logger.error("Unable to add " + entry.getKey() + " to outputs", e);
 
 325             } else if (entry.getValue() instanceof JsonNode) {
 
 327                     String str = this.convertNode((JsonNode) entry.getValue());
 
 328                     stringOutputs.put(entry.getKey(), str);
 
 329                 } catch (Exception e) {
 
 330                     logger.error("Unable to add " + entry.getKey() + " to outputs - exception converting JsonNode", e);
 
 332             } else if (entry.getValue() instanceof java.util.LinkedHashMap) {
 
 334                     String str = JSON_MAPPER.writeValueAsString(entry.getValue());
 
 335                     stringOutputs.put(entry.getKey(), str);
 
 336                 } catch (Exception e) {
 
 337                     logger.error("Unable to add " + entry.getKey() + " to outputs - exception converting LinkedHashMap",
 
 342                     String str = entry.getValue().toString();
 
 343                     stringOutputs.put(entry.getKey(), str);
 
 344                 } catch (Exception e) {
 
 345                     logger.error("Unable to add " + entry.getKey() + " to outputs - unable to call .toString() ", e);
 
 349         return stringOutputs;
 
 353     private void sendMapToDebug(Map<String, Object> inputs, String optionalName) {
 
 355         StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName);
 
 356         if (inputs == null) {
 
 358         } else if (inputs.size() < 1) {
 
 359             sb.append("\tEMPTY");
 
 361             for (Map.Entry<String, Object> entry : inputs.entrySet()) {
 
 364                     outputString = entry.getValue().toString();
 
 365                 } catch (Exception e) {
 
 366                     outputString = "Unable to call toString() on the value for " + entry.getKey();
 
 368                 sb.append("\t\nitem " + i++ + ": '" + entry.getKey() + "'='" + outputString + "'");
 
 371         logger.debug(sb.toString());
 
 374     private void sendMapToDebug(Map<String, Object> inputs) {
 
 376         StringBuilder sb = new StringBuilder("inputs:");
 
 377         if (inputs == null) {
 
 379         } else if (inputs.size() < 1) {
 
 380             sb.append("\tEMPTY");
 
 382             for (Map.Entry<String, Object> entry : inputs.entrySet()) {
 
 383                 sb.append("\titem " + i++ + ": " + entry.getKey() + "=" + entry.getValue());
 
 386         logger.debug(sb.toString());
 
 389     private String convertNode(final JsonNode node) {
 
 391             final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
 
 392             final String json = JSON_MAPPER.writeValueAsString(obj);
 
 394         } catch (JsonParseException jpe) {
 
 395             logger.error("Error converting json to string ", jpe);
 
 396         } catch (Exception e) {
 
 397             logger.error("Error converting json to string ", e);
 
 399         return "[Error converting json to string]";
 
 402     private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
 
 403         if (objectMap == null) {
 
 406         Map<String, String> stringMap = new HashMap<>();
 
 407         for (Map.Entry<String, Object> entry : objectMap.entrySet()) {
 
 408             if (!stringMap.containsKey(entry.getKey())) {
 
 409                 Object obj = entry.getValue();
 
 410                 if (obj instanceof String) {
 
 411                     stringMap.put(entry.getKey(), (String) entry.getValue());
 
 412                 } else if (obj instanceof JsonNode) {
 
 413                     // This is a bit of mess - but I think it's the least impacting
 
 414                     // let's convert it BACK to a string - then it will get converted back later
 
 416                         String str = this.convertNode((JsonNode) obj);
 
 417                         stringMap.put(entry.getKey(), str);
 
 418                     } catch (Exception e) {
 
 419                         logger.error("DANGER WILL ROBINSON: unable to convert value for JsonNode " + entry.getKey(), e);
 
 420                         // okay in this instance - only string values (fqdn) are expected to be needed
 
 422                 } else if (obj instanceof java.util.LinkedHashMap) {
 
 423                     logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
 
 425                         String str = JSON_MAPPER.writeValueAsString(obj);
 
 426                         stringMap.put(entry.getKey(), str);
 
 427                     } catch (Exception e) {
 
 429                                 "DANGER WILL ROBINSON: unable to convert value for LinkedHashMap " + entry.getKey(), e);
 
 431                 } else if (obj instanceof Integer) {
 
 433                         String str = "" + obj;
 
 434                         stringMap.put(entry.getKey(), str);
 
 435                     } catch (Exception e) {
 
 436                         logger.error("DANGER WILL ROBINSON: unable to convert value for Integer " + entry.getKey(), e);
 
 440                         String str = obj.toString();
 
 441                         stringMap.put(entry.getKey(), str);
 
 442                     } catch (Exception e) {
 
 443                         logger.error("DANGER WILL ROBINSON: unable to convert value " + entry.getKey(), e);
 
 453      * This is the "Create VF Module" web service implementation. It will instantiate a new VF Module of the requested
 
 454      * type in the specified cloud and tenant. The tenant must exist before this service is called.
 
 456      * If a VF Module with the same name already exists, this can be considered a success or failure, depending on the
 
 457      * value of the 'failIfExists' parameter.
 
 459      * All VF Modules are defined in the MSO catalog. The caller must request one of the pre-defined module types or an
 
 460      * error will be returned. Within the catalog, each VF Module references (among other things) a cloud template which
 
 461      * is used to deploy the required artifacts (VMs, networks, etc.) to the cloud. In this adapter implementation, that
 
 462      * artifact is expected to be a Cloudify blueprint.
 
 464      * Depending on the blueprint, a variable set of input parameters will be defined, some of which are required. The
 
 465      * caller is responsible to pass the necessary input data for the module or an error will be thrown.
 
 467      * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback object. This last object can be
 
 468      * passed as-is to the rollbackVnf operation to undo everything that was created for the Module. This is useful if a
 
 469      * VF module is successfully created but the orchestration fails on a subsequent step.
 
 471      * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
 
 472      * @param cloudOwner cloud owner of the cloud site in which to create the VNF
 
 473      * @param tenantId Openstack tenant identifier
 
 474      * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB. Deprecated - should use
 
 475      *        modelCustomizationUuid
 
 476      * @param vnfVersion VNF version key, should match a VNF definition in catalog DB Deprecated - VF Module versions
 
 477      *        also captured by modelCustomizationUuid
 
 478      * @param genericVnfId Generic VNF ID
 
 479      * @param vfModuleName Name to be assigned to the new VF Module
 
 480      * @param vfModuleId Id of the new VF Module
 
 481      * @param requestType Indicates if this is a Volume Group or Module request
 
 482      * @param volumeGroupId Identifier (i.e. deployment ID) for a Volume Group to attach to a VF Module
 
 483      * @param baseVfModuleId Identifier (i.e. deployment ID) of the Base Module if this is an Add-on module
 
 484      * @param modelCustomizationUuid Unique ID for the VF Module's model. Replaces the use of vfModuleType.
 
 485      * @param inputs Map of key=value inputs for VNF stack creation
 
 486      * @param failIfExists Flag whether already existing VNF should be considered
 
 487      * @param backout Flag whether to suppress automatic backout (for testing)
 
 488      * @param msoRequest Request tracking information for logs
 
 489      * @param vnfId Holder for output VNF Cloudify Deployment ID
 
 490      * @param outputs Holder for Map of VNF outputs from Deployment (assigned IPs, etc)
 
 491      * @param rollback Holder for returning VnfRollback object
 
 494     public void createVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vfModuleType,
 
 495             String vnfVersion, String genericVnfId, String vfModuleName, String vfModuleId, String requestType,
 
 496             String volumeGroupId, String baseVfModuleId, String modelCustomizationUuid, Map<String, Object> inputs,
 
 497             Boolean failIfExists, Boolean backout, Boolean enableBridge, MsoRequest msoRequest, Holder<String> vnfId,
 
 498             Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) throws VnfException {
 
 500         // Require a model customization ID. Every VF Module definition must have one.
 
 501         if (modelCustomizationUuid == null || modelCustomizationUuid.isEmpty()) {
 
 502             logger.debug("Missing required input: modelCustomizationUuid");
 
 503             String error = "Create vfModule error: Missing required input: modelCustomizationUuid";
 
 504             logger.error(LoggingAnchor.FIVE, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
 
 505                     "VF Module ModelCustomizationUuid", CLOUDIFY, ErrorCode.DataError.getValue(),
 
 506                     "Create VF Module: Missing required input: modelCustomizationUuid");
 
 508             throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 511         // Clean up some inputs to make comparisons easier
 
 512         if (requestType == null)
 
 515         if ("".equals(volumeGroupId) || "null".equals(volumeGroupId))
 
 516             volumeGroupId = null;
 
 518         if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId))
 
 519             baseVfModuleId = null;
 
 521         if (inputs == null) {
 
 522             // Create an empty set of inputs
 
 523             inputs = new HashMap<>();
 
 524             logger.debug("inputs == null - setting to empty");
 
 526             this.sendMapToDebug(inputs);
 
 529         // Check if this is for a "Volume" module
 
 530         boolean isVolumeRequest = false;
 
 531         if (requestType.startsWith("VOLUME")) {
 
 532             isVolumeRequest = true;
 
 535         logger.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = "
 
 538         // Build a default rollback object (no actions performed)
 
 539         VnfRollback vfRollback = new VnfRollback();
 
 540         vfRollback.setCloudSiteId(cloudSiteId);
 
 541         vfRollback.setCloudOwner(cloudOwner);
 
 542         vfRollback.setTenantId(tenantId);
 
 543         vfRollback.setMsoRequest(msoRequest);
 
 544         vfRollback.setRequestType(requestType);
 
 545         vfRollback.setIsBase(false); // Until we know better
 
 546         vfRollback.setVolumeGroupHeatStackId(volumeGroupId);
 
 547         vfRollback.setBaseGroupHeatStackId(baseVfModuleId);
 
 548         vfRollback.setModelCustomizationUuid(modelCustomizationUuid);
 
 549         vfRollback.setMode("CFY");
 
 551         rollback.value = vfRollback; // Default rollback - no updates performed
 
 553         // Get the VNF/VF Module definition from the Catalog DB first.
 
 554         // There are three relevant records: VfModule, VfModuleCustomization, VnfResource
 
 557         VnfResource vnfResource = null;
 
 558         VfModuleCustomization vfmc = null;
 
 561             vfmc = vfModuleCustomRepo.findFirstByModelCustomizationUUIDOrderByCreatedDesc(modelCustomizationUuid);
 
 564                 String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid="
 
 565                         + modelCustomizationUuid;
 
 567                 logger.error(LoggingAnchor.FIVE, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
 
 568                         "VF Module " + "ModelCustomizationUuid", modelCustomizationUuid, "CatalogDb",
 
 569                         ErrorCode.DataError.getValue(), error);
 
 570                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 572                 logger.debug("Found vfModuleCust entry " + vfmc.toString());
 
 575             // Get the vfModule and vnfResource records
 
 576             vf = vfmc.getVfModule();
 
 577             vnfResource = vfmc.getVfModule().getVnfResources();
 
 578         } catch (Exception e) {
 
 580             logger.error("unhandled exception in create VF - [Query]", e);
 
 581             throw new VnfException("Exception during create VF " + e.getMessage());
 
 584         // Perform a version check against cloudSite
 
 585         // Obtain the cloud site information where we will create the VF Module
 
 586         Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite(cloudSiteId);
 
 587         if (!cloudSiteOp.isPresent()) {
 
 588             throw new VnfException(new MsoCloudSiteNotFound(cloudSiteId));
 
 590         CloudSite cloudSite = cloudSiteOp.get();
 
 591         MavenLikeVersioning aicV = new MavenLikeVersioning();
 
 592         aicV.setVersion(cloudSite.getCloudVersion());
 
 594         String vnfMin = vnfResource.getAicVersionMin();
 
 595         String vnfMax = vnfResource.getAicVersionMax();
 
 597         if ((vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin)))
 
 598                 || (vnfMax != null && aicV.isMoreRecentThan(vnfMax))) {
 
 600             String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid="
 
 601                     + vnfResource.getModelUUID() + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax
 
 602                     + " NOT supported on Cloud: " + cloudSiteId + " with AIC_Version:" + cloudSite.getCloudVersion();
 
 603             logger.error(LoggingAnchor.FIVE, MessageEnum.RA_CONFIG_EXC.toString(), error, OPENSTACK,
 
 604                     ErrorCode.BusinessProcessError.getValue(), "Exception - setVersion");
 
 606             throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 611         DeploymentInfo cloudifyDeployment = null;
 
 613         // First, look up to see if the VF already exists.
 
 616             cloudifyDeployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vfModuleName);
 
 617         } catch (MsoException me) {
 
 618             // Failed to query the Deployment due to a cloudify exception.
 
 619             String error = "Create VF Module: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/"
 
 620                     + tenantId + ": " + me;
 
 621             logger.error(LoggingAnchor.EIGHT, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudSiteId,
 
 622                     tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 623                     "Exception - queryDeployment", me);
 
 626             // Convert to a generic VnfException
 
 627             me.addContext("CreateVFModule");
 
 628             throw new VnfException(me);
 
 631         // More precise handling/messaging if the Module already exists
 
 632         if (cloudifyDeployment != null && !(cloudifyDeployment.getStatus() == DeploymentStatus.NOTFOUND)) {
 
 633             // CREATED, INSTALLED, INSTALLING, FAILED, UNINSTALLING, UNKNOWN
 
 634             DeploymentStatus status = cloudifyDeployment.getStatus();
 
 635             logger.debug("Found Existing Deployment, status=" + status);
 
 637             if (status == DeploymentStatus.INSTALLED) {
 
 639                 if (failIfExists != null && failIfExists) {
 
 640                     String error = "Create VF: Deployment " + vfModuleName + " already exists in " + cloudOwner + "/"
 
 641                             + cloudSiteId + "/" + tenantId;
 
 642                     logger.error(BRACKETS, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, cloudOwner,
 
 643                             cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 644                             "Deployment " + vfModuleName + " already exists");
 
 646                     throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId,
 
 647                             cloudifyDeployment.getId());
 
 649                     // Found existing deployment and client has not requested "failIfExists".
 
 650                     // Populate the outputs from the existing deployment.
 
 652                     vnfId.value = cloudifyDeployment.getId();
 
 653                     outputs.value = copyStringOutputs(cloudifyDeployment.getOutputs());
 
 657             // Check through various detailed error cases
 
 658             if (status == DeploymentStatus.INSTALLING || status == DeploymentStatus.UNINSTALLING) {
 
 659                 // fail - it's in progress - return meaningful error
 
 660                 String error = "Create VF: Deployment " + vfModuleName + " already exists and has status "
 
 661                         + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
 
 662                         + "; please wait for it to complete, or fix manually.";
 
 663                 logger.error(BRACKETS, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, cloudOwner,
 
 664                         cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 665                         "Deployment " + vfModuleName + " already exists");
 
 667                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 668             } else if (status == DeploymentStatus.FAILED) {
 
 669                 // fail - it exists and is in a FAILED state
 
 670                 String error = "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in "
 
 671                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
 
 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 and is in FAILED state");
 
 676                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 677             } else if (status == DeploymentStatus.UNKNOWN || status == DeploymentStatus.CREATED) {
 
 678                 // fail - it exists and is in a UNKNOWN state
 
 679                 String error = "Create VF: Deployment " + vfModuleName + " already exists and has status "
 
 680                         + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
 
 681                         + "; requires manual intervention.";
 
 682                 logger.error(BRACKETS, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, cloudOwner,
 
 683                         cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 684                         "Deployment " + vfModuleName + " already " + "exists and is in " + status.toString()
 
 687                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 689                 // Unexpected, since all known status values have been tested for
 
 690                 String error = "Create VF: Deployment " + vfModuleName + " already exists with unexpected status "
 
 691                         + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
 
 692                         + "; requires manual intervention.";
 
 693                 logger.error(BRACKETS, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, cloudOwner,
 
 694                         cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 695                         "Deployment " + vfModuleName + " already " + "exists and is in an unknown state");
 
 697                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 702         // Collect outputs from Base Modules and Volume Modules
 
 703         Map<String, Object> baseModuleOutputs = null;
 
 704         Map<String, Object> volumeGroupOutputs = null;
 
 706         // If a Volume Group was provided, query its outputs for inclusion in Module input parameters
 
 707         if (volumeGroupId != null) {
 
 708             DeploymentInfo volumeDeployment = null;
 
 710                 volumeDeployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, volumeGroupId);
 
 711             } catch (MsoException me) {
 
 712                 // Failed to query the Volume GroupDeployment due to a cloudify exception.
 
 713                 String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudOwner + "/"
 
 714                         + cloudSiteId + "/" + tenantId + ": " + me;
 
 715                 logger.error(BRACKETS, MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId, cloudOwner, cloudSiteId,
 
 716                         tenantId, CLOUDIFY, "queryDeployment(volume)", ErrorCode.DataError.getValue(),
 
 717                         "Exception - queryDeployment(volume)", me);
 
 719                 // Convert to a generic VnfException
 
 720                 me.addContext("CreateVFModule(QueryVolume)");
 
 721                 throw new VnfException(me);
 
 724             if (volumeDeployment == null || volumeDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
 
 725                 String error = "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in "
 
 726                         + cloudSiteId + "/" + tenantId + " USER ERROR";
 
 727                 logger.error(BRACKETS, MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId, cloudSiteId, tenantId,
 
 728                         error, CLOUDIFY, "queryDeployment(volume)", ErrorCode.BusinessProcessError.getValue(),
 
 729                         "Create VFModule: Attached Volume Group DOES NOT EXIST");
 
 731                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 733                 logger.debug("Found nested volume group");
 
 734                 volumeGroupOutputs = volumeDeployment.getOutputs();
 
 735                 this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs");
 
 739         // If this is an Add-On Module, query the Base Module outputs
 
 740         // Note: This will be performed whether or not the current request is for an
 
 741         // Add-On Volume Group or Add-On VF Module
 
 743         if (vf.getIsBase()) {
 
 744             logger.debug("This is a BASE Module request");
 
 745             vfRollback.setIsBase(true);
 
 747             logger.debug("This is an Add-On Module request");
 
 749             // Add-On Modules should always have a Base, but just treat as a warning if not provided.
 
 750             // Add-on Volume requests may or may not specify a base.
 
 751             if (!isVolumeRequest && baseVfModuleId == null) {
 
 752                 logger.debug("WARNING:  Add-on Module request - no Base Module ID provided");
 
 755             if (baseVfModuleId != null) {
 
 756                 DeploymentInfo baseDeployment = null;
 
 758                     baseDeployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, baseVfModuleId);
 
 759                 } catch (MsoException me) {
 
 760                     // Failed to query the Volume GroupDeployment due to a cloudify exception.
 
 761                     String error = "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudOwner + "/"
 
 762                             + cloudSiteId + "/" + tenantId + ": " + me;
 
 763                     logger.error(BRACKETS, MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId, cloudOwner,
 
 764                             cloudSiteId, tenantId, CLOUDIFY, "queryDeployment(Base)", ErrorCode.DataError.getValue(),
 
 765                             "Exception - queryDeployment(Base)", me);
 
 767                     // Convert to a generic VnfException
 
 768                     me.addContext("CreateVFModule(QueryBase)");
 
 769                     throw new VnfException(me);
 
 772                 if (baseDeployment == null || baseDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
 
 773                     String error = "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in "
 
 774                             + cloudSiteId + "/" + tenantId + " USER ERROR";
 
 775                     logger.error(BRACKETS, MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId, cloudSiteId,
 
 776                             tenantId, error, CLOUDIFY, "queryDeployment(Base)",
 
 777                             ErrorCode.BusinessProcessError.getValue(),
 
 778                             "Create VFModule: Base " + "Module DOES NOT EXIST");
 
 780                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 782                     logger.debug("Found base module");
 
 783                     baseModuleOutputs = baseDeployment.getOutputs();
 
 784                     this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs");
 
 790         // Ready to deploy the new VNF
 
 792         // NOTE: For this section, heatTemplate is used for both HEAT templates and Cloudify blueprints.
 
 793         // In final implementation (post-POC), the template object would either be generic or there would
 
 794         // be a separate DB Table/Object for Blueprints.
 
 797         // NOTE: The template is fixed for the VF Module. The environment is part of the customization.
 
 798         HeatTemplate heatTemplate = null;
 
 799         HeatEnvironment heatEnvironment = null;
 
 800         if (isVolumeRequest) {
 
 801             heatTemplate = vf.getVolumeHeatTemplate();
 
 802             heatEnvironment = vfmc.getVolumeHeatEnv();
 
 804             heatTemplate = vf.getModuleHeatTemplate();
 
 805             heatEnvironment = vfmc.getHeatEnvironment();
 
 808         if (heatTemplate == null) {
 
 809             String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType
 
 810                     + ", modelCustomizationUuid=" + modelCustomizationUuid + ", vfModuleUuid=" + vf.getModelUUID()
 
 811                     + ", reqType=" + requestType;
 
 812             logger.error(LoggingAnchor.SIX, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID",
 
 813                     vfModuleType, OPENSTACK, ErrorCode.DataError.getValue(), error);
 
 814             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
 
 816             logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
 
 819         if (heatEnvironment == null) {
 
 820             String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType + ", modelCustomizationUuid="
 
 821                     + modelCustomizationUuid + ", vfModuleUuid=" + vf.getModelUUID() + ", reqType=" + requestType;
 
 822             logger.error(LoggingAnchor.FIVE, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
 
 823                     OPENSTACK, ErrorCode.DataError.getValue(), error);
 
 824             // Alarm on this error, configuration must be fixed
 
 825             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
 
 827             logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
 
 832             // All variables converted to their native object types
 
 833             HashMap<String, Object> goldenInputs = new HashMap<>();
 
 834             List<String> extraInputs = new ArrayList<>();
 
 836             // NOTE: SKIP THIS FOR CLOUDIFY for now. Just use what was passed in.
 
 837             // This whole section needs to be rewritten.
 
 838             Boolean skipInputChecks = false;
 
 840             if (skipInputChecks) {
 
 841                 goldenInputs = new HashMap<>();
 
 842                 for (Map.Entry<String, Object> entry : inputs.entrySet()) {
 
 843                     goldenInputs.put(entry.getKey(), entry.getValue());
 
 846                 // Build maps for the parameters (including aliases) to simplify checks
 
 847                 HashMap<String, HeatTemplateParam> params = new HashMap<>();
 
 849                 Set<HeatTemplateParam> paramSet = heatTemplate.getParameters();
 
 850                 logger.debug("paramSet has {} entries", paramSet.size());
 
 852                 for (HeatTemplateParam htp : paramSet) {
 
 853                     params.put(htp.getParamName(), htp);
 
 856                     String alias = htp.getParamAlias();
 
 857                     if (alias != null && !"".equals(alias) && !params.containsKey(alias)) {
 
 858                         params.put(alias, htp);
 
 862                 // First, convert all inputs to their "template" type
 
 863                 for (String key : inputs.keySet()) {
 
 864                     if (params.containsKey(key)) {
 
 865                         Object value = cloudifyUtils.convertInputValue(inputs.get(key), params.get(key));
 
 867                             goldenInputs.put(key, value);
 
 869                             logger.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to "
 
 870                                     + params.get(key).getParamType());
 
 873                         extraInputs.add(key);
 
 877                 if (!extraInputs.isEmpty()) {
 
 878                     logger.debug("Ignoring extra inputs: " + extraInputs);
 
 881                 // Next add in Volume Group Outputs if there are any. Copy directly without conversions.
 
 882                 if (volumeGroupOutputs != null && !volumeGroupOutputs.isEmpty()) {
 
 883                     for (Map.Entry<String, Object> entry : volumeGroupOutputs.entrySet()) {
 
 884                         if (params.containsKey(entry.getKey()) && !goldenInputs.containsKey(entry.getKey())) {
 
 885                             goldenInputs.put(entry.getKey(), entry.getValue());
 
 890                 // Next add in Base Module Outputs if there are any. Copy directly without conversions.
 
 891                 if (baseModuleOutputs != null && !baseModuleOutputs.isEmpty()) {
 
 892                     for (Map.Entry<String, Object> entry : baseModuleOutputs.entrySet()) {
 
 893                         if (params.containsKey(entry.getKey()) && !goldenInputs.containsKey(entry.getKey())) {
 
 894                             goldenInputs.put(entry.getKey(), entry.getValue());
 
 899                 // Last, add in values from the "environment" file.
 
 900                 // These are added to the inputs, since Cloudify doesn't pass an environment file like Heat.
 
 902                 // TODO: This may take a different form for Cloudify, but for now process it
 
 903                 // with Heat environment file syntax
 
 904                 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
 
 905                 MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry(sb);
 
 907                 if (mhee.getParameters() != null) {
 
 908                     for (MsoHeatEnvironmentParameter envParam : mhee.getParameters()) {
 
 909                         // If this is a template input, copy to golden inputs
 
 910                         String envKey = envParam.getName();
 
 911                         if (params.containsKey(envKey) && !goldenInputs.containsKey(envKey)) {
 
 912                             Object value = cloudifyUtils.convertInputValue(envParam.getValue(), params.get(envKey));
 
 914                                 goldenInputs.put(envKey, value);
 
 916                                 logger.debug("Failed to convert environment parameter " + envKey + "='"
 
 917                                         + envParam.getValue() + "' to " + params.get(envKey).getParamType());
 
 923                 this.sendMapToDebug(goldenInputs, "Final inputs sent to Cloudify");
 
 926                 // Check that required parameters have been supplied from any of the sources
 
 927                 String missingParams = null;
 
 928                 boolean checkRequiredParameters = true;
 
 930                     String propertyString = this.environment.getProperty(MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
 
 931                     if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
 
 932                         checkRequiredParameters = false;
 
 933                         logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking... {}",
 
 934                                 MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
 
 936                 } catch (Exception e) {
 
 937                     // No problem - default is true
 
 938                     logger.error("An exception occured trying to get property {}",
 
 939                             MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS, e);
 
 943                 for (HeatTemplateParam parm : heatTemplate.getParameters()) {
 
 944                     if (parm.isRequired() && (!goldenInputs.containsKey(parm.getParamName()))) {
 
 945                         logger.debug("adding to missing parameters list: {}", parm.getParamName());
 
 946                         if (missingParams == null) {
 
 947                             missingParams = parm.getParamName();
 
 949                             missingParams += "," + parm.getParamName();
 
 954                 if (missingParams != null) {
 
 955                     if (checkRequiredParameters) {
 
 956                         // Problem - missing one or more required parameters
 
 957                         String error = "Create VFModule: Missing Required inputs: " + missingParams;
 
 958                         logger.error(LoggingAnchor.FIVE, MessageEnum.RA_MISSING_PARAM.toString(), missingParams,
 
 959                                 CLOUDIFY, ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs");
 
 961                         throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 963                         logger.debug("found missing parameters [" + missingParams
 
 964                                 + "] - but checkRequiredParameters is false -" + " will not block");
 
 967                     logger.debug("No missing parameters found - ok to proceed");
 
 970             } // NOTE: END PARAMETER CHECKING
 
 972             // Ready to deploy the VF Module.
 
 973             // *First step - make sure the blueprint is loaded into Cloudify.
 
 974             String blueprintName = heatTemplate.getTemplateName();
 
 975             String blueprint = heatTemplate.getTemplateBody();
 
 976             String blueprintId = blueprintName;
 
 978             // Use the main blueprint name as the blueprint ID (strip yaml extensions).
 
 979             if (blueprintId.endsWith(".yaml"))
 
 980                 blueprintId = blueprintId.substring(0, blueprintId.lastIndexOf(".yaml"));
 
 983                 if (!cloudifyUtils.isBlueprintLoaded(cloudSiteId, blueprintId)) {
 
 984                     logger.debug("Blueprint " + blueprintId + " is not loaded.  Will upload it now.");
 
 986                     Map<String, byte[]> blueprintFiles = new HashMap<>();
 
 988                     blueprintFiles.put(blueprintName, blueprint.getBytes());
 
 990                     // TODO: Implement nested blueprint logic based on Cloudify structures.
 
 991                     // For now, just use the Heat structures.
 
 992                     // The query returns a map of String->Object, where the map keys provide one layer of
 
 993                     // indirection from the Heat template names. For this case, assume the map key matches
 
 994                     // the nested blueprint name.
 
 995                     List<HeatTemplate> nestedBlueprints = heatTemplate.getChildTemplates();
 
 996                     if (nestedBlueprints != null) {
 
 997                         for (HeatTemplate nestedBlueprint : nestedBlueprints) {
 
 998                             blueprintFiles.put(nestedBlueprint.getTemplateName(),
 
 999                                     nestedBlueprint.getTemplateBody().getBytes());
 
1003                     // TODO: Implement file artifact logic based on Cloudify structures.
 
1004                     // For now, just use the Heat structures.
 
1005                     List<HeatFiles> heatFiles = vf.getHeatFiles();
 
1006                     if (heatFiles != null) {
 
1007                         for (HeatFiles heatFile : heatFiles) {
 
1008                             blueprintFiles.put(heatFile.getFileName(), heatFile.getFileBody().getBytes());
 
1012                     // Upload the blueprint package
 
1013                     cloudifyUtils.uploadBlueprint(cloudSiteId, blueprintId, blueprintName, blueprintFiles, false);
 
1018             catch (MsoException me) {
 
1019                 me.addContext("CreateVFModule");
 
1020                 String error = "Create VF Module: Upload blueprint failed.  Blueprint=" + blueprintName + ": " + me;
 
1021                 logger.error(LoggingAnchor.SEVEN, MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudSiteId,
 
1022                         tenantId, CLOUDIFY, ErrorCode.DataError.getValue(), "MsoException - uploadBlueprint", me);
 
1023                 logger.debug(error);
 
1024                 throw new VnfException(me);
 
1027             // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
 
1028             // because we already checked for those.
 
1030                 // KLUDGE - Cloudify requires Tenant Name for Openstack. We have the ID.
 
1031                 // Go directly to Keystone until APIs could be updated to supply the name.
 
1032                 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
 
1033                 String tenantName = (msoTenant != null ? msoTenant.getTenantName() : tenantId);
 
1035                 if (backout == null) {
 
1039                 cloudifyDeployment = cloudifyUtils.createAndInstallDeployment(cloudSiteId, tenantName, vfModuleName,
 
1040                         blueprintId, goldenInputs, true, heatTemplate.getTimeoutMinutes(), backout.booleanValue());
 
1042             } catch (MsoException me) {
 
1043                 me.addContext("CreateVFModule");
 
1044                 String error = "Create VF Module " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/"
 
1045                         + tenantId + ": " + me;
 
1046                 logger.error(LoggingAnchor.EIGHT, MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner,
 
1047                         cloudSiteId, tenantId, CLOUDIFY, ErrorCode.DataError.getValue(),
 
1048                         "MsoException - createDeployment", me);
 
1049                 logger.debug(error);
 
1050                 throw new VnfException(me);
 
1051             } catch (NullPointerException npe) {
 
1052                 String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/"
 
1053                         + tenantId + ": " + npe;
 
1054                 logger.error(LoggingAnchor.EIGHT, MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner,
 
1055                         cloudSiteId, tenantId, CLOUDIFY, ErrorCode.DataError.getValue(),
 
1056                         "NullPointerException - createDeployment", npe);
 
1057                 logger.debug(error);
 
1058                 logger.debug("NULL POINTER EXCEPTION at cloudify.createAndInstallDeployment");
 
1059                 // npe.addContext ("CreateVNF");
 
1060                 throw new VnfException("NullPointerException during cloudify.createAndInstallDeployment");
 
1061             } catch (Exception e) {
 
1062                 logger.error("unhandled exception at cloudify.createAndInstallDeployment", e);
 
1063                 throw new VnfException("Exception during cloudify.createAndInstallDeployment! " + e.getMessage());
 
1065         } catch (Exception e) {
 
1066             logger.error("unhandled exception in create VF", e);
 
1067             throw new VnfException("Exception during create VF " + e.getMessage());
 
1071         // Reach this point if create is successful.
 
1072         // Populate remaining rollback info and response parameters.
 
1073         vfRollback.setVnfCreated(true);
 
1074         vfRollback.setVnfId(cloudifyDeployment.getId());
 
1075         vnfId.value = cloudifyDeployment.getId();
 
1076         outputs.value = copyStringOutputs(cloudifyDeployment.getOutputs());
 
1078         rollback.value = vfRollback;
 
1080         logger.debug("VF Module successfully created {}", vfModuleName);
 
1084     public void deleteVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, String vnfId,
 
1085             String vfModuleId, MsoRequest msoRequest, Holder<Map<String, String>> outputs) throws VnfException {
 
1086         logger.debug("Deleting VF " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId);
 
1088         // 1702 capture the output parameters on a delete
 
1089         // so we'll need to query first
 
1090         DeploymentInfo deployment = null;
 
1092             deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
 
1093         } catch (MsoException me) {
 
1094             // Failed to query the deployment. Convert to a generic VnfException
 
1095             me.addContext("DeleteVFModule");
 
1096             String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId
 
1097                     + "/" + tenantId + ": " + me;
 
1098             logger.error(BRACKETS, MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId, tenantId,
 
1099                     CLOUDIFY, "QueryDeployment", ErrorCode.DataError.getValue(), "Exception - QueryDeployment", me);
 
1100             logger.debug(error);
 
1101             throw new VnfException(me);
 
1103         // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected
 
1105         outputs.value = convertMapStringObjectToStringString(deployment.getOutputs());
 
1107         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
 
1108         // The possible outcomes of deleteStack are a StackInfo object with status
 
1109         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
 
1112             cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantId, vnfName, 5);
 
1113         } catch (MsoException me) {
 
1114             me.addContext("DeleteVfModule");
 
1115             // Convert to a generic VnfException
 
1117                     "Delete VF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
 
1118             logger.error(BRACKETS, MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId, tenantId,
 
1119                     "DeleteDeployment", "DeleteDeployment", ErrorCode.DataError.getValue(),
 
1120                     "Exception - DeleteDeployment: " + me.getMessage());
 
1121             logger.debug(error);
 
1122             throw new VnfException(me);
 
1125         // On success, nothing is returned.
 
1129     // TODO: Should Update be supported for Cloudify? What would this look like?
 
1131     public void updateVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfType,
 
1132             String vnfVersion, String vnfName, String requestType, String volumeGroupHeatStackId,
 
1133             String baseVfHeatStackId, String vfModuleStackId, String modelCustomizationUuid, Map<String, Object> inputs,
 
1134             MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback)
 
1135             throws VnfException {
 
1136         // This operation is not currently supported for Cloudify-orchestrated VF Modules.
 
1137         logger.debug("Update VF Module command attempted but not supported");
 
1138         throw new VnfException("UpdateVfModule:  Unsupported command", MsoExceptionCategory.USERDATA);