2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Modifications Copyright (C) 2018 IBM.
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=========================================================
23 package org.onap.so.adapters.vnf;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Optional;
33 import javax.jws.WebService;
34 import javax.xml.ws.Holder;
36 import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists;
37 import org.onap.so.adapters.vnf.exceptions.VnfException;
38 import org.onap.so.cloud.CloudConfig;
39 import org.onap.so.db.catalog.beans.CloudSite;
40 import org.onap.so.cloudify.beans.DeploymentInfo;
41 import org.onap.so.cloudify.beans.DeploymentStatus;
42 import org.onap.so.cloudify.exceptions.MsoCloudifyManagerNotFound;
43 import org.onap.so.cloudify.utils.MsoCloudifyUtils;
44 import org.onap.so.db.catalog.beans.HeatEnvironment;
45 import org.onap.so.db.catalog.beans.HeatFiles;
46 import org.onap.so.db.catalog.beans.HeatTemplate;
47 import org.onap.so.db.catalog.beans.HeatTemplateParam;
48 import org.onap.so.db.catalog.beans.VfModule;
49 import org.onap.so.db.catalog.beans.VfModuleCustomization;
50 import org.onap.so.db.catalog.beans.VnfResource;
51 import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository;
52 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
53 import org.onap.so.entity.MsoRequest;
54 import org.onap.so.logger.MessageEnum;
55 import org.onap.so.logger.MsoAlarmLogger;
56 import org.onap.so.logger.MsoLogger;
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.springframework.beans.factory.annotation.Autowired;
67 import org.springframework.core.env.Environment;
68 import org.springframework.stereotype.Component;
70 import com.fasterxml.jackson.core.JsonParseException;
71 import com.fasterxml.jackson.databind.JsonNode;
72 import com.fasterxml.jackson.databind.ObjectMapper;
73 import org.springframework.transaction.annotation.Transactional;
77 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter", targetNamespace = "http://org.onap.so/vnf")
78 public class MsoVnfCloudifyAdapterImpl implements MsoVnfAdapter {
80 private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
81 private static final String VNF_ADAPTER_SERVICE_NAME = "MSO-BPMN:MSO-VnfAdapter.";
82 private static final String LOG_REPLY_NAME = "MSO-VnfAdapter:MSO-BPMN.";
83 private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA, MsoVnfCloudifyAdapterImpl.class);
84 private static MsoAlarmLogger alarmLogger = new MsoAlarmLogger ();
85 private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
86 private static final String ADD_GET_FILES_ON_VOLUME_REQ = "org.onap.so.adapters.vnf.addGetFilesOnVolumeReq";
87 private static final String CLOUDIFY_RESPONSE_SUCCESS="Successfully received response from Cloudify";
88 private static final String CLOUDIFY="Cloudify";
90 private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
93 protected CloudConfig cloudConfig;
96 private VFModuleCustomizationRepository vfModuleCustomRepo;
99 private Environment environment;
102 protected MsoKeystoneUtils keystoneUtils;
105 protected MsoCloudifyUtils cloudifyUtils;
107 * Health Check web method. Does nothing but return to show the adapter is deployed.
110 public void healthCheck () {
111 LOGGER.debug ("Health check call in VNF Cloudify Adapter");
115 * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
116 * @see MsoVnfCloudifyAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
118 public MsoVnfCloudifyAdapterImpl() {
123 * This is the "Create VNF" web service implementation.
124 * This function is now unsupported and will return an error.
128 public void createVnf (String cloudSiteId,
134 String volumeGroupHeatStackId,
135 Map <String, String> inputs,
136 Boolean failIfExists,
138 Boolean enableBridge,
139 MsoRequest msoRequest,
140 Holder <String> vnfId,
141 Holder <Map <String, String>> outputs,
142 Holder <VnfRollback> rollback)
145 // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
146 LOGGER.debug ("CreateVNF command attempted but not supported");
147 throw new VnfException ("CreateVNF: Unsupported command", MsoExceptionCategory.USERDATA);
151 * This is the "Update VNF" web service implementation.
152 * This function is now unsupported and will return an error.
156 public void updateVnf (String cloudSiteId,
162 String volumeGroupHeatStackId,
163 Map <String, String> inputs,
164 MsoRequest msoRequest,
165 Holder <Map <String, String>> outputs,
166 Holder <VnfRollback> rollback)
169 // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
170 LOGGER.debug ("UpdateVNF command attempted but not supported");
171 throw new VnfException ("UpdateVNF: Unsupported command", MsoExceptionCategory.USERDATA);
175 * This is the "Query VNF" web service implementation.
177 * This really should be QueryVfModule, but nobody ever changed it.
179 * For Cloudify, this will look up a deployment by its deployment ID, which is really the same
180 * as deployment name, since it assigned by the client when a deployment is created.
181 * Also, the input cloudSiteId is used only to identify which Cloudify instance to query,
182 * and the tenantId is ignored (since that really only applies for Openstack/Heat).
184 * The method returns an indicator that the VNF exists, along with its status and outputs.
185 * The input "vnfName" will also be reflected back as its ID.
187 * @param cloudSiteId CLLI code of the cloud site in which to query
188 * @param tenantId Openstack tenant identifier - ignored for Cloudify
189 * @param vnfName VNF Name (should match a deployment ID)
190 * @param msoRequest Request tracking information for logs
191 * @param vnfExists Flag reporting the result of the query
192 * @param vnfId Holder for output VNF ID
193 * @param outputs Holder for Map of VNF outputs from Cloudify deployment (assigned IPs, etc)
196 public void queryVnf (String cloudSiteId,
199 MsoRequest msoRequest,
200 Holder <Boolean> vnfExists,
201 Holder <String> vnfId,
202 Holder <VnfStatus> status,
203 Holder <Map <String, String>> outputs)
206 MsoLogger.setLogContext (msoRequest);
207 MsoLogger.setServiceName ("QueryVnfCloudify");
208 LOGGER.debug ("Querying VNF " + vnfName + " in " + cloudSiteId + "/" + tenantId);
210 // Will capture execution time for metrics
211 long startTime = System.currentTimeMillis ();
212 long subStartTime = System.currentTimeMillis ();
214 DeploymentInfo deployment = null;
217 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
218 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, CLOUDIFY_RESPONSE_SUCCESS, CLOUDIFY, "QueryDeployment", vnfName);
220 catch (MsoCloudifyManagerNotFound e) {
221 // This site does not have a Cloudify Manager.
222 // This isn't an error, just means we won't find the VNF here.
225 catch (MsoException me) {
226 // Failed to query the Deployment due to a cloudify exception.
227 // Convert to a generic VnfException
228 me.addContext ("QueryVNF");
229 String error = "Query VNF (Cloudify): " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
230 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "QueryDeployment", vnfName);
231 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, CLOUDIFY, "QueryVNF", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment", me);
232 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
233 throw new VnfException (me);
236 if (deployment != null && deployment.getStatus() != DeploymentStatus.NOTFOUND) {
237 vnfExists.value = Boolean.TRUE;
238 status.value = deploymentStatusToVnfStatus(deployment);
239 vnfId.value = deployment.getId();
240 outputs.value = copyStringOutputs (deployment.getOutputs ());
242 LOGGER.debug ("VNF " + vnfName + " found in Cloudify, ID = " + vnfId.value);
245 vnfExists.value = Boolean.FALSE;
246 status.value = VnfStatus.NOTFOUND;
248 outputs.value = new HashMap <String, String> (); // Return as an empty map
250 LOGGER.debug ("VNF " + vnfName + " not found");
252 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully query VNF");
258 * This is the "Delete VNF" web service implementation.
259 * This function is now unsupported and will return an error.
263 public void deleteVnf (String cloudSiteId,
266 MsoRequest msoRequest) throws VnfException {
267 MsoLogger.setLogContext (msoRequest);
268 MsoLogger.setServiceName ("DeleteVnf");
270 // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
271 LOGGER.debug ("DeleteVNF command attempted but not supported");
272 throw new VnfException ("DeleteVNF: Unsupported command", MsoExceptionCategory.USERDATA);
276 * This web service endpoint will rollback a previous Create VNF operation.
277 * A rollback object is returned to the client in a successful creation
278 * response. The client can pass that object as-is back to the rollbackVnf
279 * operation to undo the creation.
281 * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup,
282 * but APIs were apparently never updated.
285 public void rollbackVnf (VnfRollback rollback) throws VnfException {
286 long startTime = System.currentTimeMillis ();
287 MsoLogger.setServiceName ("RollbackVnf");
288 // rollback may be null (e.g. if stack already existed when Create was called)
289 if (rollback == null) {
290 LOGGER.info (MessageEnum.RA_ROLLBACK_NULL, "OpenStack", "rollbackVnf", MsoLogger.getServiceName());
291 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, "Rollback request content is null");
295 // Don't rollback if nothing was done originally
296 if (!rollback.getVnfCreated()) {
297 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Rollback VF Module - nothing to roll back");
301 // Get the elements of the VnfRollback object for easier access
302 String cloudSiteId = rollback.getCloudSiteId ();
303 String tenantId = rollback.getTenantId ();
304 String vfModuleId = rollback.getVfModuleStackId ();
306 MsoLogger.setLogContext (rollback.getMsoRequest());
308 LOGGER.debug ("Rolling Back VF Module " + vfModuleId + " in " + cloudSiteId + "/" + tenantId);
310 DeploymentInfo deployment = null;
312 // Use the MsoCloudifyUtils to delete the deployment. Set the polling flag to true.
313 // The possible outcomes of deleteStack are a StackInfo object with status
314 // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
316 long subStartTime = System.currentTimeMillis ();
318 // KLUDGE - Cloudify requires Tenant Name for Openstack. We have the ID.
319 // Go directly to Keystone until APIs could be updated to supply the name.
320 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
321 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
323 // TODO: Get a reasonable timeout. Use a global property, or store the creation timeout in rollback object and use that.
324 deployment = cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantName, vfModuleId, 5);
325 LOGGER.debug("Rolled back deployment: " + deployment.getId());
326 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, CLOUDIFY_RESPONSE_SUCCESS, CLOUDIFY, "DeleteDeployment", null);
327 } catch (MsoException me) {
328 // Failed to rollback the VNF due to a cloudify exception.
329 // Convert to a generic VnfException
330 me.addContext ("RollbackVNF");
331 String error = "Rollback VF Module: " + vfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + me;
332 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "DeleteDeployment", null);
333 LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vfModuleId, cloudSiteId, tenantId, CLOUDIFY, "DeleteDeployment", MsoLogger.ErrorCode.DataError, "Exception - DeleteDeployment", me);
334 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
335 throw new VnfException (me);
337 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully roll back VF Module");
342 private VnfStatus deploymentStatusToVnfStatus (DeploymentInfo deployment) {
343 // Determine the status based on last action & status
344 // DeploymentInfo object should be enhanced to report a better status internally.
345 DeploymentStatus status = deployment.getStatus();
346 String lastAction = deployment.getLastAction();
348 if (status == null || lastAction == null) {
349 return VnfStatus.UNKNOWN;
351 else if (status == DeploymentStatus.NOTFOUND) {
352 return VnfStatus.NOTFOUND;
354 else if (status == DeploymentStatus.INSTALLED) {
355 return VnfStatus.ACTIVE;
357 else if (status == DeploymentStatus.CREATED) {
358 // Should have an INACTIVE status for this case. Shouldn't really happen, but
359 // Install was never run, or Uninstall was done but deployment didn't get deleted.
360 return VnfStatus.UNKNOWN;
362 else if (status == DeploymentStatus.FAILED) {
363 return VnfStatus.FAILED;
366 return VnfStatus.UNKNOWN;
369 private Map <String, String> copyStringOutputs (Map <String, Object> stackOutputs) {
370 Map <String, String> stringOutputs = new HashMap <String, String> ();
371 for (String key : stackOutputs.keySet ()) {
372 if (stackOutputs.get (key) instanceof String) {
373 stringOutputs.put (key, (String) stackOutputs.get (key));
374 } else if (stackOutputs.get(key) instanceof Integer) {
376 String str = "" + stackOutputs.get(key);
377 stringOutputs.put(key, str);
378 } catch (Exception e) {
379 LOGGER.debug("Unable to add " + key + " to outputs");
381 } else if (stackOutputs.get(key) instanceof JsonNode) {
383 String str = this.convertNode((JsonNode) stackOutputs.get(key));
384 stringOutputs.put(key, str);
385 } catch (Exception e) {
386 LOGGER.debug("Unable to add " + key + " to outputs - exception converting JsonNode");
388 } else if (stackOutputs.get(key) instanceof java.util.LinkedHashMap) {
390 String str = JSON_MAPPER.writeValueAsString(stackOutputs.get(key));
391 stringOutputs.put(key, str);
392 } catch (Exception e) {
393 LOGGER.debug("Unable to add " + key + " to outputs - exception converting LinkedHashMap");
397 String str = stackOutputs.get(key).toString();
398 stringOutputs.put(key, str);
399 } catch (Exception e) {
400 LOGGER.debug("Unable to add " + key + " to outputs - unable to call .toString() " + e.getMessage());
404 return stringOutputs;
408 private void sendMapToDebug(Map<String, Object> inputs, String optionalName) {
410 StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName);
411 if (inputs == null) {
414 else if (inputs.size() < 1) {
415 sb.append("\tEMPTY");
417 for (String str : inputs.keySet()) {
420 outputString = inputs.get(str).toString();
421 } catch (Exception e) {
422 outputString = "Unable to call toString() on the value for " + str;
424 sb.append("\t\nitem " + i++ + ": '" + str + "'='" + outputString + "'");
427 LOGGER.debug(sb.toString());
431 private void sendMapToDebug(Map<String, String> inputs) {
433 StringBuilder sb = new StringBuilder("inputs:");
434 if (inputs == null) {
437 else if (inputs.size() < 1) {
438 sb.append("\tEMPTY");
440 for (String str : inputs.keySet()) {
441 sb.append("\titem " + i++ + ": " + str + "=" + inputs.get(str));
444 LOGGER.debug(sb.toString());
448 private String convertNode(final JsonNode node) {
450 final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
451 final String json = JSON_MAPPER.writeValueAsString(obj);
453 } catch (JsonParseException jpe) {
454 LOGGER.debug("Error converting json to string " + jpe.getMessage());
455 } catch (Exception e) {
456 LOGGER.debug("Error converting json to string " + e.getMessage());
458 return "[Error converting json to string]";
461 private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
462 if (objectMap == null) {
465 Map<String, String> stringMap = new HashMap<String, String>();
466 for (String key : objectMap.keySet()) {
467 if (!stringMap.containsKey(key)) {
468 Object obj = objectMap.get(key);
469 if (obj instanceof String) {
470 stringMap.put(key, (String) objectMap.get(key));
471 } else if (obj instanceof JsonNode ){
472 // This is a bit of mess - but I think it's the least impacting
473 // let's convert it BACK to a string - then it will get converted back later
475 String str = this.convertNode((JsonNode) obj);
476 stringMap.put(key, str);
477 } catch (Exception e) {
478 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode "+ key);
479 //okay in this instance - only string values (fqdn) are expected to be needed
481 } else if (obj instanceof java.util.LinkedHashMap) {
482 LOGGER.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
484 String str = JSON_MAPPER.writeValueAsString(obj);
485 stringMap.put(key, str);
486 } catch (Exception e) {
487 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap "+ key);
489 } else if (obj instanceof Integer) {
491 String str = "" + obj;
492 stringMap.put(key, str);
493 } catch (Exception e) {
494 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for Integer "+ key);
498 String str = obj.toString();
499 stringMap.put(key, str);
500 } catch (Exception e) {
501 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value "+ key + " (" + e.getMessage() + ")");
511 * This is the "Create VF Module" web service implementation.
512 * It will instantiate a new VF Module of the requested type in the specified cloud
513 * and tenant. The tenant must exist before this service is called.
515 * If a VF Module with the same name already exists, this can be considered a
516 * success or failure, depending on the value of the 'failIfExists' parameter.
518 * All VF Modules are defined in the MSO catalog. The caller must request
519 * one of the pre-defined module types or an error will be returned. Within the
520 * catalog, each VF Module references (among other things) a cloud template
521 * which is used to deploy the required artifacts (VMs, networks, etc.)
522 * to the cloud. In this adapter implementation, that artifact is expected
523 * to be a Cloudify blueprint.
525 * Depending on the blueprint, a variable set of input parameters will
526 * be defined, some of which are required. The caller is responsible to
527 * pass the necessary input data for the module or an error will be thrown.
529 * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback
530 * object. This last object can be passed as-is to the rollbackVnf operation to
531 * undo everything that was created for the Module. This is useful if a VF module
532 * is successfully created but the orchestration fails on a subsequent step.
534 * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
535 * @param tenantId Openstack tenant identifier
536 * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB.
537 * Deprecated - should use modelCustomizationUuid
538 * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
539 * Deprecated - VF Module versions also captured by modelCustomizationUuid
540 * @param vfModuleName Name to be assigned to 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,
563 String volumeGroupId,
564 String baseVfModuleId,
565 String modelCustomizationUuid,
566 Map <String, String> inputs,
567 Boolean failIfExists,
569 Boolean enableBridge,
570 MsoRequest msoRequest,
571 Holder <String> vnfId,
572 Holder <Map <String, String>> outputs,
573 Holder <VnfRollback> rollback)
576 // Will capture execution time for metrics
577 long startTime = System.currentTimeMillis ();
579 MsoLogger.setLogContext (msoRequest);
580 MsoLogger.setServiceName ("CreateVfModule");
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,
587 "VF Module ModelCustomizationUuid", "null", CLOUDIFY, "", MsoLogger.ErrorCode.DataError, "Create VF Module: Missing required input: modelCustomizationUuid");
588 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
589 throw new VnfException(error, MsoExceptionCategory.USERDATA);
592 // Clean up some inputs to make comparisons easier
593 if (requestType == null)
596 if ("".equals(volumeGroupId) || "null".equals(volumeGroupId))
597 volumeGroupId = null;
599 if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId))
600 baseVfModuleId = null;
602 if (inputs == null) {
603 // Create an empty set of inputs
604 inputs = new HashMap<String,String>();
605 LOGGER.debug("inputs == null - setting to empty");
607 this.sendMapToDebug(inputs);
610 // Check if this is for a "Volume" module
611 boolean isVolumeRequest = false;
612 if (requestType.startsWith("VOLUME")) {
613 isVolumeRequest = true;
616 LOGGER.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = " + baseVfModuleId);
618 // Build a default rollback object (no actions performed)
619 VnfRollback vfRollback = new VnfRollback();
620 vfRollback.setCloudSiteId(cloudSiteId);
621 vfRollback.setTenantId(tenantId);
622 vfRollback.setMsoRequest(msoRequest);
623 vfRollback.setRequestType(requestType);
624 vfRollback.setIsBase(false); // Until we know better
625 vfRollback.setVolumeGroupHeatStackId(volumeGroupId);
626 vfRollback.setBaseGroupHeatStackId(baseVfModuleId);
627 vfRollback.setModelCustomizationUuid(modelCustomizationUuid);
628 vfRollback.setMode("CFY");
630 rollback.value = vfRollback; // Default rollback - no updates performed
632 // Get the VNF/VF Module definition from the Catalog DB first.
633 // There are three relevant records: VfModule, VfModuleCustomization, VnfResource
636 VnfResource vnfResource = null;
637 VfModuleCustomization vfmc = null;
640 vfmc = vfModuleCustomRepo.findByModelCustomizationUUID(modelCustomizationUuid);
643 String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + modelCustomizationUuid;
645 LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
646 "VF Module ModelCustomizationUuid", modelCustomizationUuid, "CatalogDb", "", MsoLogger.ErrorCode.DataError, error);
647 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
648 throw new VnfException(error, MsoExceptionCategory.USERDATA);
650 LOGGER.debug("Found vfModuleCust entry " + vfmc.toString());
653 // Get the vfModule and vnfResource records
654 vf = vfmc.getVfModule();
655 vnfResource = vfmc.getVfModule().getVnfResources();
657 catch (Exception e) {
659 LOGGER.debug("unhandled exception in create VF - [Query]" + e.getMessage());
660 throw new VnfException("Exception during create VF " + e.getMessage());
663 // Perform a version check against cloudSite
664 // Obtain the cloud site information where we will create the VF Module
665 Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite (cloudSiteId);
666 if (!cloudSiteOp.isPresent()) {
667 throw new VnfException (new MsoCloudSiteNotFound (cloudSiteId));
669 CloudSite cloudSite = cloudSiteOp.get();
670 MavenLikeVersioning aicV = new MavenLikeVersioning();
671 aicV.setVersion(cloudSite.getCloudVersion());
673 String vnfMin = vnfResource.getAicVersionMin();
674 String vnfMax = vnfResource.getAicVersionMax();
676 if ( (vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) ||
677 (vnfMax != null && aicV.isMoreRecentThan(vnfMax)))
680 String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUUID() + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax + " NOT supported on Cloud: " + cloudSiteId + " with AIC_Version:" + cloudSite.getCloudVersion();
681 LOGGER.error(MessageEnum.RA_CONFIG_EXC, error, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - setVersion");
683 throw new VnfException(error, MsoExceptionCategory.USERDATA);
688 DeploymentInfo cloudifyDeployment = null;
690 // First, look up to see if the VF already exists.
692 long subStartTime1 = System.currentTimeMillis ();
694 cloudifyDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, vfModuleName);
695 LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, CLOUDIFY_RESPONSE_SUCCESS, CLOUDIFY, "QueryDeployment", vfModuleName);
697 catch (MsoException me) {
698 // Failed to query the Deployment due to a cloudify exception.
699 String error = "Create VF Module: Query " + vfModuleName + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
700 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment", me);
701 LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "QueryDeployment", vfModuleName);
702 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
704 // Convert to a generic VnfException
705 me.addContext ("CreateVFModule");
706 throw new VnfException (me);
709 // More precise handling/messaging if the Module already exists
710 if (cloudifyDeployment != null && !(cloudifyDeployment.getStatus () == DeploymentStatus.NOTFOUND)) {
711 // CREATED, INSTALLED, INSTALLING, FAILED, UNINSTALLING, UNKNOWN
712 DeploymentStatus status = cloudifyDeployment.getStatus();
713 LOGGER.debug ("Found Existing Deployment, status=" + status);
715 if (status == DeploymentStatus.INSTALLED) {
717 if (failIfExists != null && failIfExists) {
718 String error = "Create VF: Deployment " + vfModuleName + " already exists in " + cloudSiteId + "/" + tenantId;
719 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists");
720 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
721 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
723 // Found existing deployment and client has not requested "failIfExists".
724 // Populate the outputs from the existing deployment.
726 vnfId.value = cloudifyDeployment.getId();
727 outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
728 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully create VF Module (found existing)");
732 // Check through various detailed error cases
733 if (status == DeploymentStatus.INSTALLING || status == DeploymentStatus.UNINSTALLING) {
734 // fail - it's in progress - return meaningful error
735 String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; please wait for it to complete, or fix manually.";
736 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists");
737 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
738 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
740 else if (status == DeploymentStatus.FAILED) {
741 // fail - it exists and is in a FAILED state
742 String error = "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
743 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists and is in FAILED state");
744 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
745 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
747 else if (status == DeploymentStatus.UNKNOWN || status == DeploymentStatus.CREATED) {
748 // fail - it exists and is in a UNKNOWN state
749 String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
750 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists and is in " + status.toString() + " state");
751 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
752 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
755 // Unexpected, since all known status values have been tested for
756 String error = "Create VF: Deployment " + vfModuleName + " already exists with unexpected status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
757 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists and is in an unknown state");
758 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
759 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
764 // Collect outputs from Base Modules and Volume Modules
765 Map<String, Object> baseModuleOutputs = null;
766 Map<String, Object> volumeGroupOutputs = null;
768 // If a Volume Group was provided, query its outputs for inclusion in Module input parameters
769 if (volumeGroupId != null) {
770 long subStartTime2 = System.currentTimeMillis ();
771 DeploymentInfo volumeDeployment = null;
773 volumeDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, volumeGroupId);
774 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Success response from Cloudify", CLOUDIFY, "QueryDeployment", volumeGroupId);
776 catch (MsoException me) {
777 // Failed to query the Volume GroupDeployment due to a cloudify exception.
778 String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
779 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, volumeGroupId, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment(volume)", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment(volume)", me);
780 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "QueryDeployment(volume)", volumeGroupId);
781 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
783 // Convert to a generic VnfException
784 me.addContext ("CreateVFModule(QueryVolume)");
785 throw new VnfException (me);
788 if (volumeDeployment == null || volumeDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
789 String error = "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR" ;
790 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, volumeGroupId, cloudSiteId, tenantId, error, CLOUDIFY, "queryDeployment(volume)", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: Attached Volume Group DOES NOT EXIST");
791 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
793 throw new VnfException (error, MsoExceptionCategory.USERDATA);
795 LOGGER.debug("Found nested volume group");
796 volumeGroupOutputs = volumeDeployment.getOutputs();
797 this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs");
801 // If this is an Add-On Module, query the Base Module outputs
802 // Note: This will be performed whether or not the current request is for an
803 // Add-On Volume Group or Add-On VF Module
805 if (vf.getIsBase()) {
806 LOGGER.debug("This is a BASE Module request");
807 vfRollback.setIsBase(true);
809 LOGGER.debug("This is an Add-On Module request");
811 // Add-On Modules should always have a Base, but just treat as a warning if not provided.
812 // Add-on Volume requests may or may not specify a base.
813 if (!isVolumeRequest && baseVfModuleId == null) {
814 LOGGER.debug ("WARNING: Add-on Module request - no Base Module ID provided");
817 if (baseVfModuleId != null) {
818 long subStartTime2 = System.currentTimeMillis ();
819 DeploymentInfo baseDeployment = null;
821 baseDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, baseVfModuleId);
822 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Success response from Cloudify", CLOUDIFY, "QueryDeployment(Base)", baseVfModuleId);
824 catch (MsoException me) {
825 // Failed to query the Volume GroupDeployment due to a cloudify exception.
826 String error = "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
827 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, baseVfModuleId, cloudSiteId, tenantId, CLOUDIFY, "queryDeployment(Base)", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment(Base)", me);
828 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "QueryDeployment(Base)", baseVfModuleId);
829 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
831 // Convert to a generic VnfException
832 me.addContext ("CreateVFModule(QueryBase)");
833 throw new VnfException (me);
836 if (baseDeployment == null || baseDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
837 String error = "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR" ;
838 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, baseVfModuleId, cloudSiteId, tenantId, error, CLOUDIFY, "queryDeployment(Base)", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: Base Module DOES NOT EXIST");
839 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
841 throw new VnfException (error, MsoExceptionCategory.USERDATA);
843 LOGGER.debug("Found base module");
844 baseModuleOutputs = baseDeployment.getOutputs();
845 this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs");
851 // Ready to deploy the new VNF
853 // NOTE: For this section, heatTemplate is used for both HEAT templates and Cloudify blueprints.
854 // In final implementation (post-POC), the template object would either be generic or there would
855 // be a separate DB Table/Object for Blueprints.
858 // NOTE: The template is fixed for the VF Module. The environment is part of the customization.
859 HeatTemplate heatTemplate = null;
860 HeatEnvironment heatEnvironment = null;
861 if (isVolumeRequest) {
862 heatTemplate = vf.getVolumeHeatTemplate();
863 heatEnvironment = vfmc.getVolumeHeatEnv();
865 heatTemplate = vf.getModuleHeatTemplate();
866 heatEnvironment = vfmc.getHeatEnvironment();
869 if (heatTemplate == null) {
870 String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType=" + requestType;
871 LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Template ID", vfModuleType, "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
872 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
873 alarmLogger.sendAlarm(MSO_CONFIGURATION_ERROR,
874 MsoAlarmLogger.CRITICAL, error);
875 throw new VnfException(error, MsoExceptionCategory.INTERNAL);
877 LOGGER.debug ("Got HEAT Template from DB: " + heatTemplate.getHeatTemplate());
880 if (heatEnvironment == null) {
881 String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
882 LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
883 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
884 // Alarm on this error, configuration must be fixed
885 alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
887 throw new VnfException (error, MsoExceptionCategory.INTERNAL);
889 LOGGER.debug ("Got Heat Environment from DB: " + heatEnvironment.getEnvironment());
894 // All variables converted to their native object types
895 HashMap<String, Object> goldenInputs = new HashMap<String,Object>();
896 List<String> extraInputs = new ArrayList<String>();
898 // NOTE: SKIP THIS FOR CLOUDIFY for now. Just use what was passed in.
899 // This whole section needs to be rewritten.
900 Boolean skipInputChecks = false;
902 if (skipInputChecks) {
903 goldenInputs = new HashMap<String,Object>();
904 for (String key : inputs.keySet()) {
905 goldenInputs.put(key, inputs.get(key));
909 // Build maps for the parameters (including aliases) to simplify checks
910 HashMap<String, HeatTemplateParam> params = new HashMap<String, HeatTemplateParam>();
912 Set<HeatTemplateParam> paramSet = heatTemplate.getParameters();
913 LOGGER.debug("paramSet has " + paramSet.size() + " entries");
915 for (HeatTemplateParam htp : paramSet) {
916 params.put(htp.getParamName(), htp);
919 String alias = htp.getParamAlias();
920 if (alias != null && !alias.equals("") && !params.containsKey(alias)) {
921 params.put(alias, htp);
925 // First, convert all inputs to their "template" type
926 for (String key : inputs.keySet()) {
927 if (params.containsKey(key)) {
928 Object value = cloudifyUtils.convertInputValue(inputs.get(key), params.get(key));
930 goldenInputs.put(key, value);
933 LOGGER.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to " + params.get(key).getParamType());
936 extraInputs.add(key);
940 if (!extraInputs.isEmpty()) {
941 LOGGER.debug("Ignoring extra inputs: " + extraInputs);
944 // Next add in Volume Group Outputs if there are any. Copy directly without conversions.
945 if (volumeGroupOutputs != null && !volumeGroupOutputs.isEmpty()) {
946 for (String key : volumeGroupOutputs.keySet()) {
947 if (params.containsKey(key) && !goldenInputs.containsKey(key)) {
948 goldenInputs.put(key, volumeGroupOutputs.get(key));
953 // Next add in Base Module Outputs if there are any. Copy directly without conversions.
954 if (baseModuleOutputs != null && !baseModuleOutputs.isEmpty()) {
955 for (String key : baseModuleOutputs.keySet()) {
956 if (params.containsKey(key) && !goldenInputs.containsKey(key)) {
957 goldenInputs.put(key, baseModuleOutputs.get(key));
962 // Last, add in values from the "environment" file.
963 // These are added to the inputs, since Cloudify doesn't pass an environment file like Heat.
965 // TODO: This may take a different form for Cloudify, but for now process it
966 // with Heat environment file syntax
967 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
968 MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry (sb);
970 if (mhee.getParameters() != null) {
971 for (MsoHeatEnvironmentParameter envParam : mhee.getParameters()) {
972 // If this is a template input, copy to golden inputs
973 String envKey = envParam.getName();
974 if (params.containsKey(envKey) && !goldenInputs.containsKey(envKey)) {
975 Object value = cloudifyUtils.convertInputValue(envParam.getValue(), params.get(envKey));
977 goldenInputs.put(envKey, value);
980 LOGGER.debug("Failed to convert environment parameter " + envKey + "='" + envParam.getValue() + "' to " + params.get(envKey).getParamType());
986 this.sendMapToDebug(goldenInputs, "Final inputs sent to Cloudify");
989 // Check that required parameters have been supplied from any of the sources
990 String missingParams = null;
991 boolean checkRequiredParameters = true;
993 String propertyString = this.environment.getProperty(MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
994 if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
995 checkRequiredParameters = false;
996 LOGGER.debug ("CheckRequiredParameters is FALSE. Will still check but then skip blocking..."
997 + MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
999 } catch (Exception e) {
1000 // No problem - default is true
1001 LOGGER.debug ("An exception occured trying to get property " + MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS, e);
1005 for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
1006 if (parm.isRequired () && (!goldenInputs.containsKey (parm.getParamName ()))) {
1007 LOGGER.debug ("adding to missing parameters list: " + parm.getParamName ());
1008 if (missingParams == null) {
1009 missingParams = parm.getParamName ();
1011 missingParams += "," + parm.getParamName ();
1016 if (missingParams != null) {
1017 if (checkRequiredParameters) {
1018 // Problem - missing one or more required parameters
1019 String error = "Create VFModule: Missing Required inputs: " + missingParams;
1020 LOGGER.error (MessageEnum.RA_MISSING_PARAM, missingParams, CLOUDIFY, "", MsoLogger.ErrorCode.DataError, "Create VFModule: Missing Required inputs");
1021 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
1022 throw new VnfException (error, MsoExceptionCategory.USERDATA);
1024 LOGGER.debug ("found missing parameters [" + missingParams + "] - but checkRequiredParameters is false - will not block");
1027 LOGGER.debug ("No missing parameters found - ok to proceed");
1030 } // NOTE: END PARAMETER CHECKING
1032 // Ready to deploy the VF Module.
1033 // *First step - make sure the blueprint is loaded into Cloudify.
1034 String blueprintName = heatTemplate.getTemplateName();
1035 String blueprint = heatTemplate.getTemplateBody();
1036 String blueprintId = blueprintName;
1038 // Use the main blueprint name as the blueprint ID (strip yaml extensions).
1039 if (blueprintId.endsWith(".yaml"))
1040 blueprintId = blueprintId.substring(0,blueprintId.lastIndexOf(".yaml"));
1043 if (! cloudifyUtils.isBlueprintLoaded (cloudSiteId, blueprintId)) {
1044 LOGGER.debug ("Blueprint " + blueprintId + " is not loaded. Will upload it now.");
1046 Map<String,byte[]> blueprintFiles = new HashMap<String,byte[]>();
1048 blueprintFiles.put(blueprintName, blueprint.getBytes());
1050 // TODO: Implement nested blueprint logic based on Cloudify structures.
1051 // For now, just use the Heat structures.
1052 // The query returns a map of String->Object, where the map keys provide one layer of
1053 // indirection from the Heat template names. For this case, assume the map key matches
1054 // the nested blueprint name.
1055 List<HeatTemplate> nestedBlueprints = heatTemplate.getChildTemplates();
1056 if (nestedBlueprints != null) {
1057 for (HeatTemplate nestedBlueprint: nestedBlueprints) {
1058 blueprintFiles.put(nestedBlueprint.getTemplateName(), nestedBlueprint.getTemplateBody().getBytes());
1062 // TODO: Implement file artifact logic based on Cloudify structures.
1063 // For now, just use the Heat structures.
1064 List<HeatFiles> heatFiles = vf.getHeatFiles();
1065 if (heatFiles != null) {
1066 for (HeatFiles heatFile: heatFiles) {
1067 blueprintFiles.put(heatFile.getFileName(), heatFile.getFileBody().getBytes());
1071 // Upload the blueprint package
1072 cloudifyUtils.uploadBlueprint(cloudSiteId, blueprintId, blueprintName, blueprintFiles, false);
1077 catch (MsoException me) {
1078 me.addContext ("CreateVFModule");
1079 String error = "Create VF Module: Upload blueprint failed. Blueprint=" + blueprintName + ": " + me;
1080 LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, CLOUDIFY, "", MsoLogger.ErrorCode.DataError, "MsoException - uploadBlueprint", me);
1081 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1082 throw new VnfException (me);
1086 // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1087 // because we already checked for those.
1088 long createDeploymentStarttime = System.currentTimeMillis ();
1090 // KLUDGE - Cloudify requires Tenant Name for Openstack. We have the ID.
1091 // Go directly to Keystone until APIs could be updated to supply the name.
1092 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
1093 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
1095 if (backout == null) {
1099 cloudifyDeployment = cloudifyUtils.createAndInstallDeployment (cloudSiteId,
1105 heatTemplate.getTimeoutMinutes (),
1106 backout.booleanValue());
1108 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, CLOUDIFY_RESPONSE_SUCCESS, CLOUDIFY, "CreateDeployment", vfModuleName);
1109 } catch (MsoException me) {
1110 me.addContext ("CreateVFModule");
1111 String error = "Create VF Module " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1112 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "CreateDeployment", vfModuleName);
1113 LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, CLOUDIFY, "", MsoLogger.ErrorCode.DataError, "MsoException - createDeployment", me);
1114 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1115 throw new VnfException (me);
1116 } catch (NullPointerException npe) {
1117 String error = "Create VFModule " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + npe;
1118 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "CreateDeployment", vfModuleName);
1119 LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, CLOUDIFY, "", MsoLogger.ErrorCode.DataError, "NullPointerException - createDeployment", npe);
1120 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1121 LOGGER.debug("NULL POINTER EXCEPTION at cloudify.createAndInstallDeployment");
1122 //npe.addContext ("CreateVNF");
1123 throw new VnfException ("NullPointerException during cloudify.createAndInstallDeployment");
1124 } catch (Exception e) {
1125 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while creating deployment with Cloudify", CLOUDIFY, "CreateDeployment", vfModuleName);
1126 LOGGER.debug("unhandled exception at cloudify.createAndInstallDeployment");
1127 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while creating deployment with Cloudify");
1128 throw new VnfException("Exception during cloudify.createAndInstallDeployment! " + e.getMessage());
1130 } catch (Exception e) {
1131 LOGGER.debug("unhandled exception in create VF");
1132 throw new VnfException("Exception during create VF " + e.getMessage());
1136 // Reach this point if create is successful.
1137 // Populate remaining rollback info and response parameters.
1138 vfRollback.setVnfCreated (true);
1139 vfRollback.setVnfId (cloudifyDeployment.getId());
1140 vnfId.value = cloudifyDeployment.getId();
1141 outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
1143 rollback.value = vfRollback;
1145 LOGGER.debug ("VF Module " + vfModuleName + " successfully created");
1146 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully create VF Module");
1150 public void deleteVfModule (String cloudSiteId,
1153 MsoRequest msoRequest,
1154 Holder <Map <String, String>> outputs) throws VnfException {
1155 MsoLogger.setLogContext (msoRequest);
1156 MsoLogger.setServiceName ("DeleteVf");
1157 LOGGER.debug ("Deleting VF " + vnfName + " in " + cloudSiteId + "/" + tenantId);
1158 // Will capture execution time for metrics
1159 long startTime = System.currentTimeMillis ();
1161 // 1702 capture the output parameters on a delete
1162 // so we'll need to query first
1163 DeploymentInfo deployment = null;
1165 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
1166 } catch (MsoException me) {
1167 // Failed to query the deployment. Convert to a generic VnfException
1168 me.addContext ("DeleteVFModule");
1169 String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1170 LOGGER.recordMetricEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, CLOUDIFY, "QueryDeployment", null);
1171 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, CLOUDIFY, "QueryDeployment", MsoLogger.ErrorCode.DataError, "Exception - QueryDeployment", me);
1172 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1173 throw new VnfException (me);
1175 // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected Object types
1176 outputs.value = convertMapStringObjectToStringString(deployment.getOutputs());
1178 // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
1179 // The possible outcomes of deleteStack are a StackInfo object with status
1180 // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
1182 long subStartTime = System.currentTimeMillis ();
1184 cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantId, vnfName, 5);
1185 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from DeleteDeployment", CLOUDIFY, "DeleteDeployment", vnfName);
1186 } catch (MsoException me) {
1187 me.addContext ("DeleteVfModule");
1188 // Convert to a generic VnfException
1189 String error = "Delete VF: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1190 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "DeleteDeployment", "DeleteDeployment", vnfName);
1191 LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vnfName, cloudSiteId, tenantId, "DeleteDeployment", "DeleteDeployment", MsoLogger.ErrorCode.DataError, "Exception - DeleteDeployment: " + me.getMessage());
1192 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1193 throw new VnfException (me);
1196 // On success, nothing is returned.
1197 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully delete VF");
1201 // TODO: Should Update be supported for Cloudify? What would this look like?
1203 public void updateVfModule (String cloudSiteId,
1209 String volumeGroupHeatStackId,
1210 String baseVfHeatStackId,
1211 String vfModuleStackId,
1212 String modelCustomizationUuid,
1213 Map <String, String> inputs,
1214 MsoRequest msoRequest,
1215 Holder <Map <String, String>> outputs,
1216 Holder <VnfRollback> rollback) throws VnfException
1218 // This operation is not currently supported for Cloudify-orchestrated VF Modules.
1219 LOGGER.debug ("Update VF Module command attempted but not supported");
1220 throw new VnfException ("UpdateVfModule: Unsupported command", MsoExceptionCategory.USERDATA);