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;
 
  34 import javax.jws.WebService;
 
  35 import javax.xml.ws.Holder;
 
  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;
 
  58 import org.onap.so.openstack.beans.MsoTenant;
 
  59 import org.onap.so.openstack.beans.VnfRollback;
 
  60 import org.onap.so.openstack.beans.VnfStatus;
 
  61 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
 
  62 import org.onap.so.openstack.exceptions.MsoException;
 
  63 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
 
  64 import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry;
 
  65 import org.onap.so.openstack.utils.MsoHeatEnvironmentParameter;
 
  66 import org.onap.so.openstack.utils.MsoKeystoneUtils;
 
  67 import org.slf4j.Logger;
 
  68 import org.slf4j.LoggerFactory;
 
  69 import org.springframework.beans.factory.annotation.Autowired;
 
  70 import org.springframework.core.env.Environment;
 
  71 import org.springframework.stereotype.Component;
 
  73 import com.fasterxml.jackson.core.JsonParseException;
 
  74 import com.fasterxml.jackson.databind.JsonNode;
 
  75 import com.fasterxml.jackson.databind.ObjectMapper;
 
  76 import org.springframework.transaction.annotation.Transactional;
 
  80 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter", targetNamespace = "http://org.onap.so/vnf")
 
  81 public class MsoVnfCloudifyAdapterImpl implements MsoVnfAdapter {
 
  83     private static Logger logger = LoggerFactory.getLogger(MsoVnfCloudifyAdapterImpl.class);
 
  85     private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
 
  86     private static final String VNF_ADAPTER_SERVICE_NAME = "MSO-BPMN:MSO-VnfAdapter.";
 
  87     private static final String LOG_REPLY_NAME = "MSO-VnfAdapter:MSO-BPMN.";
 
  88     private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
 
  89     private static final String ADD_GET_FILES_ON_VOLUME_REQ = "org.onap.so.adapters.vnf.addGetFilesOnVolumeReq";
 
  90     private static final String CLOUDIFY_RESPONSE_SUCCESS="Successfully received response from Cloudify";
 
  91     private static final String CLOUDIFY="Cloudify";
 
  93     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
 
  96     protected CloudConfig cloudConfig;
 
  99     private VFModuleCustomizationRepository vfModuleCustomRepo;
 
 102     private Environment environment;
 
 105     protected MsoKeystoneUtils keystoneUtils;
 
 108     protected MsoCloudifyUtils cloudifyUtils;
 
 110      * Health Check web method. Does nothing but return to show the adapter is deployed.
 
 113     public void healthCheck () {
 
 114         logger.debug("Health check call in VNF Cloudify Adapter");
 
 118      * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
 
 119      * @see MsoVnfCloudifyAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
 
 121     public MsoVnfCloudifyAdapterImpl() {
 
 126      * This is the "Create VNF" web service implementation.
 
 127      * This function is now unsupported and will return an error.
 
 131     public void createVnf (String cloudSiteId,
 
 138                            String volumeGroupHeatStackId,
 
 139                            Map <String, Object> inputs,
 
 140                            Boolean failIfExists,
 
 142                            Boolean enableBridge,
 
 143                            MsoRequest msoRequest,
 
 144                            Holder <String> vnfId,
 
 145                            Holder <Map <String, String>> outputs,
 
 146                            Holder <VnfRollback> rollback)
 
 149         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
 
 150         logger.debug("CreateVNF command attempted but not supported");
 
 151         throw new VnfException("CreateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
 
 155      * This is the "Update VNF" web service implementation.
 
 156      * This function is now unsupported and will return an error.
 
 160     public void updateVnf (String cloudSiteId,
 
 167                            String volumeGroupHeatStackId,
 
 168                            Map <String, Object> inputs,
 
 169                            MsoRequest msoRequest,
 
 170                            Holder <Map <String, String>> outputs,
 
 171                            Holder <VnfRollback> rollback)
 
 174         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
 
 175       logger.debug("UpdateVNF command attempted but not supported");
 
 176       throw new VnfException ("UpdateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
 
 180      * This is the "Query VNF" web service implementation.
 
 182      * This really should be QueryVfModule, but nobody ever changed it.
 
 184      * For Cloudify, this will look up a deployment by its deployment ID, which is really the same
 
 185      * as deployment name, since it assigned by the client when a deployment is created.
 
 186      * Also, the input cloudSiteId is used only to identify which Cloudify instance to query,
 
 187      * and the tenantId is ignored (since that really only applies for Openstack/Heat).
 
 189      * The method returns an indicator that the VNF exists, along with its status and outputs.
 
 190      * The input "vnfName" will also be reflected back as its ID.
 
 192      * @param cloudSiteId CLLI code of the cloud site in which to query
 
 193      * @param cloudOwner cloud owner of the cloud site in which to query
 
 194      * @param tenantId Openstack tenant identifier - ignored for Cloudify
 
 195      * @param vnfName VNF Name (should match a deployment ID)
 
 196      * @param msoRequest Request tracking information for logs
 
 197      * @param vnfExists Flag reporting the result of the query
 
 198      * @param vnfId Holder for output VNF ID
 
 199      * @param outputs Holder for Map of VNF outputs from Cloudify deployment (assigned IPs, etc)
 
 202     public void queryVnf (String cloudSiteId,
 
 206                           MsoRequest msoRequest,
 
 207                           Holder <Boolean> vnfExists,
 
 208                           Holder <String> vnfId,
 
 209                           Holder <VnfStatus> status,
 
 210                           Holder <Map <String, String>> outputs)
 
 213         logger.debug ("Querying VNF {} in {}", vnfName, cloudSiteId + "/" + tenantId);
 
 215         // Will capture execution time for metrics
 
 216         long startTime = System.currentTimeMillis ();
 
 217         long subStartTime = System.currentTimeMillis ();
 
 219         DeploymentInfo deployment = null;
 
 222                 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
 
 224         catch (MsoCloudifyManagerNotFound e) {
 
 225                 // This site does not have a Cloudify Manager.
 
 226                 // This isn't an error, just means we won't find the VNF here.
 
 229         catch (MsoException me) {
 
 230           // Failed to query the Deployment due to a cloudify exception.
 
 231           // Convert to a generic VnfException
 
 232           me.addContext("QueryVNF");
 
 233           String error = "Query VNF (Cloudify): " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
 
 235               .error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId, tenantId,
 
 236                   CLOUDIFY, "QueryVNF", ErrorCode.DataError.getValue(), "Exception - queryDeployment", me);
 
 238           throw new VnfException(me);
 
 241         if (deployment != null && deployment.getStatus() != DeploymentStatus.NOTFOUND) {
 
 242             vnfExists.value = Boolean.TRUE;
 
 243             status.value = deploymentStatusToVnfStatus(deployment);
 
 244             vnfId.value = deployment.getId();
 
 245             outputs.value = copyStringOutputs(deployment.getOutputs());
 
 247             logger.debug("VNF {} found in Cloudify, ID = {}", vnfName, vnfId.value);
 
 249             vnfExists.value = Boolean.FALSE;
 
 250             status.value = VnfStatus.NOTFOUND;
 
 252             outputs.value = new HashMap<String, String>(); // Return as an empty map
 
 254             logger.debug("VNF {} not found", vnfName);
 
 261      * This is the "Delete VNF" web service implementation.
 
 262      * This function is now unsupported and will return an error.
 
 266     public void deleteVnf (String cloudSiteId,
 
 270                            MsoRequest msoRequest) throws VnfException {
 
 272         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
 
 273         logger.debug("DeleteVNF command attempted but not supported");
 
 274         throw new VnfException ("DeleteVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
 
 278      * This web service endpoint will rollback a previous Create VNF operation.
 
 279      * A rollback object is returned to the client in a successful creation
 
 280      * response. The client can pass that object as-is back to the rollbackVnf
 
 281      * operation to undo the creation.
 
 283      * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup,
 
 284      * but APIs were apparently never updated.
 
 287     public void rollbackVnf (VnfRollback rollback) throws VnfException {
 
 288         long startTime = System.currentTimeMillis ();
 
 289         // rollback may be null (e.g. if stack already existed when Create was called)
 
 290         if (rollback == null) {
 
 291             logger.info ("{} {} {}", MessageEnum.RA_ROLLBACK_NULL.toString(), "OpenStack", "rollbackVnf");
 
 295         // Don't rollback if nothing was done originally
 
 296         if (!rollback.getVnfCreated()) {
 
 300         // Get the elements of the VnfRollback object for easier access
 
 301         String cloudSiteId = rollback.getCloudSiteId ();
 
 302         String cloudOwner = rollback.getCloudOwner();
 
 303         String tenantId = rollback.getTenantId ();
 
 304         String vfModuleId = rollback.getVfModuleStackId ();
 
 306         logger.debug("Rolling Back VF Module {} in {}", vfModuleId, cloudOwner + "/" + cloudSiteId + "/" + tenantId);
 
 308         DeploymentInfo deployment = null;
 
 310         // Use the MsoCloudifyUtils to delete the deployment. Set the polling flag to true.
 
 311         // The possible outcomes of deleteStack are a StackInfo object with status
 
 312         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
 
 314         long subStartTime = System.currentTimeMillis ();
 
 316                 // KLUDGE - Cloudify requires Tenant Name for Openstack.  We have the ID.
 
 317                 //          Go directly to Keystone until APIs could be updated to supply the name.
 
 318                 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
 
 319                 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
 
 321                 // TODO: Get a reasonable timeout.  Use a global property, or store the creation timeout in rollback object and use that.
 
 322             deployment = cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantName, vfModuleId, 5);
 
 323             logger.debug("Rolled back deployment: {}", deployment.getId());
 
 324         } catch (MsoException me) {
 
 325             // Failed to rollback the VNF due to a cloudify exception.
 
 326             // Convert to a generic VnfException
 
 327             me.addContext ("RollbackVNF");
 
 328             String error = "Rollback VF Module: " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
 
 329             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vfModuleId, cloudOwner, cloudSiteId,
 
 330                 tenantId, CLOUDIFY, "DeleteDeployment", ErrorCode.DataError.getValue(),
 
 331                 "Exception - DeleteDeployment", me);
 
 333             throw new VnfException (me);
 
 339     private VnfStatus deploymentStatusToVnfStatus (DeploymentInfo deployment) {
 
 340         // Determine the status based on last action & status
 
 341         // DeploymentInfo object should be enhanced to report a better status internally.
 
 342         DeploymentStatus status = deployment.getStatus();
 
 343         String lastAction = deployment.getLastAction();
 
 345         if (status == null  ||  lastAction == null) {
 
 346                 return VnfStatus.UNKNOWN;
 
 348         else if (status == DeploymentStatus.NOTFOUND) {
 
 349                         return VnfStatus.NOTFOUND;
 
 351         else if (status == DeploymentStatus.INSTALLED) {
 
 352                         return VnfStatus.ACTIVE;
 
 354         else if (status == DeploymentStatus.CREATED) {
 
 355                 // Should have an INACTIVE status for this case.  Shouldn't really happen, but
 
 356                 // Install was never run, or Uninstall was done but deployment didn't get deleted.
 
 357                 return VnfStatus.UNKNOWN;
 
 359         else if (status == DeploymentStatus.FAILED) {
 
 360                 return VnfStatus.FAILED;
 
 363         return VnfStatus.UNKNOWN;
 
 366     private Map <String, String> copyStringOutputs (Map <String, Object> stackOutputs) {
 
 367         Map <String, String> stringOutputs = new HashMap <String, String> ();
 
 368         for (String key : stackOutputs.keySet ()) {
 
 369             if (stackOutputs.get (key) instanceof String) {
 
 370                 stringOutputs.put (key, (String) stackOutputs.get (key));
 
 371             } else if (stackOutputs.get(key) instanceof Integer)  {
 
 373                         String str = "" + stackOutputs.get(key);
 
 374                         stringOutputs.put(key, str);
 
 375                 } catch (Exception e) {
 
 376                         logger.debug("Unable to add " + key + " to outputs");
 
 378             } else if (stackOutputs.get(key) instanceof JsonNode) {
 
 380                         String str = this.convertNode((JsonNode) stackOutputs.get(key));
 
 381                         stringOutputs.put(key, str);
 
 382                 } catch (Exception e) {
 
 383                   logger.debug("Unable to add " + key + " to outputs - exception converting JsonNode");
 
 385             } else if (stackOutputs.get(key) instanceof java.util.LinkedHashMap) {
 
 387                                         String str = JSON_MAPPER.writeValueAsString(stackOutputs.get(key));
 
 388                         stringOutputs.put(key, str);
 
 389                 } catch (Exception e) {
 
 390                         logger.debug("Unable to add " + key + " to outputs - exception converting LinkedHashMap");
 
 394                         String str = stackOutputs.get(key).toString();
 
 395                         stringOutputs.put(key, str);
 
 396                 } catch (Exception e) {
 
 397                         logger.debug("Unable to add " + key + " to outputs - unable to call .toString() " + e.getMessage());
 
 401         return stringOutputs;
 
 405     private void sendMapToDebug(Map<String, Object> inputs, String optionalName) {
 
 407         StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName);
 
 408         if (inputs == null) {
 
 411         else if (inputs.size() < 1) {
 
 412                 sb.append("\tEMPTY");
 
 414                 for (String str : inputs.keySet()) {
 
 417                                 outputString = inputs.get(str).toString();
 
 418                         } catch (Exception e) {
 
 419                                 outputString = "Unable to call toString() on the value for " + str;
 
 421                         sb.append("\t\nitem " + i++ + ": '" + str + "'='" + outputString + "'");
 
 424         logger.debug(sb.toString());
 
 428     private void sendMapToDebug(Map<String, Object> inputs) {
 
 430         StringBuilder sb = new StringBuilder("inputs:");
 
 431         if (inputs == null) {
 
 434         else if (inputs.size() < 1) {
 
 435                 sb.append("\tEMPTY");
 
 437                 for (String str : inputs.keySet()) {
 
 438                         sb.append("\titem " + i++ + ": " + str + "=" + inputs.get(str));
 
 441         logger.debug(sb.toString());
 
 445     private String convertNode(final JsonNode node) {
 
 447             final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
 
 448             final String json = JSON_MAPPER.writeValueAsString(obj);
 
 450         } catch (JsonParseException jpe) {
 
 451             logger.debug("Error converting json to string " + jpe.getMessage());
 
 452         } catch (Exception e) {
 
 453             logger.debug("Error converting json to string " + e.getMessage());
 
 455         return "[Error converting json to string]";
 
 458     private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
 
 459         if (objectMap == null) {
 
 462         Map<String, String> stringMap = new HashMap<String, String>();
 
 463         for (String key : objectMap.keySet()) {
 
 464             if (!stringMap.containsKey(key)) {
 
 465                 Object obj = objectMap.get(key);
 
 466                 if (obj instanceof String) {
 
 467                     stringMap.put(key, (String) objectMap.get(key));
 
 468                 } else if (obj instanceof JsonNode ){
 
 469                     // This is a bit of mess - but I think it's the least impacting
 
 470                     // let's convert it BACK to a string - then it will get converted back later
 
 472                         String str = this.convertNode((JsonNode) obj);
 
 473                         stringMap.put(key, str);
 
 474                     } catch (Exception e) {
 
 475                                                 logger.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode "+ key);
 
 476                         //okay in this instance - only string values (fqdn) are expected to be needed
 
 478                 } else if (obj instanceof java.util.LinkedHashMap) {
 
 479                     logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
 
 481                         String str = JSON_MAPPER.writeValueAsString(obj);
 
 482                         stringMap.put(key, str);
 
 483                     } catch (Exception e) {
 
 484                         logger.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap "+ key);
 
 486                                 }  else if (obj instanceof Integer) {
 
 488                                                 String str = "" + obj;
 
 489                                                 stringMap.put(key, str);
 
 490                                         } catch (Exception e) {
 
 491               logger.debug("DANGER WILL ROBINSON: unable to convert value for Integer "+ key);
 
 495                                                 String str = obj.toString();
 
 496                         stringMap.put(key, str);
 
 497                     } catch (Exception e) {
 
 498                         logger.debug("DANGER WILL ROBINSON: unable to convert value "+ key + " (" + e.getMessage() + ")");
 
 508      * This is the "Create VF Module" web service implementation.
 
 509      * It will instantiate a new VF Module of the requested type in the specified cloud
 
 510      * and tenant. The tenant must exist before this service is called.
 
 512      * If a VF Module with the same name already exists, this can be considered a
 
 513      * success or failure, depending on the value of the 'failIfExists' parameter.
 
 515      * All VF Modules are defined in the MSO catalog. The caller must request
 
 516      * one of the pre-defined module types or an error will be returned. Within the
 
 517      * catalog, each VF Module references (among other things) a cloud template
 
 518      * which is used to deploy the required  artifacts (VMs, networks, etc.)
 
 519      * to the cloud.  In this adapter implementation, that artifact is expected
 
 520      * to be a Cloudify blueprint.
 
 522      * Depending on the blueprint, a variable set of input parameters will
 
 523      * be defined, some of which are required. The caller is responsible to
 
 524      * pass the necessary input data for the module or an error will be thrown.
 
 526      * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback
 
 527      * object. This last object can be passed as-is to the rollbackVnf operation to
 
 528      * undo everything that was created for the Module. This is useful if a VF module
 
 529      * is successfully created but the orchestration fails on a subsequent step.
 
 531      * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
 
 532      * @param cloudOwner cloud owner of the cloud site in which to create the VNF
 
 533      * @param tenantId Openstack tenant identifier
 
 534      * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB.
 
 535      *        Deprecated - should use modelCustomizationUuid
 
 536      * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
 
 537      *        Deprecated - VF Module versions also captured by modelCustomizationUuid
 
 538      * @param genericVnfId Generic VNF ID
 
 539      * @param vfModuleName Name to be assigned to the new VF Module
 
 540      * @param vfModuleId Id of the new VF Module
 
 541      * @param requestType Indicates if this is a Volume Group or Module request
 
 542      * @param volumeGroupId Identifier (i.e. deployment ID) for a Volume Group
 
 543      *        to attach to a VF Module
 
 544      * @param baseVfModuleId Identifier (i.e. deployment ID) of the Base Module if
 
 545      *        this is an Add-on module
 
 546      * @param modelCustomizationUuid Unique ID for the VF Module's model.  Replaces
 
 547      *        the use of vfModuleType.
 
 548      * @param inputs Map of key=value inputs for VNF stack creation
 
 549      * @param failIfExists Flag whether already existing VNF should be considered
 
 550      * @param backout Flag whether to suppress automatic backout (for testing)
 
 551      * @param msoRequest Request tracking information for logs
 
 552      * @param vnfId Holder for output VNF Cloudify Deployment ID
 
 553      * @param outputs Holder for Map of VNF outputs from Deployment (assigned IPs, etc)
 
 554      * @param rollback Holder for returning VnfRollback object
 
 557     public void createVfModule(String cloudSiteId,
 
 566             String volumeGroupId,
 
 567             String baseVfModuleId,
 
 568             String modelCustomizationUuid,
 
 569             Map <String, Object> inputs,
 
 570             Boolean failIfExists,
 
 572             Boolean enableBridge,
 
 573             MsoRequest msoRequest,
 
 574             Holder <String> vnfId,
 
 575             Holder <Map <String, String>> outputs,
 
 576             Holder <VnfRollback> rollback)
 
 579         // Will capture execution time for metrics
 
 580         long startTime = System.currentTimeMillis ();
 
 582         // Require a model customization ID.  Every VF Module definition must have one.
 
 583         if (modelCustomizationUuid == null || modelCustomizationUuid.isEmpty()) {
 
 584             logger.debug("Missing required input: modelCustomizationUuid");
 
 585             String error = "Create vfModule error: Missing required input: modelCustomizationUuid";
 
 586             logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
 
 587                 "VF Module ModelCustomizationUuid", CLOUDIFY, ErrorCode.DataError.getValue(),
 
 588                 "Create VF Module: Missing required input: modelCustomizationUuid");
 
 590             throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 593         // Clean up some inputs to make comparisons easier
 
 594         if (requestType == null)
 
 597         if ("".equals(volumeGroupId) || "null".equals(volumeGroupId))
 
 598                 volumeGroupId = null;
 
 600         if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId))
 
 601                 baseVfModuleId = null;
 
 603         if (inputs == null) {
 
 604                 // Create an empty set of inputs
 
 605                 inputs = new HashMap<>();
 
 606                 logger.debug("inputs == null - setting to empty");
 
 608                 this.sendMapToDebug(inputs);
 
 611         // Check if this is for a "Volume" module
 
 612         boolean isVolumeRequest = false;
 
 613         if (requestType.startsWith("VOLUME")) {
 
 614                 isVolumeRequest = true;
 
 617         logger.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = " +
 
 620         // Build a default rollback object (no actions performed)
 
 621         VnfRollback vfRollback = new VnfRollback();
 
 622         vfRollback.setCloudSiteId(cloudSiteId);
 
 623         vfRollback.setCloudOwner(cloudOwner);
 
 624         vfRollback.setTenantId(tenantId);
 
 625         vfRollback.setMsoRequest(msoRequest);
 
 626         vfRollback.setRequestType(requestType);
 
 627         vfRollback.setIsBase(false);    // Until we know better
 
 628         vfRollback.setVolumeGroupHeatStackId(volumeGroupId);
 
 629         vfRollback.setBaseGroupHeatStackId(baseVfModuleId);
 
 630         vfRollback.setModelCustomizationUuid(modelCustomizationUuid);
 
 631         vfRollback.setMode("CFY");
 
 633                 rollback.value = vfRollback; // Default rollback - no updates performed
 
 635         // Get the VNF/VF Module definition from the Catalog DB first.
 
 636         // There are three relevant records:  VfModule, VfModuleCustomization, VnfResource
 
 639         VnfResource vnfResource = null;
 
 640         VfModuleCustomization vfmc = null;
 
 643             vfmc = vfModuleCustomRepo.findByModelCustomizationUUID(modelCustomizationUuid);
 
 646                 String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid="
 
 647                     + modelCustomizationUuid;
 
 649                 logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VF Module "
 
 650                         + "ModelCustomizationUuid",
 
 651                     modelCustomizationUuid, "CatalogDb", ErrorCode.DataError.getValue(), error);
 
 652                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 654                 logger.debug("Found vfModuleCust entry " + vfmc.toString());
 
 657             // Get the vfModule and vnfResource records
 
 658                 vf = vfmc.getVfModule();
 
 659                 vnfResource = vfmc.getVfModule().getVnfResources();
 
 661         catch (Exception e) {
 
 663                 logger.debug("unhandled exception in create VF - [Query]" + e.getMessage());
 
 664                 throw new VnfException("Exception during create VF " + e.getMessage());
 
 667         //  Perform a version check against cloudSite
 
 668         // Obtain the cloud site information where we will create the VF Module
 
 669         Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite (cloudSiteId);
 
 670         if (!cloudSiteOp.isPresent()) {
 
 671             throw new VnfException (new MsoCloudSiteNotFound (cloudSiteId));
 
 673         CloudSite cloudSite = cloudSiteOp.get();
 
 674                 MavenLikeVersioning aicV = new MavenLikeVersioning();
 
 675                 aicV.setVersion(cloudSite.getCloudVersion());
 
 677                 String vnfMin = vnfResource.getAicVersionMin();
 
 678                 String vnfMax = vnfResource.getAicVersionMax();
 
 680                 if ( (vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) ||
 
 681                      (vnfMax != null && aicV.isMoreRecentThan(vnfMax)))
 
 684         String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUUID()
 
 685             + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax + " NOT supported on Cloud: " + cloudSiteId
 
 686             + " with AIC_Version:" + cloudSite.getCloudVersion();
 
 687         logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack",
 
 688             ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
 
 690         throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 695         DeploymentInfo cloudifyDeployment = null;
 
 697         // First, look up to see if the VF already exists.
 
 699         long subStartTime1 = System.currentTimeMillis ();
 
 701             cloudifyDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, vfModuleName);
 
 703         catch (MsoException me) {
 
 704             // Failed to query the Deployment due to a cloudify exception.
 
 705             String error = "Create VF Module: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
 
 706             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudSiteId,
 
 707                 tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 708                 "Exception - queryDeployment", me);
 
 711             // Convert to a generic VnfException
 
 712             me.addContext ("CreateVFModule");
 
 713             throw new VnfException (me);
 
 716         // More precise handling/messaging if the Module already exists
 
 717         if (cloudifyDeployment != null && !(cloudifyDeployment.getStatus () == DeploymentStatus.NOTFOUND)) {
 
 718                 // CREATED, INSTALLED, INSTALLING, FAILED, UNINSTALLING, UNKNOWN
 
 719                 DeploymentStatus status = cloudifyDeployment.getStatus();
 
 720                         logger.debug ("Found Existing Deployment, status=" + status);
 
 722                 if (status == DeploymentStatus.INSTALLED) {
 
 724                         if (failIfExists != null && failIfExists) {
 
 725                                 String error = "Create VF: Deployment " + vfModuleName + " already exists in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId;
 
 726                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
 
 727                     cloudOwner, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 728                     "Deployment " + vfModuleName + " already exists");
 
 730                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 732                                 // Found existing deployment and client has not requested "failIfExists".
 
 733                                 // Populate the outputs from the existing deployment.
 
 735                                 vnfId.value = cloudifyDeployment.getId();
 
 736                                 outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
 
 740                 // Check through various detailed error cases
 
 741                 if (status == DeploymentStatus.INSTALLING || status == DeploymentStatus.UNINSTALLING) {
 
 742                         // fail - it's in progress - return meaningful error
 
 743               String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; please wait for it to complete, or fix manually.";
 
 744               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
 
 745                   cloudOwner, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 746                   "Deployment " + vfModuleName + " already exists");
 
 748                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 750                 else if (status == DeploymentStatus.FAILED) {
 
 751                         // fail - it exists and is in a FAILED state
 
 752               String error = "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
 
 753               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
 
 754                   cloudOwner, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 755                   "Deployment " + vfModuleName + " already " + "exists and is in FAILED state");
 
 757                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 759                 else if (status == DeploymentStatus.UNKNOWN || status == DeploymentStatus.CREATED) {
 
 760                         // fail - it exists and is in a UNKNOWN state
 
 761               String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
 
 762               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
 
 763                   cloudOwner, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 764                   "Deployment " + vfModuleName + " already " + "exists and is in " + status.toString() + " state");
 
 766                 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 769                         // Unexpected, since all known status values have been tested for
 
 771                   "Create VF: Deployment " + vfModuleName + " already exists with unexpected status " + status
 
 772                       .toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
 
 773               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
 
 774                   cloudOwner, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", ErrorCode.DataError.getValue(),
 
 775                   "Deployment " + vfModuleName + " already " + "exists and is in an unknown state");
 
 777               throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, cloudifyDeployment.getId());
 
 782         // Collect outputs from Base Modules and Volume Modules
 
 783         Map<String, Object> baseModuleOutputs = null;
 
 784         Map<String, Object> volumeGroupOutputs = null;
 
 786         // If a Volume Group was provided, query its outputs for inclusion in Module input parameters
 
 787         if (volumeGroupId != null) {
 
 788             long subStartTime2 = System.currentTimeMillis ();
 
 789             DeploymentInfo volumeDeployment = null;
 
 791                 volumeDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, volumeGroupId);
 
 793             catch (MsoException me) {
 
 794                 // Failed to query the Volume GroupDeployment due to a cloudify exception.
 
 795                 String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
 
 796                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId,
 
 797                     cloudOwner, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment(volume)",
 
 798                     ErrorCode.DataError.getValue(), "Exception - queryDeployment(volume)", me);
 
 800                 // Convert to a generic VnfException
 
 801                 me.addContext ("CreateVFModule(QueryVolume)");
 
 802                 throw new VnfException (me);
 
 805                 if (volumeDeployment == null || volumeDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
 
 807                   "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in " + cloudSiteId + "/"
 
 808                       + tenantId + " USER ERROR";
 
 809               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId,
 
 810                   cloudSiteId, tenantId, error, CLOUDIFY, "queryDeployment(volume)",
 
 811                   ErrorCode.BusinessProcesssError.getValue(),
 
 812                   "Create VFModule: Attached Volume Group DOES NOT EXIST");
 
 814               throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 816                         logger.debug("Found nested volume group");
 
 817                         volumeGroupOutputs = volumeDeployment.getOutputs();
 
 818                         this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs");
 
 822         // If this is an Add-On Module, query the Base Module outputs
 
 823         // Note: This will be performed whether or not the current request is for an
 
 824         //       Add-On Volume Group or Add-On VF Module
 
 826         if (vf.getIsBase()) {
 
 827             logger.debug("This is a BASE Module request");
 
 828             vfRollback.setIsBase(true);
 
 830             logger.debug("This is an Add-On Module request");
 
 832             // Add-On Modules should always have a Base, but just treat as a warning if not provided.
 
 833             // Add-on Volume requests may or may not specify a base.
 
 834             if (!isVolumeRequest && baseVfModuleId == null) {
 
 835                 logger.debug ("WARNING:  Add-on Module request - no Base Module ID provided");
 
 838             if (baseVfModuleId != null) {
 
 839                     long subStartTime2 = System.currentTimeMillis ();
 
 840                     DeploymentInfo baseDeployment = null;
 
 842                         baseDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, baseVfModuleId);
 
 844                     catch (MsoException me) {
 
 845                         // Failed to query the Volume GroupDeployment due to a cloudify exception.
 
 847                       "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": "
 
 849                   logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId,
 
 850                       cloudOwner, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment(Base)",
 
 851                       ErrorCode.DataError.getValue(), "Exception - queryDeployment(Base)", me);
 
 853                   // Convert to a generic VnfException
 
 854                   me.addContext("CreateVFModule(QueryBase)");
 
 855                   throw new VnfException (me);
 
 858                 if (baseDeployment == null || baseDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
 
 860                         "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in " + cloudSiteId + "/"
 
 861                             + tenantId + " USER ERROR";
 
 862                     logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId,
 
 863                         cloudSiteId, tenantId, error, CLOUDIFY, "queryDeployment(Base)",
 
 864                         ErrorCode.BusinessProcesssError.getValue(),
 
 865                         "Create VFModule: Base " + "Module DOES NOT EXIST");
 
 867                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
 869                     logger.debug("Found base module");
 
 870                     baseModuleOutputs = baseDeployment.getOutputs();
 
 871                     this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs");
 
 877         // Ready to deploy the new VNF
 
 879         // NOTE:  For this section, heatTemplate is used for both HEAT templates and Cloudify blueprints.
 
 880         // In final implementation (post-POC), the template object would either be generic or there would
 
 881         // be a separate DB Table/Object for Blueprints.
 
 884                 // NOTE: The template is fixed for the VF Module.  The environment is part of the customization.
 
 885         HeatTemplate heatTemplate = null;
 
 886         HeatEnvironment heatEnvironment = null;
 
 887         if (isVolumeRequest) {
 
 888                         heatTemplate = vf.getVolumeHeatTemplate();
 
 889                         heatEnvironment = vfmc.getVolumeHeatEnv();
 
 891                         heatTemplate = vf.getModuleHeatTemplate();
 
 892                         heatEnvironment = vfmc.getHeatEnvironment();
 
 895                 if (heatTemplate == null) {
 
 896         String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType="
 
 898         logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID", vfModuleType,
 
 899             "OpenStack", ErrorCode.DataError.getValue(), error);
 
 900         throw new VnfException(error, MsoExceptionCategory.INTERNAL);
 
 902                         logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
 
 905         if (heatEnvironment == null) {
 
 906             String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
 
 907             logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
 
 908                 "OpenStack", ErrorCode.DataError.getValue(), error);
 
 909             // Alarm on this error, configuration must be fixed
 
 910             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
 
 912             logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
 
 917             // All variables converted to their native object types
 
 918             HashMap<String, Object> goldenInputs = new HashMap<String,Object>();
 
 919             List<String> extraInputs = new ArrayList<String>();
 
 921             // NOTE: SKIP THIS FOR CLOUDIFY for now.  Just use what was passed in.
 
 922             //  This whole section needs to be rewritten.
 
 923                         Boolean skipInputChecks = false;
 
 925                         if (skipInputChecks) {
 
 926                                 goldenInputs = new HashMap<String,Object>();
 
 927                                 for (String key : inputs.keySet()) {
 
 928                                         goldenInputs.put(key, inputs.get(key));
 
 932                                 // Build maps for the parameters (including aliases) to simplify checks
 
 933                                 HashMap<String, HeatTemplateParam> params = new HashMap<String, HeatTemplateParam>();
 
 935                                 Set<HeatTemplateParam> paramSet = heatTemplate.getParameters();
 
 936                                 logger.debug("paramSet has {} entries", paramSet.size());
 
 938                                 for (HeatTemplateParam htp : paramSet) {
 
 939                                         params.put(htp.getParamName(), htp);
 
 942                                         String alias = htp.getParamAlias();
 
 943                                         if (alias != null && !alias.equals("") && !params.containsKey(alias)) {
 
 944                                                 params.put(alias, htp);
 
 948                                 // First, convert all inputs to their "template" type
 
 949                                 for (String key : inputs.keySet()) {
 
 950                                         if (params.containsKey(key)) {
 
 951                                                 Object value = cloudifyUtils.convertInputValue(inputs.get(key), params.get(key));
 
 953                                                         goldenInputs.put(key, value);
 
 956                                                         logger.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to " + params.get(key)
 
 960                                                 extraInputs.add(key);
 
 964                                 if (!extraInputs.isEmpty()) {
 
 965                                         logger.debug("Ignoring extra inputs: " + extraInputs);
 
 968                                 // Next add in Volume Group Outputs if there are any.  Copy directly without conversions.
 
 969                                 if (volumeGroupOutputs != null  &&  !volumeGroupOutputs.isEmpty()) {
 
 970                                         for (String key : volumeGroupOutputs.keySet()) {
 
 971                                                 if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
 
 972                                                         goldenInputs.put(key, volumeGroupOutputs.get(key));
 
 977                                 // Next add in Base Module Outputs if there are any.  Copy directly without conversions.
 
 978                                 if (baseModuleOutputs != null  &&  !baseModuleOutputs.isEmpty()) {
 
 979                                         for (String key : baseModuleOutputs.keySet()) {
 
 980                                                 if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
 
 981                                                         goldenInputs.put(key, baseModuleOutputs.get(key));
 
 986                                 // Last, add in values from the "environment" file.
 
 987                                 // These are added to the inputs, since Cloudify doesn't pass an environment file like Heat.
 
 989                                 // TODO: This may take a different form for Cloudify, but for now process it
 
 990                                 //       with Heat environment file syntax
 
 991                 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
 
 992                                 MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry (sb);
 
 994                                 if (mhee.getParameters() != null) {
 
 995                                         for (MsoHeatEnvironmentParameter envParam : mhee.getParameters()) {
 
 996                                                 // If this is a template input, copy to golden inputs
 
 997                                                 String envKey = envParam.getName();
 
 998                                                 if (params.containsKey(envKey)  &&  !goldenInputs.containsKey(envKey)) {
 
 999                                                         Object value = cloudifyUtils.convertInputValue(envParam.getValue(), params.get(envKey));
 
1000                                                         if (value != null) {
 
1001                                                                 goldenInputs.put(envKey, value);
 
1004                                                                 logger.debug("Failed to convert environment parameter " + envKey + "='" + envParam.getValue() + "' to " +
 
1005                     params.get(envKey).getParamType());
 
1011                     this.sendMapToDebug(goldenInputs, "Final inputs sent to Cloudify");
 
1014                     // Check that required parameters have been supplied from any of the sources
 
1015                     String missingParams = null;
 
1016                     boolean checkRequiredParameters = true;
 
1018                         String propertyString = this.environment.getProperty(MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
 
1019                         if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
 
1020                             checkRequiredParameters = false;
 
1021                       logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking... {}",
 
1022                           MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
 
1024                     } catch (Exception e) {
 
1025                         // No problem - default is true
 
1026                   logger.debug("An exception occured trying to get property {}",
 
1027                       MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS, e);
 
1031                     for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
 
1032                         if (parm.isRequired () && (!goldenInputs.containsKey (parm.getParamName ()))) {
 
1033                       logger.debug("adding to missing parameters list: {}", parm.getParamName());
 
1034                       if (missingParams == null) {
 
1035                           missingParams = parm.getParamName();
 
1037                           missingParams += "," + parm.getParamName();
 
1042           if (missingParams != null) {
 
1043               if (checkRequiredParameters) {
 
1044                   // Problem - missing one or more required parameters
 
1045                   String error = "Create VFModule: Missing Required inputs: " + missingParams;
 
1046                   logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, CLOUDIFY,
 
1047                       ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs");
 
1048                   logger.debug(error);
 
1049                   throw new VnfException(error, MsoExceptionCategory.USERDATA);
 
1052                       "found missing parameters [" + missingParams + "] - but checkRequiredParameters is false -"
 
1053                           + " will not block");
 
1056               logger.debug("No missing parameters found - ok to proceed");
 
1059                         } // NOTE: END PARAMETER CHECKING
 
1061                         // Ready to deploy the VF Module.
 
1062                         // *First step - make sure the blueprint is loaded into Cloudify.
 
1063                         String blueprintName = heatTemplate.getTemplateName();
 
1064                         String blueprint = heatTemplate.getTemplateBody();
 
1065                         String blueprintId = blueprintName;
 
1067                         // Use the main blueprint name as the blueprint ID (strip yaml extensions).
 
1068             if (blueprintId.endsWith(".yaml"))
 
1069                 blueprintId = blueprintId.substring(0,blueprintId.lastIndexOf(".yaml"));
 
1072                                 if (! cloudifyUtils.isBlueprintLoaded (cloudSiteId, blueprintId)) {
 
1073           logger.debug("Blueprint " + blueprintId + " is not loaded.  Will upload it now.");
 
1075                                         Map<String,byte[]> blueprintFiles = new HashMap<String,byte[]>();
 
1077                                         blueprintFiles.put(blueprintName, blueprint.getBytes());
 
1079                             // TODO:  Implement nested blueprint logic based on Cloudify structures.
 
1080                                         //        For now, just use the Heat structures.
 
1081                                         //        The query returns a map of String->Object, where the map keys provide one layer of
 
1082                                         //        indirection from the Heat template names.  For this case, assume the map key matches
 
1083                                         //        the nested blueprint name.
 
1084                             List<HeatTemplate> nestedBlueprints = heatTemplate.getChildTemplates();
 
1085                             if (nestedBlueprints != null) {
 
1086                                     for (HeatTemplate nestedBlueprint: nestedBlueprints) {
 
1087                                         blueprintFiles.put(nestedBlueprint.getTemplateName(), nestedBlueprint.getTemplateBody().getBytes());
 
1091                             // TODO:  Implement file artifact logic based on Cloudify structures.
 
1092                             //        For now, just use the Heat structures.
 
1093                             List<HeatFiles> heatFiles = vf.getHeatFiles();
 
1094                             if (heatFiles != null) {
 
1095                                     for (HeatFiles heatFile: heatFiles) {
 
1096                                         blueprintFiles.put(heatFile.getFileName(), heatFile.getFileBody().getBytes());
 
1100                             // Upload the blueprint package
 
1101                                         cloudifyUtils.uploadBlueprint(cloudSiteId, blueprintId, blueprintName, blueprintFiles, false);
 
1106                         catch (MsoException me) {
 
1107           me.addContext("CreateVFModule");
 
1108           String error = "Create VF Module: Upload blueprint failed.  Blueprint=" + blueprintName + ": " + me;
 
1109           logger.error("{} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudSiteId,
 
1110               tenantId, CLOUDIFY, ErrorCode.DataError.getValue(), "MsoException - uploadBlueprint", me);
 
1111           logger.debug(error);
 
1112           throw new VnfException(me);
 
1115             // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
 
1116             // because we already checked for those.
 
1117             long createDeploymentStarttime = System.currentTimeMillis ();
 
1119                 // KLUDGE - Cloudify requires Tenant Name for Openstack.  We have the ID.
 
1120                 //          Go directly to Keystone until APIs could be updated to supply the name.
 
1121                 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
 
1122                 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
 
1124                 if (backout == null) {
 
1128                 cloudifyDeployment = cloudifyUtils.createAndInstallDeployment (cloudSiteId,
 
1134                                               heatTemplate.getTimeoutMinutes (),
 
1135                                               backout.booleanValue());
 
1137             } catch (MsoException me) {
 
1138                 me.addContext ("CreateVFModule");
 
1139                 String error = "Create VF Module " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
 
1141                     .error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, cloudSiteId,
 
1142                         tenantId, CLOUDIFY, ErrorCode.DataError.getValue(), "MsoException - createDeployment",
 
1144                 logger.debug(error);
 
1145                 throw new VnfException (me);
 
1146             } catch (NullPointerException npe) {
 
1147                 String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + npe;
 
1149                     .error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, cloudSiteId,
 
1150                         tenantId, CLOUDIFY, ErrorCode.DataError.getValue(),
 
1151                         "NullPointerException - createDeployment", npe);
 
1152                 logger.debug(error);
 
1153                 logger.debug("NULL POINTER EXCEPTION at cloudify.createAndInstallDeployment");
 
1154                 //npe.addContext ("CreateVNF");
 
1155                 throw new VnfException ("NullPointerException during cloudify.createAndInstallDeployment");
 
1156             } catch (Exception e) {
 
1157                 logger.debug("unhandled exception at cloudify.createAndInstallDeployment");
 
1158                 throw new VnfException("Exception during cloudify.createAndInstallDeployment! " + e.getMessage());
 
1160         } catch (Exception e) {
 
1161                 logger.debug("unhandled exception in create VF");
 
1162                 throw new VnfException("Exception during create VF " + e.getMessage());
 
1166         // Reach this point if create is successful.
 
1167         // Populate remaining rollback info and response parameters.
 
1168         vfRollback.setVnfCreated (true);
 
1169         vfRollback.setVnfId (cloudifyDeployment.getId());
 
1170         vnfId.value = cloudifyDeployment.getId();
 
1171         outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
 
1173         rollback.value = vfRollback;
 
1175         logger.debug("VF Module successfully created", vfModuleName);
 
1179     public void deleteVfModule (String cloudSiteId,
 
1183                            MsoRequest msoRequest,
 
1184                            Holder <Map <String, String>> outputs) throws VnfException {
 
1185         logger.debug ("Deleting VF " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId);
 
1186         // Will capture execution time for metrics
 
1187         long startTime = System.currentTimeMillis ();
 
1189         // 1702 capture the output parameters on a delete
 
1190         // so we'll need to query first
 
1191         DeploymentInfo deployment = null;
 
1193                 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
 
1194         } catch (MsoException me) {
 
1195             // Failed to query the deployment.  Convert to a generic VnfException
 
1196             me.addContext ("DeleteVFModule");
 
1197             String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
 
1198             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
 
1199                 tenantId, CLOUDIFY, "QueryDeployment", ErrorCode.DataError.getValue(),
 
1200                 "Exception - QueryDeployment", me);
 
1201             logger.debug(error);
 
1202             throw new VnfException (me);
 
1204         // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected Object types
 
1205         outputs.value = convertMapStringObjectToStringString(deployment.getOutputs());
 
1207         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
 
1208         // The possible outcomes of deleteStack are a StackInfo object with status
 
1209         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
 
1211         long subStartTime = System.currentTimeMillis ();
 
1213             cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantId, vnfName, 5);
 
1214         } catch (MsoException me) {
 
1215             me.addContext("DeleteVfModule");
 
1216             // Convert to a generic VnfException
 
1217             String error = "Delete VF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
 
1218             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
 
1219                 tenantId, "DeleteDeployment", "DeleteDeployment", ErrorCode.DataError.getValue(),
 
1220                 "Exception - DeleteDeployment: " + me.getMessage());
 
1221             logger.debug(error);
 
1222             throw new VnfException(me);
 
1225         // On success, nothing is returned.
 
1229     // TODO:  Should Update be supported for Cloudify?  What would this look like?
 
1231     public void updateVfModule (String cloudSiteId,
 
1238                            String volumeGroupHeatStackId,
 
1239                            String baseVfHeatStackId,
 
1240                            String vfModuleStackId,
 
1241                            String modelCustomizationUuid,
 
1242                            Map <String, Object> inputs,
 
1243                            MsoRequest msoRequest,
 
1244                            Holder <Map <String, String>> outputs,
 
1245                            Holder <VnfRollback> rollback) throws VnfException
 
1247                 // This operation is not currently supported for Cloudify-orchestrated VF Modules.
 
1248                 logger.debug("Update VF Module command attempted but not supported");
 
1249                 throw new VnfException ("UpdateVfModule:  Unsupported command", MsoExceptionCategory.USERDATA);