From 821851f259e2a11fde31b41ea0740aad81ec7baa Mon Sep 17 00:00:00 2001 From: Eric Multanen Date: Fri, 14 Sep 2018 04:42:38 -0700 Subject: Add multicloud to VnfPluginAdapter Adds new mode 'multicloud' to VnfPluginAdapter along with MsoMulticloudUtils and supporting code. Change-Id: I1cfdc9ba09c58315fb0bfc025854cf0122a32759 Issue-ID: SO-752 Signed-off-by: Eric Multanen --- .../onap/so/openstack/utils/MsoCommonUtils.java | 131 +++++- .../org/onap/so/openstack/utils/MsoHeatUtils.java | 242 ++++------- .../so/openstack/utils/MsoMulticloudParam.java | 110 +++++ .../so/openstack/utils/MsoMulticloudUtils.java | 483 +++++++++++++++++++++ 4 files changed, 795 insertions(+), 171 deletions(-) create mode 100644 adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudParam.java create mode 100644 adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java (limited to 'adapters/mso-adapter-utils/src/main/java/org') diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoCommonUtils.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoCommonUtils.java index 98793601d0..da81da91ea 100644 --- a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoCommonUtils.java +++ b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoCommonUtils.java @@ -2,14 +2,15 @@ * ============LICENSE_START======================================================= * ONAP - SO * ================================================================================ + * Copyright (C) 2018 Intel Corp. All rights reserved. * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,6 +23,9 @@ package org.onap.so.openstack.utils; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import org.onap.so.config.beans.PoConfig; import org.onap.so.logger.MessageEnum; @@ -35,10 +39,13 @@ import org.onap.so.openstack.exceptions.MsoOpenstackException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.woorea.openstack.base.client.OpenStackBaseException; import com.woorea.openstack.base.client.OpenStackConnectException; import com.woorea.openstack.base.client.OpenStackRequest; import com.woorea.openstack.base.client.OpenStackResponseException; +import com.woorea.openstack.heat.model.CreateStackParam; import com.woorea.openstack.heat.model.Explanation; import com.woorea.openstack.keystone.model.Error; import com.woorea.openstack.quantum.model.NeutronError; @@ -57,11 +64,11 @@ public class MsoCommonUtils { * sub-category that identifies the specific call (using the real * openstack-java-sdk classname of the OpenStackRequest parameter). */ - + protected T executeAndRecordOpenstackRequest (OpenStackRequest request) { - + int limit; - + long start = System.currentTimeMillis (); String requestType; if (request.getClass ().getEnclosingClass () != null) { @@ -70,17 +77,17 @@ public class MsoCommonUtils { } else { requestType = request.getClass ().getSimpleName (); } - + int retryDelay = poConfig.getRetryDelay(); int retryCount = poConfig.getRetryCount(); String retryCodes = poConfig.getRetryCodes(); - + // Run the actual command. All exceptions will be propagated while (true) { try { return request.execute (); - } + } catch (OpenStackResponseException e) { boolean retry = false; if (retryCodes != null ) { @@ -128,11 +135,11 @@ public class MsoCommonUtils { } else throw e; - + } } } - + /* * Convert an Openstack Exception on a Keystone call to an MsoException. * This method supports both OpenstackResponseException and OpenStackConnectException. @@ -281,7 +288,7 @@ public class MsoCommonUtils { return me; } - + protected MsoException ioExceptionToMsoException(IOException e, String context) { MsoAdapterException me = new MsoAdapterException (e.getMessage (), e); me.addContext (context); @@ -297,7 +304,105 @@ public class MsoCommonUtils { public boolean isNullOrEmpty (String s) { return s == null || s.isEmpty(); } - - + + + protected CreateStackParam createStackParam(String stackName, + String heatTemplate, + Map stackInputs, + int timeoutMinutes, + String environment, + Map files, + Map heatFiles) { + + // Create local variables checking to see if we have an environment, nested, get_files + // Could later add some checks to see if it's valid. + boolean haveEnvtVariable = true; + if (environment == null || "".equalsIgnoreCase (environment.trim ())) { + haveEnvtVariable = false; + logger.debug ("createStackParam called with no environment variable"); + } else { + logger.debug ("createStackParam called with an environment variable: " + environment); + } + + boolean haveFiles = true; + if (files == null || files.isEmpty ()) { + haveFiles = false; + logger.debug ("createStackParam called with no files / child template ids"); + } else { + logger.debug ("createStackParam called with " + files.size () + " files / child template ids"); + } + + boolean haveHeatFiles = true; + if (heatFiles == null || heatFiles.isEmpty ()) { + haveHeatFiles = false; + logger.debug ("createStackParam called with no heatFiles"); + } else { + logger.debug ("createStackParam called with " + heatFiles.size () + " heatFiles"); + } + + //force entire stackInput object to generic Map for openstack compatibility + ObjectMapper mapper = new ObjectMapper(); + Map normalized = new HashMap<>(); + try { + normalized = mapper.readValue(mapper.writeValueAsString(stackInputs), new TypeReference>() {}); + } catch (IOException e1) { + logger.debug("could not map json", e1); + } + + // Build up the stack to create + // Disable auto-rollback, because error reason is lost. Always rollback in the code. + CreateStackParam stack = new CreateStackParam (); + stack.setStackName (stackName); + stack.setTimeoutMinutes (timeoutMinutes); + stack.setParameters (normalized); + stack.setTemplate (heatTemplate); + stack.setDisableRollback (true); + // TJM New for PO Adapter - add envt variable + if (haveEnvtVariable) { + logger.debug ("Found an environment variable - value: " + environment); + stack.setEnvironment (environment); + } + // Now handle nested templates or get_files - have to combine if we have both + // as they're both treated as "files:" on the stack. + if (haveFiles && haveHeatFiles) { + // Let's do this here - not in the bean + logger.debug ("Found files AND heatFiles - combine and add!"); + Map combinedFiles = new HashMap <> (); + for (Entry entry : files.entrySet()) { + combinedFiles.put(entry.getKey(), entry.getValue()); + } + for (Entry entry : heatFiles.entrySet()) { + combinedFiles.put(entry.getKey(), entry.getValue()); + } + stack.setFiles (combinedFiles); + } else { + // Handle if we only have one or neither: + if (haveFiles) { + logger.debug ("Found files - adding to stack"); + stack.setFiles (files); + } + if (haveHeatFiles) { + logger.debug ("Found heatFiles - adding to stack"); + // the setFiles was modified to handle adding the entries + stack.setFiles (heatFiles); + } + } + + // 1802 - attempt to add better formatted printout of request to openstack + try { + Map inputs = new HashMap<>(); + for (Entry entry : stackInputs.entrySet()) { + if (entry.getValue() != null) { + inputs.put(entry.getKey(), entry.getValue()); + } + } + logger.debug("stack request:" + stack.toString()); + } catch (Exception e) { + // that's okay - this is a nice-to-have + logger.debug("(had an issue printing nicely formatted request to debuglog) " + e.getMessage()); + } + + return stack; + } } diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoHeatUtils.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoHeatUtils.java index 6b66970ea0..15f84890b7 100644 --- a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoHeatUtils.java +++ b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoHeatUtils.java @@ -8,9 +8,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -106,18 +106,18 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ // Fetch cloud configuration each time (may be cached in CloudConfig class) @Autowired protected CloudConfig cloudConfig; - + @Autowired private Environment environment; @Autowired private AuthenticationMethodFactory authenticationMethodFactory; - + @Autowired private MsoTenantUtilsFactory tenantUtilsFactory; - + private static final MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA, MsoHeatUtils.class); - + // Properties names and variables (with default values) protected String createPollIntervalProp = "ecomp.mso.adapters.po.pollInterval"; private String deletePollIntervalProp = "ecomp.mso.adapters.po.pollInterval"; @@ -125,7 +125,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ protected static final String createPollIntervalDefault = "15"; private static final String deletePollIntervalDefault = "15"; - + private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); /** @@ -275,31 +275,19 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ Map files, Map heatFiles, boolean backout) throws MsoException { - // Create local variables checking to see if we have an environment, nested, get_files - // Could later add some checks to see if it's valid. - boolean haveEnvtVariable = true; - if (environment == null || "".equalsIgnoreCase (environment.trim ())) { - haveEnvtVariable = false; - LOGGER.debug ("createStack called with no environment variable"); - } else { - LOGGER.debug ("createStack called with an environment variable: " + environment); - } - boolean haveFiles = true; - if (files == null || files.isEmpty ()) { - haveFiles = false; - LOGGER.debug ("createStack called with no files / child template ids"); - } else { - LOGGER.debug ("createStack called with " + files.size () + " files / child template ids"); + // Take out the multicloud inputs, if present. + String[] directives = { "oof_directives", "sdnc_directives", "generic_vnf_id", "vf_module_id" }; + for (String key : directives) { + if (stackInputs.containsKey(key)) { + stackInputs.remove(key); + if (stackInputs.isEmpty()) { + break; + } + } } - boolean haveHeatFiles = true; - if (heatFiles == null || heatFiles.isEmpty ()) { - haveHeatFiles = false; - LOGGER.debug ("createStack called with no heatFiles"); - } else { - LOGGER.debug ("createStack called with " + heatFiles.size () + " heatFiles"); - } + CreateStackParam stack = createStackParam(stackName, heatTemplate, stackInputs, timeoutMinutes, environment, files, heatFiles); // Obtain the cloud site information where we will create the stack CloudSite cloudSite = cloudConfig.getCloudSite(cloudSiteId).orElseThrow( @@ -309,73 +297,11 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ // This could throw MsoTenantNotFound or MsoOpenstackException (both propagated) Heat heatClient = getHeatClient (cloudSite, tenantId); if (heatClient != null) { - LOGGER.debug("Found: " + heatClient.toString()); + LOGGER.debug("Found: " + heatClient.toString()); } LOGGER.debug ("Ready to Create Stack (" + heatTemplate + ") with input params: " + stackInputs); - //force entire stackInput object to generic Map for openstack compatibility - ObjectMapper mapper = new ObjectMapper(); - Map normalized = new HashMap<>(); - try { - normalized = mapper.readValue(mapper.writeValueAsString(stackInputs), new TypeReference>() {}); - } catch (IOException e1) { - LOGGER.debug("could not map json", e1); - } - - // Build up the stack to create - // Disable auto-rollback, because error reason is lost. Always rollback in the code. - CreateStackParam stack = new CreateStackParam (); - stack.setStackName (stackName); - stack.setTimeoutMinutes (timeoutMinutes); - stack.setParameters (normalized); - stack.setTemplate (heatTemplate); - stack.setDisableRollback (true); - // TJM New for PO Adapter - add envt variable - if (haveEnvtVariable) { - LOGGER.debug ("Found an environment variable - value: " + environment); - stack.setEnvironment (environment); - } - // Now handle nested templates or get_files - have to combine if we have both - // as they're both treated as "files:" on the stack. - if (haveFiles && haveHeatFiles) { - // Let's do this here - not in the bean - LOGGER.debug ("Found files AND heatFiles - combine and add!"); - Map combinedFiles = new HashMap <> (); - for (Entry entry : files.entrySet()) { - combinedFiles.put(entry.getKey(), entry.getValue()); - } - for (Entry entry : heatFiles.entrySet()) { - combinedFiles.put(entry.getKey(), entry.getValue()); - } - stack.setFiles (combinedFiles); - } else { - // Handle if we only have one or neither: - if (haveFiles) { - LOGGER.debug ("Found files - adding to stack"); - stack.setFiles (files); - } - if (haveHeatFiles) { - LOGGER.debug ("Found heatFiles - adding to stack"); - // the setFiles was modified to handle adding the entries - stack.setFiles (heatFiles); - } - } - - // 1802 - attempt to add better formatted printout of request to openstack - try { - Map inputs = new HashMap<>(); - for (Entry entry : stackInputs.entrySet()) { - if (entry.getValue() != null) { - inputs.put(entry.getKey(), entry.getValue()); - } - } - LOGGER.debug(this.printStackRequest(tenantId, heatFiles, files, environment, inputs, stackName, heatTemplate, timeoutMinutes, backout, cloudSiteId)); - } catch (Exception e) { - // that's okay - this is a nice-to-have - LOGGER.debug("(had an issue printing nicely formatted request to debuglog) " + e.getMessage()); - } - Stack heatStack = null; try { // Execute the actual Openstack command to create the Heat stack @@ -401,7 +327,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ throw me; } else { // Convert the OpenStackResponseException to an MsoOpenstackException - LOGGER.debug("ERROR STATUS = " + e.getStatus() + ",\n" + e.getMessage() + "\n" + e.getLocalizedMessage()); + LOGGER.debug("ERROR STATUS = " + e.getStatus() + ",\n" + e.getMessage() + "\n" + e.getLocalizedMessage()); throw heatExceptionToMsoException (e, CREATE_STACK); } } catch (OpenStackConnectException e) { @@ -422,8 +348,8 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ // Set a time limit on overall polling. // Use the resource (template) timeout for Openstack (expressed in minutes) // and add one poll interval to give Openstack a chance to fail on its own.s - - int createPollInterval = Integer.parseInt(this.environment.getProperty(createPollIntervalProp, createPollIntervalDefault)); + + int createPollInterval = Integer.parseInt(this.environment.getProperty(createPollIntervalProp, createPollIntervalDefault)); int pollTimeout = (timeoutMinutes * 60) + createPollInterval; // New 1610 - poll on delete if we rollback - use same values for now int deletePollInterval = createPollInterval; @@ -741,7 +667,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ else { LOGGER.debug ("Heat Client is NULL" ); } - + executeAndRecordOpenstackRequest (request); } catch (OpenStackResponseException e) { if (e.getStatus () == 404) { @@ -765,7 +691,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ if (pollForCompletion) { // Set a timeout on polling - + int pollInterval = Integer.parseInt(this.environment.getProperty(deletePollIntervalProp, "" + deletePollIntervalDefault)); int pollTimeout = Integer.parseInt(this.environment.getProperty(deletePollTimeoutProp, "" + deletePollIntervalDefault)); @@ -914,7 +840,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ // Remove any extraneous parameters (don't throw an error) Map updatedParams = new HashMap <> (); List extraParams = new ArrayList <> (); - + for (Entry entry : inputParams.entrySet()) { if (!paramList.contains(entry.getKey())) { // This is not a valid parameter for this template @@ -1079,7 +1005,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ */ protected Stack queryHeatStack (Heat heatClient, String stackName) throws MsoException { if (stackName == null) { - return null; + return null; } try { OpenStackRequest request = heatClient.getStacks ().byName (stackName); @@ -1204,7 +1130,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } - private StringBuilder getOutputsAsStringBuilder(Stack heatStack) { + protected StringBuilder getOutputsAsStringBuilder(Stack heatStack) { // This should only be used as a utility to print out the stack outputs // to the log StringBuilder sb = new StringBuilder(""); @@ -1237,7 +1163,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } catch (Exception e) { LOGGER.debug("Exception :",e); sb.append("(a LinkedHashMap value that would not convert nicely)"); - } + } } else if (obj instanceof Integer) { String str = ""; try { @@ -1281,8 +1207,8 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ sb.append("[END]"); return sb; } - - + + public void copyBaseOutputsToInputs(Map inputs, Map otherStackOutputs, List paramNames, Map aliases) { if (inputs == null || otherStackOutputs == null) @@ -1331,7 +1257,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } return; } - + public List convertCdlToArrayList(String cdl) { String cdl2 = cdl.trim(); String cdl3; @@ -1342,7 +1268,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } return new ArrayList<>(Arrays.asList(cdl3.split(","))); } - + /** * New with 1707 - this method will convert all the String *values* of the inputs * to their "actual" object type (based on the param type: in the db - which comes from the template): @@ -1364,12 +1290,12 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ HashMap newInputs = new HashMap<>(); HashMap params = new HashMap<>(); HashMap paramAliases = new HashMap<>(); - + if (inputs == null) { LOGGER.debug("convertInputMap - inputs is null - nothing to do here"); return new HashMap<>(); } - + LOGGER.debug("convertInputMap in MsoHeatUtils called, with " + inputs.size() + " inputs, and template " + template.getArtifactUuid()); try { LOGGER.debug(template.toString()); @@ -1378,7 +1304,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } catch (Exception e) { LOGGER.debug("Exception occurred in convertInputMap:" + e.getMessage(), e); } - + for (HeatTemplateParam htp : template.getParameters()) { LOGGER.debug("Adding " + htp.getParamName()); params.put(htp.getParamName(), htp); @@ -1413,9 +1339,9 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ if ("string".equalsIgnoreCase(type)) { // Easiest! String str = inputs.get(key); - if (alias) + if (alias) newInputs.put(realName, str); - else + else newInputs.put(key, str); } else if ("number".equalsIgnoreCase(type)) { String integerString = inputs.get(key); @@ -1480,9 +1406,9 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } return newInputs; } - - /* - * This helpful method added for Valet + + /* + * This helpful method added for Valet */ public String getCloudSiteKeystoneUrl(String cloudSiteId) throws MsoCloudSiteNotFound { String keystone_url = null; @@ -1498,17 +1424,17 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } return keystone_url; } - + /* - * Create a string suitable for being dumped to a debug log that creates a + * Create a string suitable for being dumped to a debug log that creates a * pseudo-JSON request dumping what's being sent to Openstack API in the create or update request */ - - private String printStackRequest(String tenantId, + + private String printStackRequest(String tenantId, Map heatFiles, Map nestedTemplates, String environment, - Map inputs, + Map inputs, String vfModuleName, String template, int timeoutMinutes, @@ -1520,14 +1446,14 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ sb.append("{\n"); sb.append(" \"stack_name\": \"" + vfModuleName + "\",\n"); sb.append(" \"disable_rollback\": " + backout + ",\n"); - sb.append(" \"timeout_mins\": " + timeoutMinutes + ",\n"); + sb.append(" \"timeout_mins\": " + timeoutMinutes + ",\n"); sb.append(" \"template\": {\n"); sb.append(template); sb.append(" },\n"); sb.append(" \"environment\": {\n"); - if (environment == null) + if (environment == null) sb.append(""); - else + else sb.append(environment); sb.append(" },\n"); sb.append(" \"files\": {\n"); @@ -1574,19 +1500,19 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } } sb.append("\n }\n}\n"); - + return sb.toString(); } - + /******************************************************************************* - * + * * Methods (and associated utilities) to implement the VduPlugin interface - * + * *******************************************************************************/ - + /** * VduPlugin interface for instantiate function. - * + * * Translate the VduPlugin parameters to the corresponding 'createStack' parameters, * and then invoke the existing function. */ @@ -1601,7 +1527,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ { String cloudSiteId = cloudInfo.getCloudSiteId(); String tenantId = cloudInfo.getTenantId(); - + // Translate the VDU ModelInformation structure to that which is needed for // creating the Heat stack. Loop through the artifacts, looking specifically // for MAIN_TEMPLATE and ENVIRONMENT. Any other artifact will @@ -1610,7 +1536,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ Map nestedTemplates = new HashMap<>(); Map files = new HashMap<>(); String heatEnvironment = null; - + for (VduArtifact vduArtifact: vduModel.getArtifacts()) { if (vduArtifact.getType() == ArtifactType.MAIN_TEMPLATE) { heatTemplate = new String(vduArtifact.getContent()); @@ -1622,7 +1548,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ heatEnvironment = new String(vduArtifact.getContent()); } } - + try { StackInfo stackInfo = createStack (cloudSiteId, tenantId, @@ -1635,7 +1561,7 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ nestedTemplates, files, rollbackOnFailure); - + // Populate a vduInstance from the StackInfo return stackInfoToVduInstance(stackInfo); } @@ -1643,8 +1569,8 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ throw new VduException ("MsoHeatUtils (instantiateVDU): createStack Exception", e); } } - - + + /** * VduPlugin interface for query function. */ @@ -1654,19 +1580,19 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ { String cloudSiteId = cloudInfo.getCloudSiteId(); String tenantId = cloudInfo.getTenantId(); - + try { // Query the Cloudify Deployment object and populate a VduInstance StackInfo stackInfo = queryStack (cloudSiteId, tenantId, instanceId); - + return stackInfoToVduInstance(stackInfo); } catch (Exception e) { throw new VduException ("MsoHeatUtile (queryVdu): queryStack Exception ", e); } } - - + + /** * VduPlugin interface for delete function. */ @@ -1676,31 +1602,31 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ { String cloudSiteId = cloudInfo.getCloudSiteId(); String tenantId = cloudInfo.getTenantId(); - + try { // Delete the Heat stack StackInfo stackInfo = deleteStack (tenantId, cloudSiteId, instanceId, true); - + // Populate a VduInstance based on the deleted Cloudify Deployment object VduInstance vduInstance = stackInfoToVduInstance(stackInfo); - + // Override return state to DELETED (HeatUtils sets to NOTFOUND) vduInstance.getStatus().setState(VduStateType.DELETED); - + return vduInstance; } catch (Exception e) { throw new VduException ("Delete VDU Exception", e); } } - - + + /** * VduPlugin interface for update function. - * + * * Update is currently not supported in the MsoHeatUtils implementation of VduPlugin. * Just return a VduException. - * + * */ @Override public VduInstance updateVdu ( @@ -1713,38 +1639,38 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ { throw new VduException ("MsoHeatUtils: updateVdu interface not supported"); } - - + + /* * Convert the local DeploymentInfo object (Cloudify-specific) to a generic VduInstance object */ - private VduInstance stackInfoToVduInstance (StackInfo stackInfo) + protected VduInstance stackInfoToVduInstance (StackInfo stackInfo) { VduInstance vduInstance = new VduInstance(); - + // The full canonical name as the instance UUID vduInstance.setVduInstanceId(stackInfo.getCanonicalName()); vduInstance.setVduInstanceName(stackInfo.getName()); - + // Copy inputs and outputs vduInstance.setInputs(stackInfo.getParameters()); vduInstance.setOutputs(stackInfo.getOutputs()); - + // Translate the status elements vduInstance.setStatus(stackStatusToVduStatus (stackInfo)); - + return vduInstance; } - + private VduStatus stackStatusToVduStatus (StackInfo stackInfo) { VduStatus vduStatus = new VduStatus(); - + // Map the status fields to more generic VduStatus. // There are lots of HeatStatus values, so this is a bit long... HeatStatus heatStatus = stackInfo.getStatus(); String statusMessage = stackInfo.getStatusMessage(); - + if (heatStatus == HeatStatus.INIT || heatStatus == HeatStatus.BUILDING) { vduStatus.setState(VduStateType.INSTANTIATING); vduStatus.setLastAction((new PluginAction ("create", "in_progress", statusMessage))); @@ -1774,10 +1700,10 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ } else { vduStatus.setState(VduStateType.UNKNOWN); } - + return vduStatus; } - + private void sleep(long time) { try { Thread.sleep(time); @@ -1786,5 +1712,5 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{ Thread.currentThread().interrupt(); } } - + } diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudParam.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudParam.java new file mode 100644 index 0000000000..9b2475a1c4 --- /dev/null +++ b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudParam.java @@ -0,0 +1,110 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2018 Intel Corp. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.so.openstack.utils; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MsoMulticloudParam { + + @JsonProperty("generic-vnf-id") + private String genericVnfId; + + @JsonProperty("vf-module-id") + private String vfModuleId; + + @JsonProperty("oof_directives") + private String oofDirectives; + + @JsonProperty("sdnc_directives") + private String sdncDirectives; + + @JsonProperty("template_type") + private String templateType; + + @JsonProperty("template_data") + private String templateData; + + public void setGenericVnfId(String genericVnfId){ + this.genericVnfId = genericVnfId; + } + + public String getGenericVnfId(){ + return this.genericVnfId; + } + + public void setVfModuleId(String vfModuleId){ + this.vfModuleId = vfModuleId; + } + + public String getVfModuleId(){ + return this.vfModuleId; + } + + public void setOofDirectives(String oofDirectives){ + this.oofDirectives = oofDirectives; + } + + public String getOofDirectives(){ + return this.oofDirectives; + } + + public void setSdncDirectives(String sdncDirectives){ + this.sdncDirectives = sdncDirectives; + } + + public String getSdncDirectives(){ + return this.sdncDirectives; + } + + public void setTemplateType(String templateType){ + this.templateType = templateType; + } + + public String TemplateType(){ + return this.templateType; + } + + public void setTemplateData(String templateData){ + this.templateData = templateData; + } + + public String getTemplateData(){ + return this.templateData; + } + + @Override + public String toString() { + return String.format("MulticloudParam{" + + "genericVnfId='%s'," + + " vfModuleId='%s'," + + " oofDirectives='%s'," + + " sdncDirectives='%s'," + + " templateType='%s'," + + " templateData='%s'" + + "}", + genericVnfId, + vfModuleId, + oofDirectives, + sdncDirectives, + templateType, + templateData); + } +} diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java new file mode 100644 index 0000000000..4ed35a4d28 --- /dev/null +++ b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java @@ -0,0 +1,483 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2018 Intel Corp. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.so.openstack.utils; + +import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriBuilderException; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.onap.so.adapters.vdu.CloudInfo; +import org.onap.so.adapters.vdu.PluginAction; +import org.onap.so.adapters.vdu.VduArtifact; +import org.onap.so.adapters.vdu.VduArtifact.ArtifactType; +import org.onap.so.adapters.vdu.VduException; +import org.onap.so.adapters.vdu.VduInstance; +import org.onap.so.adapters.vdu.VduModelInfo; +import org.onap.so.adapters.vdu.VduPlugin; +import org.onap.so.adapters.vdu.VduStateType; +import org.onap.so.adapters.vdu.VduStatus; +import org.onap.so.openstack.beans.HeatStatus; +import org.onap.so.openstack.beans.StackInfo; +import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound; +import org.onap.so.openstack.exceptions.MsoException; +import org.onap.so.openstack.exceptions.MsoOpenstackException; +import org.onap.so.openstack.mappers.StackInfoMapper; +import org.onap.so.client.HttpClient; +import org.onap.so.client.RestClient; +import org.onap.so.cloud.CloudConfig; +import org.onap.so.db.catalog.beans.CloudSite; +import org.onap.so.utils.TargetEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import com.woorea.openstack.heat.model.CreateStackParam; +import com.woorea.openstack.heat.model.Stack; + +@Component +public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin{ + + @Autowired + protected CloudConfig cloudConfig; + + @Autowired + private Environment env; + + private static final String ONAP_IP = "ONAP_IP"; + + private static final String DEFAULT_MSB_IP = "127.0.0.1"; + + private static final Integer DEFAULT_MSB_PORT = 80; + + private static final Logger logger = LoggerFactory.getLogger(MsoMulticloudUtils.class); + + /****************************************************************************** + * + * Methods (and associated utilities) to implement the VduPlugin interface + * + *******************************************************************************/ + + /** + * Create a new Stack in the specified cloud location and tenant. The Heat template + * and parameter map are passed in as arguments, along with the cloud access credentials. + * It is expected that parameters have been validated and contain at minimum the required + * parameters for the given template with no extra (undefined) parameters.. + * + * The Stack name supplied by the caller must be unique in the scope of this tenant. + * However, it should also be globally unique, as it will be the identifier for the + * resource going forward in Inventory. This latter is managed by the higher levels + * invoking this function. + * + * The caller may choose to let this function poll Openstack for completion of the + * stack creation, or may handle polling itself via separate calls to query the status. + * In either case, a StackInfo object will be returned containing the current status. + * When polling is enabled, a status of CREATED is expected. When not polling, a + * status of BUILDING is expected. + * + * An error will be thrown if the requested Stack already exists in the specified + * Tenant and Cloud. + * + * For 1510 - add "environment", "files" (nested templates), and "heatFiles" (get_files) as + * parameters for createStack. If environment is non-null, it will be added to the stack. + * The nested templates and get_file entries both end up being added to the "files" on the + * stack. We must combine them before we add them to the stack if they're both non-null. + * + * @param cloudSiteId The cloud (may be a region) in which to create the stack. + * @param tenantId The Openstack ID of the tenant in which to create the Stack + * @param stackName The name of the stack to create + * @param heatTemplate The Heat template + * @param stackInputs A map of key/value inputs + * @param pollForCompletion Indicator that polling should be handled in Java vs. in the client + * @param environment An optional yaml-format string to specify environmental parameters + * @param files a Map that lists the child template IDs (file is the string, object is an int of + * Template id) + * @param heatFiles a Map that lists the get_file entries (fileName, fileBody) + * @param backout Donot delete stack on create Failure - defaulted to True + * @return A StackInfo object + * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception. + */ + + @SuppressWarnings("unchecked") + @Override + public StackInfo createStack (String cloudSiteId, + String tenantId, + String stackName, + String heatTemplate, + Map stackInputs, + boolean pollForCompletion, + int timeoutMinutes, + String environment, + Map files, + Map heatFiles, + boolean backout) throws MsoException { + + // Get the directives, if present. + String oofDirectives = null; + String sdncDirectives = null; + String genericVnfId = null; + String vfModuleId = null; + + String key = "oof_directives"; + if (!stackInputs.isEmpty() && stackInputs.containsKey(key)) { + oofDirectives = (String) stackInputs.get(key); + stackInputs.remove(key); + } + key = "sdnc_directives"; + if (!stackInputs.isEmpty() && stackInputs.containsKey(key)) { + sdncDirectives = (String) stackInputs.get(key); + stackInputs.remove(key); + } + key = "generic_vnf_id"; + if (!stackInputs.isEmpty() && stackInputs.containsKey(key)) { + genericVnfId = (String) stackInputs.get(key); + stackInputs.remove(key); + } + key = "vf_module_id"; + if (!stackInputs.isEmpty() && stackInputs.containsKey(key)) { + vfModuleId = (String) stackInputs.get(key); + stackInputs.remove(key); + } + + // create the multicloud payload + CreateStackParam stack = createStackParam(stackName, heatTemplate, stackInputs, timeoutMinutes, environment, files, heatFiles); + + MsoMulticloudParam multicloudParam = new MsoMulticloudParam(); + multicloudParam.setGenericVnfId(genericVnfId); + multicloudParam.setVfModuleId(vfModuleId); + multicloudParam.setOofDirectives(oofDirectives); + multicloudParam.setSdncDirectives(sdncDirectives); + multicloudParam.setTemplateType("heat"); + multicloudParam.setTemplateData(stack.toString()); + + + String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, null); + RestClient multicloudClient = getMulticloudClient(multicloudEndpoint); + + if (multicloudClient != null) { + Response res = multicloudClient.post(multicloudParam); + logger.debug("Multicloud Post response is: " + res); + } + + Stack responseStack = new Stack(); + responseStack.setStackStatus(HeatStatus.CREATED.toString()); + + return new StackInfoMapper(responseStack).map(); + } + + public Map queryStackForOutputs(String cloudSiteId, + String tenantId, String stackName) throws MsoException { + logger.debug("MsoHeatUtils.queryStackForOutputs)"); + StackInfo heatStack = this.queryStack(cloudSiteId, tenantId, stackName); + if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) { + return null; + } + return heatStack.getOutputs(); + } + + /** + * Query for a single stack (by Name) in a tenant. This call will always return a + * StackInfo object. If the stack does not exist, an "empty" StackInfo will be + * returned - containing only the stack name and a status of NOTFOUND. + * + * @param tenantId The Openstack ID of the tenant in which to query + * @param cloudSiteId The cloud identifier (may be a region) in which to query + * @param stackName The name of the stack to query (may be simple or canonical) + * @return A StackInfo object + * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception. + */ + @Override + public StackInfo queryStack (String cloudSiteId, String tenantId, String stackName) throws MsoException { + logger.debug ("Query multicloud HEAT stack: " + stackName + " in tenant " + tenantId); + + String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, stackName); + + RestClient multicloudClient = getMulticloudClient(multicloudEndpoint); + + if (multicloudClient != null) { + Response response = multicloudClient.get(); + logger.debug("Multicloud Get response is: " + response); + + return new StackInfo (stackName, HeatStatus.CREATED); + } + + return new StackInfo (stackName, HeatStatus.NOTFOUND); + } + + public StackInfo deleteStack (String cloudSiteId, String tenantId, String stackName) throws MsoException { + logger.debug ("Delete multicloud HEAT stack: " + stackName + " in tenant " + tenantId); + + String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, stackName); + + RestClient multicloudClient = getMulticloudClient(multicloudEndpoint); + + if (multicloudClient != null) { + Response response = multicloudClient.delete(); + logger.debug("Multicloud Get response is: " + response); + + return new StackInfo (stackName, HeatStatus.DELETING); + } + + return new StackInfo (stackName, HeatStatus.FAILED); + } + + // --------------------------------------------------------------- + // PRIVATE FUNCTIONS FOR USE WITHIN THIS CLASS + + + private String getMsbHost() { + // MSB_IP will be set as ONAP_IP environment parameter in install flow. + String msbIp = System.getenv().get(ONAP_IP); + + // if ONAP IP is not set. get it from config file. + if (null == msbIp || msbIp.isEmpty()) { + msbIp = env.getProperty("mso.msb-ip", DEFAULT_MSB_IP); + } + Integer msbPort = env.getProperty("mso.msb-port", Integer.class, DEFAULT_MSB_PORT); + + return UriBuilder.fromPath("").host(msbIp).port(msbPort).scheme("http").build().toString(); + } + + private String getMulticloudEndpoint(String cloudSiteId, String workloadId) throws MsoCloudSiteNotFound { + + CloudSite cloudSite = cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId)); + String endpoint = getMsbHost() + cloudSite.getIdentityService().getIdentityUrl(); + + if (workloadId != null) { + return endpoint + workloadId; + } else { + return endpoint; + } + } + + private RestClient getMulticloudClient(String endpoint) { + RestClient client = null; + try { + client= new HttpClient(UriBuilder.fromUri(endpoint).build().toURL(), + "application/json", TargetEntity.OPENSTACK_ADAPTER); + } catch (MalformedURLException e) { + logger.debug("Encountered malformed URL error getting multicloud rest client " + e.getMessage()); + } catch (IllegalArgumentException e) { + logger.debug("Encountered illegal argument getting multicloud rest client " + e.getMessage()); + } catch (UriBuilderException e) { + logger.debug("Encountered URI builder error getting multicloud rest client " + e.getMessage()); + } + return client; + } + + /** + * VduPlugin interface for instantiate function. + * + * Translate the VduPlugin parameters to the corresponding 'createStack' parameters, + * and then invoke the existing function. + */ + @Override + public VduInstance instantiateVdu ( + CloudInfo cloudInfo, + String instanceName, + Map inputs, + VduModelInfo vduModel, + boolean rollbackOnFailure) + throws VduException + { + String cloudSiteId = cloudInfo.getCloudSiteId(); + String tenantId = cloudInfo.getTenantId(); + + // Translate the VDU ModelInformation structure to that which is needed for + // creating the Heat stack. Loop through the artifacts, looking specifically + // for MAIN_TEMPLATE and ENVIRONMENT. Any other artifact will + // be attached as a FILE. + String heatTemplate = null; + Map nestedTemplates = new HashMap<>(); + Map files = new HashMap<>(); + String heatEnvironment = null; + + for (VduArtifact vduArtifact: vduModel.getArtifacts()) { + if (vduArtifact.getType() == ArtifactType.MAIN_TEMPLATE) { + heatTemplate = new String(vduArtifact.getContent()); + } + else if (vduArtifact.getType() == ArtifactType.NESTED_TEMPLATE) { + nestedTemplates.put(vduArtifact.getName(), new String(vduArtifact.getContent())); + } + else if (vduArtifact.getType() == ArtifactType.ENVIRONMENT) { + heatEnvironment = new String(vduArtifact.getContent()); + } + } + + try { + StackInfo stackInfo = createStack (cloudSiteId, + tenantId, + instanceName, + heatTemplate, + inputs, + true, // poll for completion + vduModel.getTimeoutMinutes(), + heatEnvironment, + nestedTemplates, + files, + rollbackOnFailure); + // Populate a vduInstance from the StackInfo + return stackInfoToVduInstance(stackInfo); + } + catch (Exception e) { + throw new VduException ("MsoMulticloudUtils (instantiateVDU): createStack Exception", e); + } + } + + + /** + * VduPlugin interface for query function. + */ + @Override + public VduInstance queryVdu (CloudInfo cloudInfo, String instanceId) + throws VduException + { + String cloudSiteId = cloudInfo.getCloudSiteId(); + String tenantId = cloudInfo.getTenantId(); + + try { + // Query the Cloudify Deployment object and populate a VduInstance + StackInfo stackInfo = queryStack (cloudSiteId, tenantId, instanceId); + + return stackInfoToVduInstance(stackInfo); + } + catch (Exception e) { + throw new VduException ("MsoMulticloudUtils (queryVdu): queryStack Exception ", e); + } + } + + + /** + * VduPlugin interface for delete function. + */ + @Override + public VduInstance deleteVdu (CloudInfo cloudInfo, String instanceId, int timeoutMinutes) + throws VduException + { + String cloudSiteId = cloudInfo.getCloudSiteId(); + String tenantId = cloudInfo.getTenantId(); + + try { + // Delete the Multicloud stack + StackInfo stackInfo = deleteStack (tenantId, cloudSiteId, instanceId, true); + + // Populate a VduInstance based on the deleted Cloudify Deployment object + VduInstance vduInstance = stackInfoToVduInstance(stackInfo); + + // Override return state to DELETED (MulticloudUtils sets to NOTFOUND) + vduInstance.getStatus().setState(VduStateType.DELETED); + + return vduInstance; + } + catch (Exception e) { + throw new VduException ("Delete VDU Exception", e); + } + } + + + /** + * VduPlugin interface for update function. + * + * Update is currently not supported in the MsoMulticloudUtils implementation of VduPlugin. + * Just return a VduException. + * + */ + @Override + public VduInstance updateVdu ( + CloudInfo cloudInfo, + String instanceId, + Map inputs, + VduModelInfo vduModel, + boolean rollbackOnFailure) + throws VduException + { + throw new VduException ("MsoMulticloudUtils: updateVdu interface not supported"); + } + + + /* + * Convert the local DeploymentInfo object (Cloudify-specific) to a generic VduInstance object + */ + protected VduInstance stackInfoToVduInstance (StackInfo stackInfo) + { + VduInstance vduInstance = new VduInstance(); + + // The full canonical name as the instance UUID + vduInstance.setVduInstanceId(stackInfo.getCanonicalName()); + vduInstance.setVduInstanceName(stackInfo.getName()); + + // Copy inputs and outputs + vduInstance.setInputs(stackInfo.getParameters()); + vduInstance.setOutputs(stackInfo.getOutputs()); + + // Translate the status elements + vduInstance.setStatus(stackStatusToVduStatus (stackInfo)); + + return vduInstance; + } + + private VduStatus stackStatusToVduStatus (StackInfo stackInfo) + { + VduStatus vduStatus = new VduStatus(); + + // Map the status fields to more generic VduStatus. + // There are lots of HeatStatus values, so this is a bit long... + HeatStatus heatStatus = stackInfo.getStatus(); + String statusMessage = stackInfo.getStatusMessage(); + + if (heatStatus == HeatStatus.INIT || heatStatus == HeatStatus.BUILDING) { + vduStatus.setState(VduStateType.INSTANTIATING); + vduStatus.setLastAction((new PluginAction ("create", "in_progress", statusMessage))); + } + else if (heatStatus == HeatStatus.NOTFOUND) { + vduStatus.setState(VduStateType.NOTFOUND); + } + else if (heatStatus == HeatStatus.CREATED) { + vduStatus.setState(VduStateType.INSTANTIATED); + vduStatus.setLastAction((new PluginAction ("create", "complete", statusMessage))); + } + else if (heatStatus == HeatStatus.UPDATED) { + vduStatus.setState(VduStateType.INSTANTIATED); + vduStatus.setLastAction((new PluginAction ("update", "complete", statusMessage))); + } + else if (heatStatus == HeatStatus.UPDATING) { + vduStatus.setState(VduStateType.UPDATING); + vduStatus.setLastAction((new PluginAction ("update", "in_progress", statusMessage))); + } + else if (heatStatus == HeatStatus.DELETING) { + vduStatus.setState(VduStateType.DELETING); + vduStatus.setLastAction((new PluginAction ("delete", "in_progress", statusMessage))); + } + else if (heatStatus == HeatStatus.FAILED) { + vduStatus.setState(VduStateType.FAILED); + vduStatus.setErrorMessage(stackInfo.getStatusMessage()); + } else { + vduStatus.setState(VduStateType.UNKNOWN); + } + + return vduStatus; + } +} -- cgit 1.2.3-korg