2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.so.adapters.vnf;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Optional;
31 import javax.jws.WebService;
32 import javax.xml.ws.Holder;
34 import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists;
35 import org.onap.so.adapters.vnf.exceptions.VnfException;
36 import org.onap.so.cloud.CloudConfig;
37 import org.onap.so.cloud.CloudSite;
38 import org.onap.so.cloudify.beans.DeploymentInfo;
39 import org.onap.so.cloudify.beans.DeploymentStatus;
40 import org.onap.so.cloudify.exceptions.MsoCloudifyManagerNotFound;
41 import org.onap.so.cloudify.utils.MsoCloudifyUtils;
42 import org.onap.so.db.catalog.beans.HeatEnvironment;
43 import org.onap.so.db.catalog.beans.HeatFiles;
44 import org.onap.so.db.catalog.beans.HeatTemplate;
45 import org.onap.so.db.catalog.beans.HeatTemplateParam;
46 import org.onap.so.db.catalog.beans.VfModule;
47 import org.onap.so.db.catalog.beans.VfModuleCustomization;
48 import org.onap.so.db.catalog.beans.VnfResource;
49 import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository;
50 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
51 import org.onap.so.entity.MsoRequest;
52 import org.onap.so.logger.MessageEnum;
53 import org.onap.so.logger.MsoAlarmLogger;
54 import org.onap.so.logger.MsoLogger;
55 import org.onap.so.openstack.beans.MsoTenant;
56 import org.onap.so.openstack.beans.VnfRollback;
57 import org.onap.so.openstack.beans.VnfStatus;
58 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
59 import org.onap.so.openstack.exceptions.MsoException;
60 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
61 import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry;
62 import org.onap.so.openstack.utils.MsoHeatEnvironmentParameter;
63 import org.onap.so.openstack.utils.MsoKeystoneUtils;
64 import org.springframework.beans.factory.annotation.Autowired;
65 import org.springframework.core.env.Environment;
66 import org.springframework.stereotype.Component;
68 import com.fasterxml.jackson.core.JsonParseException;
69 import com.fasterxml.jackson.databind.JsonNode;
70 import com.fasterxml.jackson.databind.ObjectMapper;
71 import org.springframework.transaction.annotation.Transactional;
75 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter", targetNamespace = "http://org.onap.so/vnf")
76 public class MsoVnfCloudifyAdapterImpl implements MsoVnfAdapter {
78 private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
79 private static final String VNF_ADAPTER_SERVICE_NAME = "MSO-BPMN:MSO-VnfAdapter.";
80 private static final String LOG_REPLY_NAME = "MSO-VnfAdapter:MSO-BPMN.";
81 private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA, MsoVnfCloudifyAdapterImpl.class);
82 private static MsoAlarmLogger alarmLogger = new MsoAlarmLogger ();
83 private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
84 private static final String ADD_GET_FILES_ON_VOLUME_REQ = "org.onap.so.adapters.vnf.addGetFilesOnVolumeReq";
85 private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
88 protected CloudConfig cloudConfig;
91 private VFModuleCustomizationRepository vfModuleCustomRepo;
94 private Environment environment;
97 protected MsoKeystoneUtils keystoneUtils;
100 protected MsoCloudifyUtils cloudifyUtils;
102 * Health Check web method. Does nothing but return to show the adapter is deployed.
105 public void healthCheck () {
106 LOGGER.debug ("Health check call in VNF Cloudify Adapter");
110 * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
111 * @see MsoVnfCloudifyAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
113 public MsoVnfCloudifyAdapterImpl() {
118 * This is the "Create VNF" web service implementation.
119 * This function is now unsupported and will return an error.
123 public void createVnf (String cloudSiteId,
129 String volumeGroupHeatStackId,
130 Map <String, String> inputs,
131 Boolean failIfExists,
133 Boolean enableBridge,
134 MsoRequest msoRequest,
135 Holder <String> vnfId,
136 Holder <Map <String, String>> outputs,
137 Holder <VnfRollback> rollback)
140 // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
141 LOGGER.debug ("CreateVNF command attempted but not supported");
142 throw new VnfException ("CreateVNF: Unsupported command", MsoExceptionCategory.USERDATA);
146 * This is the "Update VNF" web service implementation.
147 * This function is now unsupported and will return an error.
151 public void updateVnf (String cloudSiteId,
157 String volumeGroupHeatStackId,
158 Map <String, String> inputs,
159 MsoRequest msoRequest,
160 Holder <Map <String, String>> outputs,
161 Holder <VnfRollback> rollback)
164 // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
165 LOGGER.debug ("UpdateVNF command attempted but not supported");
166 throw new VnfException ("UpdateVNF: Unsupported command", MsoExceptionCategory.USERDATA);
170 * This is the "Query VNF" web service implementation.
172 * This really should be QueryVfModule, but nobody ever changed it.
174 * For Cloudify, this will look up a deployment by its deployment ID, which is really the same
175 * as deployment name, since it assigned by the client when a deployment is created.
176 * Also, the input cloudSiteId is used only to identify which Cloudify instance to query,
177 * and the tenantId is ignored (since that really only applies for Openstack/Heat).
179 * The method returns an indicator that the VNF exists, along with its status and outputs.
180 * The input "vnfName" will also be reflected back as its ID.
182 * @param cloudSiteId CLLI code of the cloud site in which to query
183 * @param tenantId Openstack tenant identifier - ignored for Cloudify
184 * @param vnfName VNF Name (should match a deployment ID)
185 * @param msoRequest Request tracking information for logs
186 * @param vnfExists Flag reporting the result of the query
187 * @param vnfId Holder for output VNF ID
188 * @param outputs Holder for Map of VNF outputs from Cloudify deployment (assigned IPs, etc)
191 public void queryVnf (String cloudSiteId,
194 MsoRequest msoRequest,
195 Holder <Boolean> vnfExists,
196 Holder <String> vnfId,
197 Holder <VnfStatus> status,
198 Holder <Map <String, String>> outputs)
201 MsoLogger.setLogContext (msoRequest);
202 MsoLogger.setServiceName ("QueryVnfCloudify");
203 LOGGER.debug ("Querying VNF " + vnfName + " in " + cloudSiteId + "/" + tenantId);
205 // Will capture execution time for metrics
206 long startTime = System.currentTimeMillis ();
207 long subStartTime = System.currentTimeMillis ();
209 DeploymentInfo deployment = null;
212 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
213 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Cloudify", "Cloudify", "QueryDeployment", vnfName);
215 catch (MsoCloudifyManagerNotFound e) {
216 // This site does not have a Cloudify Manager.
217 // This isn't an error, just means we won't find the VNF here.
220 catch (MsoException me) {
221 // Failed to query the Deployment due to a cloudify exception.
222 // Convert to a generic VnfException
223 me.addContext ("QueryVNF");
224 String error = "Query VNF (Cloudify): " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
225 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "QueryDeployment", vnfName);
226 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, "Cloudify", "QueryVNF", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment", me);
227 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
228 throw new VnfException (me);
231 if (deployment != null && deployment.getStatus() != DeploymentStatus.NOTFOUND) {
232 vnfExists.value = Boolean.TRUE;
233 status.value = deploymentStatusToVnfStatus(deployment);
234 vnfId.value = deployment.getId();
235 outputs.value = copyStringOutputs (deployment.getOutputs ());
237 LOGGER.debug ("VNF " + vnfName + " found in Cloudify, ID = " + vnfId.value);
240 vnfExists.value = Boolean.FALSE;
241 status.value = VnfStatus.NOTFOUND;
243 outputs.value = new HashMap <String, String> (); // Return as an empty map
245 LOGGER.debug ("VNF " + vnfName + " not found");
247 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully query VNF");
253 * This is the "Delete VNF" web service implementation.
254 * This function is now unsupported and will return an error.
258 public void deleteVnf (String cloudSiteId,
261 MsoRequest msoRequest) throws VnfException {
262 MsoLogger.setLogContext (msoRequest);
263 MsoLogger.setServiceName ("DeleteVnf");
265 // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules.
266 LOGGER.debug ("DeleteVNF command attempted but not supported");
267 throw new VnfException ("DeleteVNF: Unsupported command", MsoExceptionCategory.USERDATA);
271 * This web service endpoint will rollback a previous Create VNF operation.
272 * A rollback object is returned to the client in a successful creation
273 * response. The client can pass that object as-is back to the rollbackVnf
274 * operation to undo the creation.
276 * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup,
277 * but APIs were apparently never updated.
280 public void rollbackVnf (VnfRollback rollback) throws VnfException {
281 long startTime = System.currentTimeMillis ();
282 MsoLogger.setServiceName ("RollbackVnf");
283 // rollback may be null (e.g. if stack already existed when Create was called)
284 if (rollback == null) {
285 LOGGER.info (MessageEnum.RA_ROLLBACK_NULL, "OpenStack", "rollbackVnf", MsoLogger.getServiceName());
286 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, "Rollback request content is null");
290 // Don't rollback if nothing was done originally
291 if (!rollback.getVnfCreated()) {
292 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Rollback VF Module - nothing to roll back");
296 // Get the elements of the VnfRollback object for easier access
297 String cloudSiteId = rollback.getCloudSiteId ();
298 String tenantId = rollback.getTenantId ();
299 String vfModuleId = rollback.getVfModuleStackId ();
301 MsoLogger.setLogContext (rollback.getMsoRequest());
303 LOGGER.debug ("Rolling Back VF Module " + vfModuleId + " in " + cloudSiteId + "/" + tenantId);
305 DeploymentInfo deployment = null;
307 // Use the MsoCloudifyUtils to delete the deployment. Set the polling flag to true.
308 // The possible outcomes of deleteStack are a StackInfo object with status
309 // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
311 long subStartTime = System.currentTimeMillis ();
313 // KLUDGE - Cloudify requires Tenant Name for Openstack. We have the ID.
314 // Go directly to Keystone until APIs could be updated to supply the name.
315 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
316 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
318 // TODO: Get a reasonable timeout. Use a global property, or store the creation timeout in rollback object and use that.
319 deployment = cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantName, vfModuleId, 5);
320 LOGGER.debug("Rolled back deployment: " + deployment.getId());
321 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Cloudify", "Cloudify", "DeleteDeployment", null);
322 } catch (MsoException me) {
323 // Failed to rollback the VNF due to a cloudify exception.
324 // Convert to a generic VnfException
325 me.addContext ("RollbackVNF");
326 String error = "Rollback VF Module: " + vfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + me;
327 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "DeleteDeployment", null);
328 LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vfModuleId, cloudSiteId, tenantId, "Cloudify", "DeleteDeployment", MsoLogger.ErrorCode.DataError, "Exception - DeleteDeployment", me);
329 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
330 throw new VnfException (me);
332 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully roll back VF Module");
337 private VnfStatus deploymentStatusToVnfStatus (DeploymentInfo deployment) {
338 // Determine the status based on last action & status
339 // DeploymentInfo object should be enhanced to report a better status internally.
340 DeploymentStatus status = deployment.getStatus();
341 String lastAction = deployment.getLastAction();
343 if (status == null || lastAction == null) {
344 return VnfStatus.UNKNOWN;
346 else if (status == DeploymentStatus.NOTFOUND) {
347 return VnfStatus.NOTFOUND;
349 else if (status == DeploymentStatus.INSTALLED) {
350 return VnfStatus.ACTIVE;
352 else if (status == DeploymentStatus.CREATED) {
353 // Should have an INACTIVE status for this case. Shouldn't really happen, but
354 // Install was never run, or Uninstall was done but deployment didn't get deleted.
355 return VnfStatus.UNKNOWN;
357 else if (status == DeploymentStatus.FAILED) {
358 return VnfStatus.FAILED;
361 return VnfStatus.UNKNOWN;
364 private Map <String, String> copyStringOutputs (Map <String, Object> stackOutputs) {
365 Map <String, String> stringOutputs = new HashMap <String, String> ();
366 for (String key : stackOutputs.keySet ()) {
367 if (stackOutputs.get (key) instanceof String) {
368 stringOutputs.put (key, (String) stackOutputs.get (key));
369 } else if (stackOutputs.get(key) instanceof Integer) {
371 String str = "" + stackOutputs.get(key);
372 stringOutputs.put(key, str);
373 } catch (Exception e) {
374 LOGGER.debug("Unable to add " + key + " to outputs");
376 } else if (stackOutputs.get(key) instanceof JsonNode) {
378 String str = this.convertNode((JsonNode) stackOutputs.get(key));
379 stringOutputs.put(key, str);
380 } catch (Exception e) {
381 LOGGER.debug("Unable to add " + key + " to outputs - exception converting JsonNode");
383 } else if (stackOutputs.get(key) instanceof java.util.LinkedHashMap) {
385 String str = JSON_MAPPER.writeValueAsString(stackOutputs.get(key));
386 stringOutputs.put(key, str);
387 } catch (Exception e) {
388 LOGGER.debug("Unable to add " + key + " to outputs - exception converting LinkedHashMap");
392 String str = stackOutputs.get(key).toString();
393 stringOutputs.put(key, str);
394 } catch (Exception e) {
395 LOGGER.debug("Unable to add " + key + " to outputs - unable to call .toString() " + e.getMessage());
399 return stringOutputs;
403 private void sendMapToDebug(Map<String, Object> inputs, String optionalName) {
405 StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName);
406 if (inputs == null) {
409 else if (inputs.size() < 1) {
410 sb.append("\tEMPTY");
412 for (String str : inputs.keySet()) {
415 outputString = inputs.get(str).toString();
416 } catch (Exception e) {
417 outputString = "Unable to call toString() on the value for " + str;
419 sb.append("\t\nitem " + i++ + ": '" + str + "'='" + outputString + "'");
422 LOGGER.debug(sb.toString());
426 private void sendMapToDebug(Map<String, String> inputs) {
428 StringBuilder sb = new StringBuilder("inputs:");
429 if (inputs == null) {
432 else if (inputs.size() < 1) {
433 sb.append("\tEMPTY");
435 for (String str : inputs.keySet()) {
436 sb.append("\titem " + i++ + ": " + str + "=" + inputs.get(str));
439 LOGGER.debug(sb.toString());
443 private String convertNode(final JsonNode node) {
445 final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
446 final String json = JSON_MAPPER.writeValueAsString(obj);
448 } catch (JsonParseException jpe) {
449 LOGGER.debug("Error converting json to string " + jpe.getMessage());
450 } catch (Exception e) {
451 LOGGER.debug("Error converting json to string " + e.getMessage());
453 return "[Error converting json to string]";
456 private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
457 if (objectMap == null) {
460 Map<String, String> stringMap = new HashMap<String, String>();
461 for (String key : objectMap.keySet()) {
462 if (!stringMap.containsKey(key)) {
463 Object obj = objectMap.get(key);
464 if (obj instanceof String) {
465 stringMap.put(key, (String) objectMap.get(key));
466 } else if (obj instanceof JsonNode ){
467 // This is a bit of mess - but I think it's the least impacting
468 // let's convert it BACK to a string - then it will get converted back later
470 String str = this.convertNode((JsonNode) obj);
471 stringMap.put(key, str);
472 } catch (Exception e) {
473 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode "+ key);
474 //okay in this instance - only string values (fqdn) are expected to be needed
476 } else if (obj instanceof java.util.LinkedHashMap) {
477 LOGGER.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
479 String str = JSON_MAPPER.writeValueAsString(obj);
480 stringMap.put(key, str);
481 } catch (Exception e) {
482 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap "+ key);
484 } else if (obj instanceof Integer) {
486 String str = "" + obj;
487 stringMap.put(key, str);
488 } catch (Exception e) {
489 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for Integer "+ key);
493 String str = obj.toString();
494 stringMap.put(key, str);
495 } catch (Exception e) {
496 LOGGER.debug("DANGER WILL ROBINSON: unable to convert value "+ key + " (" + e.getMessage() + ")");
506 * This is the "Create VF Module" web service implementation.
507 * It will instantiate a new VF Module of the requested type in the specified cloud
508 * and tenant. The tenant must exist before this service is called.
510 * If a VF Module with the same name already exists, this can be considered a
511 * success or failure, depending on the value of the 'failIfExists' parameter.
513 * All VF Modules are defined in the MSO catalog. The caller must request
514 * one of the pre-defined module types or an error will be returned. Within the
515 * catalog, each VF Module references (among other things) a cloud template
516 * which is used to deploy the required artifacts (VMs, networks, etc.)
517 * to the cloud. In this adapter implementation, that artifact is expected
518 * to be a Cloudify blueprint.
520 * Depending on the blueprint, a variable set of input parameters will
521 * be defined, some of which are required. The caller is responsible to
522 * pass the necessary input data for the module or an error will be thrown.
524 * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback
525 * object. This last object can be passed as-is to the rollbackVnf operation to
526 * undo everything that was created for the Module. This is useful if a VF module
527 * is successfully created but the orchestration fails on a subsequent step.
529 * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
530 * @param tenantId Openstack tenant identifier
531 * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB.
532 * Deprecated - should use modelCustomizationUuid
533 * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
534 * Deprecated - VF Module versions also captured by modelCustomizationUuid
535 * @param vfModuleName Name to be assigned to the new VF Module
536 * @param requestType Indicates if this is a Volume Group or Module request
537 * @param volumeGroupId Identifier (i.e. deployment ID) for a Volume Group
538 * to attach to a VF Module
539 * @param baseVfModuleId Identifier (i.e. deployment ID) of the Base Module if
540 * this is an Add-on module
541 * @param modelCustomizationUuid Unique ID for the VF Module's model. Replaces
542 * the use of vfModuleType.
543 * @param inputs Map of key=value inputs for VNF stack creation
544 * @param failIfExists Flag whether already existing VNF should be considered
545 * @param backout Flag whether to suppress automatic backout (for testing)
546 * @param msoRequest Request tracking information for logs
547 * @param vnfId Holder for output VNF Cloudify Deployment ID
548 * @param outputs Holder for Map of VNF outputs from Deployment (assigned IPs, etc)
549 * @param rollback Holder for returning VnfRollback object
552 public void createVfModule(String cloudSiteId,
558 String volumeGroupId,
559 String baseVfModuleId,
560 String modelCustomizationUuid,
561 Map <String, String> inputs,
562 Boolean failIfExists,
564 Boolean enableBridge,
565 MsoRequest msoRequest,
566 Holder <String> vnfId,
567 Holder <Map <String, String>> outputs,
568 Holder <VnfRollback> rollback)
571 // Will capture execution time for metrics
572 long startTime = System.currentTimeMillis ();
574 MsoLogger.setLogContext (msoRequest);
575 MsoLogger.setServiceName ("CreateVfModule");
577 // Require a model customization ID. Every VF Module definition must have one.
578 if (modelCustomizationUuid == null || modelCustomizationUuid.isEmpty()) {
579 LOGGER.debug("Missing required input: modelCustomizationUuid");
580 String error = "Create vfModule error: Missing required input: modelCustomizationUuid";
581 LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
582 "VF Module ModelCustomizationUuid", "null", "Cloudify", "", MsoLogger.ErrorCode.DataError, "Create VF Module: Missing required input: modelCustomizationUuid");
583 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
584 throw new VnfException(error, MsoExceptionCategory.USERDATA);
587 // Clean up some inputs to make comparisons easier
588 if (requestType == null)
591 if ("".equals(volumeGroupId) || "null".equals(volumeGroupId))
592 volumeGroupId = null;
594 if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId))
595 baseVfModuleId = null;
597 if (inputs == null) {
598 // Create an empty set of inputs
599 inputs = new HashMap<String,String>();
600 LOGGER.debug("inputs == null - setting to empty");
602 this.sendMapToDebug(inputs);
605 // Check if this is for a "Volume" module
606 boolean isVolumeRequest = false;
607 if (requestType.startsWith("VOLUME")) {
608 isVolumeRequest = true;
611 LOGGER.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = " + baseVfModuleId);
613 // Build a default rollback object (no actions performed)
614 VnfRollback vfRollback = new VnfRollback();
615 vfRollback.setCloudSiteId(cloudSiteId);
616 vfRollback.setTenantId(tenantId);
617 vfRollback.setMsoRequest(msoRequest);
618 vfRollback.setRequestType(requestType);
619 vfRollback.setIsBase(false); // Until we know better
620 vfRollback.setVolumeGroupHeatStackId(volumeGroupId);
621 vfRollback.setBaseGroupHeatStackId(baseVfModuleId);
622 vfRollback.setModelCustomizationUuid(modelCustomizationUuid);
623 vfRollback.setMode("CFY");
625 rollback.value = vfRollback; // Default rollback - no updates performed
627 // Get the VNF/VF Module definition from the Catalog DB first.
628 // There are three relevant records: VfModule, VfModuleCustomization, VnfResource
631 VnfResource vnfResource = null;
632 VfModuleCustomization vfmc = null;
635 vfmc = vfModuleCustomRepo.findByModelCustomizationUUID(modelCustomizationUuid);
638 String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + modelCustomizationUuid;
640 LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
641 "VF Module ModelCustomizationUuid", modelCustomizationUuid, "CatalogDb", "", MsoLogger.ErrorCode.DataError, error);
642 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
643 throw new VnfException(error, MsoExceptionCategory.USERDATA);
645 LOGGER.debug("Found vfModuleCust entry " + vfmc.toString());
648 // Get the vfModule and vnfResource records
649 vf = vfmc.getVfModule();
650 vnfResource = vfmc.getVfModule().getVnfResources();
652 catch (Exception e) {
654 LOGGER.debug("unhandled exception in create VF - [Query]" + e.getMessage());
655 throw new VnfException("Exception during create VF " + e.getMessage());
658 // Perform a version check against cloudSite
659 // Obtain the cloud site information where we will create the VF Module
660 Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite (cloudSiteId);
661 if (!cloudSiteOp.isPresent()) {
662 throw new VnfException (new MsoCloudSiteNotFound (cloudSiteId));
664 CloudSite cloudSite = cloudSiteOp.get();
665 MavenLikeVersioning aicV = new MavenLikeVersioning();
666 aicV.setVersion(cloudSite.getAicVersion());
668 String vnfMin = vnfResource.getAicVersionMin();
669 String vnfMax = vnfResource.getAicVersionMax();
671 if ( (vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) ||
672 (vnfMax != null && aicV.isMoreRecentThan(vnfMax)))
675 String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUUID() + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax + " NOT supported on Cloud: " + cloudSiteId + " with AIC_Version:" + cloudSite.getAicVersion();
676 LOGGER.error(MessageEnum.RA_CONFIG_EXC, error, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - setVersion");
678 throw new VnfException(error, MsoExceptionCategory.USERDATA);
683 DeploymentInfo cloudifyDeployment = null;
685 // First, look up to see if the VF already exists.
687 long subStartTime1 = System.currentTimeMillis ();
689 cloudifyDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, vfModuleName);
690 LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Cloudify", "Cloudify", "QueryDeployment", vfModuleName);
692 catch (MsoException me) {
693 // Failed to query the Deployment due to a cloudify exception.
694 String error = "Create VF Module: Query " + vfModuleName + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
695 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, "Cloudify", "queryDeployment", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment", me);
696 LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "QueryDeployment", vfModuleName);
697 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
699 // Convert to a generic VnfException
700 me.addContext ("CreateVFModule");
701 throw new VnfException (me);
704 // More precise handling/messaging if the Module already exists
705 if (cloudifyDeployment != null && !(cloudifyDeployment.getStatus () == DeploymentStatus.NOTFOUND)) {
706 // CREATED, INSTALLED, INSTALLING, FAILED, UNINSTALLING, UNKNOWN
707 DeploymentStatus status = cloudifyDeployment.getStatus();
708 LOGGER.debug ("Found Existing Deployment, status=" + status);
710 if (status == DeploymentStatus.INSTALLED) {
712 if (failIfExists != null && failIfExists) {
713 String error = "Create VF: Deployment " + vfModuleName + " already exists in " + cloudSiteId + "/" + tenantId;
714 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "Cloudify", "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists");
715 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
716 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
718 // Found existing deployment and client has not requested "failIfExists".
719 // Populate the outputs from the existing deployment.
721 vnfId.value = cloudifyDeployment.getId();
722 outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
723 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully create VF Module (found existing)");
727 // Check through various detailed error cases
728 if (status == DeploymentStatus.INSTALLING || status == DeploymentStatus.UNINSTALLING) {
729 // fail - it's in progress - return meaningful error
730 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.";
731 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "Cloudify", "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists");
732 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
733 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
735 else if (status == DeploymentStatus.FAILED) {
736 // fail - it exists and is in a FAILED state
737 String error = "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
738 LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "Cloudify", "queryDeployment", MsoLogger.ErrorCode.DataError, "Deployment " + vfModuleName + " already exists and is in FAILED state");
739 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
740 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
742 else if (status == DeploymentStatus.UNKNOWN || status == DeploymentStatus.CREATED) {
743 // fail - it exists and is in a UNKNOWN state
744 String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
745 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");
746 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
747 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
750 // Unexpected, since all known status values have been tested for
751 String error = "Create VF: Deployment " + vfModuleName + " already exists with unexpected status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
752 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");
753 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
754 throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, cloudifyDeployment.getId());
759 // Collect outputs from Base Modules and Volume Modules
760 Map<String, Object> baseModuleOutputs = null;
761 Map<String, Object> volumeGroupOutputs = null;
763 // If a Volume Group was provided, query its outputs for inclusion in Module input parameters
764 if (volumeGroupId != null) {
765 long subStartTime2 = System.currentTimeMillis ();
766 DeploymentInfo volumeDeployment = null;
768 volumeDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, volumeGroupId);
769 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Success response from Cloudify", "Cloudify", "QueryDeployment", volumeGroupId);
771 catch (MsoException me) {
772 // Failed to query the Volume GroupDeployment due to a cloudify exception.
773 String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
774 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, volumeGroupId, cloudSiteId, tenantId, "Cloudify", "queryDeployment(volume)", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment(volume)", me);
775 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "QueryDeployment(volume)", volumeGroupId);
776 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
778 // Convert to a generic VnfException
779 me.addContext ("CreateVFModule(QueryVolume)");
780 throw new VnfException (me);
783 if (volumeDeployment == null || volumeDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
784 String error = "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR" ;
785 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");
786 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
788 throw new VnfException (error, MsoExceptionCategory.USERDATA);
790 LOGGER.debug("Found nested volume group");
791 volumeGroupOutputs = volumeDeployment.getOutputs();
792 this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs");
796 // If this is an Add-On Module, query the Base Module outputs
797 // Note: This will be performed whether or not the current request is for an
798 // Add-On Volume Group or Add-On VF Module
800 if (vf.getIsBase()) {
801 LOGGER.debug("This is a BASE Module request");
802 vfRollback.setIsBase(true);
804 LOGGER.debug("This is an Add-On Module request");
806 // Add-On Modules should always have a Base, but just treat as a warning if not provided.
807 // Add-on Volume requests may or may not specify a base.
808 if (!isVolumeRequest && baseVfModuleId == null) {
809 LOGGER.debug ("WARNING: Add-on Module request - no Base Module ID provided");
812 if (baseVfModuleId != null) {
813 long subStartTime2 = System.currentTimeMillis ();
814 DeploymentInfo baseDeployment = null;
816 baseDeployment = cloudifyUtils.queryDeployment (cloudSiteId, tenantId, baseVfModuleId);
817 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Success response from Cloudify", "Cloudify", "QueryDeployment(Base)", baseVfModuleId);
819 catch (MsoException me) {
820 // Failed to query the Volume GroupDeployment due to a cloudify exception.
821 String error = "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
822 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, baseVfModuleId, cloudSiteId, tenantId, "Cloudify", "queryDeployment(Base)", MsoLogger.ErrorCode.DataError, "Exception - queryDeployment(Base)", me);
823 LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "QueryDeployment(Base)", baseVfModuleId);
824 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
826 // Convert to a generic VnfException
827 me.addContext ("CreateVFModule(QueryBase)");
828 throw new VnfException (me);
831 if (baseDeployment == null || baseDeployment.getStatus() == DeploymentStatus.NOTFOUND) {
832 String error = "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR" ;
833 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, baseVfModuleId, cloudSiteId, tenantId, error, "Cloudify", "queryDeployment(Base)", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: Base Module DOES NOT EXIST");
834 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
836 throw new VnfException (error, MsoExceptionCategory.USERDATA);
838 LOGGER.debug("Found base module");
839 baseModuleOutputs = baseDeployment.getOutputs();
840 this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs");
846 // Ready to deploy the new VNF
848 // NOTE: For this section, heatTemplate is used for both HEAT templates and Cloudify blueprints.
849 // In final implementation (post-POC), the template object would either be generic or there would
850 // be a separate DB Table/Object for Blueprints.
853 // NOTE: The template is fixed for the VF Module. The environment is part of the customization.
854 HeatTemplate heatTemplate = null;
855 HeatEnvironment heatEnvironment = null;
856 if (isVolumeRequest) {
857 heatTemplate = vf.getVolumeHeatTemplate();
858 heatEnvironment = vfmc.getVolumeHeatEnv();
860 heatTemplate = vf.getModuleHeatTemplate();
861 heatEnvironment = vfmc.getHeatEnvironment();
864 if (heatTemplate == null) {
865 String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType=" + requestType;
866 LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Template ID", vfModuleType, "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
867 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
868 alarmLogger.sendAlarm(MSO_CONFIGURATION_ERROR,
869 MsoAlarmLogger.CRITICAL, error);
870 throw new VnfException(error, MsoExceptionCategory.INTERNAL);
872 LOGGER.debug ("Got HEAT Template from DB: " + heatTemplate.getHeatTemplate());
875 if (heatEnvironment == null) {
876 String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
877 LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
878 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
879 // Alarm on this error, configuration must be fixed
880 alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
882 throw new VnfException (error, MsoExceptionCategory.INTERNAL);
884 LOGGER.debug ("Got Heat Environment from DB: " + heatEnvironment.getEnvironment());
889 // All variables converted to their native object types
890 HashMap<String, Object> goldenInputs = new HashMap<String,Object>();
891 List<String> extraInputs = new ArrayList<String>();
893 // NOTE: SKIP THIS FOR CLOUDIFY for now. Just use what was passed in.
894 // This whole section needs to be rewritten.
895 Boolean skipInputChecks = false;
897 if (skipInputChecks) {
898 goldenInputs = new HashMap<String,Object>();
899 for (String key : inputs.keySet()) {
900 goldenInputs.put(key, inputs.get(key));
904 // Build maps for the parameters (including aliases) to simplify checks
905 HashMap<String, HeatTemplateParam> params = new HashMap<String, HeatTemplateParam>();
907 Set<HeatTemplateParam> paramSet = heatTemplate.getParameters();
908 LOGGER.debug("paramSet has " + paramSet.size() + " entries");
910 for (HeatTemplateParam htp : paramSet) {
911 params.put(htp.getParamName(), htp);
914 String alias = htp.getParamAlias();
915 if (alias != null && !alias.equals("") && !params.containsKey(alias)) {
916 params.put(alias, htp);
920 // First, convert all inputs to their "template" type
921 for (String key : inputs.keySet()) {
922 if (params.containsKey(key)) {
923 Object value = cloudifyUtils.convertInputValue(inputs.get(key), params.get(key));
925 goldenInputs.put(key, value);
928 LOGGER.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to " + params.get(key).getParamType());
931 extraInputs.add(key);
935 if (!extraInputs.isEmpty()) {
936 LOGGER.debug("Ignoring extra inputs: " + extraInputs);
939 // Next add in Volume Group Outputs if there are any. Copy directly without conversions.
940 if (volumeGroupOutputs != null && !volumeGroupOutputs.isEmpty()) {
941 for (String key : volumeGroupOutputs.keySet()) {
942 if (params.containsKey(key) && !goldenInputs.containsKey(key)) {
943 goldenInputs.put(key, volumeGroupOutputs.get(key));
948 // Next add in Base Module Outputs if there are any. Copy directly without conversions.
949 if (baseModuleOutputs != null && !baseModuleOutputs.isEmpty()) {
950 for (String key : baseModuleOutputs.keySet()) {
951 if (params.containsKey(key) && !goldenInputs.containsKey(key)) {
952 goldenInputs.put(key, baseModuleOutputs.get(key));
957 // Last, add in values from the "environment" file.
958 // These are added to the inputs, since Cloudify doesn't pass an environment file like Heat.
960 // TODO: This may take a different form for Cloudify, but for now process it
961 // with Heat environment file syntax
962 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
963 MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry (sb);
965 if (mhee.getParameters() != null) {
966 for (MsoHeatEnvironmentParameter envParam : mhee.getParameters()) {
967 // If this is a template input, copy to golden inputs
968 String envKey = envParam.getName();
969 if (params.containsKey(envKey) && !goldenInputs.containsKey(envKey)) {
970 Object value = cloudifyUtils.convertInputValue(envParam.getValue(), params.get(envKey));
972 goldenInputs.put(envKey, value);
975 LOGGER.debug("Failed to convert environment parameter " + envKey + "='" + envParam.getValue() + "' to " + params.get(envKey).getParamType());
981 this.sendMapToDebug(goldenInputs, "Final inputs sent to Cloudify");
984 // Check that required parameters have been supplied from any of the sources
985 String missingParams = null;
986 boolean checkRequiredParameters = true;
988 String propertyString = this.environment.getProperty(MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
989 if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
990 checkRequiredParameters = false;
991 LOGGER.debug ("CheckRequiredParameters is FALSE. Will still check but then skip blocking..."
992 + MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS);
994 } catch (Exception e) {
995 // No problem - default is true
996 LOGGER.debug ("An exception occured trying to get property " + MsoVnfCloudifyAdapterImpl.CHECK_REQD_PARAMS, e);
1000 for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
1001 if (parm.isRequired () && (!goldenInputs.containsKey (parm.getParamName ()))) {
1002 LOGGER.debug ("adding to missing parameters list: " + parm.getParamName ());
1003 if (missingParams == null) {
1004 missingParams = parm.getParamName ();
1006 missingParams += "," + parm.getParamName ();
1011 if (missingParams != null) {
1012 if (checkRequiredParameters) {
1013 // Problem - missing one or more required parameters
1014 String error = "Create VFModule: Missing Required inputs: " + missingParams;
1015 LOGGER.error (MessageEnum.RA_MISSING_PARAM, missingParams, "Cloudify", "", MsoLogger.ErrorCode.DataError, "Create VFModule: Missing Required inputs");
1016 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
1017 throw new VnfException (error, MsoExceptionCategory.USERDATA);
1019 LOGGER.debug ("found missing parameters [" + missingParams + "] - but checkRequiredParameters is false - will not block");
1022 LOGGER.debug ("No missing parameters found - ok to proceed");
1025 } // NOTE: END PARAMETER CHECKING
1027 // Ready to deploy the VF Module.
1028 // *First step - make sure the blueprint is loaded into Cloudify.
1029 String blueprintName = heatTemplate.getTemplateName();
1030 String blueprint = heatTemplate.getTemplateBody();
1031 String blueprintId = blueprintName;
1033 // Use the main blueprint name as the blueprint ID (strip yaml extensions).
1034 if (blueprintId.endsWith(".yaml"))
1035 blueprintId = blueprintId.substring(0,blueprintId.lastIndexOf(".yaml"));
1038 if (! cloudifyUtils.isBlueprintLoaded (cloudSiteId, blueprintId)) {
1039 LOGGER.debug ("Blueprint " + blueprintId + " is not loaded. Will upload it now.");
1041 Map<String,byte[]> blueprintFiles = new HashMap<String,byte[]>();
1043 blueprintFiles.put(blueprintName, blueprint.getBytes());
1045 // TODO: Implement nested blueprint logic based on Cloudify structures.
1046 // For now, just use the Heat structures.
1047 // The query returns a map of String->Object, where the map keys provide one layer of
1048 // indirection from the Heat template names. For this case, assume the map key matches
1049 // the nested blueprint name.
1050 List<HeatTemplate> nestedBlueprints = heatTemplate.getChildTemplates();
1051 if (nestedBlueprints != null) {
1052 for (HeatTemplate nestedBlueprint: nestedBlueprints) {
1053 blueprintFiles.put(nestedBlueprint.getTemplateName(), nestedBlueprint.getTemplateBody().getBytes());
1057 // TODO: Implement file artifact logic based on Cloudify structures.
1058 // For now, just use the Heat structures.
1059 List<HeatFiles> heatFiles = vf.getHeatFiles();
1060 if (heatFiles != null) {
1061 for (HeatFiles heatFile: heatFiles) {
1062 blueprintFiles.put(heatFile.getFileName(), heatFile.getFileBody().getBytes());
1066 // Upload the blueprint package
1067 cloudifyUtils.uploadBlueprint(cloudSiteId, blueprintId, blueprintName, blueprintFiles, false);
1072 catch (MsoException me) {
1073 me.addContext ("CreateVFModule");
1074 String error = "Create VF Module: Upload blueprint failed. Blueprint=" + blueprintName + ": " + me;
1075 LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "Cloudify", "", MsoLogger.ErrorCode.DataError, "MsoException - uploadBlueprint", me);
1076 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1077 throw new VnfException (me);
1081 // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1082 // because we already checked for those.
1083 long createDeploymentStarttime = System.currentTimeMillis ();
1085 // KLUDGE - Cloudify requires Tenant Name for Openstack. We have the ID.
1086 // Go directly to Keystone until APIs could be updated to supply the name.
1087 MsoTenant msoTenant = keystoneUtils.queryTenant(tenantId, cloudSiteId);
1088 String tenantName = (msoTenant != null? msoTenant.getTenantName() : tenantId);
1090 if (backout == null) {
1094 cloudifyDeployment = cloudifyUtils.createAndInstallDeployment (cloudSiteId,
1100 heatTemplate.getTimeoutMinutes (),
1101 backout.booleanValue());
1103 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Cloudify", "Cloudify", "CreateDeployment", vfModuleName);
1104 } catch (MsoException me) {
1105 me.addContext ("CreateVFModule");
1106 String error = "Create VF Module " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1107 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "CreateDeployment", vfModuleName);
1108 LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "Cloudify", "", MsoLogger.ErrorCode.DataError, "MsoException - createDeployment", me);
1109 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1110 throw new VnfException (me);
1111 } catch (NullPointerException npe) {
1112 String error = "Create VFModule " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + npe;
1113 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "CreateDeployment", vfModuleName);
1114 LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "Cloudify", "", MsoLogger.ErrorCode.DataError, "NullPointerException - createDeployment", npe);
1115 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1116 LOGGER.debug("NULL POINTER EXCEPTION at cloudify.createAndInstallDeployment");
1117 //npe.addContext ("CreateVNF");
1118 throw new VnfException ("NullPointerException during cloudify.createAndInstallDeployment");
1119 } catch (Exception e) {
1120 LOGGER.recordMetricEvent (createDeploymentStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while creating deployment with Cloudify", "Cloudify", "CreateDeployment", vfModuleName);
1121 LOGGER.debug("unhandled exception at cloudify.createAndInstallDeployment");
1122 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while creating deployment with Cloudify");
1123 throw new VnfException("Exception during cloudify.createAndInstallDeployment! " + e.getMessage());
1125 } catch (Exception e) {
1126 LOGGER.debug("unhandled exception in create VF");
1127 throw new VnfException("Exception during create VF " + e.getMessage());
1131 // Reach this point if create is successful.
1132 // Populate remaining rollback info and response parameters.
1133 vfRollback.setVnfCreated (true);
1134 vfRollback.setVnfId (cloudifyDeployment.getId());
1135 vnfId.value = cloudifyDeployment.getId();
1136 outputs.value = copyStringOutputs (cloudifyDeployment.getOutputs ());
1138 rollback.value = vfRollback;
1140 LOGGER.debug ("VF Module " + vfModuleName + " successfully created");
1141 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully create VF Module");
1145 public void deleteVfModule (String cloudSiteId,
1148 MsoRequest msoRequest,
1149 Holder <Map <String, String>> outputs) throws VnfException {
1150 MsoLogger.setLogContext (msoRequest);
1151 MsoLogger.setServiceName ("DeleteVf");
1152 LOGGER.debug ("Deleting VF " + vnfName + " in " + cloudSiteId + "/" + tenantId);
1153 // Will capture execution time for metrics
1154 long startTime = System.currentTimeMillis ();
1156 // 1702 capture the output parameters on a delete
1157 // so we'll need to query first
1158 DeploymentInfo deployment = null;
1160 deployment = cloudifyUtils.queryDeployment(cloudSiteId, tenantId, vnfName);
1161 } catch (MsoException me) {
1162 // Failed to query the deployment. Convert to a generic VnfException
1163 me.addContext ("DeleteVFModule");
1164 String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1165 LOGGER.recordMetricEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "Cloudify", "QueryDeployment", null);
1166 LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, "Cloudify", "QueryDeployment", MsoLogger.ErrorCode.DataError, "Exception - QueryDeployment", me);
1167 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1168 throw new VnfException (me);
1170 // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected Object types
1171 outputs.value = convertMapStringObjectToStringString(deployment.getOutputs());
1173 // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
1174 // The possible outcomes of deleteStack are a StackInfo object with status
1175 // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
1177 long subStartTime = System.currentTimeMillis ();
1179 cloudifyUtils.uninstallAndDeleteDeployment(cloudSiteId, tenantId, vnfName, 5);
1180 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from DeleteDeployment", "Cloudify", "DeleteDeployment", vnfName);
1181 } catch (MsoException me) {
1182 me.addContext ("DeleteVfModule");
1183 // Convert to a generic VnfException
1184 String error = "Delete VF: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1185 LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "DeleteDeployment", "DeleteDeployment", vnfName);
1186 LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vnfName, cloudSiteId, tenantId, "DeleteDeployment", "DeleteDeployment", MsoLogger.ErrorCode.DataError, "Exception - DeleteDeployment: " + me.getMessage());
1187 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1188 throw new VnfException (me);
1191 // On success, nothing is returned.
1192 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully delete VF");
1196 // TODO: Should Update be supported for Cloudify? What would this look like?
1198 public void updateVfModule (String cloudSiteId,
1204 String volumeGroupHeatStackId,
1205 String baseVfHeatStackId,
1206 String vfModuleStackId,
1207 String modelCustomizationUuid,
1208 Map <String, String> inputs,
1209 MsoRequest msoRequest,
1210 Holder <Map <String, String>> outputs,
1211 Holder <VnfRollback> rollback) throws VnfException
1213 // This operation is not currently supported for Cloudify-orchestrated VF Modules.
1214 LOGGER.debug ("Update VF Module command attempted but not supported");
1215 throw new VnfException ("UpdateVfModule: Unsupported command", MsoExceptionCategory.USERDATA);