Initial commit of VDU plugin implementation 93/38193/6
authorKalkere Ramesh, Sharan (sk720x) <sk720x@att.com>
Fri, 23 Mar 2018 20:11:44 +0000 (16:11 -0400)
committerRob Daugherty <rd472p@att.com>
Wed, 28 Mar 2018 00:35:13 +0000 (20:35 -0400)
This was documented in the API we committed to at M3.

Change-Id: Id0c886d956dc46dba71157cfa35d88844028e7fd
Issue-ID: SO-511
Signed-off-by: Kalkere Ramesh, Sharan (sk720x) <sk720x@att.com>
21 files changed:
adapters/mso-adapter-utils/pom.xml
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/CloudInfo.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/PluginAction.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduArtifact.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduException.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduInstance.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduModelInfo.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduPlugin.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduStateType.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduStatus.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/cloudify/utils/MsoCloudifyUtils.java
adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/openstack/utils/MsoHeatUtils.java
adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/adapters/vdu/BeansTest.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/cloudify/utils/MsoCloudifyUtilsTest.java
adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/cloudify/utils/MsoCloudifyUtilsTest2.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/openstack/utils/MsoHeatUtilsTest2.java [new file with mode: 0644]
adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vdu/mapper/VfModuleCustomizationToVduMapper.java [new file with mode: 0644]
adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfPluginAdapterImpl.java [new file with mode: 0644]
adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VnfAdapterRestUtils.java
adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/MsoVnfPluginAdapterImplTest.java [new file with mode: 0644]
cloudify-client/src/main/java/org/openecomp/mso/cloudify/v3/model/AzureConfig.java [new file with mode: 0644]

index e264a20..ee70fb6 100644 (file)
                        <artifactId>snakeyaml</artifactId>
                        <version>1.15</version>
                </dependency>
-
+       <dependency>
+                       <groupId>com.shazam</groupId>
+                       <artifactId>shazamcrest</artifactId>
+                       <version>0.11</version>
+                       <scope>test</scope>
+                       <exclusions>
+                               <exclusion>
+                               <groupId>com.google.guava</groupId>
+                               <artifactId>guava</artifactId>
+                               </exclusion>
+                               <exclusion>
+                               <groupId>org.apache.commons</groupId>
+                               <artifactId>commons-lang3</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
        </dependencies>
 </project>
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/CloudInfo.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/CloudInfo.java
new file mode 100644 (file)
index 0000000..0355245
--- /dev/null
@@ -0,0 +1,69 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+/**\r
+ * Cloud information structure for deploying/managing a VDU.  Includes the cloud site\r
+ * as well as tenant information within the site.  Currently this is defined as a \r
+ * cloud site ID. which would map to a CloudConfig entry.\r
+ * Perhaps the CloudConfig entry itself should be provided, instead of requiring each\r
+ * plug-in to query it.\r
+ * \r
+ * The meaning of 'tenant' may differ by cloud provider, but every cloud supports some\r
+ * sort of tenant partitioning.\r
+ * \r
+ */\r
+public class CloudInfo {\r
+       \r
+       private String cloudSiteId;\r
+       private String tenantId;        \r
+       private String tenantName;//bpmn query and pass\r
+       \r
+       public CloudInfo() {\r
+       }\r
+       \r
+       public CloudInfo (String cloudSiteId, String tenantId, String tenantName) {\r
+               this.cloudSiteId = cloudSiteId;\r
+               this.tenantId = tenantId;\r
+               this.tenantName = tenantName;\r
+       }\r
+       \r
+       public String getCloudSiteId() {\r
+               return cloudSiteId;\r
+       }\r
+       public void setCloudSiteId(String cloudSiteId) {\r
+               this.cloudSiteId = cloudSiteId;\r
+       }\r
+       public String getTenantId() {\r
+               return tenantId;\r
+       }\r
+       public void setTenantId(String tenantId) {\r
+               this.tenantId = tenantId;\r
+       }\r
+       public String getTenantName() {\r
+               return tenantName;\r
+       }\r
+       public void setTenantName(String tenantName) {\r
+               this.tenantName = tenantName;\r
+       }\r
+       \r
+       \r
+}
\ No newline at end of file
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/PluginAction.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/PluginAction.java
new file mode 100644 (file)
index 0000000..1f3cf2f
--- /dev/null
@@ -0,0 +1,63 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+/**\r
+ * Java beam representing a detailed action performed within a plugin during VDU\r
+ * orchestration. This allows the plugin to convey more detailed information about\r
+ * recent activities it has performed.  It is primarily intended for logging and\r
+ * troubleshooting, so plugins are free to populate this as desired.\r
+ */\r
+public class PluginAction {\r
+       \r
+       private String action;  \r
+       private String status;\r
+       private String rawMessage;      \r
+       \r
+       public PluginAction () {\r
+       }\r
+       \r
+       public PluginAction (String action, String status, String rawMessage) {\r
+               this.action = action;\r
+               this.status = status;\r
+               this.rawMessage = rawMessage;\r
+       }\r
+       \r
+       public String getAction() {\r
+               return action;\r
+       }\r
+       public void setAction(String action) {\r
+               this.action = action;\r
+       }\r
+       public String getStatus() {\r
+               return status;\r
+       }\r
+       public void setStatus(String status) {\r
+               this.status = status;\r
+       }\r
+       public String getRawMessage() {\r
+               return rawMessage;\r
+       }\r
+       public void setRawMessage(String rawMessage) {\r
+               this.rawMessage = rawMessage;\r
+       }\r
+       \r
+}
\ No newline at end of file
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduArtifact.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduArtifact.java
new file mode 100644 (file)
index 0000000..394d13d
--- /dev/null
@@ -0,0 +1,64 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+public class VduArtifact {\r
+       \r
+       // Enumerate the types of artifacts permitted.  This may need to be a variable string\r
+       // value if arbitrary (cloud-specific) artifacts may be attached to VDUs in ASDC.\r
+       public enum ArtifactType {\r
+               MAIN_TEMPLATE, NESTED_TEMPLATE, CONFIG_FILE, SCRIPT_FILE, TEXT_FILE, ENVIRONMENT\r
+       }\r
+       \r
+       private String name;\r
+       private byte[] content;\r
+       private ArtifactType type;\r
+       \r
+       // Default constructor\r
+       public VduArtifact() {}\r
+       \r
+       // Fully specified constructor\r
+       public VduArtifact (String name, byte[] content, ArtifactType type) {\r
+               this.name = name;\r
+               this.content = content;\r
+               this.type = type;\r
+       }\r
+       \r
+       public String getName() {\r
+               return name;\r
+       }\r
+       public void setName (String name) {\r
+               this.name = name;\r
+       }\r
+       public byte[] getContent() {\r
+               return content;\r
+       }\r
+       public void setContent(byte[] content) {\r
+               this.content = content;\r
+       }\r
+       public ArtifactType getType() {\r
+               return type;\r
+       }\r
+       public void setType(ArtifactType type) {\r
+               this.type = type;\r
+       }       \r
+       \r
+}
\ No newline at end of file
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduException.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduException.java
new file mode 100644 (file)
index 0000000..3fd1d2e
--- /dev/null
@@ -0,0 +1,60 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * OPENECOMP - MSO\r
+ * ================================================================================\r
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+import org.openecomp.mso.openstack.exceptions.MsoException;\r
+import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;\r
+\r
+/**\r
+ * OpenStack exception.\r
+ */\r
+public class VduException extends MsoException\r
+{\r
+       \r
+       /**\r
+     * Serialization id.\r
+     */\r
+    private static final long serialVersionUID = 3313636124141766495L;\r
+    \r
+       /**\r
+        * Constructor to create a new VduException instance\r
+        * @param detail error details\r
+        */\r
+       public VduException (String detail) {\r
+               // Set the detailed error as the Exception 'message'\r
+               super(detail);\r
+               // TODO:  Need a more generic category than OPENSTACK\r
+               super.category = MsoExceptionCategory.OPENSTACK;\r
+       }\r
+       \r
+       /**\r
+        * Constructor to create a new VduException instance\r
+        * @param detail error details\r
+        * @param e the cause\r
+        */\r
+       public VduException (String detail, Exception e) {\r
+               // Set the detailed error as the Exception 'message'\r
+               super(detail, e);\r
+               // TODO:  Need a more generic category than OPENSTACK\r
+               super.category = MsoExceptionCategory.OPENSTACK;\r
+       }\r
+\r
+}
\ No newline at end of file
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduInstance.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduInstance.java
new file mode 100644 (file)
index 0000000..5a5a6ab
--- /dev/null
@@ -0,0 +1,80 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * OPENECOMP - MSO\r
+ * ================================================================================\r
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+import java.util.Map;\r
+import java.util.HashMap;\r
+\r
+/*\r
+ * This Java bean class relays VDU status information in a cloud-agnostic format.\r
+ * \r
+ * This bean is returned by all implementors of the VduPlugin interface operations\r
+ * (instantiate, query, delete).\r
+ */\r
+\r
+public class VduInstance {\r
+       // Set defaults for everything\r
+       protected String vduInstanceId;\r
+       protected String vduInstanceName;\r
+       protected VduStatus status;\r
+       protected Map<String, Object> outputs = new HashMap<>();\r
+       protected Map<String, Object> inputs = new HashMap<>();\r
+\r
+       public String getVduInstanceId() {\r
+               return vduInstanceId;\r
+       }\r
+\r
+       public void setVduInstanceId(String vduInstanceId) {\r
+               this.vduInstanceId = vduInstanceId;\r
+       }\r
+\r
+       public String getVduInstanceName() {\r
+               return vduInstanceName;\r
+       }\r
+\r
+       public void setVduInstanceName(String vduInstanceName) {\r
+               this.vduInstanceName = vduInstanceName;\r
+       }\r
+\r
+       public VduStatus getStatus() {\r
+               return status;\r
+       }\r
+\r
+       public void setStatus(VduStatus status) {\r
+               this.status = status;\r
+       }\r
+\r
+       public Map<String, Object> getOutputs() {\r
+               return outputs;\r
+       }\r
+\r
+       public void setOutputs(Map<String, Object> outputs) {\r
+               this.outputs = outputs;\r
+       }\r
+\r
+       public Map<String, Object> getInputs() {\r
+               return inputs;\r
+       }\r
+\r
+       public void setInputs(Map<String, Object> inputs) {\r
+               this.inputs = inputs;\r
+       }\r
+}
\ No newline at end of file
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduModelInfo.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduModelInfo.java
new file mode 100644 (file)
index 0000000..3cef292
--- /dev/null
@@ -0,0 +1,50 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+public class VduModelInfo {    \r
+       private String modelCustomizationUUID;\r
+       private int timeoutMinutes;\r
+       private List<VduArtifact> artifacts = new ArrayList<>();\r
+       \r
+       public String getModelCustomizationUUID() {\r
+               return modelCustomizationUUID;\r
+       }\r
+       public void setModelCustomizationUUID(String modelCustomizationUUID) {\r
+               this.modelCustomizationUUID = modelCustomizationUUID;\r
+       }\r
+       public int getTimeoutMinutes() {\r
+               return timeoutMinutes;\r
+       }\r
+       public void setTimeoutMinutes(int timeoutMinutes) {\r
+               this.timeoutMinutes = timeoutMinutes;\r
+       }\r
+       public List<VduArtifact> getArtifacts() {\r
+               return artifacts;\r
+       }\r
+       public void setArtifacts(List<VduArtifact> artifacts) {\r
+               this.artifacts = artifacts;\r
+       }\r
+       \r
+}
\ No newline at end of file
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduPlugin.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduPlugin.java
new file mode 100644 (file)
index 0000000..3484646
--- /dev/null
@@ -0,0 +1,186 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+/**\r
+ * This interface defines a common API for template-based cloud deployments.\r
+ * The methods here should be adaptable for Openstack (Heat), Cloudify (TOSCA),\r
+ * Aria (TOSCA), Multi-VIM (TBD), and others (e.g. Azure Resource Manager).\r
+ * \r
+ * The deployed instances are referred to here as Virtual Deployment Units (VDUs).\r
+ * The package of templates that define a give VDU is referred to as its blueprint.\r
+ * \r
+ * Template-based orchestrators all follow a similar template/blueprint model.\r
+ * - One main template that is the top level definition\r
+ * - Optional nested templates referenced/included by the main template\r
+ * - Optional files attached to the template package, typically containing\r
+ *   configuration files, install scripts, orchestration scripts, etc.\r
+ *   \r
+ * The main template also defines the required inputs for creating a new instance,\r
+ * and output values exposed by successfully deployed instances.  Inputs and outputs\r
+ * may include simple or complex (JSON) data types.\r
+ *   \r
+ * Each implementation of this interface is expected to understand the MSO CloudConfig\r
+ * to obtain the credentials for its sub-orchestrator and the targeted cloud.\r
+ * The sub-orchestrator may have different credentials from the cloud (e.g. an Aria \r
+ * instance in front of an Openstack cloud) or they may be the same (e.g. Heat)\r
+ */\r
+import java.util.Map;\r
+\r
+public interface VduPlugin {\r
+\r
+    /**\r
+     * The instantiateVdu interface deploys a new VDU instance from a vdu model package.\r
+     * \r
+     * For some VIMs, this may be a single command (e.g. Heat -> create stack) or may\r
+     * require a series of API calls (e.g. Cloudify -> upload blueprint, create deployment,\r
+     * execute install workflow).  These details are hidden within the plug-in implementation.\r
+     * The instantiation should be fully completed before returning.  On failures, this\r
+     * method is expected to back out the attempt, leaving the cloud in its previous state.\r
+     * \r
+     * It is expected that parameters have been validated and contain at minimum the\r
+     * required parameters for the given template with no extra parameters.\r
+     *\r
+     * The VDU name supplied by the caller will be globally unique, and identify the artifact\r
+     * in A&AI.  Inventory is managed by the higher levels invoking this function.\r
+     *\r
+     * @param cloudInfo The target cloud + tenant identifiers for the VDU.\r
+     * @param instanceName A unique name for the VDU instance to update.\r
+     * @param inputs A map of key/value inputs.  Values may be strings, numbers, or JSON objects.\r
+     *                 Will completely replace any inputs provided on the original instantiation.\r
+     * @param vduModel Object containing the collection of templates and files that comprise\r
+     *                 the blueprint for this VDU.\r
+     * @param rollbackOnFailure Flag to preserve or roll back the update on Failure.  Should normally\r
+     *         be True except in troubleshooting/debug cases.  Might not be supported in all plug-ins.\r
+     * \r
+     * @return A VduInstance object\r
+     * @throws VduException Thrown if the sub-orchestrator API calls fail or if a timeout occurs.\r
+     * Various subclasses of VduException may be thrown.\r
+     */\r
+    public VduInstance instantiateVdu (\r
+                               CloudInfo cloudInfo,\r
+                               String instanceName,\r
+                               Map<String,Object> inputs,\r
+                               VduModelInfo vduModel,\r
+                               boolean rollbackOnFailure)\r
+                       throws VduException;\r
+    \r
+    /**\r
+     * Query a deployed VDU instance.  This call will return a VduInstance object, or null\r
+     * if the deployment does not exist.\r
+     * \r
+     * Some VIM orchestrators identify deployment instances by string UUIDs, and others \r
+     * by integers.  In the latter case, the ID will be passed in as a numeric string.\r
+     *\r
+     * The returned VduInstance object contains the input and output parameter maps,\r
+     * as well as other properties of the deployment (name, status, last action, etc.).\r
+     * \r
+     * @param cloudInfo The target cloud + tenant identifiers for the VDU.\r
+     * @param vduInstanceId The ID of the deployment to query\r
+     * \r
+     * @return A VduInstance object\r
+     * @throws VduException Thrown if the sub-orchestrator API calls fail or if a timeout occurs.\r
+     * Various subclasses of VduException may be thrown.\r
+     */\r
+    public VduInstance queryVdu (\r
+                               CloudInfo cloudInfo,\r
+                               String vduInstanceId)\r
+                       throws VduException;\r
+\r
+    \r
+    /**\r
+     * Delete a VDU instance by ID.  If the VIM sub-orchestrator supports pre-installation\r
+     * of blueprints/models, the blueprint itself may remain installed.  This is recommended,\r
+     * since other VDU instances may be using it.\r
+     * \r
+     * Some VIM orchestrators identify deployment instances by string UUIDs, and others \r
+     * by integers.  In the latter case, the ID will be passed in as a numeric string.\r
+     * \r
+     * For some VIMs, deletion may be a single command (e.g. Heat -> delete stack) or a\r
+     * series of API calls (e.g. Cloudify -> execute uninstall workflow, delete deployment).\r
+     * These details are hidden within the plug-in implementation.  The deletion should be\r
+     * fully completed before returning.    \r
+     *  \r
+     * The successful return is a VduInstance object which contains the state of the VDU\r
+     * just prior to deletion, with a status of DELETED.  If the deployment was not found,\r
+     * the VduInstance object should be empty (with a status of NOTFOUND).\r
+     * There is no rollback from a successful deletion.\r
+     * \r
+     * A deletion failure will result in an undefined deployment state - the components may\r
+     * or may not have been all or partially uninstalled, so the resulting deployment must\r
+     * be considered invalid.\r
+     *\r
+     * @param cloudInfo The target cloud + tenant identifiers for the VDU.\r
+     * @param instanceId The unique id of the deployment to delete.\r
+     * @param timeoutMinutes Timeout after which the delete action will be cancelled.\r
+     *                 Consider sending the entire model here, if it may be of use to the plug-in?\r
+     * \r
+     * @return A VduInstance object, representing its state just prior to deletion.\r
+     * \r
+     * @throws VduException Thrown if the API calls fail or if a timeout occurs.\r
+     * Various subclasses of VduException may be thrown.\r
+     */\r
+    public VduInstance deleteVdu (\r
+                               CloudInfo cloudInfo,\r
+                               String instanceId,\r
+                               int timeoutMinutes)\r
+                       throws VduException;\r
+\r
+    \r
+    /**\r
+     * The updateVdu interface attempts to update a VDU in-place, using either new inputs or\r
+     * a new model definition (i.e. updated templates/blueprints).  This depends on the\r
+     * capabilities of the targeted sub-orchestrator, as not all implementations are expected\r
+     * to support this ability.  It is primary included initially only for Heat.\r
+        *\r
+     * It is expected that parameters have been validated and contain at minimum the required\r
+     * parameters for the given template with no extra parameters.  The VDU instance name cannot\r
+     * be updated. \r
+     * \r
+        * The update should be fully completed before returning. The successful return is a\r
+        * VduInstance object containing the updated VDU state.\r
+     * \r
+     * An update failure will result in an undefined deployment state - the components may\r
+     * or may not have been all or partially modified, deleted, recreated, etc.  So the resulting\r
+     * VDU must be considered invalid.\r
+     * \r
+     * @param cloudInfo The target cloud + tenant identifiers for the VDU.\r
+     * @param instanceId The unique ID for the VDU instance to update.\r
+     * @param inputs A map of key/value inputs.  Values may be strings, numbers, or JSON objects.\r
+     *                 Will completely replace any inputs provided on the original instantiation.\r
+     * @param vduModel Object containing the collection of templates and files that comprise\r
+     *                 the blueprint for this VDU.\r
+     * @param rollbackOnFailure Flag to preserve or roll back the update on Failure.  Should normally\r
+     *         be True except in troubleshooting/debug cases.  Might not be supported in all plug-ins.\r
+     * \r
+     * @return A VduInfo object\r
+     * @throws VduException Thrown if the sub-orchestrator API calls fail or if a timeout occurs.\r
+     * Various subclasses of VduException may be thrown.\r
+     */\r
+    public VduInstance updateVdu (\r
+                       CloudInfo cloudInfo,\r
+                       String instanceId,\r
+                       Map<String,Object> inputs,\r
+                       VduModelInfo vduModel,\r
+                       boolean rollbackOnFailure)\r
+                                       throws VduException;\r
+\r
+}
\ No newline at end of file
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduStateType.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduStateType.java
new file mode 100644 (file)
index 0000000..92f5cda
--- /dev/null
@@ -0,0 +1,36 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * OPENECOMP - MSO\r
+ * ================================================================================\r
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+\r
+/*\r
+ * Enum status values to capture the state of a generic (cloud-agnostic) VDU.\r
+ */\r
+public enum VduStateType {\r
+       NOTFOUND,\r
+       INSTANTIATING,\r
+       INSTANTIATED,\r
+       DELETING,\r
+       DELETED,                // Note - only returned in success response to deleteVdu call.\r
+       UPDATING,\r
+       FAILED,\r
+       UNKNOWN\r
+}\r
diff --git a/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduStatus.java b/adapters/mso-adapter-utils/src/main/java/org/openecomp/mso/adapters/vdu/VduStatus.java
new file mode 100644 (file)
index 0000000..174bed9
--- /dev/null
@@ -0,0 +1,54 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu;\r
+\r
+public class VduStatus {\r
+       \r
+       private VduStateType state;     \r
+       private String errorMessage;\r
+       private PluginAction lastAction;        \r
+       \r
+       public VduStateType getState() {\r
+               return state;\r
+       }\r
+       public void setState(VduStateType state) {\r
+               this.state = state;\r
+       }\r
+       public String getErrorMessage() {\r
+               return errorMessage;\r
+       }\r
+       public void setErrorMessage(String errorMessage) {\r
+               this.errorMessage = errorMessage;\r
+       }\r
+       public PluginAction getLastAction() {\r
+               return lastAction;\r
+       }\r
+       public void setLastAction(PluginAction lastAction) {\r
+               this.lastAction = lastAction;\r
+       }\r
+       public void setLastAction (String action, String status, String rawCloudMessage) {\r
+               lastAction = new PluginAction();\r
+               lastAction.setAction (action);\r
+               lastAction.setStatus (status);\r
+               lastAction.setRawMessage(rawCloudMessage);\r
+       }\r
+       \r
+}
\ No newline at end of file
index f72e46a..bc3aa4f 100644 (file)
@@ -26,11 +26,22 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
+import org.openecomp.mso.adapters.vdu.CloudInfo;
+import org.openecomp.mso.adapters.vdu.PluginAction;
+import org.openecomp.mso.adapters.vdu.VduArtifact;
+import org.openecomp.mso.adapters.vdu.VduArtifact.ArtifactType;
+import org.openecomp.mso.adapters.vdu.VduException;
+import org.openecomp.mso.adapters.vdu.VduInstance;
+import org.openecomp.mso.adapters.vdu.VduModelInfo;
+import org.openecomp.mso.adapters.vdu.VduPlugin;
+import org.openecomp.mso.adapters.vdu.VduStateType;
+import org.openecomp.mso.adapters.vdu.VduStatus;
 import org.openecomp.mso.cloud.CloudConfig;
 import org.openecomp.mso.cloud.CloudConfigFactory;
 import org.openecomp.mso.cloud.CloudSite;
