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 +++++++++++++++++++++ .../so/adapters/vnf/MsoVnfPluginAdapterImpl.java | 174 ++++---- .../onap/so/adapters/vnf/VnfAdapterRestUtils.java | 23 +- .../adapters/vnf/MsoVnfPluginAdapterImplTest.java | 111 ++++- .../java/org/onap/so/bpmn/mock/StubOpenStack.java | 6 + .../OpenstackResponse_AccessMulticloud.json | 40 ++ 9 files changed, 1057 insertions(+), 263 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 create mode 100644 adapters/mso-openstack-adapters/src/test/resources/__files/OpenstackResponse_AccessMulticloud.json 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; + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImpl.java index b440f7d521..e9567170dd 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImpl.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImpl.java @@ -29,7 +29,7 @@ * - base and volume module queries * - rollback logic * - logging and error handling - * + * * Then based on the orchestration mode of the VNF, it will invoke different VDU plug-ins * to perform the low level instantiations, deletions, and queries. At this time, the * set of available plug-ins is hard-coded, though in the future a dynamic selection @@ -81,6 +81,7 @@ import org.onap.so.openstack.exceptions.MsoExceptionCategory; import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry; import org.onap.so.openstack.utils.MsoHeatUtils; import org.onap.so.openstack.utils.MsoKeystoneUtils; +import org.onap.so.openstack.utils.MsoMulticloudUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; @@ -100,28 +101,31 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { private static MsoAlarmLogger alarmLogger = new MsoAlarmLogger (); private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters"; private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); - + @Autowired protected CloudConfig cloudConfig; - + @Autowired private VFModuleCustomizationRepository vfModuleCustomRepo; - + @Autowired private Environment environment; @Autowired protected MsoKeystoneUtils keystoneUtils; - + @Autowired protected MsoCloudifyUtils cloudifyUtils; - + @Autowired protected MsoHeatUtils heatUtils; - + + @Autowired + protected MsoMulticloudUtils multicloudUtils; + @Autowired protected VfModuleCustomizationToVduMapper vduMapper; - + /** * Health Check web method. Does nothing but return to show the adapter is deployed. */ @@ -192,9 +196,9 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { /** * This is the "Query VNF" web service implementation. - * + * * This really should be QueryVfModule, but nobody ever changed it. - * + * * The method returns an indicator that the VNF exists, along with its status and outputs. * The input "vnfName" will also be reflected back as its ID. * @@ -244,7 +248,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error); throw new VnfException (e); } - + if (vduInstance != null && vduInstance.getStatus().getState() != VduStateType.NOTFOUND) { vnfExists.value = Boolean.TRUE; status.value = vduStatusToVnfStatus(vduInstance); @@ -265,7 +269,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { return; } - + /** * This is the "Delete VNF" web service implementation. * This function is now unsupported and will return an error. @@ -278,7 +282,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { MsoRequest msoRequest) throws VnfException { MsoLogger.setLogContext (msoRequest); MsoLogger.setServiceName ("DeleteVnf"); - + // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules. LOGGER.debug ("DeleteVNF command attempted but not supported"); throw new VnfException ("DeleteVNF: Unsupported command", MsoExceptionCategory.USERDATA); @@ -289,7 +293,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { * A rollback object is returned to the client in a successful creation * response. The client can pass that object as-is back to the rollbackVnf * operation to undo the creation. - * + * * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup, * but APIs were apparently never updated. */ @@ -309,7 +313,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Rollback VF Module - nothing to roll back"); return; } - + // Get the elements of the VnfRollback object for easier access String cloudSiteId = rollback.getCloudSiteId (); String tenantId = rollback.getTenantId (); @@ -330,7 +334,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { try { // TODO: Get a reasonable timeout. Use a global property, or store the creation timeout in rollback object and use that. vduInstance = vduPlugin.deleteVdu(cloudInfo, vfModuleId, 5); - + LOGGER.debug("Rolled back VDU instantiation: " + vduInstance.getVduInstanceId()); LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from VDU Plugin", "VDU", "DeleteVdu", null); } @@ -354,7 +358,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { // DeploymentInfo object should be enhanced to report a better status internally. VduStatus vduStatus = vdu.getStatus(); VduStateType status = vduStatus.getState(); - + if (status == null) { return VnfStatus.UNKNOWN; } @@ -379,7 +383,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { { String type = templateParam.getParamType(); LOGGER.debug("Parameter: " + templateParam.getParamName() + " is of type " + type); - + if (type.equalsIgnoreCase("number")) { try { return Integer.valueOf(inputValue); @@ -400,7 +404,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } else if (type.equalsIgnoreCase("boolean")) { return new Boolean(inputValue); } - + // Nothing else matched. Return the original string return inputValue; } @@ -464,9 +468,9 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } } LOGGER.debug(sb.toString()); - return; + return; } - + private void sendMapToDebug(Map inputs) { int i = 0; StringBuilder sb = new StringBuilder("inputs:"); @@ -612,7 +616,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { { // Will capture execution time for metrics long startTime = System.currentTimeMillis (); - + MsoLogger.setLogContext (msoRequest); MsoLogger.setServiceName ("CreateVfModule"); @@ -625,14 +629,14 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error); throw new VnfException(error, MsoExceptionCategory.USERDATA); } - + // Clean up some inputs to make comparisons easier if (requestType == null) requestType = ""; - + if ("".equals(volumeGroupId) || "null".equals(volumeGroupId)) - volumeGroupId = null; - + volumeGroupId = null; + if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId)) baseVfModuleId = null; @@ -643,7 +647,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } else { this.sendMapToDebug(inputs); } - + // Check if this is for a "Volume" module boolean isVolumeRequest = false; if (requestType.startsWith("VOLUME")) { @@ -663,7 +667,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { vfRollback.setBaseGroupHeatStackId(baseVfModuleId); vfRollback.setModelCustomizationUuid(modelCustomizationUuid); vfRollback.setMode("CFY"); - + rollback.value = vfRollback; // Default rollback - no updates performed // Get the VNF/VF Module definition from the Catalog DB first. @@ -675,7 +679,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { try { vfModuleCust = vfModuleCustomRepo.findByModelCustomizationUUID(modelCustomizationUuid); - + if (vfModuleCust == null) { String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + modelCustomizationUuid; LOGGER.debug(error); @@ -692,7 +696,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { vnfResource = vfModuleCust.getVfModule().getVnfResources(); } catch (Exception e) { - + LOGGER.debug("unhandled exception in create VF - [Query]" + e.getMessage()); throw new VnfException("Exception during create VF " + e.getMessage()); } @@ -706,10 +710,10 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { CloudSite cloudSite = cloudSiteOp.get(); MavenLikeVersioning aicV = new MavenLikeVersioning(); aicV.setVersion(cloudSite.getCloudVersion()); - + String vnfMin = vnfResource.getAicVersionMin(); String vnfMax = vnfResource.getAicVersionMax(); - + if ( (vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) || (vnfMax != null && aicV.isMoreRecentThan(vnfMax))) { @@ -720,11 +724,11 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { throw new VnfException(error, MsoExceptionCategory.USERDATA); } // End Version check - - + + VduInstance vduInstance = null; CloudInfo cloudInfo = new CloudInfo (cloudSiteId, tenantId, null); - + // Use the VduPlugin. VduPlugin vduPlugin = getVduPlugin(cloudSiteId); @@ -746,12 +750,12 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { me.addContext ("CreateVFModule"); throw new VnfException (me); } - + // More precise handling/messaging if the Module already exists if (vduInstance != null && !(vduInstance.getStatus().getState() == VduStateType.NOTFOUND)) { VduStateType status = vduInstance.getStatus().getState(); LOGGER.debug ("Found Existing VDU, status=" + status); - + if (status == VduStateType.INSTANTIATED) { if (failIfExists != null && failIfExists) { // fail - it exists @@ -799,8 +803,8 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, vduInstance.getVduInstanceId()); } } - - + + // Collect outputs from Base Modules and Volume Modules Map baseModuleOutputs = null; Map volumeGroupOutputs = null; @@ -824,7 +828,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { me.addContext ("CreateVFModule(QueryVolume)"); throw new VnfException (me); } - + if (volumeVdu == null || volumeVdu.getStatus().getState() == VduStateType.NOTFOUND) { String error = "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR" ; LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, volumeGroupId, cloudSiteId, tenantId, error, "VDU", "queryVdu(volume)", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: Attached Volume Group DOES NOT EXIST"); @@ -837,7 +841,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs"); } } - + // If this is an Add-On Module, query the Base Module outputs // Note: This will be performed whether or not the current request is for an // Add-On Volume Group or Add-On VF Module @@ -847,7 +851,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { vfRollback.setIsBase(true); } else { LOGGER.debug("This is an Add-On Module request"); - + // Add-On Modules should always have a Base, but just treat as a warning if not provided. // Add-on Volume requests may or may not specify a base. if (!isVolumeRequest && baseVfModuleId == null) { @@ -867,12 +871,12 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, baseVfModuleId, cloudSiteId, tenantId, "VDU", "queryVdu(Base)", MsoLogger.ErrorCode.DataError, "Exception - queryVdu(Base)", me); LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "VDU", "QueryVdu(Base)", baseVfModuleId); LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error); - + // Convert to a generic VnfException me.addContext ("CreateVFModule(QueryBase)"); throw new VnfException (me); } - + if (baseVdu == null || baseVdu.getStatus().getState() == VduStateType.NOTFOUND) { String error = "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR" ; LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, baseVfModuleId, cloudSiteId, tenantId, error, "VDU", "queryVdu(Base)", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: Base Module DOES NOT EXIST"); @@ -886,14 +890,14 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } } } - + // NOTE: For this section, heatTemplate is used for all template artifacts. // In final implementation (post-POC), the template object would either be generic or there would // be a separate DB Table/Object for different sub-orchestrators. // NOTE: The template is fixed for the VF Module. The environment is part of the customization. - + HeatTemplate heatTemplate = null; HeatEnvironment heatEnvironment = null; if (isVolumeRequest) { @@ -903,7 +907,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { heatTemplate = vfModule.getModuleHeatTemplate(); heatEnvironment = vfModuleCust.getHeatEnvironment(); } - + if (heatTemplate == null) { String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType=" + requestType; LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Template ID", vfModuleType, "VNF", "", MsoLogger.ErrorCode.DataError, error); @@ -914,7 +918,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } else { LOGGER.debug ("Got HEAT Template from DB: " + heatTemplate.getHeatTemplate()); } - + if (heatEnvironment == null) { String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType; LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", "OpenStack", "", MsoLogger.ErrorCode.DataError, error); @@ -927,15 +931,15 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { LOGGER.debug ("Got Heat Environment from DB: " + heatEnvironment.getEnvironment()); } - + // Create the combined set of parameters from the incoming request, base-module outputs, // volume-module outputs. Also, convert all variables to their native object types. - + HashMap goldenInputs = new HashMap(); List extraInputs = new ArrayList(); Boolean skipInputChecks = false; - + if (skipInputChecks) { goldenInputs = new HashMap(); for (String key : inputs.keySet()) { @@ -945,10 +949,10 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { else { // Build maps for the parameters (including aliases) to simplify checks HashMap params = new HashMap(); - + Set paramSet = heatTemplate.getParameters(); LOGGER.debug("paramSet has " + paramSet.size() + " entries"); - + for (HeatTemplateParam htp : paramSet) { params.put(htp.getParamName(), htp); @@ -958,7 +962,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { params.put(alias, htp); } } - + // First, convert all inputs to their "template" type for (String key : inputs.keySet()) { if (params.containsKey(key)) { @@ -973,11 +977,22 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { extraInputs.add(key); } } - + if (!extraInputs.isEmpty()) { + // Add directive inputs + String[] directives = { "oof_directives", "sdnc_directives" }; + for (String key : directives) { + if (extraInputs.contains(key)) { + goldenInputs.put(key, inputs.get(key)); + extraInputs.remove(key); + if (extraInputs.isEmpty()) { + break; + } + } + } LOGGER.debug("Ignoring extra inputs: " + extraInputs); } - + // Next add in Volume Group Outputs if there are any. Copy directly without conversions. if (volumeGroupOutputs != null && !volumeGroupOutputs.isEmpty()) { for (String key : volumeGroupOutputs.keySet()) { @@ -986,7 +1001,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } } } - + // Next add in Base Module Outputs if there are any. Copy directly without conversions. if (baseModuleOutputs != null && !baseModuleOutputs.isEmpty()) { for (String key : baseModuleOutputs.keySet()) { @@ -995,14 +1010,13 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } } } - + // TODO: The model should support a mechanism to pre-assign default parameter values // per "customization" (i.e. usage) of a given module. In HEAT, this is specified by // an Environment file. There is not a general mechanism in the model to handle this. // For the general case, any such parameter/values can be added dynamically to the // inputs (only if not already specified). - - + // Check that required parameters have been supplied from any of the sources String missingParams = null; boolean checkRequiredParameters = true; @@ -1017,7 +1031,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { // No problem - default is true LOGGER.debug ("An exception occured trying to get property " + MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS, e); } - + // Do the actual parameter checking. // Include looking at the ENV file as a valid definition of a parameter value. // TODO: This handling of ENV applies only to Heat. A general mechanism to @@ -1040,7 +1054,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } } } - + if (missingParams != null) { if (checkRequiredParameters) { // Problem - missing one or more required parameters @@ -1056,12 +1070,12 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } } // NOTE: END PARAMETER CHECKING - - + + // Here we go... ready to deploy the VF Module. long instantiateVduStartTime = System.currentTimeMillis (); if (backout == null) backout = true; - + try { // Construct the VDU Model structure to pass to the targeted VduPlugin VduModelInfo vduModel = null; @@ -1070,10 +1084,10 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { } else { vduModel = vduMapper.mapVfModuleCustVolumeToVdu(vfModuleCust); } - + // Invoke the VduPlugin to instantiate the VF Module vduInstance = vduPlugin.instantiateVdu(cloudInfo, vfModuleName, goldenInputs, vduModel, backout); - + LOGGER.recordMetricEvent (instantiateVduStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from VduPlugin", "VDU", "instantiateVdu", vfModuleName); } catch (VduException me) { @@ -1100,7 +1114,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.UnknownError, error); LOGGER.debug("Unhandled exception at vduPlugin.instantiateVdu", e); throw new VnfException("Exception during instantiateVdu: " + e.getMessage()); - } + } // Reach this point if create is successful. @@ -1108,7 +1122,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { vfRollback.setVnfCreated (true); vfRollback.setVnfId (vduInstance.getVduInstanceId()); vnfId.value = vduInstance.getVduInstanceId(); - outputs.value = copyStringOutputs (vduInstance.getOutputs ()); + outputs.value = copyStringOutputs (vduInstance.getOutputs ()); rollback.value = vfRollback; @@ -1117,7 +1131,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { return; } - + public void deleteVfModule (String cloudSiteId, String tenantId, String vfModuleId, @@ -1126,15 +1140,15 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { { MsoLogger.setLogContext (msoRequest); MsoLogger.setServiceName ("DeleteVfModule"); - + LOGGER.debug ("Deleting VF Module " + vfModuleId + " in " + cloudSiteId + "/" + tenantId); // Will capture execution time for metrics long startTime = System.currentTimeMillis (); - + // Capture the output parameters on a delete, so need to query first VduInstance vduInstance = null; CloudInfo cloudInfo = new CloudInfo(cloudSiteId, tenantId, null); - + // Use the VduPlugin. VduPlugin vduPlugin = getVduPlugin(cloudSiteId); @@ -1152,7 +1166,7 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error); throw new VnfException (e); } - + // call method which handles the conversion from Map to Map for our expected Object types outputs.value = convertMapStringObjectToStringString(vduInstance.getOutputs()); @@ -1214,16 +1228,18 @@ public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter { if (cloudSiteOp.isPresent()) { CloudSite cloudSite = cloudSiteOp.get(); String orchestrator = cloudSite.getOrchestrator(); - + if (orchestrator.equalsIgnoreCase("CLOUDIFY")) { - return cloudifyUtils; + return cloudifyUtils; } else if (orchestrator.equalsIgnoreCase("HEAT")) { return heatUtils; } + if (orchestrator.equalsIgnoreCase("MULTICLOUD")) { + LOGGER.debug ("Got MulticloudUtils for vduPlugin"); + return multicloudUtils; } } - - // Default - return HEAT plugin, though will fail later + // Default - return HEAT plugin, though will fail later return heatUtils; } -} \ No newline at end of file +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/VnfAdapterRestUtils.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/VnfAdapterRestUtils.java index 4da026f454..88f102c2a5 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/VnfAdapterRestUtils.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/VnfAdapterRestUtils.java @@ -7,9 +7,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. @@ -32,16 +32,19 @@ import org.springframework.stereotype.Component; public class VnfAdapterRestUtils { private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA, VnfAdapterRestUtils.class); - + @Autowired private CloudConfig cloudConfig; - + @Autowired private MsoVnfCloudifyAdapterImpl cloudifyImpl; - + @Autowired private MsoVnfAdapterImpl vnfImpl; - + + @Autowired + private MsoVnfPluginAdapterImpl vnfPluginImpl; + /* * Choose which implementation of VNF Adapter to use, based on the orchestration mode. * Currently, the two supported orchestrators are HEAT and CLOUDIFY. @@ -72,7 +75,7 @@ public class VnfAdapterRestUtils LOGGER.debug ("GetVnfAdapterImpl: mode=" + mode); MsoVnfAdapter vnfAdapter = null; - + // TODO: Make this more dynamic (e.g. Service Loader) if ("CLOUDIFY".equalsIgnoreCase(mode)) { LOGGER.debug ("GetVnfAdapterImpl: Return Cloudify Adapter"); @@ -82,12 +85,16 @@ public class VnfAdapterRestUtils LOGGER.debug ("GetVnfAdapterImpl: Return Heat Adapter"); vnfAdapter = vnfImpl; } + else if ("MULTICLOUD".equalsIgnoreCase(mode)) { + LOGGER.debug ("GetVnfAdapterImpl: Return Plugin (multicloud) Adapter"); + vnfAdapter = vnfPluginImpl; + } else { // Don't expect this, but default is the HEAT adapter LOGGER.debug ("GetVnfAdapterImpl: Return Default (Heat) Adapter"); vnfAdapter = vnfImpl; } - + return vnfAdapter; } diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImplTest.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImplTest.java index b21f1f3db2..77ef8d4776 100644 --- a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImplTest.java +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImplTest.java @@ -7,9 +7,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. @@ -20,15 +20,31 @@ package org.onap.so.adapters.vnf; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.client.WireMock; import org.apache.http.HttpStatus; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.MockitoAnnotations; +import org.onap.so.adapters.vdu.CloudInfo; +import org.onap.so.adapters.vdu.VduInstance; +import org.onap.so.adapters.vdu.VduStateType; +import org.onap.so.adapters.vdu.VduStatus; import org.onap.so.adapters.vnf.exceptions.VnfException; +import org.onap.so.db.catalog.beans.AuthenticationType; +import org.onap.so.db.catalog.beans.CloudIdentity; +import org.onap.so.db.catalog.beans.CloudSite; +import org.onap.so.db.catalog.beans.ServerType; import org.onap.so.entity.MsoRequest; +import org.onap.so.openstack.beans.HeatStatus; +import org.onap.so.openstack.beans.StackInfo; import org.onap.so.openstack.beans.VnfRollback; +import org.onap.so.openstack.utils.MsoMulticloudUtils; import org.springframework.beans.factory.annotation.Autowired; +import javax.ws.rs.core.MediaType; import javax.xml.ws.Holder; import java.util.HashMap; import java.util.Map; @@ -36,11 +52,15 @@ import java.util.Map; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.reset; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static org.mockito.Mockito.when; import static org.onap.so.bpmn.mock.StubOpenStack.mockOpenStackGetStackVfModule_200; import static org.onap.so.bpmn.mock.StubOpenStack.mockOpenStackGetStackVfModule_404; import static org.onap.so.bpmn.mock.StubOpenStack.mockOpenStackResponseAccess; +import static org.onap.so.bpmn.mock.StubOpenStack.mockOpenStackResponseAccessMulticloud; +import static org.onap.so.bpmn.mock.StubOpenStack.mockOpenstackGetWithResponse; public class MsoVnfPluginAdapterImplTest extends BaseRestTestUtils { @@ -52,6 +72,53 @@ public class MsoVnfPluginAdapterImplTest extends BaseRestTestUtils { String vnfName = "DEV-VF-1802-it3-pwt3-v6-vSAMP10a-addon2-Replace-1001/stackId"; + /*** + * Before each test execution, updating IdentityUrl port value to the ramdom wireMockPort + * Since URL will be used as a rest call and required to be mocked in unit tests + */ + @Before + public void setUp() throws Exception { + reset(); + mapper = new ObjectMapper(); + + CloudIdentity identity = new CloudIdentity(); + identity.setId("MTN13"); + identity.setMsoId("m93945"); + identity.setMsoPass("93937EA01B94A10A49279D4572B48369"); + identity.setAdminTenant("admin"); + identity.setMemberRole("admin"); + identity.setTenantMetadata(new Boolean(true)); + identity.setIdentityUrl("http://localhost:"+wireMockPort+"/v2.0"); + identity.setIdentityAuthenticationType(AuthenticationType.USERNAME_PASSWORD); + + CloudSite cloudSite = new CloudSite(); + cloudSite.setId("MTN13"); + cloudSite.setCloudVersion("3.0"); + cloudSite.setClli("MDT13"); + cloudSite.setRegionId("MTN13"); + cloudSite.setOrchestrator("multicloud" + + ""); + identity.setIdentityServerType(ServerType.KEYSTONE); + cloudSite.setIdentityService(identity); + + + + stubFor(get(urlPathEqualTo("/cloudSite/MTN13")).willReturn(aResponse() + .withBody(getBody(mapper.writeValueAsString(cloudSite),wireMockPort, "")) + .withHeader(org.apache.http.HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .withStatus(HttpStatus.SC_OK))); + stubFor(get(urlPathEqualTo("/cloudSite/DEFAULT")).willReturn(aResponse() + .withBody(getBody(mapper.writeValueAsString(cloudSite),wireMockPort, "")) + .withHeader(org.apache.http.HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON) + .withStatus(HttpStatus.SC_OK))); + stubFor(get(urlPathEqualTo("/cloudIdentity/MTN13")).willReturn(aResponse() + .withBody(getBody(mapper.writeValueAsString(identity),wireMockPort, "")) + .withHeader(org.apache.http.HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON) + .withStatus(HttpStatus.SC_OK))); + cloudConfig.getCloudSite("MTN13").get().getIdentityService().setIdentityUrl("http://localhost:" + wireMockPort + "/v2.0"); + + } + @Test public void createVfModule_ModelCustUuidIsNull() throws Exception { expectedException.expect(VnfException.class); @@ -88,18 +155,52 @@ public class MsoVnfPluginAdapterImplTest extends BaseRestTestUtils { new Holder()); } - @Test + /* @Test public void createVfModule_INSTANTIATED() throws Exception { mockOpenStackResponseAccess(wireMockPort); mockOpenStackGetStackVfModule_200(); + MsoRequest msoRequest = getMsoRequest(); Map map = new HashMap<>(); map.put("key1", "value1"); - msoVnfPluginAdapter.createVfModule("mtn13", "88a6ca3ee0394ade9403f075db23167e", "vnf", "1", vnfName, "VFMOD", - "volumeGroupHeatStackId|1", "baseVfHeatStackId", "9b339a61-69ca-465f-86b8-1c72c582b8e8", map, + msoVnfPluginAdapter.createVfModule("MTN13", "88a6ca3ee0394ade9403f075db23167e", "vnf", "1", vnfName, "VFMOD", + null, "baseVfHeatStackId", "9b339a61-69ca-465f-86b8-1c72c582b8e8", map, + Boolean.FALSE, Boolean.TRUE, Boolean.FALSE, msoRequest, new Holder<>(), new Holder>(), + new Holder()); + }*/ + + @Test + public void createVfModule_INSTANTIATED_Multicloud() throws Exception { + mockOpenStackResponseAccessMulticloud(wireMockPort); + mockOpenStackGetStackVfModule_200(); + + MsoRequest msoRequest = getMsoRequest(); + Map map = new HashMap<>(); + map.put("key1", "value1"); + msoVnfPluginAdapter.createVfModule("MTN13", "88a6ca3ee0394ade9403f075db23167e", "vnf", "1", vnfName, "VFMOD", + null, "baseVfHeatStackId", "9b339a61-69ca-465f-86b8-1c72c582b8e8", map, + Boolean.FALSE, Boolean.TRUE, Boolean.FALSE, msoRequest, new Holder<>(), new Holder>(), + new Holder()); + } + + /* + @Test + public void createVfModule_Multicloud() throws Exception { + expectedException.expect(VnfException.class); + mockOpenStackResponseAccessMulticloud(wireMockPort); + mockOpenStackGetStackVfModule_404(); + + MsoRequest msoRequest = getMsoRequest(); + Map map = new HashMap<>(); + map.put("key1", "value1"); + map.put("oof_directives", "{ abc: 123 }"); + map.put("sdnc_directives", "{ def: 456 }"); + msoVnfPluginAdapter.createVfModule("MTN13", "88a6ca3ee0394ade9403f075db23167e", "vnf", "1", vnfName, "VFMOD", + null, "baseVfHeatStackId", "9b339a61-69ca-465f-86b8-1c72c582b8e8", map, Boolean.FALSE, Boolean.TRUE, Boolean.FALSE, msoRequest, new Holder<>(), new Holder>(), new Holder()); } + */ @Test public void createVfModule_queryVduNotFoundWithVolumeGroupId() throws Exception { diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/bpmn/mock/StubOpenStack.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/bpmn/mock/StubOpenStack.java index 98d5f7eb5f..569a845caa 100644 --- a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/bpmn/mock/StubOpenStack.java +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/bpmn/mock/StubOpenStack.java @@ -49,6 +49,12 @@ public class StubOpenStack { .withStatus(HttpStatus.SC_OK))); } + public static void mockOpenStackResponseAccessMulticloud(int port) throws IOException { + stubFor(post(urlPathEqualTo("/v2.0/tokens")).willReturn(aResponse().withHeader("Content-Type", "application/json") + .withBody(getBodyFromFile("OpenstackResponse_AccessMulticloud.json", port, "/mockPublicUrl")) + .withStatus(HttpStatus.SC_OK))); + } + public static void mockOpenStackResponseAccessQueryNetwork(int port) throws IOException { stubFor(post(urlPathEqualTo("/v2.0/tokens")) .withRequestBody(containing("tenantId")) diff --git a/adapters/mso-openstack-adapters/src/test/resources/__files/OpenstackResponse_AccessMulticloud.json b/adapters/mso-openstack-adapters/src/test/resources/__files/OpenstackResponse_AccessMulticloud.json new file mode 100644 index 0000000000..23fbe840e4 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/__files/OpenstackResponse_AccessMulticloud.json @@ -0,0 +1,40 @@ +{ + "access": { + "token": { + "id": "tokenId1234", + "issued_at": null, + "expires": "1517418429142", + "tenant": null + }, + "serviceCatalog": [ + { + "type": "orchestration", + "name": null, + "endpoints": [ + { + "region": "MTN13", + "publicURL": "port", + "internalURL": null, + "adminURL": null + } + ], + "endpointsLinks": null + }, + { + "type": "network", + "name": null, + "endpoints": [ + { + "region": "MTN13", + "publicURL": "port", + "internalURL": null, + "adminURL": null + } + ], + "endpointsLinks": null + } + ], + "user": null, + "metadata": null + } +} -- cgit 1.2.3-korg