@@ -56,6 +67,7 @@ import org.openecomp.mso.cloudify.v3.client.ExecutionsResource.CancelExecution;
 import org.openecomp.mso.cloudify.v3.client.ExecutionsResource.GetExecution;
 import org.openecomp.mso.cloudify.v3.client.ExecutionsResource.ListExecutions;
 import org.openecomp.mso.cloudify.v3.client.ExecutionsResource.StartExecution;
+import org.openecomp.mso.cloudify.v3.model.AzureConfig;
 import org.openecomp.mso.cloudify.v3.model.Blueprint;
 import org.openecomp.mso.cloudify.v3.model.CancelExecutionParams;
 import org.openecomp.mso.cloudify.v3.model.CloudifyError;
@@ -85,7 +97,7 @@ import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-public class MsoCloudifyUtils extends MsoCommonUtils {
+public class MsoCloudifyUtils extends MsoCommonUtils implements VduPlugin{
 
        private MsoPropertiesFactory msoPropertiesFactory;
        private CloudConfigFactory cloudConfigFactory;
@@ -111,6 +123,12 @@ public class MsoCloudifyUtils extends MsoCommonUtils {
     
     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
 
+    /**
+     * This constructor MUST be used ONLY in the JUNIT tests, not for real code.
+     */
+    public MsoCloudifyUtils() {
+       
+    }
     /**
      * This constructor MUST be used ONLY in the JUNIT tests, not for real code.
      * The MsoPropertiesFactory will be added by EJB injection.
@@ -186,17 +204,24 @@ public class MsoCloudifyUtils extends MsoCommonUtils {
         
         Cloudify cloudify = getCloudifyClient (cloudSite.get());
 
-        // Create the Cloudify OpenstackConfig with the credentials
-        OpenstackConfig openstackConfig = getOpenstackConfig (cloudSite.get(), tenantId);
-        
         LOGGER.debug ("Ready to Create Deployment (" + deploymentId + ") with input params: " + inputs);
 
         // Build up the inputs, including:
         // - from provided "environment" file
         // - passed in by caller
-        // - special input for Openstack Credentials
-        Map<String,Object> expandedInputs = new HashMap<String,Object> (inputs);
-        expandedInputs.put("openstack_config", openstackConfig);
+        // - special input for cloud-specific Credentials
+        Map<String,Object> expandedInputs = new HashMap<> (inputs);
+        
+        String platform = cloudSite.get().getPlatform();
+        if (platform == null || platform.equals("") || platform.equalsIgnoreCase("OPENSTACK")) {
+               // Create the Cloudify OpenstackConfig with the credentials
+               OpenstackConfig openstackConfig = getOpenstackConfig (cloudSite.get(), tenantId);
+               expandedInputs.put("openstack_config", openstackConfig);
+        } else if (platform.equalsIgnoreCase("AZURE")) {
+               // Create Cloudify AzureConfig with the credentials
+               AzureConfig azureConfig = getAzureConfig (cloudSite.get(), tenantId);
+               expandedInputs.put("azure_config",  azureConfig);
+        }
          
         // Build up the parameters to create a new deployment
        CreateDeploymentParams deploymentParams = new CreateDeploymentParams();
@@ -236,10 +261,11 @@ public class MsoCloudifyUtils extends MsoCommonUtils {
 
        /*
         * It can take some time for Cloudify to be ready to execute a workflow
-        * on the deployment.  Sleep 10 seconds.
+        * on the deployment.  Sleep 30 seconds based on observation of behavior
+        * in a Cloudify VM instance (delay due to "create_deployment_environment").
         */
        try {
-               Thread.sleep(10000);
+               Thread.sleep(30000);
        } catch (InterruptedException e) {}
        
        /*
@@ -825,7 +851,7 @@ public class MsoCloudifyUtils extends MsoCommonUtils {
     /*
      * Common method to load a blueprint.  May be called from 
      */
-    private boolean uploadBlueprint (Cloudify cloudify, String blueprintId, String mainFileName, Map<String,byte[]> blueprintFiles)
+    protected boolean uploadBlueprint (Cloudify cloudify, String blueprintId, String mainFileName, Map<String,byte[]> blueprintFiles)
        throws MsoException
     {
        // Check if it already exists.  If so, return false.
@@ -1204,11 +1230,234 @@ public class MsoCloudifyUtils extends MsoCommonUtils {
         return me;
     }
 
+
+
+    /*******************************************************************************
+     * 
+     * Methods (and associated utilities) to implement the VduPlugin interface
+     * 
+     *******************************************************************************/
+    
+    /**
+     * VduPlugin interface for instantiate function.
+     * 
+     * This one is a bit more complex, in that it will first upload the blueprint if needed,
+     * then create the Cloudify deployment and execute the install workflow.
+     * 
+     * This implementation also merges any parameters defined in the ENV file with the other
+     * other input parameters for any undefined parameters).
+     * The basic MsoCloudifyUtils separates blueprint management from deploument actions,
+     * but the VduPlugin does not declare blueprint management operations.
+     */
+    public VduInstance instantiateVdu (
+                       CloudInfo cloudInfo,
+                       String instanceName,
+                       Map<String,Object> 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 and uploading a blueprint.  Use the model customization UUID as
+       // the blueprint identifier.
+       
+       String blueprintId = vduModel.getModelCustomizationUUID();
+       
+               try {
+                       
+               if (! isBlueprintLoaded (cloudSiteId, blueprintId)) {
+                               LOGGER.debug ("Blueprint " + blueprintId + " is not loaded.  Will upload it now.");
+       
+                               // Prepare the blueprint inputs.  Need the set of blueprint templates and files,
+                               // plus the main blueprint name.
+                               Map<String,byte[]> blueprintFiles = new HashMap<>();
+                               String mainTemplate = "";
+       
+                               // Add all of the blueprint artifacts from the VDU model
+                               List<VduArtifact> vduArtifacts = vduModel.getArtifacts();
+                               for (VduArtifact vduArtifact: vduArtifacts)
+                               {
+                                       // Add all artifacts to the blueprint, with one exception.
+                                       // ENVIRONMENT files will be processed later as additional parameters.
+                                       
+                                       ArtifactType artifactType = vduArtifact.getType();
+                                       if (artifactType != ArtifactType.ENVIRONMENT) {
+                                               blueprintFiles.put(vduArtifact.getName(), vduArtifact.getContent());
+                                               
+                                               if (artifactType == ArtifactType.MAIN_TEMPLATE) {
+                                                       mainTemplate = vduArtifact.getName();
+                                               }
+                                       }
+                               }
+                                               
+                   // Upload the blueprint package
+                               uploadBlueprint(cloudSiteId, blueprintId, mainTemplate, blueprintFiles, false);
+                       }
+       }
+       catch (Exception e) {
+               throw new VduException ("CloudifyUtils (instantiateVDU): blueprint Exception", e);
+       }
+       
+               
+       // Next, create and install a new deployment based on the blueprint.
+       // For Cloudify, the deploymentId is specified by the client.  Just use the instance name
+       // as the ID.
+       
+       try {
+               // Query the Cloudify Deployment object and  populate a VduInstance
+               DeploymentInfo deployment = createAndInstallDeployment (cloudSiteId,
+                    tenantId,
+                    instanceName,
+                    blueprintId,
+                    inputs,
+                    true,  // (poll for completion)
+                    vduModel.getTimeoutMinutes(),
+                    rollbackOnFailure);
+               
+               VduInstance vduInstance = deploymentInfoToVduInstance(deployment);
+               
+               return vduInstance;
+       }
+       catch (Exception e) {
+               throw new VduException ("CloudifyUtils (instantiateVDU): Create-and-install-deployment Exception", e);
+       }
+    }
+    
+    
+    /**
+     * VduPlugin interface for query function.
+     */
+    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
+               DeploymentInfo deployment = queryDeployment (cloudSiteId, tenantId, instanceId);
+               
+               VduInstance vduInstance = deploymentInfoToVduInstance(deployment);
+               
+               return vduInstance;
+       }
+       catch (Exception e) {
+               throw new VduException ("Query VDU Exception", e);
+       }
+    }
+    
+    
+    /**
+     * VduPlugin interface for delete function.
+     */
+    public VduInstance deleteVdu (CloudInfo cloudInfo, String instanceId, int timeoutMinutes)
+       throws VduException
+    {
+       String cloudSiteId = cloudInfo.getCloudSiteId();
+       String tenantId = cloudInfo.getTenantId();
+       
+       try {
+               // Uninstall and delete the Cloudify Deployment
+               DeploymentInfo deployment = uninstallAndDeleteDeployment (cloudSiteId, tenantId, instanceId, timeoutMinutes);
+               
+               // Populate a VduInstance based on the deleted Cloudify Deployment object
+               VduInstance vduInstance = deploymentInfoToVduInstance(deployment);
+               
+               return vduInstance;
+       }
+       catch (Exception e) {
+               throw new VduException ("Delete VDU Exception", e);
+       }
+    }
+    
+    
+    /**
+     * VduPlugin interface for update function.
+     * 
+     * Update is currently not supported in the MsoCloudifyUtils implementation.
+     * Just return a VduException.
+     * 
+     */
+    public VduInstance updateVdu (
+                       CloudInfo cloudInfo,
+                       String instanceId,
+                       Map<String,Object> inputs,
+                       VduModelInfo vduModel,
+                       boolean rollbackOnFailure)
+       throws VduException
+    {
+       throw new VduException ("CloudifyUtils: updateVDU interface not supported");
+    }
+    
+       
+    /*
+     * Convert the local DeploymentInfo object (Cloudify-specific) to a generic VduInstance object
+     */
+    protected VduInstance deploymentInfoToVduInstance (DeploymentInfo deployment)
+    {
+       VduInstance vduInstance = new VduInstance();
+       
+       // only one ID in Cloudify, use for both VDU name and ID
+       vduInstance.setVduInstanceId(deployment.getId());
+       vduInstance.setVduInstanceName(deployment.getId());
+       
+       // Copy inputs and outputs
+       vduInstance.setInputs(deployment.getInputs());
+       vduInstance.setOutputs(deployment.getOutputs());
+       
+       // Translate the status elements
+       vduInstance.setStatus(deploymentStatusToVduStatus (deployment));
+       
+       return vduInstance;
+    }
+    
+    protected VduStatus deploymentStatusToVduStatus (DeploymentInfo deployment)
+    {
+       VduStatus vduStatus = new VduStatus();
+       
+       // Determine the status based on last action & status
+       // DeploymentInfo object should be enhanced to report a better status internally.
+       DeploymentStatus status = deployment.getStatus();
+       
+       if (status == null) {
+               vduStatus.setState(VduStateType.UNKNOWN);
+       }
+       else if (status == DeploymentStatus.NOTFOUND) {
+               vduStatus.setState(VduStateType.NOTFOUND);
+       }
+       else if (status == DeploymentStatus.INSTALLED) {
+               vduStatus.setState(VduStateType.INSTANTIATED);
+       }
+       else if (status == DeploymentStatus.CREATED) {
+               // Deployment exists but is not installed.  This shouldn't really happen,
+               // since create + install or uninstall + delete are always done together.
+               // But account for it anyway, assuming the operation is still in progress.
+               String lastAction = deployment.getLastAction();
+               if (lastAction == null)
+                       vduStatus.setState(VduStateType.INSTANTIATING);
+               else
+                       vduStatus.setState(VduStateType.DELETING);
+       }
+       else if (status == DeploymentStatus.FAILED) {
+               vduStatus.setState(VduStateType.FAILED);
+       } else {
+               vduStatus.setState(VduStateType.UNKNOWN);
+       }
+       
+       vduStatus.setErrorMessage(deployment.getErrorMessage());
+       vduStatus.setLastAction(new PluginAction(deployment.getLastAction(), deployment.getActionStatus(), deployment.getErrorMessage()));
+       
+       return vduStatus;
+    }
+    
     /*
      * Return an OpenstackConfig object as expected by Cloudify Openstack Plug-in.
      * Base the values on the CloudSite definition.
      */
-    private OpenstackConfig getOpenstackConfig (CloudSite cloudSite, String tenantId) {
+    protected OpenstackConfig getOpenstackConfig (CloudSite cloudSite, String tenantId) {
         OpenstackConfig openstackConfig = new OpenstackConfig();
         openstackConfig.setRegion (cloudSite.getRegionId());
         openstackConfig.setAuthUrl (cloudSite.getIdentityService().getIdentityUrl());
@@ -1217,4 +1466,18 @@ public class MsoCloudifyUtils extends MsoCommonUtils {
         openstackConfig.setTenantName (tenantId);
         return openstackConfig;
     }
+    
+    /*
+     * Return an Azure object as expected by Cloudify Azure Plug-in.
+     * Base the values on the CloudSite definition.
+     */
+    protected AzureConfig getAzureConfig (CloudSite cloudSite, String tenantId) {
+        AzureConfig azureConfig = new AzureConfig();
+        // TODO: Use adminTenant for now, instead of adding another element
+        azureConfig.setSubscriptionId (cloudSite.getIdentityService().getAdminTenant());
+        azureConfig.setTenantId (tenantId);
+        azureConfig.setClientId (cloudSite.getIdentityService().getMsoId());
+        azureConfig.setClientSecret (cloudSite.getIdentityService().getMsoPass());
+        return azureConfig;
+    }
 }
index 7dd14d8..3f5da19 100644 (file)
@@ -30,6 +30,16 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.openecomp.mso.adapters.vdu.CloudInfo;
+import org.openecomp.mso.adapters.vdu.PluginAction;
+import org.openecomp.mso.adapters.vdu.VduArtifact;
+import org.openecomp.mso.adapters.vdu.VduArtifact.ArtifactType;
+import org.openecomp.mso.adapters.vdu.VduException;
+import org.openecomp.mso.adapters.vdu.VduInstance;
+import org.openecomp.mso.adapters.vdu.VduModelInfo;
+import org.openecomp.mso.adapters.vdu.VduPlugin;
+import org.openecomp.mso.adapters.vdu.VduStateType;
+import org.openecomp.mso.adapters.vdu.VduStatus;
 import org.openecomp.mso.cloud.CloudConfig;
 import org.openecomp.mso.cloud.CloudConfigFactory;
 import org.openecomp.mso.cloud.CloudIdentity;
@@ -52,7 +62,6 @@ import org.openecomp.mso.properties.MsoJavaProperties;
 import org.openecomp.mso.properties.MsoPropertiesException;
 import org.openecomp.mso.properties.MsoPropertiesFactory;
 
-import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.woorea.openstack.base.client.OpenStackConnectException;
@@ -68,7 +77,7 @@ import com.woorea.openstack.keystone.model.Access;
 import com.woorea.openstack.keystone.model.Authentication;
 import com.woorea.openstack.keystone.utils.KeystoneUtils;
 
-public class MsoHeatUtils extends MsoCommonUtils {
+public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{
 
        private MsoPropertiesFactory msoPropertiesFactory;
 
@@ -110,6 +119,13 @@ public class MsoHeatUtils extends MsoCommonUtils {
 
     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
 
+    /**
+     * This constructor MUST be used ONLY in the JUNIT tests, not for real code.
+     */
+    public MsoHeatUtils() {
+       
+    }
+    
     /**
      * This constructor MUST be used ONLY in the JUNIT tests, not for real code.
      * The MsoPropertiesFactory will be added by EJB injection.
@@ -1643,4 +1659,204 @@ public class MsoHeatUtils extends MsoCommonUtils {
                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.
+     */
+    public VduInstance instantiateVdu (
+                       CloudInfo cloudInfo,
+                       String instanceName,
+                       Map<String,Object> 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<String,Object> nestedTemplates = new HashMap<>();
+       Map<String,Object> 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
+               VduInstance vduInstance = stackInfoToVduInstance(stackInfo);
+               
+               return vduInstance;
+       }
+       catch (Exception e) {
+               throw new VduException ("MsoHeatUtils (instantiateVDU): createStack Exception", e);
+       }
+    }
+    
+    
+    /**
+     * VduPlugin interface for query function.
+     */
+    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);
+               
+               VduInstance vduInstance = stackInfoToVduInstance(stackInfo);
+               
+               return vduInstance;
+       }
+       catch (Exception e) {
+               throw new VduException ("MsoHeatUtile (queryVdu): queryStack Exception ", e);
+       }
+    }
+    
+    
+    /**
+     * VduPlugin interface for delete function.
+     */
+    public VduInstance deleteVdu (CloudInfo cloudInfo, String instanceId, int timeoutMinutes)
+       throws VduException
+    {
+       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.
+     * 
+     */
+    public VduInstance updateVdu (
+                       CloudInfo cloudInfo,
+                       String instanceId,
+                       Map<String,Object> inputs,
+                       VduModelInfo vduModel,
+                       boolean rollbackOnFailure)
+       throws VduException
+    {
+       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)
+    {
+       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-adapter-utils/src/test/java/org/openecomp/mso/adapters/vdu/BeansTest.java b/adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/adapters/vdu/BeansTest.java
new file mode 100644 (file)
index 0000000..1452c15
--- /dev/null
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2018 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.adapters.vdu;
+
+import org.junit.Test;
+
+import com.openpojo.reflection.PojoClass;
+import com.openpojo.reflection.PojoClassFilter;
+import com.openpojo.reflection.filters.FilterPackageInfo;
+import com.openpojo.validation.Validator;
+import com.openpojo.validation.ValidatorBuilder;
+import com.openpojo.validation.rule.impl.GetterMustExistRule;
+import com.openpojo.validation.test.impl.GetterTester;
+import com.openpojo.validation.test.impl.SetterTester;
+
+public class BeansTest {
+
+       private PojoClassFilter filterTestClasses = new FilterTestClasses();
+
+       @Test
+       public void pojoStructure() {
+               test("org.openecomp.mso.adapters.vdu");
+       }
+
+       private void test(String pojoPackage) {
+               Validator validator = ValidatorBuilder.create()
+                               .with(new GetterMustExistRule())
+                               .with(new SetterTester())
+                               .with(new GetterTester())
+                               .build();
+               validator.validate(pojoPackage, new FilterPackageInfo(), filterTestClasses);
+       }
+       private static class FilterTestClasses implements PojoClassFilter {
+               public boolean include(PojoClass pojoClass) {
+                       return !pojoClass.getSourcePath().contains("/test-classes/");
+               }
+       }
+}
diff --git a/adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/cloudify/utils/MsoCloudifyUtilsTest2.java b/adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/cloudify/utils/MsoCloudifyUtilsTest2.java
new file mode 100644 (file)
index 0000000..05608b4
--- /dev/null
@@ -0,0 +1,254 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.cloudify.utils;\r
+\r
+import static com.shazam.shazamcrest.MatcherAssert.assertThat;\r
+import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;\r
+import static org.mockito.Mockito.doReturn;\r
+import static org.mockito.Mockito.when;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import org.junit.Test;\r
+import org.mockito.Mockito;\r
+import org.openecomp.mso.adapters.vdu.CloudInfo;\r
+import org.openecomp.mso.adapters.vdu.PluginAction;\r
+import org.openecomp.mso.adapters.vdu.VduArtifact;\r
+import org.openecomp.mso.adapters.vdu.VduArtifact.ArtifactType;\r
+import org.openecomp.mso.adapters.vdu.VduInstance;\r
+import org.openecomp.mso.adapters.vdu.VduModelInfo;\r
+import org.openecomp.mso.adapters.vdu.VduStateType;\r
+import org.openecomp.mso.adapters.vdu.VduStatus;\r
+import org.openecomp.mso.cloud.CloudConfig;\r
+import org.openecomp.mso.cloud.CloudIdentity;\r
+import org.openecomp.mso.cloud.CloudSite;\r
+import org.openecomp.mso.cloudify.beans.DeploymentInfo;\r
+import org.openecomp.mso.cloudify.beans.DeploymentStatus;\r
+import org.openecomp.mso.cloudify.v3.client.Cloudify;\r
+import org.openecomp.mso.cloudify.v3.model.AzureConfig;\r
+import org.openecomp.mso.cloudify.v3.model.OpenstackConfig;\r
+import org.openecomp.mso.openstack.exceptions.MsoException;\r
+\r
+public class MsoCloudifyUtilsTest2 {\r
+\r
+       @Test\r
+       public void instantiateVduTest() throws MsoException {\r
+               VduInstance expected = new VduInstance();\r
+               expected.setVduInstanceId("id");\r
+               expected.setVduInstanceName("id");\r
+               VduStatus status = new VduStatus();\r
+               status.setState(VduStateType.INSTANTIATED);\r
+               status.setLastAction(new PluginAction(null, null, null));\r
+               expected.setStatus(status);\r
+\r
+               MsoCloudifyUtils cloudify = Mockito.spy(MsoCloudifyUtils.class);\r
+               CloudSite site = new CloudSite();\r
+               Optional<CloudSite> opSite = Optional.ofNullable(site);\r
+               CloudConfig config = Mockito.mock(CloudConfig.class);\r
+               cloudify.cloudConfig = config;\r
+               Cloudify cloudifyClient = new Cloudify("cloudSite");\r
+               CloudInfo cloudInfo = new CloudInfo();\r
+               cloudInfo.setCloudSiteId("cloudSiteId");\r
+               cloudInfo.setTenantId("tenantId");\r
+               VduModelInfo vduModel = new VduModelInfo();\r
+               vduModel.setModelCustomizationUUID("blueprintId");\r
+               vduModel.setTimeoutMinutes(1);\r
+               VduArtifact artifact = new VduArtifact();\r
+               artifact.setName("name");\r
+               artifact.setType(ArtifactType.MAIN_TEMPLATE);\r
+               byte[] content = new byte[1];\r
+               artifact.setContent(content);\r
+               List<VduArtifact> artifacts = new ArrayList<>();\r
+               artifacts.add(artifact);\r
+               vduModel.setArtifacts(artifacts);\r
+               DeploymentInfo deployment = new DeploymentInfo();\r
+               deployment.setId("id");\r
+               deployment.setStatus(DeploymentStatus.INSTALLED);\r
+               Map<String, byte[]> blueprintFiles = new HashMap<>();\r
+               blueprintFiles.put(artifact.getName(), artifact.getContent());\r
+               String instanceName = "instanceName";\r
+               Map<String, Object> inputs = new HashMap<>();\r
+               boolean rollbackOnFailure = true;\r
+\r
+               when(config.getCloudSite(cloudInfo.getCloudSiteId())).thenReturn(opSite);\r
+               doReturn(false).when(cloudify).isBlueprintLoaded(cloudInfo.getCloudSiteId(),\r
+                               vduModel.getModelCustomizationUUID());\r
+               doReturn(cloudifyClient).when(cloudify).getCloudifyClient(site);\r
+               doReturn(true).when(cloudify).uploadBlueprint(cloudifyClient, vduModel.getModelCustomizationUUID(),\r
+                               artifact.getName(), blueprintFiles);\r
+               doReturn(deployment).when(cloudify).createAndInstallDeployment(cloudInfo.getCloudSiteId(),\r
+                               cloudInfo.getTenantId(), instanceName, vduModel.getModelCustomizationUUID(), inputs, true,\r
+                               vduModel.getTimeoutMinutes(), rollbackOnFailure);\r
+\r
+               VduInstance actual = cloudify.instantiateVdu(cloudInfo, instanceName, inputs, vduModel, rollbackOnFailure);\r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+       @Test\r
+       public void queryVduTest() throws MsoException {\r
+               VduInstance expected = new VduInstance();\r
+               expected.setVduInstanceId("id");\r
+               expected.setVduInstanceName("id");\r
+               VduStatus status = new VduStatus();\r
+               status.setState(VduStateType.INSTANTIATED);\r
+               status.setLastAction(new PluginAction(null, null, null));\r
+               expected.setStatus(status);\r
+\r
+               CloudInfo cloudInfo = new CloudInfo();\r
+               cloudInfo.setCloudSiteId("cloudSiteId");\r
+               cloudInfo.setTenantId("tenantId");\r
+               DeploymentInfo deployment = new DeploymentInfo();\r
+               deployment.setId("id");\r
+               deployment.setStatus(DeploymentStatus.INSTALLED);\r
+               String instanceId = "instanceId";\r
+\r
+               MsoCloudifyUtils cloudify = Mockito.spy(MsoCloudifyUtils.class);\r
+\r
+               doReturn(deployment).when(cloudify).queryDeployment(cloudInfo.getCloudSiteId(), cloudInfo.getTenantId(),\r
+                               instanceId);\r
+\r
+               VduInstance actual = cloudify.queryVdu(cloudInfo, instanceId);\r
+\r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+       @Test\r
+       public void deleteVduTest() throws MsoException {\r
+               VduInstance expected = new VduInstance();\r
+               expected.setVduInstanceId("id");\r
+               expected.setVduInstanceName("id");\r
+               VduStatus status = new VduStatus();\r
+               status.setState(VduStateType.DELETING);\r
+               status.setLastAction(new PluginAction("deleting", null, null));\r
+               expected.setStatus(status);\r
+\r
+               CloudInfo cloudInfo = new CloudInfo();\r
+               cloudInfo.setCloudSiteId("cloudSiteId");\r
+               cloudInfo.setTenantId("tenantId");\r
+               String instanceId = "instanceId";\r
+               int timeoutMinutes = 1;\r
+               DeploymentInfo deployment = Mockito.mock(DeploymentInfo.class);\r
+               deployment.setId("id");\r
+               deployment.setStatus(DeploymentStatus.CREATED);\r
+               when(deployment.getId()).thenReturn("id");\r
+               when(deployment.getStatus()).thenReturn(DeploymentStatus.CREATED);\r
+               when(deployment.getLastAction()).thenReturn("deleting");\r
+               MsoCloudifyUtils cloudify = Mockito.spy(MsoCloudifyUtils.class);\r
+               doReturn(deployment).when(cloudify).uninstallAndDeleteDeployment(cloudInfo.getCloudSiteId(),\r
+                               cloudInfo.getTenantId(), instanceId, timeoutMinutes);\r
+\r
+               VduInstance actual = cloudify.deleteVdu(cloudInfo, instanceId, timeoutMinutes);\r
+\r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+       @Test\r
+       public void deploymentInfoToVduInstanceTest() {\r
+               VduInstance expected = new VduInstance();\r
+               expected.setVduInstanceId("id");\r
+               expected.setVduInstanceName("id");\r
+               VduStatus status = new VduStatus();\r
+               status.setState(VduStateType.DELETING);\r
+               status.setLastAction(new PluginAction("deleting", null, null));\r
+               expected.setStatus(status);\r
+\r
+               DeploymentInfo deployment = Mockito.mock(DeploymentInfo.class);\r
+               deployment.setId("id");\r
+               deployment.setStatus(DeploymentStatus.CREATED);\r
+               when(deployment.getId()).thenReturn("id");\r
+               when(deployment.getStatus()).thenReturn(DeploymentStatus.CREATED);\r
+               when(deployment.getLastAction()).thenReturn("deleting");\r
+\r
+               MsoCloudifyUtils cloudify = new MsoCloudifyUtils();\r
+\r
+               VduInstance actual = cloudify.deploymentInfoToVduInstance(deployment);\r
+\r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+       @Test\r
+       public void deploymentStatusToVduStatusTest() {\r
+               VduStatus expected = new VduStatus();\r
+               expected.setState(VduStateType.DELETING);\r
+               expected.setLastAction(new PluginAction("deleting", null, null));\r
+\r
+               DeploymentInfo deployment = Mockito.mock(DeploymentInfo.class);\r
+               deployment.setId("id");\r
+               deployment.setStatus(DeploymentStatus.CREATED);\r
+               when(deployment.getId()).thenReturn("id");\r
+               when(deployment.getStatus()).thenReturn(DeploymentStatus.CREATED);\r
+               when(deployment.getLastAction()).thenReturn("deleting");\r
+\r
+               MsoCloudifyUtils cloudify = new MsoCloudifyUtils();\r
+\r
+               VduStatus actual = cloudify.deploymentStatusToVduStatus(deployment);\r
+\r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+       @Test\r
+       public void getOpenstackConfigTest() {\r
+               OpenstackConfig expected = new OpenstackConfig();\r
+               expected.setRegion("regionId");\r
+               expected.setAuthUrl("identityUrl");\r
+               expected.setUsername("msoId");\r
+               expected.setPassword("msoPass");\r
+               expected.setTenantName("tenantId");\r
+\r
+               MsoCloudifyUtils cloudify = new MsoCloudifyUtils();\r
+               CloudSite cloudSite = Mockito.mock(CloudSite.class);\r
+               CloudIdentity cloudIdentity = Mockito.mock(CloudIdentity.class);\r
+               when(cloudSite.getIdentityService()).thenReturn(cloudIdentity);\r
+               when(cloudSite.getRegionId()).thenReturn("regionId");\r
+               when(cloudIdentity.getIdentityUrl()).thenReturn("identityUrl");\r
+               when(cloudIdentity.getMsoId()).thenReturn("msoId");\r
+               when(cloudIdentity.getMsoPass()).thenReturn("msoPass");\r
+               String tenantId = "tenantId";\r
+               OpenstackConfig actual = cloudify.getOpenstackConfig(cloudSite, tenantId);\r
+\r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+       @Test\r
+       public void getAzureConfigTest() {\r
+               AzureConfig expected = new AzureConfig();\r
+               expected.setSubscriptionId("subscriptionId");\r
+               expected.setTenantId("tenantId");\r
+               expected.setClientId("msoId");\r
+               expected.setClientSecret("msoPass");\r
+\r
+               MsoCloudifyUtils cloudify = new MsoCloudifyUtils();\r
+               CloudSite cloudSite = Mockito.mock(CloudSite.class);\r
+               CloudIdentity cloudIdentity = Mockito.mock(CloudIdentity.class);\r
+               when(cloudSite.getIdentityService()).thenReturn(cloudIdentity);\r
+               when(cloudIdentity.getAdminTenant()).thenReturn("subscriptionId");\r
+               when(cloudIdentity.getMsoId()).thenReturn("msoId");\r
+               when(cloudIdentity.getMsoPass()).thenReturn("msoPass");\r
+               String tenantId = "tenantId";\r
+               AzureConfig actual = cloudify.getAzureConfig(cloudSite, tenantId);\r
+\r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+}\r
diff --git a/adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/openstack/utils/MsoHeatUtilsTest2.java b/adapters/mso-adapter-utils/src/test/java/org/openecomp/mso/openstack/utils/MsoHeatUtilsTest2.java
new file mode 100644 (file)
index 0000000..a0ab8e6
--- /dev/null
@@ -0,0 +1,169 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.openstack.utils;\r
+\r
+import static com.shazam.shazamcrest.MatcherAssert.assertThat;\r
+import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;\r
+import static org.mockito.Mockito.doReturn;\r
+import static org.mockito.Mockito.when;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import org.junit.Test;\r
+import org.mockito.Mockito;\r
+import org.openecomp.mso.adapters.vdu.CloudInfo;\r
+import org.openecomp.mso.adapters.vdu.PluginAction;\r
+import org.openecomp.mso.adapters.vdu.VduArtifact;\r
+import org.openecomp.mso.adapters.vdu.VduArtifact.ArtifactType;\r
+import org.openecomp.mso.adapters.vdu.VduInstance;\r
+import org.openecomp.mso.adapters.vdu.VduModelInfo;\r
+import org.openecomp.mso.adapters.vdu.VduStateType;\r
+import org.openecomp.mso.adapters.vdu.VduStatus;\r
+import org.openecomp.mso.cloud.CloudConfig;\r
+import org.openecomp.mso.cloud.CloudSite;\r
+import org.openecomp.mso.cloudify.beans.DeploymentInfo;\r
+import org.openecomp.mso.cloudify.beans.DeploymentStatus;\r
+import org.openecomp.mso.cloudify.utils.MsoCloudifyUtils;\r
+import org.openecomp.mso.openstack.beans.HeatStatus;\r
+import org.openecomp.mso.openstack.beans.StackInfo;\r
+import org.openecomp.mso.openstack.exceptions.MsoException;\r
+\r
+import com.fasterxml.jackson.core.JsonProcessingException;\r
+\r
+public class MsoHeatUtilsTest2 {\r
+\r
+       @Test\r
+       public void instantiateVduTest() throws MsoException, JsonProcessingException {\r
+               VduInstance expected = new VduInstance();\r
+               expected.setVduInstanceId("canonicalName");\r
+               expected.setVduInstanceName("name");\r
+               VduStatus status = new VduStatus();\r
+               status.setState(VduStateType.INSTANTIATED);\r
+               status.setLastAction((new PluginAction("create", "complete", "")));\r
+               expected.setStatus(status);\r
+\r
+               MsoHeatUtils heatUtils = Mockito.spy(MsoHeatUtils.class);\r
+               CloudSite site = new CloudSite();\r
+               Optional<CloudSite> opSite = Optional.ofNullable(site);\r
+               CloudConfig config = Mockito.mock(CloudConfig.class);\r
+               heatUtils.cloudConfig = config;\r
+               CloudInfo cloudInfo = new CloudInfo();\r
+               cloudInfo.setCloudSiteId("cloudSiteId");\r
+               cloudInfo.setTenantId("tenantId");\r
+               VduModelInfo vduModel = new VduModelInfo();\r
+               vduModel.setModelCustomizationUUID("blueprintId");\r
+               vduModel.setTimeoutMinutes(1);\r
+               VduArtifact artifact = new VduArtifact();\r
+               artifact.setName("name");\r
+               artifact.setType(ArtifactType.MAIN_TEMPLATE);\r
+               byte[] content = new byte[1];\r
+               artifact.setContent(content);\r
+               List<VduArtifact> artifacts = new ArrayList<>();\r
+               artifacts.add(artifact);\r
+               vduModel.setArtifacts(artifacts);\r
+               Map<String, byte[]> blueprintFiles = new HashMap<>();\r
+               blueprintFiles.put(artifact.getName(), artifact.getContent());\r
+               String instanceName = "instanceName";\r
+               Map<String, Object> inputs = new HashMap<>();\r
+               boolean rollbackOnFailure = true;\r
+               String heatTemplate = new String(artifact.getContent());\r
+               when(config.getCloudSite(cloudInfo.getCloudSiteId())).thenReturn(opSite);\r
+               Map<String, Object> nestedTemplates = new HashMap<String, Object>();\r
+               Map<String, Object> files = new HashMap<String, Object>();\r
+\r
+               StackInfo stackInfo = new StackInfo();\r
+               stackInfo.setCanonicalName("canonicalName");\r
+               stackInfo.setName("name");\r
+               stackInfo.setStatus(HeatStatus.CREATED);\r
+\r
+               doReturn(stackInfo).when(heatUtils).createStack(cloudInfo.getCloudSiteId(), cloudInfo.getTenantId(),\r
+                               instanceName, heatTemplate, inputs, true, vduModel.getTimeoutMinutes(), null, nestedTemplates, files,\r
+                               rollbackOnFailure);\r
+\r
+               VduInstance actual = heatUtils.instantiateVdu(cloudInfo, instanceName, inputs, vduModel, rollbackOnFailure);\r
+               \r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+       \r
+       @Test\r
+       public void queryVduTest() throws MsoException, JsonProcessingException {\r
+               VduInstance expected = new VduInstance();\r
+               expected.setVduInstanceId("canonicalName");\r
+               expected.setVduInstanceName("name");\r
+               VduStatus status = new VduStatus();\r
+               status.setState(VduStateType.INSTANTIATED);\r
+               status.setLastAction((new PluginAction("create", "complete", "")));\r
+               expected.setStatus(status);\r
+\r
+               CloudInfo cloudInfo = new CloudInfo();\r
+               cloudInfo.setCloudSiteId("cloudSiteId");\r
+               cloudInfo.setTenantId("tenantId");\r
+               String instanceId = "instanceId";\r
+\r
+               StackInfo stackInfo = new StackInfo();\r
+               stackInfo.setCanonicalName("canonicalName");\r
+               stackInfo.setName("name");\r
+               stackInfo.setStatus(HeatStatus.CREATED);\r
+               \r
+               MsoHeatUtils heatUtils = Mockito.spy(MsoHeatUtils.class);\r
+\r
+               doReturn(stackInfo).when(heatUtils).queryStack(cloudInfo.getCloudSiteId(), cloudInfo.getTenantId(), instanceId);\r
+\r
+               VduInstance actual = heatUtils.queryVdu(cloudInfo, instanceId);\r
+               \r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+       @Test\r
+       public void deleteVduTest() throws MsoException {\r
+               VduInstance expected = new VduInstance();\r
+               expected.setVduInstanceId("canonicalName");\r
+               expected.setVduInstanceName("name");\r
+               VduStatus status = new VduStatus();\r
+               status.setState(VduStateType.DELETED);\r
+               expected.setStatus(status);\r
+\r
+               CloudInfo cloudInfo = new CloudInfo();\r
+               cloudInfo.setCloudSiteId("cloudSiteId");\r
+               cloudInfo.setTenantId("tenantId");\r
+               String instanceId = "instanceId";\r
+\r
+               StackInfo stackInfo = new StackInfo();\r
+               stackInfo.setCanonicalName("canonicalName");\r
+               stackInfo.setName("name");\r
+               stackInfo.setStatus(HeatStatus.NOTFOUND);\r
+               \r
+               int timeoutInMinutes = 1;\r
+               \r
+               MsoHeatUtils heatUtils = Mockito.spy(MsoHeatUtils.class);\r
+\r
+               doReturn(stackInfo).when(heatUtils).deleteStack( cloudInfo.getTenantId(), cloudInfo.getCloudSiteId(), instanceId, true);\r
+\r
+               VduInstance actual = heatUtils.deleteVdu(cloudInfo, instanceId, timeoutInMinutes);\r
+               \r
+               assertThat(actual, sameBeanAs(expected));\r
+       }\r
+\r
+}\r
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vdu/mapper/VfModuleCustomizationToVduMapper.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vdu/mapper/VfModuleCustomizationToVduMapper.java
new file mode 100644 (file)
index 0000000..22d988f
--- /dev/null
@@ -0,0 +1,174 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.adapters.vdu.mapper;\r
+\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.openecomp.mso.adapters.vdu.VduArtifact;\r
+import org.openecomp.mso.adapters.vdu.VduArtifact.ArtifactType;\r
+import org.openecomp.mso.adapters.vdu.VduModelInfo;\r
+import org.openecomp.mso.db.catalog.CatalogDatabase;\r
+import org.openecomp.mso.db.catalog.beans.HeatEnvironment;\r
+import org.openecomp.mso.db.catalog.beans.HeatFiles;\r
+import org.openecomp.mso.db.catalog.beans.HeatTemplate;\r
+import org.openecomp.mso.db.catalog.beans.VfModuleCustomization;\r
+import org.openecomp.mso.logger.MsoLogger;\r
+import org.springframework.stereotype.Component;\r
+\r
+@Component\r
+public class VfModuleCustomizationToVduMapper {\r
+\r
+       private static MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.RA);\r
+\r
+       public VduModelInfo mapVfModuleCustomizationToVdu(VfModuleCustomization vfModuleCustom) throws Exception {\r
+               CatalogDatabase db = CatalogDatabase.getInstance();\r
+               VduModelInfo vduModel = new VduModelInfo();\r
+               vduModel.setModelCustomizationUUID(vfModuleCustom.getModelCustomizationUuid());\r
+               try {\r
+                       // Map the cloud templates, attached files, and environment file\r
+                       mapCloudTemplates(\r
+                                       db.getHeatTemplateByArtifactUuid(vfModuleCustom.getVfModule().getHeatTemplateArtifactUUId()),\r
+                                       vduModel);\r
+                       mapCloudFiles(vfModuleCustom, vduModel);\r
+                       mapEnvironment(db.getHeatEnvironmentByArtifactUuid(vfModuleCustom.getHeatEnvironmentArtifactUuid()),\r
+                                       vduModel);\r
+               } catch (Exception e) {\r
+                       LOGGER.debug("unhandled exception in mapVfModuleCustomizationToVdu", e);\r
+                       throw new Exception("Exception during mapVfModuleCustomizationToVdu " + e.getMessage());\r
+               } finally {\r
+                       // Make sure DB session is closed\r
+                       db.close();\r
+               }\r
+\r
+               return vduModel;\r
+       }\r
+\r
+       public VduModelInfo mapVfModuleCustVolumeToVdu(VfModuleCustomization vfModuleCustom) throws Exception {\r
+               CatalogDatabase db = CatalogDatabase.getInstance();\r
+               VduModelInfo vduModel = new VduModelInfo();\r
+               vduModel.setModelCustomizationUUID(vfModuleCustom.getModelCustomizationUuid());\r
+               try {\r
+                       // Map the cloud templates, attached files, and environment file\r
+                       mapCloudTemplates(\r
+                                       db.getHeatTemplateByArtifactUuid(vfModuleCustom.getVfModule().getVolHeatTemplateArtifactUUId()),\r
+                                       vduModel);\r
+                       mapCloudFiles(vfModuleCustom, vduModel);\r
+                       mapEnvironment(db.getHeatEnvironmentByArtifactUuid(vfModuleCustom.getVolEnvironmentArtifactUuid()),\r
+                                       vduModel);\r
+               } catch (Exception e) {\r
+                       LOGGER.debug("unhandled exception in mapVfModuleCustVolumeToVdu", e);\r
+                       throw new Exception("Exception during mapVfModuleCustVolumeToVdu " + e.getMessage());\r
+               } finally {\r
+                       // Make sure DB session is closed\r
+                       db.close();\r
+               }\r
+\r
+               return vduModel;\r
+       }\r
+\r
+       private void mapCloudTemplates(HeatTemplate heatTemplate, VduModelInfo vduModel) throws Exception {\r
+               // TODO: These catalog objects will be refactored to be\r
+               // non-Heat-specific\r
+               CatalogDatabase db = CatalogDatabase.getInstance();\r
+               try {\r
+                       List<VduArtifact> vduArtifacts = vduModel.getArtifacts();\r
+\r
+                       // Main template. Also set the VDU timeout based on the main\r
+                       // template.\r
+                       vduArtifacts.add(mapHeatTemplateToVduArtifact(heatTemplate, ArtifactType.MAIN_TEMPLATE));\r
+                       vduModel.setTimeoutMinutes(heatTemplate.getTimeoutMinutes());\r
+                       \r
+                       // Nested templates\r
+                       Map<String,Object> nestedTemplates = db.getNestedTemplates(heatTemplate.getArtifactUuid());\r
+                       if (nestedTemplates != null) {\r
+                               for (String name : nestedTemplates.keySet()) {\r
+                                       String body = (String) nestedTemplates.get(name);\r
+                                       VduArtifact vduArtifact = new VduArtifact(name, body.getBytes(), ArtifactType.NESTED_TEMPLATE);\r
+                                       vduArtifacts.add(vduArtifact);\r
+                               }\r
+                       }\r
+                       \r
+               } catch (Exception e) {\r
+                       LOGGER.debug("unhandled exception in mapCloudTemplates", e);\r
+                       throw new Exception("Exception during mapCloudTemplates " + e.getMessage());\r
+               } finally {\r
+                       // Make sure DB session is closed\r
+                       db.close();\r
+               }\r
+       }\r
+\r
+       private VduArtifact mapHeatTemplateToVduArtifact(HeatTemplate heatTemplate, ArtifactType artifactType) {\r
+               VduArtifact vduArtifact = new VduArtifact();\r
+               vduArtifact.setName(heatTemplate.getTemplateName());\r
+               vduArtifact.setContent(heatTemplate.getHeatTemplate().getBytes());\r
+               vduArtifact.setType(artifactType);\r
+               return vduArtifact;\r
+       }\r
+\r
+       private void mapCloudFiles(VfModuleCustomization vfModuleCustom, VduModelInfo vduModel) throws Exception {\r
+               // TODO: These catalog objects will be refactored to be\r
+               // non-Heat-specific\r
+               CatalogDatabase db = CatalogDatabase.getInstance();\r
+\r
+               try{\r
+                       Map <String, HeatFiles> heatFiles = db.getHeatFilesForVfModule(vfModuleCustom.getVfModuleModelUuid());\r
+                       if (heatFiles != null) {\r
+                               for (HeatFiles heatFile: heatFiles.values()) {\r
+                                       mapCloudFileToVduArtifact(heatFile, ArtifactType.TEXT_FILE);\r
+                               }\r
+                       }\r
+               } catch (Exception e) {\r
+                       LOGGER.debug("unhandled exception in mapCloudFiles", e);\r
+                       throw new Exception("Exception during mapCloudFiles " + e.getMessage());\r
+               } finally {\r
+                       // Make sure DB session is closed\r
+                       db.close();\r
+               }\r
+               \r
+       }\r
+\r
+       private VduArtifact mapCloudFileToVduArtifact(HeatFiles heatFile, ArtifactType artifactType) {\r
+               VduArtifact vduArtifact = new VduArtifact();\r
+               vduArtifact.setName(heatFile.getFileName());\r
+               vduArtifact.setContent(heatFile.getFileBody().getBytes());\r
+               vduArtifact.setType(artifactType);\r
+               return vduArtifact;\r
+       }\r
+\r
+       private void mapEnvironment(HeatEnvironment heatEnvironment, VduModelInfo vduModel) {\r
+               // TODO: These catalog objects will be refactored to be\r
+               // non-Heat-specific\r
+               if (heatEnvironment != null) {\r
+                       List<VduArtifact> vduArtifacts = vduModel.getArtifacts();\r
+                       vduArtifacts.add(mapEnvironmentFileToVduArtifact(heatEnvironment));\r
+               }\r
+       }\r
+\r
+       private VduArtifact mapEnvironmentFileToVduArtifact(HeatEnvironment heatEnv) {\r
+               VduArtifact vduArtifact = new VduArtifact();\r
+               vduArtifact.setName(heatEnv.getName());\r
+               vduArtifact.setContent(heatEnv.getEnvironment().getBytes());\r
+               vduArtifact.setType(ArtifactType.ENVIRONMENT);\r
+               return vduArtifact;\r
+       }\r
+\r
+}
\ No newline at end of file
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfPluginAdapterImpl.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfPluginAdapterImpl.java
new file mode 100644 (file)
index 0000000..0a0747a
--- /dev/null
@@ -0,0 +1,1239 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+/**
+ * This VNF Adapter implementation is based on the VDU Plugin model.  It assumes that each
+ * VF Module definition in the MSO catalog is expressed via a set of template and/or file
+ * artifacts that are appropriate for some specific sub-orchestrator that provides an
+ * implementation of the VduPlugin interface.  This adapter handles all of the common
+ * VF Module logic, including:
+ * - catalog lookups for artifact retrieval
+ * - parameter filtering and validation
+ * - 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
+ * is expected (e.g. via a service-provider interface).
+ */
+package org.openecomp.mso.adapters.vnf;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.jws.WebService;
+import javax.xml.ws.Holder;
+
+import org.openecomp.mso.adapters.vdu.CloudInfo;
+import org.openecomp.mso.adapters.vdu.VduException;
+import org.openecomp.mso.adapters.vdu.VduInstance;
+import org.openecomp.mso.adapters.vdu.VduModelInfo;
+import org.openecomp.mso.adapters.vdu.VduPlugin;
+import org.openecomp.mso.adapters.vdu.VduStateType;
+import org.openecomp.mso.adapters.vdu.VduStatus;
+import org.openecomp.mso.adapters.vdu.mapper.VfModuleCustomizationToVduMapper;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfAlreadyExists;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+import org.openecomp.mso.cloud.CloudConfig;
+import org.openecomp.mso.cloud.CloudConfigFactory;
+import org.openecomp.mso.cloud.CloudSite;
+import org.openecomp.mso.cloudify.utils.MsoCloudifyUtils;
+import org.openecomp.mso.db.catalog.CatalogDatabase;
+import org.openecomp.mso.db.catalog.beans.HeatEnvironment;
+import org.openecomp.mso.db.catalog.beans.HeatTemplate;
+import org.openecomp.mso.db.catalog.beans.HeatTemplateParam;
+import org.openecomp.mso.db.catalog.beans.VfModule;
+import org.openecomp.mso.db.catalog.beans.VfModuleCustomization;
+import org.openecomp.mso.db.catalog.beans.VnfResource;
+import org.openecomp.mso.db.catalog.utils.MavenLikeVersioning;
+import org.openecomp.mso.entity.MsoRequest;
+import org.openecomp.mso.logger.MessageEnum;
+import org.openecomp.mso.logger.MsoAlarmLogger;
+import org.openecomp.mso.logger.MsoLogger;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+import org.openecomp.mso.openstack.beans.VnfStatus;
+import org.openecomp.mso.openstack.exceptions.MsoCloudSiteNotFound;
+import org.openecomp.mso.openstack.exceptions.MsoException;
+import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;
+import org.openecomp.mso.openstack.utils.MsoHeatEnvironmentEntry;
+import org.openecomp.mso.openstack.utils.MsoHeatUtils;
+import org.openecomp.mso.properties.MsoPropertiesFactory;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@WebService(serviceName = "VnfAdapter", endpointInterface = "org.openecomp.mso.adapters.vnf.MsoVnfAdapter", targetNamespace = "http://org.openecomp.mso/vnf")
+public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter {
+
+       CloudConfigFactory cloudConfigFactory = new CloudConfigFactory();
+       protected CloudConfig cloudConfig = cloudConfigFactory.getCloudConfig();
+       protected MsoHeatUtils heatUtils;
+       protected VfModuleCustomizationToVduMapper vduMapper;
+       protected MsoCloudifyUtils cloudifyUtils;
+       
+       MsoPropertiesFactory msoPropertiesFactory=new MsoPropertiesFactory();
+
+       private static final String MSO_PROP_VNF_ADAPTER = "MSO_PROP_VNF_ADAPTER";
+    private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
+    private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA);
+    private static MsoAlarmLogger alarmLogger = new MsoAlarmLogger ();
+    private static final String CHECK_REQD_PARAMS = "org.openecomp.mso.adapters.vnf.checkRequiredParameters";
+    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
+
+    /**
+     * Health Check web method. Does nothing but return to show the adapter is deployed.
+     */
+    @Override
+    public void healthCheck () {
+        LOGGER.debug ("Health check call in VNF Plugin Adapter");
+    }
+
+    /**
+     * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
+     * @see MsoVnfPluginAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
+     */
+    public MsoVnfPluginAdapterImpl() {
+
+    }
+
+    /**
+     * This constructor MUST be used if this class is called with the new operator.
+     * @param msoPropFactory
+     */
+    public MsoVnfPluginAdapterImpl(MsoPropertiesFactory msoPropFactory, CloudConfigFactory cloudConfigFact) {
+       this.msoPropertiesFactory = msoPropFactory;
+       this.cloudConfigFactory = cloudConfigFact;
+       heatUtils = new MsoHeatUtils(MSO_PROP_VNF_ADAPTER, msoPropertiesFactory, cloudConfigFactory);
+       vduMapper = new VfModuleCustomizationToVduMapper();
+       cloudifyUtils = new MsoCloudifyUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+    }
+
+    /**
+     * This is the "Create VNF" web service implementation.
+     * This function is now unsupported and will return an error.
+     *
+     */
+    @Override
+    public void createVnf (String cloudSiteId,
+                           String tenantId,
+                           String vnfType,
+                           String vnfVersion,
+                           String vnfName,
+                           String requestType,
+                           String volumeGroupHeatStackId,
+                           Map <String, String> inputs,
+                           Boolean failIfExists,
+                           Boolean backout,
+                           MsoRequest msoRequest,
+                           Holder <String> vnfId,
+                           Holder <Map <String, String>> outputs,
+                           Holder <VnfRollback> rollback)
+       throws VnfException
+    {
+       // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
+       LOGGER.debug ("CreateVNF command attempted but not supported");
+       throw new VnfException ("CreateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
+    }
+
+    /**
+     * This is the "Update VNF" web service implementation.
+     * This function is now unsupported and will return an error.
+     *
+     */
+    @Override
+    public void updateVnf (String cloudSiteId,
+                           String tenantId,
+                           String vnfType,
+                           String vnfVersion,
+                           String vnfName,
+                           String requestType,
+                           String volumeGroupHeatStackId,
+                           Map <String, String> inputs,
+                           MsoRequest msoRequest,
+                           Holder <Map <String, String>> outputs,
+                           Holder <VnfRollback> rollback)
+               throws VnfException
+       {
+       // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
+       LOGGER.debug ("UpdateVNF command attempted but not supported");
+       throw new VnfException ("UpdateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
+    }
+
+    /**
+     * 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.
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to query
+     * @param tenantId Openstack tenant identifier
+     * @param vnfNameOrId VNF Name or ID to query
+     * @param msoRequest Request tracking information for logs
+     * @param vnfExists Flag reporting the result of the query
+     * @param vnfId Holder for output VNF ID
+     * @param outputs Holder for Map of outputs from the deployed VF Module (assigned IPs, etc)
+     */
+    @Override
+    public void queryVnf (String cloudSiteId,
+                          String tenantId,
+                          String vnfNameOrId,
+                          MsoRequest msoRequest,
+                          Holder <Boolean> vnfExists,
+                          Holder <String> vnfId,
+                          Holder <VnfStatus> status,
+                          Holder <Map <String, String>> outputs)
+        throws VnfException
+    {
+        MsoLogger.setLogContext (msoRequest);
+       MsoLogger.setServiceName ("QueryVnf");
+        LOGGER.debug ("Querying VNF " + vnfNameOrId + " in " + cloudSiteId + "/" + tenantId);
+
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+        long subStartTime = System.currentTimeMillis ();
+        
+        VduInstance vduInstance = null;
+       CloudInfo cloudInfo = new CloudInfo(cloudSiteId, tenantId, null);
+
+        VduPlugin vduPlugin = getVduPlugin(cloudSiteId);
+        
+       try {
+               vduInstance =  vduPlugin.queryVdu(cloudInfo, vnfNameOrId);
+               LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received VDU Query response", "VDU", "QueryVDU", vnfNameOrId);
+       }
+       catch (VduException e) {
+               // Failed to query the VDU due to a plugin exception.
+               e.addContext ("QueryVNF");
+            String error = "Query VNF (VDU): " + vnfNameOrId + " in " + cloudSiteId + "/" + tenantId + ": " + e;
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "VDU", "QueryVNF", vnfNameOrId);
+            LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfNameOrId, cloudSiteId, tenantId, "VDU", "QueryVNF", MsoLogger.ErrorCode.DataError, "Exception - queryVDU", e);
+            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);
+            vnfId.value = vduInstance.getVduInstanceId();
+            outputs.value = copyStringOutputs (vduInstance.getOutputs ());
+
+            LOGGER.debug ("VNF " + vnfNameOrId + " found, ID = " + vnfId.value);
+       }
+        else {
+            vnfExists.value = Boolean.FALSE;
+            status.value = VnfStatus.NOTFOUND;
+            vnfId.value = null;
+            outputs.value = new HashMap <String, String> (); // Return as an empty map
+
+            LOGGER.debug ("VNF " + vnfNameOrId + " not found");
+       }
+        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully query VNF");
+        return;
+    }
+
+    
+    /**
+     * This is the "Delete VNF" web service implementation.
+     * This function is now unsupported and will return an error.
+     *
+     */
+    @Override
+    public void deleteVnf (String cloudSiteId,
+                           String tenantId,
+                           String vnfName,
+                           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);
+    }
+
+    /**
+     * This web service endpoint will rollback a previous Create VNF operation.
+     * 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.
+     */
+    @Override
+    public void rollbackVnf (VnfRollback rollback) throws VnfException {
+        long startTime = System.currentTimeMillis ();
+        MsoLogger.setServiceName ("RollbackVnf");
+       // rollback may be null (e.g. if stack already existed when Create was called)
+        if (rollback == null) {
+            LOGGER.info (MessageEnum.RA_ROLLBACK_NULL, "OpenStack", "rollbackVnf");
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, "Rollback request content is null");
+            return;
+        }
+
+        // Don't rollback if nothing was done originally
+        if (!rollback.getVnfCreated()) {
+            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 ();
+        CloudInfo cloudInfo = new CloudInfo (cloudSiteId, tenantId, null);
+
+        String vfModuleId = rollback.getVfModuleStackId ();
+
+        MsoLogger.setLogContext (rollback.getMsoRequest());
+
+        LOGGER.debug ("Rolling Back VF Module " + vfModuleId + " in " + cloudSiteId + "/" + tenantId);
+
+        VduInstance vduInstance = null;
+
+        // Use the VduPlugin to delete the VF Module.
+        VduPlugin vduPlugin = getVduPlugin(cloudSiteId);
+
+        long subStartTime = System.currentTimeMillis ();
+        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);
+        }
+        catch (VduException ve) {
+            // Failed to rollback the VF Module due to a plugin exception.
+            // Convert to a generic VnfException
+            ve.addContext ("RollbackVFModule");
+            String error = "Rollback VF Module: " + vfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + ve;
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "VDU", "DeleteVdu", null);
+            LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vfModuleId, cloudSiteId, tenantId, "VDU", "DeleteVdu", MsoLogger.ErrorCode.DataError, "Exception - DeleteVdu", ve);
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+            throw new VnfException (ve);
+        }
+        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully roll back VF Module");
+        return;
+    }
+
+
+    private VnfStatus vduStatusToVnfStatus (VduInstance vdu) {
+       // Determine the status based on last action & status
+       // 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;
+       }
+       else if (status == VduStateType.NOTFOUND) {
+                       return VnfStatus.NOTFOUND;
+       }
+       else if (status == VduStateType.INSTANTIATED) {
+                       return VnfStatus.ACTIVE;
+       }
+       else if (status == VduStateType.FAILED) {
+               return VnfStatus.FAILED;
+       }
+
+       return VnfStatus.UNKNOWN;
+    }
+    
+    /*
+        * Normalize an input value to an Object, based on the target parameter type.
+        * If the type is not recognized, it will just be returned unchanged (as a string).
+        */
+       private Object convertInputValue (String inputValue, HeatTemplateParam templateParam)
+       {
+               String type = templateParam.getParamType();
+               LOGGER.debug("Parameter: " + templateParam.getParamName() + " is of type " + type);
+               
+               if (type.equalsIgnoreCase("number")) {
+                       try {
+                               return Integer.valueOf(inputValue);
+                       }
+                       catch (Exception e) {
+                               LOGGER.debug("Unable to convert " + inputValue + " to an integer!");
+                               return null;
+                       }
+               } else if (type.equalsIgnoreCase("json")) {
+                       try {
+                               JsonNode jsonNode = new ObjectMapper().readTree(inputValue);
+                               return jsonNode;
+                       }
+                       catch (Exception e) {
+                               LOGGER.debug("Unable to convert " + inputValue + " to a JsonNode!");
+                               return null;
+                       }
+               } else if (type.equalsIgnoreCase("boolean")) {
+                       return new Boolean(inputValue);
+               }
+               
+               // Nothing else matched.  Return the original string
+               return inputValue;
+       }
+    
+    private Map <String, String> copyStringOutputs (Map <String, Object> stackOutputs) {
+        Map <String, String> stringOutputs = new HashMap <> ();
+        for (String key : stackOutputs.keySet ()) {
+            if (stackOutputs.get (key) instanceof String) {
+                stringOutputs.put (key, (String) stackOutputs.get (key));
+            } else if (stackOutputs.get(key) instanceof Integer)  {
+               try {
+                       String str = "" + stackOutputs.get(key);
+                       stringOutputs.put(key, str);
+               } catch (Exception e) {
+                       LOGGER.debug("Unable to add " + key + " to outputs");
+               }
+            } else if (stackOutputs.get(key) instanceof JsonNode) {
+               try {
+                       String str = this.convertNode((JsonNode) stackOutputs.get(key));
+                       stringOutputs.put(key, str);
+               } catch (Exception e) {
+                       LOGGER.debug("Unable to add " + key + " to outputs - exception converting JsonNode");
+               }
+            } else if (stackOutputs.get(key) instanceof java.util.LinkedHashMap) {
+               try {
+                                       String str = JSON_MAPPER.writeValueAsString(stackOutputs.get(key));
+                       stringOutputs.put(key, str);
+               } catch (Exception e) {
+                       LOGGER.debug("Unable to add " + key + " to outputs - exception converting LinkedHashMap");
+               }
+            } else {
+               try {
+                       String str = stackOutputs.get(key).toString();
+                       stringOutputs.put(key, str);
+               } catch (Exception e) {
+                       LOGGER.debug("Unable to add " + key + " to outputs - unable to call .toString() " + e.getMessage());
+               }
+            }
+        }
+        return stringOutputs;
+    }
+
+
+    private void sendMapToDebug(Map<String, Object> inputs, String optionalName) {
+       int i = 0;
+       StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName);
+       if (inputs == null) {
+               sb.append("\tNULL");
+       }
+       else if (inputs.size() < 1) {
+               sb.append("\tEMPTY");
+       } else {
+               for (String str : inputs.keySet()) {
+                       String outputString;
+                       try {
+                               outputString = inputs.get(str).toString();
+                       } catch (Exception e) {
+                               outputString = "Unable to call toString() on the value for " + str;
+                       }
+                       sb.append("\t\nitem " + i++ + ": '" + str + "'='" + outputString + "'");
+               }
+       }
+       LOGGER.debug(sb.toString());
+       return; 
+    }
+    
+    private void sendMapToDebug(Map<String, String> inputs) {
+       int i = 0;
+       StringBuilder sb = new StringBuilder("inputs:");
+       if (inputs == null) {
+               sb.append("\tNULL");
+       }
+       else if (inputs.size() < 1) {
+               sb.append("\tEMPTY");
+       } else {
+               for (String str : inputs.keySet()) {
+                       sb.append("\titem " + i++ + ": " + str + "=" + inputs.get(str));
+               }
+       }
+       LOGGER.debug(sb.toString());
+       return;
+    }
+
+    private String convertNode(final JsonNode node) {
+        try {
+            final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
+            final String json = JSON_MAPPER.writeValueAsString(obj);
+            return json;
+        } catch (JsonParseException jpe) {
+            LOGGER.debug("Error converting json to string " + jpe.getMessage());
+        } catch (Exception e) {
+            LOGGER.debug("Error converting json to string " + e.getMessage());
+        }
+        return "[Error converting json to string]";
+    }
+
+    private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
+        if (objectMap == null) {
+            return null;
+        }
+        Map<String, String> stringMap = new HashMap<>();
+        for (String key : objectMap.keySet()) {
+            if (!stringMap.containsKey(key)) {
+                Object obj = objectMap.get(key);
+                if (obj instanceof String) {
+                    stringMap.put(key, (String) objectMap.get(key));
+                } else if (obj instanceof JsonNode ){
+                    // This is a bit of mess - but I think it's the least impacting
+                    // let's convert it BACK to a string - then it will get converted back later
+                    try {
+                        String str = this.convertNode((JsonNode) obj);
+                        stringMap.put(key, str);
+                    } catch (Exception e) {
+                                               LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode "+ key);
+                        //okay in this instance - only string values (fqdn) are expected to be needed
+                    }
+                } else if (obj instanceof java.util.LinkedHashMap) {
+                    LOGGER.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
+                    try {
+                        String str = JSON_MAPPER.writeValueAsString(obj);
+                        stringMap.put(key, str);
+                    } catch (Exception e) {
+                                               LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap "+ key);
+                                       }
+                               }  else if (obj instanceof Integer) {
+                                       try {
+                                               String str = "" + obj;
+                                               stringMap.put(key, str);
+                                       } catch (Exception e) {
+                                               LOGGER.debug("DANGER WILL ROBINSON: unable to convert value for Integer "+ key);
+                    }
+                } else {
+                    try {
+                                               String str = obj.toString();
+                        stringMap.put(key, str);
+                    } catch (Exception e) {
+                                               LOGGER.debug("DANGER WILL ROBINSON: unable to convert value "+ key + " (" + e.getMessage() + ")");
+                    }
+                }
+            }
+        }
+
+        return stringMap;
+    }
+
+    /**
+     * This is the "Create VF Module" web service implementation.
+     * It will instantiate a new VF Module of the requested type in the specified cloud
+     * and tenant. The tenant must exist before this service is called.
+     *
+     * If a VF Module with the same name already exists, this can be considered a
+     * success or failure, depending on the value of the 'failIfExists' parameter.
+     *
+     * All VF Modules are defined in the MSO catalog. The caller must request one of
+     * the pre-defined module types or an error will be returned. Within the catalog,
+     * each VF Module references (among other things) a collection of artifacts that
+     * are used to deploy the required cloud resources (VMs, networks, etc.).
+     *
+     * Depending on the module templates, a variable set of input parameters will
+     * be defined, some of which are required. The caller is responsible to
+     * pass the necessary input data for the module or an error will be thrown.
+     *
+     * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback
+     * object. This last object can be passed as-is to the rollbackVnf operation to
+     * undo everything that was created for the Module. This is useful if a VF module
+     * is successfully created but the orchestration fails on a subsequent step.
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
+     * @param tenantId Openstack tenant identifier
+     * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB.
+     *        Deprecated - should use modelCustomizationUuid
+     * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
+     *        Deprecated - VF Module versions also captured by modelCustomizationUuid
+     * @param vfModuleName Name to be assigned to the new VF Module
+     * @param requestType Indicates if this is a Volume Group or Module request
+     * @param volumeGroupId Identifier (i.e. deployment ID) for a Volume Group
+     *        to attach to a VF Module
+     * @param baseVfModuleId Identifier (i.e. deployment ID) of the Base Module if
+     *        this is an Add-on module
+     * @param modelCustomizationUuid Unique ID for the VF Module's model.  Replaces
+     *        the use of vfModuleType.
+     * @param inputs Map of key=value inputs for VNF stack creation
+     * @param failIfExists Flag whether already existing VNF should be considered
+     * @param backout Flag whether to suppress automatic backout (for testing)
+     * @param msoRequest Request tracking information for logs
+     * @param vnfId Holder for output VF Module instance ID in the cloud
+     * @param outputs Holder for Map of VNF outputs from Deployment (assigned IPs, etc)
+     * @param rollback Holder for returning VnfRollback object
+     */
+    public void createVfModule(String cloudSiteId,
+            String tenantId,
+            String vfModuleType,
+            String vnfVersion,
+            String vfModuleName,
+            String requestType,
+            String volumeGroupId,
+            String baseVfModuleId,
+            String modelCustomizationUuid,
+            Map <String, String> inputs,
+            Boolean failIfExists,
+            Boolean backout,
+            MsoRequest msoRequest,
+            Holder <String> vnfId,
+            Holder <Map <String, String>> outputs,
+            Holder <VnfRollback> rollback)
+        throws VnfException
+    {
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+        
+       MsoLogger.setLogContext (msoRequest);
+       MsoLogger.setServiceName ("CreateVfModule");
+
+        // Require a model customization ID.  Every VF Module definition must have one.
+        if (modelCustomizationUuid == null  ||  modelCustomizationUuid.isEmpty()) {
+                       LOGGER.debug("Missing required input: modelCustomizationUuid");
+                       String error = "Create vfModule error: Missing required input: modelCustomizationUuid";
+            LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
+                    "VF Module ModelCustomizationUuid", "null", "VDU", "", MsoLogger.ErrorCode.DataError, "Create VF Module: Missing required input: modelCustomizationUuid");
+            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;  
+        
+        if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId))
+               baseVfModuleId = null;
+
+        if (inputs == null) {
+               // Create an empty set of inputs
+               inputs = new HashMap<String,String>();
+               LOGGER.debug("inputs == null - setting to empty");
+        } else {
+               this.sendMapToDebug(inputs);
+        }
+        
+        // Check if this is for a "Volume" module
+        boolean isVolumeRequest = false;
+        if (requestType.startsWith("VOLUME")) {
+               isVolumeRequest = true;
+        }
+
+        LOGGER.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = " + baseVfModuleId);
+
+        // Build a default rollback object (no actions performed)
+        VnfRollback vfRollback = new VnfRollback();
+        vfRollback.setCloudSiteId(cloudSiteId);
+        vfRollback.setTenantId(tenantId);
+        vfRollback.setMsoRequest(msoRequest);
+        vfRollback.setRequestType(requestType);
+        vfRollback.setIsBase(false);   // Until we know better
+        vfRollback.setVolumeGroupHeatStackId(volumeGroupId);
+        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.
+        // There are three relevant records:  VfModule, VfModuleCustomization, VnfResource
+
+        CatalogDatabase db = CatalogDatabase.getInstance();
+       VfModule vfModule = null;
+       VnfResource vnfResource = null;
+       VfModuleCustomization vfModuleCust = null;
+
+        try {
+               vfModuleCust = db.getVfModuleCustomizationByModelCustomizationId(modelCustomizationUuid);
+               
+            if (vfModuleCust == null) {
+                       String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + modelCustomizationUuid;
+                       LOGGER.debug(error);
+                LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
+                            "VF Module ModelCustomizationUuid", modelCustomizationUuid, "CatalogDb", "", MsoLogger.ErrorCode.DataError, error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+                throw new VnfException(error, MsoExceptionCategory.USERDATA);
+            } else {
+                       LOGGER.debug("Found vfModuleCust entry " + vfModuleCust.toString());
+            }
+
+            // Get the vfModule and vnfResource records
+               vfModule = vfModuleCust.getVfModule();
+               vnfResource = db.getVnfResourceByModelUuid(vfModule.getVnfResourceModelUUId());
+        }
+        catch (Exception e) {
+            db.close ();
+               LOGGER.debug("unhandled exception in create VF - [Query]" + e.getMessage());
+               throw new VnfException("Exception during create VF " + e.getMessage());
+        }
+
+        //  Perform a version check against cloudSite
+        // Obtain the cloud site information where we will create the VF Module
+        Optional<CloudSite> cloudSite = cloudConfig.getCloudSite (cloudSiteId);
+        if (!cloudSite.isPresent()) {
+            throw new VnfException (new MsoCloudSiteNotFound (cloudSiteId));
+        }
+               MavenLikeVersioning aicV = new MavenLikeVersioning();
+               aicV.setVersion(cloudSite.get().getAic_version());
+    
+               String vnfMin = vnfResource.getAicVersionMin();
+               String vnfMax = vnfResource.getAicVersionMax();
+               
+               if ( (vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) ||
+                    (vnfMax != null && aicV.isMoreRecentThan(vnfMax)))
+               {
+                       // ERROR
+                       String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUuid() + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax + " NOT supported on Cloud: " + cloudSite.get().getId() + " with AIC_Version:" + cloudSite.get().getAic_version();
+                       LOGGER.error(MessageEnum.RA_CONFIG_EXC, error, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - setVersion");
+                       LOGGER.debug(error);
+                       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);
+        
+        // First, look up to see if the VF already exists.
+
+        long subStartTime1 = System.currentTimeMillis ();
+        try {
+            vduInstance = vduPlugin.queryVdu (cloudInfo, vfModuleName);
+            LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from VduPlugin", "VDU", "QueryVDU", vfModuleName);
+        }
+        catch (VduException me) {
+            // Failed to query the VDU due to a plugin exception.
+            String error = "Create VF Module: Query " + vfModuleName + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+            LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, "VDU", "queryVdu", MsoLogger.ErrorCode.DataError, "Exception - queryVdu", me);
+            LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "VDU", "QueryVdu", vfModuleName);
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+
+            // Convert to a generic VnfException
+            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
+                               String error = "Create VF: Deployment " + vfModuleName + " already exists in " + cloudSiteId + "/" + tenantId;
+                               LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "VDU", "queryVdu", MsoLogger.ErrorCode.DataError, "VF Module " + vfModuleName + " already exists");
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                               throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, vduInstance.getVduInstanceId());
+                       } else {
+                               // Found existing deployment and client has not requested "failIfExists".
+                               // Populate the outputs from the existing deployment.
+
+                               vnfId.value = vduInstance.getVduInstanceId();
+                               outputs.value = copyStringOutputs (vduInstance.getOutputs ());
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully create VF Module (found existing)");
+                    return;
+                       }
+               }
+               // Check through various detailed error cases
+               else if (status == VduStateType.INSTANTIATING || status == VduStateType.DELETING || status == VduStateType.UPDATING) {
+                       // fail - it's in progress - return meaningful error
+                String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; please wait for it to complete, or fix manually.";
+                LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "VDU", "queryVdu", MsoLogger.ErrorCode.DataError, "VF Module " + vfModuleName + " already exists");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, vduInstance.getVduInstanceId());
+               }
+               else if (status == VduStateType.FAILED) {
+                       // fail - it exists and is in a FAILED state
+                String error = "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
+                LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "VDU", "queryVdu", MsoLogger.ErrorCode.DataError, "VF Module " + vfModuleName + " already exists and is in FAILED state");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, vduInstance.getVduInstanceId());
+               }
+               else if (status == VduStateType.UNKNOWN) {
+                       // fail - it exists and is in a UNKNOWN state
+                String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
+                LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "VDU", "queryVdu", MsoLogger.ErrorCode.DataError, "VF Module " + vfModuleName + " already exists and is in " + status.toString() + " state");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, vduInstance.getVduInstanceId());
+               }
+               else {
+                       // Unexpected, since all known status values have been tested for
+                String error = "Create VF: Deployment " + vfModuleName + " already exists with unexpected status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
+                LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "VDU", "queryVdu", MsoLogger.ErrorCode.DataError, "VF Module " + vfModuleName + " already exists and is in an unknown state");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, vduInstance.getVduInstanceId());
+               }
+        }
+   
+        
+        // Collect outputs from Base Modules and Volume Modules
+        Map<String, Object> baseModuleOutputs = null;
+        Map<String, Object> volumeGroupOutputs = null;
+
+        // If a Volume Group was provided, query its outputs for inclusion in Module input parameters
+        if (volumeGroupId != null) {
+            long subStartTime2 = System.currentTimeMillis ();
+            VduInstance volumeVdu = null;
+            try {
+                volumeVdu = vduPlugin.queryVdu (cloudInfo, volumeGroupId);
+                LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Success response from VduPlugin", "VDU", "QueryVdu", volumeGroupId);
+            }
+            catch (VduException me) {
+                // Failed to query the Volume Group VDU due to a plugin exception.
+                String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+                LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, volumeGroupId, cloudSiteId, tenantId, "VDU", "queryVdu(volume)", MsoLogger.ErrorCode.DataError, "Exception - queryVdu(volume)", me);
+                LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "VDU", "QueryVdu(volume)", volumeGroupId);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+
+                // Convert to a generic VnfException
+                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");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                   LOGGER.debug(error);
+                   throw new VnfException (error, MsoExceptionCategory.USERDATA);
+               } else {
+                       LOGGER.debug("Found nested volume group");
+                       volumeGroupOutputs = volumeVdu.getOutputs();
+                       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
+
+        if (vfModule.isBase()) {
+            LOGGER.debug("This is a BASE Module request");
+            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) {
+                LOGGER.debug ("WARNING:  Add-on Module request - no Base Module ID provided");
+            }
+
+            if (baseVfModuleId != null) {
+                   long subStartTime2 = System.currentTimeMillis ();
+                   VduInstance baseVdu = null;
+                   try {
+                       baseVdu = vduPlugin.queryVdu (cloudInfo, baseVfModuleId);
+                       LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Success response from VduPlugin", "VDU", "QueryVdu(Base)", baseVfModuleId);
+                   }
+                   catch (MsoException me) {
+                       // Failed to query the Base VF Module due to a Vdu Plugin exception.
+                       String error = "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+                       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");
+                       LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                           LOGGER.debug(error);
+                           throw new VnfException (error, MsoExceptionCategory.USERDATA);
+                       } else {
+                               LOGGER.debug("Found base module");
+                               baseModuleOutputs = baseVdu.getOutputs();
+                               this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs");
+                       }
+            }
+        }
+
+        // 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.
+
+               // NOTE: The template is fixed for the VF Module.  The environment is part of the customization.
+            String heatTemplateArtifactUuid = null;
+            String heatEnvironmentArtifactUuid = null;
+
+                       if (isVolumeRequest) {
+                               heatTemplateArtifactUuid = vfModule.getVolHeatTemplateArtifactUUId();
+                               heatEnvironmentArtifactUuid = vfModuleCust.getVolEnvironmentArtifactUuid();
+                       } else {
+                               heatTemplateArtifactUuid = vfModule.getHeatTemplateArtifactUUId();
+                               heatEnvironmentArtifactUuid = vfModuleCust.getHeatEnvironmentArtifactUuid();
+                       }
+                       
+                       if (heatTemplateArtifactUuid == null || heatTemplateArtifactUuid.equals("")) {
+                               String error = "Create: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType=" + requestType;
+                               LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Template ID", vfModuleType, "Cloudify", "", MsoLogger.ErrorCode.DataError, "Create: No Heat Template ID defined in catalog database");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+                               alarmLogger.sendAlarm(MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
+                               throw new VnfException(error, MsoExceptionCategory.INTERNAL);
+                       }
+                       
+                       HeatTemplate heatTemplate = db.getHeatTemplateByArtifactUuidRegularQuery(heatTemplateArtifactUuid);
+
+                       if (heatTemplate == null) {
+                               String error = "Create VF/VNF: no entry found for heat template ID = " + heatTemplateArtifactUuid;
+                               LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
+                                               "Heat Template ID",
+                                               String.valueOf(heatTemplateArtifactUuid), "Cloudify", "", MsoLogger.ErrorCode.BusinessProcesssError, "Create VF/VNF: no entry found for heat template ID = " + heatTemplateArtifactUuid);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+                               alarmLogger.sendAlarm(MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
+                               throw new VnfException(error, MsoExceptionCategory.INTERNAL);
+                       }
+                       LOGGER.debug("Got HEAT Template record from DB");
+
+                       // Next get the Environment record.  This is optional.
+            HeatEnvironment heatEnvironment = null;
+            if (heatEnvironmentArtifactUuid != null && !heatEnvironmentArtifactUuid.equals(""))
+            {
+                heatEnvironment = db.getHeatEnvironmentByArtifactUuid(heatEnvironmentArtifactUuid);
+                if (heatEnvironment == null) {
+                    String error = "Create VFModule: undefined Heat Environment. VFModule=" + vfModuleType
+                                   + ", Environment ID="
+                                   + heatEnvironmentArtifactUuid;
+                    LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", String.valueOf(heatEnvironmentArtifactUuid), "Cloudify", "getEnvironment", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: undefined Heat Environment");
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+                    // Alarm on this error, configuration must be fixed
+                    alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
+
+                    throw new VnfException (error, MsoExceptionCategory.INTERNAL);
+                }
+                LOGGER.debug ("Got Heat Environment from DB");
+            } else {
+                LOGGER.debug ("no environment parameter found for this Type " + vfModuleType);
+            }
+
+            
+         // 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<String, Object> goldenInputs = new HashMap<>();
+            List<String> extraInputs = new ArrayList<>();
+
+               Boolean skipInputChecks = false;
+            
+               if (skipInputChecks) {
+                       goldenInputs = new HashMap<String,Object>();
+                       for (String key : inputs.keySet()) {
+                               goldenInputs.put(key, inputs.get(key));
+                       }
+               }
+               else {
+                       // Build maps for the parameters (including aliases) to simplify checks
+                       HashMap<String, HeatTemplateParam> params = new HashMap<>();
+                       
+                       Set<HeatTemplateParam> paramSet = heatTemplate.getParameters();
+                       LOGGER.debug("paramSet has " + paramSet.size() + " entries");
+                       
+                       for (HeatTemplateParam htp : paramSet) {
+                               params.put(htp.getParamName(), htp);
+
+                               // Include aliases.
+                               String alias = htp.getParamAlias();
+                               if (alias != null && !alias.equals("") && !params.containsKey(alias)) {
+                                       params.put(alias, htp);
+                               }
+                       }
+                       
+                       // First, convert all inputs to their "template" type
+                       for (String key : inputs.keySet()) {
+                               if (params.containsKey(key)) {
+                                       Object value = convertInputValue(inputs.get(key), params.get(key));
+                                       if (value != null) {
+                                               goldenInputs.put(key, value);
+                                       }
+                                       else {
+                                               LOGGER.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to " + params.get(key).getParamType());
+                                       }
+                               } else {
+                                       extraInputs.add(key);
+                               }
+                       }
+                       
+                       if (!extraInputs.isEmpty()) {
+                               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()) {
+                                       if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
+                                               goldenInputs.put(key, volumeGroupOutputs.get(key));
+                                       }
+                               }
+                       }
+                       
+                       // Next add in Base Module Outputs if there are any.  Copy directly without conversions.
+                       if (baseModuleOutputs != null  &&  !baseModuleOutputs.isEmpty()) {
+                               for (String key : baseModuleOutputs.keySet()) {
+                                       if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
+                                               goldenInputs.put(key, baseModuleOutputs.get(key));
+                                       }
+                               }
+                       }
+                               
+                               // 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;
+                   try {
+                       String propertyString = this.msoPropertiesFactory.getMsoJavaProperties(MSO_PROP_VNF_ADAPTER)
+                                       .getProperty(MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS,null);
+                       if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
+                           checkRequiredParameters = false;
+                           LOGGER.debug ("CheckRequiredParameters is FALSE. Will still check but then skip blocking..."
+                                         + MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS);
+                       }
+                   } catch (Exception e) {
+                       // 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
+                   // support pre-set parameter/values does not yet exist in the model.
+                   //
+                               StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
+                               MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry (sb);
+                   for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
+                       if (parm.isRequired () && (!goldenInputs.containsKey (parm.getParamName ()))) {
+                           if (mhee != null && mhee.containsParameter(parm.getParamName())) {
+                               LOGGER.debug ("Required parameter " + parm.getParamName ()
+                                             + " appears to be in environment - do not count as missing");
+                           } else {
+                                   LOGGER.debug ("adding to missing parameters list: " + parm.getParamName ());
+                                   if (missingParams == null) {
+                                       missingParams = parm.getParamName ();
+                                   } else {
+                                       missingParams += "," + parm.getParamName ();
+                                   }
+                           }
+                       }
+                   }
+                       
+                   if (missingParams != null) {
+                       if (checkRequiredParameters) {
+                               // Problem - missing one or more required parameters
+                               String error = "Create VFModule: Missing Required inputs: " + missingParams;
+                               LOGGER.error (MessageEnum.RA_MISSING_PARAM, missingParams, "VDU", "", MsoLogger.ErrorCode.DataError, "Create VFModule: Missing Required inputs");
+                           LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
+                               throw new VnfException (error, MsoExceptionCategory.USERDATA);
+                       } else {
+                               LOGGER.debug ("found missing parameters [" + missingParams + "] - but checkRequiredParameters is false - will not block");
+                       }
+                   } else {
+                       LOGGER.debug ("No missing parameters found - ok to proceed");
+                   }
+
+                       } // 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;
+                               if (! isVolumeRequest) {
+                                       vduModel = vduMapper.mapVfModuleCustomizationToVdu(vfModuleCust);
+                               } 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) {
+                   // Failed to instantiate the VDU.
+                   me.addContext ("CreateVFModule");
+                   String error = "Create VF Module " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+                   LOGGER.recordMetricEvent (instantiateVduStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "VDU", "instantiateVdu", vfModuleName);
+                   LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "VDU", "", MsoLogger.ErrorCode.DataError, "MsoException - instantiateVdu", me);
+                   LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+                   // Convert to a generic VnfException
+                   throw new VnfException (me);
+               }
+                   catch (NullPointerException npe) {
+                       String error = "Create VFModule " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + npe;
+                       LOGGER.recordMetricEvent (instantiateVduStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error, "VDU", "instantiateVdu", vfModuleName);
+                       LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "VDU", "", MsoLogger.ErrorCode.DataError, "NullPointerException - instantiateVdu", npe);
+                       LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error);
+                       LOGGER.debug("NULL POINTER EXCEPTION at vduPlugin.instantiateVdu", npe);
+                       throw new VnfException ("NullPointerException during instantiateVdu");
+                   }
+                       catch (Exception e) {
+                       String error = "Create VFModule " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + e;
+                       LOGGER.recordMetricEvent (instantiateVduStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.UnknownError, error, "VDU", "instantiateVdu", vfModuleName);
+                       LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.UnknownError, error);
+                       LOGGER.debug("Unhandled exception at vduPlugin.instantiateVdu", e);
+                       } finally {
+                               db.close();
+                       }
+
+        // Reach this point if create is successful.
+        // Populate remaining rollback info and response parameters.
+        vfRollback.setVnfCreated (true);
+        vfRollback.setVnfId (vduInstance.getVduInstanceId());
+        vnfId.value = vduInstance.getVduInstanceId();
+        outputs.value = copyStringOutputs (vduInstance.getOutputs ());         
+
+        rollback.value = vfRollback;
+
+        LOGGER.debug ("VF Module " + vfModuleName + " successfully created");
+        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully create VF Module");
+        return;
+    }
+
+    public void deleteVfModule (String cloudSiteId,
+            String tenantId,
+            String vfModuleId,
+            MsoRequest msoRequest,
+            Holder <Map <String, String>> outputs) throws VnfException
+       {
+               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);
+               
+               try {
+                       vduInstance = vduPlugin.queryVdu (cloudInfo, vfModuleId);
+                       LOGGER.recordMetricEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received VDU Query response", "VDU", "QueryVDU", vfModuleId);
+               }
+               catch (VduException e) {
+                       // Failed to query the VDU due to a plugin exception.
+                       // Convert to a generic VnfException
+                       e.addContext ("QueryVFModule");
+                       String error = "Query VfModule (VDU): " + vfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + e;
+                       LOGGER.recordMetricEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "VDU", "QueryVNF", vfModuleId);
+                       LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleId, cloudSiteId, tenantId, "VDU", "QueryVFModule", MsoLogger.ErrorCode.DataError, "Exception - queryVDU", e);
+                       LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+                       throw new VnfException (e);
+               }
+               
+               // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected Object types
+               outputs.value = convertMapStringObjectToStringString(vduInstance.getOutputs());
+               
+               // Use the VduPlugin to delete the VDU.
+               // The possible outcomes of deleteVdu are
+               // - a vnfInstance object with status of DELETED (success)
+               // - a vnfInstance object with status of NOTFOUND (VDU did not exist, treat as success)
+               // - a vnfInstance object with status of FAILED (error)
+               // Also, VduException could be thrown.
+               long subStartTime = System.currentTimeMillis ();
+               try {
+                       // TODO:  Get an appropriate timeout value - require access to the model
+                       vduPlugin.deleteVdu(cloudInfo, vfModuleId, 5);
+                       LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from deleteVdu", "VDU", "DeleteVdu", vfModuleId);
+               } catch (VduException me) {
+                       me.addContext ("DeleteVfModule");
+                       // Convert to a generic VnfException
+                       String error = "Delete VF: " + vfModuleId + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+                       LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "DeleteVdu", "DeleteVdu", vfModuleId);
+                       LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vfModuleId, cloudSiteId, tenantId, "VDU", "DeleteVdu", MsoLogger.ErrorCode.DataError, "Exception - DeleteVdu: " + me.getMessage());
+                       LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+                       throw new VnfException (me);
+               }
+               
+               // On success, nothing is returned.
+               LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully delete VF");
+               return;
+       }
+       
+       // Update VF Module not yet implemented for generic VDU plug-in model.
+       @Override
+       public void updateVfModule (String cloudSiteId,
+                   String tenantId,
+                   String vnfType,
+                   String vnfVersion,
+                   String vnfName,
+                   String requestType,
+                   String volumeGroupHeatStackId,
+                   String baseVfHeatStackId,
+                   String vfModuleStackId,
+                   String modelCustomizationUuid,
+                   Map <String, String> inputs,
+                   MsoRequest msoRequest,
+                   Holder <Map <String, String>> outputs,
+                   Holder <VnfRollback> rollback) throws VnfException
+       {
+               // This operation is not currently supported for VduPlugin-orchestrated VF Modules.
+               LOGGER.debug ("Update VF Module command attempted but not supported");
+               throw new VnfException ("UpdateVfModule:  Unsupported command", MsoExceptionCategory.USERDATA);
+       }
+    
+    /*
+     * Dynamic selection of a VduPlugin version.  For initial tests, base on the "orchestrator"
+     * defined for the target cloud.  Should really be looking at the VNF Model (ochestration_mode)
+     * but we don't currently have access to that in Query and Delete cases.
+     */
+    private VduPlugin getVduPlugin (String cloudSiteId) {
+       Optional<CloudSite> cloudSite = cloudConfig.getCloudSite(cloudSiteId);
+       if (cloudSite.isPresent()) {
+               String orchestrator = cloudSite.get().getOrchestrator();
+               
+               if (orchestrator.equalsIgnoreCase("CLOUDIFY")) {
+                       return cloudifyUtils;                           
+               }
+               else if (orchestrator.equalsIgnoreCase("HEAT")) {
+                       return heatUtils;
+               }
+       }
+       
+       // Default - return HEAT plugin, though will fail later
+       return heatUtils;
+    }
+
+}
index f7302c0..487e9c2 100644 (file)
@@ -74,9 +74,9 @@ public class VnfAdapterRestUtils
                        vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory, cloudConfigFactory);
                }
                else {
-                       // Don't expect this, but default is the HEAT adapter
-                       LOGGER.debug ("GetVnfAdapterImpl: Return Default (Heat) Adapter");
-                       vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory, cloudConfigFactory);
+                       // Default is the PLUGIN adapter
+                       LOGGER.debug ("GetVnfAdapterImpl: Return Default (Plugin) Adapter");
+                       vnfAdapter = new MsoVnfPluginAdapterImpl (msoPropertiesFactory, cloudConfigFactory);
                }
                
                return vnfAdapter;
diff --git a/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/MsoVnfPluginAdapterImplTest.java b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/MsoVnfPluginAdapterImplTest.java
new file mode 100644 (file)
index 0000000..37ca4f7
--- /dev/null
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2018 Huawei 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.ws.Holder;
+
+import org.junit.Test;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+import org.openecomp.mso.entity.MsoRequest;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+
+public class MsoVnfPluginAdapterImplTest {
+
+       @Test(expected = NullPointerException.class)
+    public void queryVnfNullPointerExceptionTest() throws Exception {
+        MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+        MsoRequest msoRequest = new MsoRequest();
+        msoRequest.setRequestId("12345");
+        msoRequest.setServiceInstanceId("12345");
+
+        instance.queryVnf("siteid", "1234", "vfname",
+                msoRequest, new Holder<>(), new Holder<>(), new Holder<>(),
+                new Holder<>());
+    }
+
+       @Test(expected = VnfException.class)
+    public void deleteVnfVnfExceptionTest() throws Exception {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+        MsoRequest msoRequest = new MsoRequest();
+        msoRequest.setRequestId("12345");
+        msoRequest.setServiceInstanceId("12345");
+
+        instance.deleteVnf("12344", "234", "vnfname", msoRequest);
+
+    }
+
+       @Test
+       public void rollbackVnf() throws Exception {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+               MsoRequest msoRequest = new MsoRequest();
+               msoRequest.setRequestId("12345");
+               msoRequest.setServiceInstanceId("12345");
+
+        VnfRollback vnfRollback = new VnfRollback();
+        vnfRollback.setModelCustomizationUuid("1234");
+        vnfRollback.setVfModuleStackId("2134");
+        vnfRollback.setVnfId("123");
+        vnfRollback.setModelCustomizationUuid("1234");
+
+        instance.rollbackVnf(vnfRollback);
+       }
+
+       @Test(expected = VnfException.class)
+       public void createVfModuleVnfException() throws Exception {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+               MsoRequest msoRequest = new MsoRequest();
+               msoRequest.setRequestId("12345");
+               msoRequest.setServiceInstanceId("12345");
+
+               instance.createVfModule("123", "123", "vf", "v1", "module-005", "create", "3245", "234", "123", new HashMap<>(), true, true, msoRequest, new Holder<>(), new Holder<>(), new Holder<>());
+       }
+
+       @Test(expected = VnfException.class)
+       public void updateVfModuleVnfException() throws Exception {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+               MsoRequest msoRequest = new MsoRequest();
+               msoRequest.setRequestId("12345");
+               msoRequest.setServiceInstanceId("12345");
+
+               instance.updateVfModule("123", "1234", "fw", "v2", "vnf1", "create", "123", "12", "233", "234", new HashMap<>(), msoRequest, new Holder<>(), new Holder<>());
+       }
+
+       @Test
+       public void healthCheckVNFTest() {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+               instance.healthCheck();
+       }
+
+       @Test
+       public void createVnfTest() {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+               MsoRequest msoRequest = new MsoRequest();
+               msoRequest.setRequestId("12345");
+               msoRequest.setServiceInstanceId("12345");
+
+               Map<String, String> map = new HashMap<>();
+               map.put("key1", "value1");
+               try {
+                       instance.createVnf("mdt1", "88a6ca3ee0394ade9403f075db23167e", "vnf", "1", "vSAMP12", "VFMOD",
+                                       "volumeGroupHeatStackId|1", map,
+                                       Boolean.FALSE, Boolean.TRUE, msoRequest, new Holder<>(), new Holder<>(),
+                new Holder<>());
+               } catch (Exception e) {
+               }
+       }
+
+       @Test
+       public void updateVnfTest() {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+               MsoRequest msoRequest = new MsoRequest();
+               msoRequest.setRequestId("12345");
+               msoRequest.setServiceInstanceId("12345");
+
+               Map<String, String> map = new HashMap<>();
+               
+               map.put("key1", "value1");
+               try {
+                       instance.updateVnf("mdt1", "88a6ca3ee0394ade9403f075db23167e", "vnf", "1", "vSAMP12", "VFMOD",
+                                       "volumeGroupHeatStackId|1",  map, msoRequest, new Holder<>(),
+                new Holder<>());
+               } catch (Exception e) {
+
+               }
+       }
+
+       @Test
+       public void deleteVnfTest() {
+               MsoVnfPluginAdapterImpl instance = new MsoVnfPluginAdapterImpl();
+               MsoRequest msoRequest = new MsoRequest();
+               msoRequest.setRequestId("12345");
+               msoRequest.setServiceInstanceId("12345");
+               try {
+                       instance.deleteVnf("mdt1", "88a6ca3ee0394ade9403f075db23167e", "vSAMP12", msoRequest);
+               } catch (Exception e) {
+
+               }
+       }
+
+}
diff --git a/cloudify-client/src/main/java/org/openecomp/mso/cloudify/v3/model/AzureConfig.java b/cloudify-client/src/main/java/org/openecomp/mso/cloudify/v3/model/AzureConfig.java
new file mode 100644 (file)
index 0000000..ddfd1fd
--- /dev/null
@@ -0,0 +1,75 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * ONAP - SO\r
+ * ================================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+\r
+package org.openecomp.mso.cloudify.v3.model;\r
+\r
+import java.io.Serializable;\r
+\r
+import com.fasterxml.jackson.annotation.JsonProperty;\r
+\r
+public class AzureConfig implements Serializable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       @JsonProperty("subscription_id")\r
+       String subscriptionId;\r
+\r
+       @JsonProperty("tenant_id")\r
+       String tenantId;\r
+\r
+       @JsonProperty("client_id")\r
+       String clientId;\r
+\r
+       @JsonProperty("client_secret")\r
+       String clientSecret;\r
+\r
+       public String getSubscriptionId() {\r
+               return subscriptionId;\r
+       }\r
+\r
+       public void setSubscriptionId(String subscriptionId) {\r
+               this.subscriptionId = subscriptionId;\r
+       }\r
+\r
+       public String getTenantId() {\r
+               return tenantId;\r
+       }\r
+\r
+       public void setTenantId(String tenantId) {\r
+               this.tenantId = tenantId;\r
+       }\r
+\r
+       public String getClientId() {\r
+               return clientId;\r
+       }\r
+\r
+       public void setClientId(String clientId) {\r
+               this.clientId = clientId;\r
+       }\r
+\r
+       public String getClientSecret() {\r
+               return clientSecret;\r
+       }\r
+\r
+       public void setClientSecret(String clientSecret) {\r
+               this.clientSecret = clientSecret;\r
+       }\r
+       \r
+}
\ No newline at end of file