Initial OpenECOMP MSO commit

Change-Id: Ia6a7574859480717402cc2f22534d9973a78fa6d
Signed-off-by: ChrisC <cc697w@intl.att.com>
diff --git a/adapters/mso-vnf-adapter/README.md b/adapters/mso-vnf-adapter/README.md
new file mode 100644
index 0000000..2085fc9
--- /dev/null
+++ b/adapters/mso-vnf-adapter/README.md
@@ -0,0 +1,11 @@
+This artifact is the MSO VNF adapter.  It serves both SOAP and REST requests to the following URLs:
+
+  * http://host:port/vnfs/VnfAdapter?wsdl
+  * http://host:port/vnfs/VnfAdapterAsync?wsdl
+  * http://host:port/vnfs/rest/v1/vnfs/healthcheck
+  * http://host:port/vnfs/rest/v1/vnfs/{aaiVnfId}/vf-modules
+  * http://host:port/vnfs/rest/v1/vnfs/{aaiVnfId}/vf-modules/{aaiVfModuleId}
+  * http://host:port/vnfs/rest/v1/vnfs/{aaiVnfId}/vf-modules/{aaiVfModuleId}/rollback
+  * http://host:port/vnfs/rest/v1/volume-groups
+  * http://host:port/vnfs/rest/v1/volume-groups/{aaiVolumeGroupId}
+  * http://host:port/vnfs/rest/v1/volume-groups/{aaiVolumeGroupId}/rollback
diff --git a/adapters/mso-vnf-adapter/WebContent/META-INF/MANIFEST.MF b/adapters/mso-vnf-adapter/WebContent/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/adapters/mso-vnf-adapter/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0

+Class-Path: 

+

diff --git a/adapters/mso-vnf-adapter/WebContent/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector b/adapters/mso-vnf-adapter/WebContent/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector
new file mode 100644
index 0000000..1281d32
--- /dev/null
+++ b/adapters/mso-vnf-adapter/WebContent/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector
@@ -0,0 +1 @@
+com.woorea.openstack.connector.HttpClientConnector
\ No newline at end of file
diff --git a/adapters/mso-vnf-adapter/WebContent/WEB-INF/jboss-deployment-structure.xml b/adapters/mso-vnf-adapter/WebContent/WEB-INF/jboss-deployment-structure.xml
new file mode 100644
index 0000000..58ddb4a
--- /dev/null
+++ b/adapters/mso-vnf-adapter/WebContent/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,16 @@
+<jboss-deployment-structure>

+	<deployment>

+		<!-- Exclusions allow you to prevent the server from automatically adding some dependencies -->

+		<exclusions>

+			<module name="org.apache.log4j" />

+			<module name="org.slf4j" />

+			<module name="org.slf4j.impl" />

+		</exclusions>

+		<dependencies>

+			    <module name="org.jboss.jandex" slot="main" />

+                <module name="org.javassist" slot="main" />

+                <module name="org.antlr" slot="main" />

+                <module name="org.dom4j" slot="main" />

+		</dependencies>

+	</deployment>

+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/adapters/mso-vnf-adapter/WebContent/WEB-INF/jboss-web.xml b/adapters/mso-vnf-adapter/WebContent/WEB-INF/jboss-web.xml
new file mode 100644
index 0000000..6224f07
--- /dev/null
+++ b/adapters/mso-vnf-adapter/WebContent/WEB-INF/jboss-web.xml
@@ -0,0 +1,3 @@
+<jboss-web>

+	<context-root>vnfs</context-root>

+</jboss-web>
\ No newline at end of file
diff --git a/adapters/mso-vnf-adapter/WebContent/WEB-INF/web.xml b/adapters/mso-vnf-adapter/WebContent/WEB-INF/web.xml
new file mode 100644
index 0000000..3a50781
--- /dev/null
+++ b/adapters/mso-vnf-adapter/WebContent/WEB-INF/web.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
+  <display-name>mso-vnf-adapter</display-name>
+
+  <context-param>
+	<param-name>resteasy.jndi.resources</param-name>
+	<param-value>java:module/MsoPropertiesFactory,java:module/CloudConfigFactory</param-value>
+  </context-param>
+
+  <context-param>
+    <param-name>log.configuration</param-name>
+    <param-value>logback.vnf.xml</param-value>
+  </context-param>
+  <context-param>
+    <param-name>mso.configuration</param-name>
+    <param-value>MSO_PROP_VNF_ADAPTER=mso.vnf.properties,MSO_PROP_TOPOLOGY=topology.properties</param-value>
+  </context-param>
+  <context-param>
+  	<param-name>mso.cloud_config.configuration</param-name>
+  	<param-value>cloud_config.json=2</param-value>
+  </context-param>
+  <context-param>
+    <param-name>resteasy.resources</param-name>
+    <param-value>
+	org.openecomp.mso.MsoStatusHandler,
+	org.openecomp.mso.logger.MsoLoggingServlet,
+	org.openecomp.mso.adapters.vnf.HealthCheckHandler,
+	org.openecomp.mso.adapters.vnf.VnfAdapterRest,
+	org.openecomp.mso.adapters.vnf.VolumeAdapterRest
+    </param-value>
+  </context-param>
+  <context-param>
+    <param-name>resteasy.servlet.mapping.prefix</param-name>
+    <param-value>/rest</param-value>
+  </context-param>
+  <servlet>
+    <servlet-name>Resteasy</servlet-name>
+    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>Resteasy</servlet-name>
+    <url-pattern>/rest/*</url-pattern>
+  </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>RestRequests</web-resource-name>
+            <description>Rest Ingress Requests</description>
+            <url-pattern>/rest/v1/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>DELETE</http-method>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>BPEL-Client</role-name>
+        </auth-constraint>
+    </security-constraint>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>SoapRequests</web-resource-name>
+            <description>Soap Ingress Requests</description>
+            <url-pattern>/VnfAdapter</url-pattern>
+            <url-pattern>/VnfAdapterAsync</url-pattern>
+            <http-method>POST</http-method>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>BPEL-Client</role-name>
+        </auth-constraint>
+    </security-constraint>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>SiteStatus</web-resource-name>
+            <description>SiteStatus APIs</description>
+            <url-pattern>/rest/setStatus/*</url-pattern>
+            <http-method>POST</http-method>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>SiteControl-Client</role-name>
+        </auth-constraint>
+    </security-constraint>
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>ApplicationRealm</realm-name>
+    </login-config>
+    <security-role>
+        <role-name>BPEL-Client</role-name>
+    </security-role>
+    <security-role>
+        <role-name>SiteControl-Client</role-name>
+    </security-role>
+
+    <filter>
+		<filter-name>LogFilter</filter-name>
+		<filter-class>org.openecomp.mso.logger.LogFilter</filter-class>
+  </filter>
+    <filter-mapping>
+		<filter-name>LogFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+</web-app>
diff --git a/adapters/mso-vnf-adapter/WebContent/check.html b/adapters/mso-vnf-adapter/WebContent/check.html
new file mode 100644
index 0000000..00f37d6
--- /dev/null
+++ b/adapters/mso-vnf-adapter/WebContent/check.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>

+<html>

+<head>

+<meta charset="ISO-8859-1">

+<title>Health Check</title>

+</head>

+<body>

+Application ready

+</body>

+</html>
\ No newline at end of file
diff --git a/adapters/mso-vnf-adapter/pom.xml b/adapters/mso-vnf-adapter/pom.xml
new file mode 100644
index 0000000..1914eb1
--- /dev/null
+++ b/adapters/mso-vnf-adapter/pom.xml
@@ -0,0 +1,159 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.openecomp.mso</groupId>
+		<artifactId>adapters</artifactId>
+		<version>0.0.4-SNAPSHOT</version>
+	</parent>
+	<groupId>org.openecomp.mso.adapters</groupId>
+	<artifactId>mso-vnf-adapter</artifactId>
+	<packaging>war</packaging>
+	<name>mso-vnf-adapter</name>
+	<description>Web Service and REST endpoint for VNF operations</description>
+
+	<build>
+		<finalName>${project.artifactId}-${project.version}</finalName>
+		<plugins>
+			<plugin>
+				<artifactId>maven-war-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<warSourceDirectory>WebContent</warSourceDirectory>
+					<failOnMissingWebXml>false</failOnMissingWebXml>
+					<attachClasses>true</attachClasses>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.jvnet.jax-ws-commons</groupId>
+				<artifactId>jaxws-maven-plugin</artifactId>
+				<version>2.3</version>
+				<executions>
+					<execution>
+					    <id>Synch</id>
+						<goals>
+							<goal>wsgen</goal>
+						</goals>
+						<configuration>
+							<verbose>true</verbose>
+							<sei>org.openecomp.mso.adapters.vnf.MsoVnfAdapterImpl</sei>
+							<genWsdl>true</genWsdl>
+							<inlineSchemas>true</inlineSchemas>
+						</configuration>
+					</execution>
+					<execution>
+					        <id>Asynch</id>
+						<goals>
+							<goal>wsgen</goal>
+						</goals>
+						<configuration>
+							<verbose>true</verbose>
+							<sei>org.openecomp.mso.adapters.vnf.MsoVnfAdapterAsyncImpl</sei>
+							<genWsdl>true</genWsdl>
+							<inlineSchemas>true</inlineSchemas>
+						</configuration>
+					</execution>
+				</executions>
+				<dependencies>
+					<dependency>
+						<groupId>org.openecomp.mso.adapters</groupId>
+						<artifactId>mso-adapter-utils</artifactId>
+						<version>${project.version}</version>
+					</dependency>
+					<dependency>
+						<groupId>com.sun.xml.ws</groupId>
+						<artifactId>jaxws-tools</artifactId>
+						<version>2.2.7</version>
+					</dependency>
+				</dependencies>
+			</plugin>
+		</plugins>
+		<pluginManagement>
+			<plugins>
+				<!--This plugin's configuration is used to store Eclipse m2e settings
+					only. It has no influence on the Maven build itself. -->
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>
+											org.jvnet.jax-ws-commons
+										</groupId>
+										<artifactId>
+											jaxws-maven-plugin
+										</artifactId>
+										<versionRange>
+											[2.3,)
+										</versionRange>
+										<goals>
+											<goal>wsgen</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore></ignore>
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>org.openecomp.mso.adapters</groupId>
+			<artifactId>mso-adapter-utils</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.openecomp.mso.adapters</groupId>
+			<artifactId>mso-adapters-rest-interface</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.openecomp.mso.adapters</groupId>
+			<artifactId>mso-vnf-adapter-async-client</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>javax</groupId>
+			<artifactId>javaee-web-api</artifactId>
+			<version>6.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.spec.javax.ejb</groupId>
+			<artifactId>jboss-ejb-api_3.2_spec</artifactId>
+			<version>1.0.0.Final</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.ejb3</groupId>
+			<artifactId>jboss-ejb3-ext-api</artifactId>
+			<version>2.2.0.Final</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.openecomp.mso</groupId>
+			<artifactId>status-control</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<!-- <dependency> -->
+		<!-- <groupId>org.openecomp.mso</groupId> -->
+		<!-- <artifactId>mso-catalog-db</artifactId> -->
+		<!-- <version>${project.version}</version> -->
+		<!-- </dependency> -->
+		<!-- <dependency> -->
+		<!-- <groupId>log4j</groupId> -->
+		<!-- <artifactId>log4j</artifactId> -->
+		<!-- <version>1.2.17</version> -->
+		<!-- </dependency> -->
+	</dependencies>
+</project>
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/BpelRestClient.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/BpelRestClient.java
new file mode 100644
index 0000000..e76aa40
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/BpelRestClient.java
@@ -0,0 +1,304 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import org.openecomp.mso.logger.MessageEnum;
+import org.openecomp.mso.logger.MsoLogger;
+import org.openecomp.mso.properties.MsoJavaProperties;
+import org.openecomp.mso.properties.MsoPropertiesException;
+import org.openecomp.mso.properties.MsoPropertiesFactory;
+
+/**
+ * This is the class that is used to POST replies from the MSO adapters to the BPEL engine.
+ * It can be configured via property file, or modified using the member methods.
+ * The properties to use are:
+ * org.openecomp.mso.adapters.vnf.bpelauth  encrypted authorization string to send to BEPL engine
+ * org.openecomp.mso.adapters.vnf.sockettimeout socket timeout value
+ * org.openecomp.mso.adapters.vnf.connecttimeout connect timeout value
+ * org.openecomp.mso.adapters.vnf.retrycount number of times to retry failed connections
+ * org.openecomp.mso.adapters.vnf.retryinterval interval (in seconds) between retries
+ * org.openecomp.mso.adapters.vnf.retrylist list of response codes that will trigger a retry (the special code
+ * 			900 means "connection was not established")
+ */
+public class BpelRestClient {
+	public  static final String MSO_PROP_VNF_ADAPTER     = "MSO_PROP_VNF_ADAPTER";
+	private static final String PROPERTY_DOMAIN          = "org.openecomp.mso.adapters.vnf";
+	private static final String BPEL_AUTH_PROPERTY       = PROPERTY_DOMAIN+".bpelauth";
+	private static final String SOCKET_TIMEOUT_PROPERTY  = PROPERTY_DOMAIN+".sockettimeout";
+	private static final String CONN_TIMEOUT_PROPERTY    = PROPERTY_DOMAIN+".connecttimeout";
+	private static final String RETRY_COUNT_PROPERTY     = PROPERTY_DOMAIN+".retrycount";
+	private static final String RETRY_INTERVAL_PROPERTY  = PROPERTY_DOMAIN+".retryinterval";
+	private static final String RETRY_LIST_PROPERTY      = PROPERTY_DOMAIN+".retrylist";
+	private static final String ENCRYPTION_KEY           = "aa3871669d893c7fb8abbcda31b88b4f";
+	private static final MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA);
+
+	/** Default socket timeout (in seconds) */
+	public static final int DEFAULT_SOCKET_TIMEOUT = 5;
+	/** Default connect timeout (in seconds) */
+	public static final int DEFAULT_CONNECT_TIMEOUT = 5;
+	/** By default, retry up to five times */
+	public static final int DEFAULT_RETRY_COUNT = 5;
+	/** Default interval to wait between retries (in seconds), negative means use backoff algorithm */
+	public static final int DEFAULT_RETRY_INTERVAL = -15;
+	/** Default list of response codes to trigger a retry */
+	public static final String DEFAULT_RETRY_LIST = "408,429,500,502,503,504,900";	// 900 is "connection failed"
+	/** Default credentials */
+	public static final String DEFAULT_CREDENTIALS = "";
+
+	// Properties of the BPEL client -- all are configurable
+	private int socketTimeout;
+	private int connectTimeout;
+	private int retryCount;
+	private int retryInterval;
+	private Set<Integer> retryList;
+	private String credentials;
+
+	// last response from BPEL engine
+	private int lastResponseCode;
+	private String lastResponse;
+
+	/**
+	 * Create a client to send results to the BPEL engine, using configuration from the
+	 * MSO_PROP_VNF_ADAPTER properties.
+	 */
+	public BpelRestClient() {
+		socketTimeout  = DEFAULT_SOCKET_TIMEOUT;
+		connectTimeout = DEFAULT_CONNECT_TIMEOUT;
+		retryCount     = DEFAULT_RETRY_COUNT;
+		retryInterval  = DEFAULT_RETRY_INTERVAL;
+		setRetryList(DEFAULT_RETRY_LIST);
+		credentials    = DEFAULT_CREDENTIALS;
+		lastResponseCode = 0;
+		lastResponse = "";
+
+		try {
+			MsoPropertiesFactory msoPropertiesFactory = new MsoPropertiesFactory();
+			MsoJavaProperties jp = msoPropertiesFactory.getMsoJavaProperties (MSO_PROP_VNF_ADAPTER);
+			socketTimeout  = jp.getIntProperty(SOCKET_TIMEOUT_PROPERTY, DEFAULT_SOCKET_TIMEOUT);
+			connectTimeout = jp.getIntProperty(CONN_TIMEOUT_PROPERTY,   DEFAULT_CONNECT_TIMEOUT);
+			retryCount     = jp.getIntProperty(RETRY_COUNT_PROPERTY,    DEFAULT_RETRY_COUNT);
+			retryInterval  = jp.getIntProperty(RETRY_INTERVAL_PROPERTY, DEFAULT_RETRY_INTERVAL);
+			setRetryList(jp.getProperty(RETRY_LIST_PROPERTY, DEFAULT_RETRY_LIST));
+			credentials    = jp.getEncryptedProperty(BPEL_AUTH_PROPERTY, DEFAULT_CREDENTIALS, ENCRYPTION_KEY);
+		} catch (MsoPropertiesException e) {
+			String error = "Unable to get properties:" + MSO_PROP_VNF_ADAPTER;
+			LOGGER.error (MessageEnum.RA_CONFIG_EXC, error, "Camunda", "", MsoLogger.ErrorCode.AvailabilityError, "MsoPropertiesException - Unable to get properties", e);
+		}
+	}
+
+	public int getSocketTimeout() {
+		return socketTimeout;
+	}
+
+	public void setSocketTimeout(int socketTimeout) {
+		this.socketTimeout = socketTimeout;
+	}
+
+	public int getConnectTimeout() {
+		return connectTimeout;
+	}
+
+	public void setConnectTimeout(int connectTimeout) {
+		this.connectTimeout = connectTimeout;
+	}
+
+	public int getRetryCount() {
+		return retryCount;
+	}
+
+	public void setRetryCount(int retryCount) {
+		if (retryCount < 0)
+			retryCount = DEFAULT_RETRY_COUNT;
+		this.retryCount = retryCount;
+	}
+
+	public int getRetryInterval() {
+		return retryInterval;
+	}
+
+	public void setRetryInterval(int retryInterval) {
+		this.retryInterval = retryInterval;
+	}
+
+	public String getCredentials() {
+		return credentials;
+	}
+
+	public void setCredentials(String credentials) {
+		this.credentials = credentials;
+	}
+
+	public String getRetryList() {
+		if (retryList.size() == 0)
+			return "";
+		String t = retryList.toString();
+		return t.substring(1, t.length()-1);
+	}
+
+	public void setRetryList(String retryList) {
+		Set<Integer> s = new TreeSet<Integer>();
+		for (String t : retryList.split("[, ]")) {
+			try {
+				s.add(Integer.parseInt(t));
+			} catch (NumberFormatException x) {
+				// ignore
+			}
+		}
+		this.retryList = s;
+	}
+
+	public int getLastResponseCode() {
+		return lastResponseCode;
+	}
+
+	public String getLastResponse() {
+		return lastResponse;
+	}
+
+	/**
+	 * Post a response to the URL of the BPEL engine.  As long as the response code is one of those in
+	 * the retryList, the post will be retried up to "retrycount" times with an interval (in seconds)
+	 * of "retryInterval".  If retryInterval is negative, then each successive retry interval will be
+	 * double the previous one.
+	 * @param toBpelStr the content (XML or JSON) to post
+	 * @param bpelUrl the URL to post to
+	 * @param isxml true if the content is XML, otherwise assumed to be JSON
+	 * @return true if the post succeeded, false if all retries failed
+	 */
+	public boolean bpelPost(final String toBpelStr, final String bpelUrl, final boolean isxml)  {
+		debug("Sending response to BPEL: " + toBpelStr);
+		int totalretries = 0;
+		int retryint = retryInterval;
+		while (true) {
+			sendOne(toBpelStr, bpelUrl, isxml);
+			// Note: really should handle response code 415 by switching between content types if needed
+			if (!retryList.contains(lastResponseCode)) {
+				debug("Got response code: " + lastResponseCode + ": returning.");
+				return true;
+			}
+			if (totalretries >= retryCount) {
+				debug("Retried " + totalretries + " times, giving up.");
+				LOGGER.error(MessageEnum.RA_SEND_VNF_NOTIF_ERR, "Could not deliver response to BPEL after "+totalretries+" tries: "+toBpelStr, "Camunda", "", MsoLogger.ErrorCode.BusinessProcesssError, "Could not deliver response to BPEL");
+				return false;
+			}
+			totalretries++;
+			int sleepinterval = retryint;
+			if (retryint < 0) {
+				// if retry interval is negative double the retry on each pass
+				sleepinterval = -retryint;
+				retryint *= 2;
+			}
+			debug("Sleeping for " + sleepinterval + " seconds.");
+			try {
+				Thread.sleep(sleepinterval * 1000L);
+			} catch (InterruptedException e) {
+				// ignore
+			}
+		}
+	}
+	private void debug(String m) {
+		LOGGER.debug(m);
+//		System.err.println(m);
+	}
+	private void sendOne(final String toBpelStr, final String bpelUrl, final boolean isxml) {
+		LOGGER.debug("Sending to BPEL server: "+bpelUrl);
+		LOGGER.debug("Content is: "+toBpelStr);
+
+		//POST
+		HttpPost post = new HttpPost(bpelUrl);
+		if (credentials != null && !credentials.isEmpty())
+			post.addHeader("Authorization", "Basic " + DatatypeConverter.printBase64Binary(credentials.getBytes()));
+
+        //ContentType
+        ContentType ctype = isxml ? ContentType.APPLICATION_XML : ContentType.APPLICATION_JSON;
+        post.setEntity(new StringEntity(toBpelStr, ctype));
+
+        //Timeouts
+		RequestConfig requestConfig = RequestConfig
+			.custom()
+			.setSocketTimeout(socketTimeout * 1000)
+			.setConnectTimeout(connectTimeout * 1000)
+			.build();
+		post.setConfig(requestConfig);
+
+        //Client 4.3+
+        //Execute & GetResponse
+        try (CloseableHttpClient client = HttpClients.createDefault();
+             CloseableHttpResponse response = client.execute(post)) {
+			if (response != null) {
+				lastResponseCode = response.getStatusLine().getStatusCode();
+				HttpEntity entity = response.getEntity();
+				lastResponse = (entity != null) ? EntityUtils.toString(entity) : "";
+			} else {
+				lastResponseCode = 900;
+				lastResponse = "";
+			}
+		} catch (Exception e) {
+			String error = "Error sending Bpel notification:" + toBpelStr;
+			LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, error, "Camunda", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - Error sending Bpel notification", e);
+			lastResponseCode = 900;
+			lastResponse = "";
+		}
+		LOGGER.debug("Response code from BPEL server: "+lastResponseCode);
+		LOGGER.debug("Response body is: "+lastResponse);
+	}
+
+	public static void main(String[] a) throws MsoPropertiesException {
+		final String bpelengine = "http://mtmac1.research.att.com:8080/catch.jsp";
+		final String propfile = "/tmp/mso.vnf.properties";
+		// "/Users/eby/src/mso.rest/mso/packages/mso-config-centralized/mso-po-adapter-config/mso.vnf.properties"
+		final String xml =
+			"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
+			"<updateVolumeGroupResponse><volumeGroupId>1464013300723</volumeGroupId><volumeGroupOutputs>" +
+	        "<entry><key>clyde</key><value>10</value></entry>" +
+	        "<entry><key>wayne</key><value>99</value></entry>" +
+	        "<entry><key>mickey</key><value>7</value></entry>" +
+	        "</volumeGroupOutputs></updateVolumeGroupResponse>";
+
+		MsoPropertiesFactory msoPropertiesFactory = new MsoPropertiesFactory();
+		msoPropertiesFactory.initializeMsoProperties (MSO_PROP_VNF_ADAPTER, propfile);
+
+		BpelRestClient bc = new BpelRestClient();
+		System.out.println(bc.getRetryList());
+		System.out.println(bc.getCredentials()); // poAvos:Domain2.0!
+
+		bc.bpelPost(xml, bpelengine, true);
+		System.out.println("respcode = "+bc.getLastResponseCode());
+		System.out.println("resp = "+bc.getLastResponse());
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/HealthCheckHandler.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/HealthCheckHandler.java
new file mode 100644
index 0000000..108baf3
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/HealthCheckHandler.java
@@ -0,0 +1,66 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.mso.logger.MsoLogger;
+import org.openecomp.mso.HealthCheckUtils;
+import org.openecomp.mso.utils.UUIDChecker;
+
+
+@Path("/")
+public class HealthCheckHandler {
+
+	private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.RA);
+    private static final String MSO_PROP_VNF_ADAPTER = "MSO_PROP_VNF_ADAPTER";
+
+	@HEAD
+	@GET
+	@Path("/healthcheck")
+	@Produces(MediaType.TEXT_HTML)
+	public Response healthcheck (@QueryParam("requestId") String requestId) {
+		long startTime = System.currentTimeMillis ();
+		MsoLogger.setServiceName ("Healthcheck");
+		UUIDChecker.verifyOldUUID(requestId, msoLogger);
+		HealthCheckUtils healthCheck = new HealthCheckUtils ();
+		if (!healthCheck.siteStatusCheck(msoLogger, startTime)) {
+			return HealthCheckUtils.HEALTH_CHECK_NOK_RESPONSE;
+		}
+
+		if (!healthCheck.configFileCheck(msoLogger, startTime, MSO_PROP_VNF_ADAPTER)) {
+			return HealthCheckUtils.NOT_STARTED_RESPONSE;
+		}
+
+		if (!healthCheck.catalogDBCheck (msoLogger, startTime)) {
+			return HealthCheckUtils.NOT_STARTED_RESPONSE;
+		}
+		msoLogger.debug("healthcheck - Successful");
+		return HealthCheckUtils.HEALTH_CHECK_RESPONSE;
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapter.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapter.java
new file mode 100644
index 0000000..95d8ee2
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapter.java
@@ -0,0 +1,142 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+import javax.jws.WebMethod;
+import javax.jws.WebParam;
+import javax.jws.WebParam.Mode;
+import javax.jws.WebService;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.ws.Holder;
+
+import org.openecomp.mso.openstack.beans.VnfStatus;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfAlreadyExists;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+import org.openecomp.mso.entity.MsoRequest;
+
+import java.util.Map;
+
+@WebService (name="VnfAdapter", targetNamespace="http://com.att.mso/vnf")
+public interface MsoVnfAdapter
+{
+	/**
+	 * This is the "Create VNF" Web Service Endpoint definition.
+	 */
+	@WebMethod
+	public void createVnf (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfType") @XmlElement(required=true) String vnfType,
+							@WebParam(name="vnfVersion") @XmlElement(required=false) String vnfVersion,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="requestType") @XmlElement(required=false) String requestType,
+							@WebParam(name="volumeGroupHeatStackId") @XmlElement(required=false) String volumeGroupHeatStackId,
+							@WebParam(name="inputs") Map<String,String> inputs,
+							@WebParam(name="failIfExists") Boolean failIfExists,
+							@WebParam(name="backout") Boolean backout,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="vnfId", mode=Mode.OUT) Holder<String> vnfId,
+							@WebParam(name="outputs", mode=Mode.OUT) Holder<Map<String,String>> outputs,
+							@WebParam(name="rollback", mode=Mode.OUT) Holder<VnfRollback> rollback )
+		throws VnfException, VnfAlreadyExists;
+	
+	@WebMethod
+	public void updateVnf (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfType") @XmlElement(required=true) String vnfType,
+							@WebParam(name="vnfVersion") @XmlElement(required=false) String vnfVersion,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="requestType") @XmlElement(required=false) String requestType,
+							@WebParam(name="volumeGroupHeatStackId") @XmlElement(required=false) String volumeGroupHeatStackId,
+							@WebParam(name="inputs") Map<String,String> inputs,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="outputs", mode=Mode.OUT) Holder<Map<String,String>> outputs,
+							@WebParam(name="rollback", mode=Mode.OUT) Holder<VnfRollback> rollback )
+		throws VnfException;
+	
+	@WebMethod
+	public void queryVnf (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="vnfExists", mode=Mode.OUT) Holder<Boolean> vnfExists,
+							@WebParam(name="vnfId", mode=Mode.OUT) Holder<String> vnfId,
+							@WebParam(name="status", mode=Mode.OUT) Holder<VnfStatus> status,
+							@WebParam(name="outputs", mode=Mode.OUT) Holder<Map<String,String>> outputs )
+		throws VnfException;
+	
+	@WebMethod
+	public void deleteVnf (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="request") MsoRequest msoRequest)
+		throws VnfException;
+	
+	
+	@WebMethod
+	public void rollbackVnf (@WebParam(name="rollback") @XmlElement(required=true) VnfRollback rollback)
+		throws VnfException;
+	
+	@WebMethod
+	public void createVfModule (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfType") @XmlElement(required=true) String vnfType,
+							@WebParam(name="vnfVersion") @XmlElement(required=false) String vnfVersion,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="requestType") @XmlElement(required=false) String requestType,
+							@WebParam(name="volumeGroupHeatStackId") @XmlElement(required=false) String volumeGroupHeatStackId,
+							@WebParam(name="baseVfHeatStackId") @XmlElement(required=false) String baseVfHeatStackId,
+							@WebParam(name="inputs") Map<String,String> inputs,
+							@WebParam(name="failIfExists") Boolean failIfExists,
+							@WebParam(name="backout") Boolean backout,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="vnfId", mode=Mode.OUT) Holder<String> vnfId,
+							@WebParam(name="outputs", mode=Mode.OUT) Holder<Map<String,String>> outputs,
+							@WebParam(name="rollback", mode=Mode.OUT) Holder<VnfRollback> rollback )
+		throws VnfException, VnfAlreadyExists;
+	
+	@WebMethod
+	public void deleteVfModule (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vfName") @XmlElement(required=true) String vfName,
+							@WebParam(name="request") MsoRequest msoRequest)
+		throws VnfException;
+	
+	@WebMethod
+	public void updateVfModule (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfType") @XmlElement(required=true) String vnfType,
+							@WebParam(name="vnfVersion") @XmlElement(required=false) String vnfVersion,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="requestType") @XmlElement(required=false) String requestType,
+							@WebParam(name="volumeGroupHeatStackId") @XmlElement(required=false) String volumeGroupHeatStackId,
+							@WebParam(name="baseVfHeatStackId") @XmlElement(required=false) String baseVfHeatStackId,
+							@WebParam(name="vfModuleStackId") @XmlElement(required=false) String vfModuleStackId,
+							@WebParam(name="inputs") Map<String,String> inputs,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="outputs", mode=Mode.OUT) Holder<Map<String,String>> outputs,
+							@WebParam(name="rollback", mode=Mode.OUT) Holder<VnfRollback> rollback )
+		throws VnfException;
+	
+	@WebMethod
+	public void healthCheck ();
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterAsync.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterAsync.java
new file mode 100644
index 0000000..d58e9ee
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterAsync.java
@@ -0,0 +1,103 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+import javax.jws.Oneway;
+import javax.jws.WebMethod;
+import javax.jws.WebParam;
+import javax.jws.WebService;
+import javax.xml.bind.annotation.XmlElement;
+
+import org.openecomp.mso.entity.MsoRequest;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+import java.util.Map;
+
+/**
+ * This webservice defines the Asynchronous versions of VNF adapter calls.
+ * The notification messages for final responses are documented elsewhere
+ * (by the client service WSDL).
+ *
+ */
+@WebService (name="VnfAdapterAsync", targetNamespace="http://com.att.mso/vnfA")
+public interface MsoVnfAdapterAsync
+{
+	/**
+	 * This is the "Create VNF" Web Service Endpoint definition.
+	 */
+	@WebMethod
+	@Oneway
+	public void createVnfA (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfType") @XmlElement(required=true) String vnfType,
+							@WebParam(name="vnfVersion") @XmlElement(required=false) String vnfVersion,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="requestType") @XmlElement(required=false) String requestType,
+							@WebParam(name="volumeGroupHeatStackId") @XmlElement(required=false) String volumeGroupHeatStackId,
+							@WebParam(name="inputs") Map<String,String> inputs,
+							@WebParam(name="failIfExists") Boolean failIfExists,
+							@WebParam(name="backout") Boolean backout,
+							@WebParam(name="messageId") @XmlElement(required=true) String messageId,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="notificationUrl") @XmlElement(required=true) String notificationUrl );
+	
+	@WebMethod
+	@Oneway
+	public void updateVnfA (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfType") @XmlElement(required=true) String vnfType,
+							@WebParam(name="vnfVersion") @XmlElement(required=false) String vnfVersion,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="requestType") @XmlElement(required=false) String requestType,
+							@WebParam(name="volumeGroupHeatStackId") @XmlElement(required=false) String volumeGroupHeatStackId,
+							@WebParam(name="inputs") Map<String,String> inputs,
+							@WebParam(name="messageId") @XmlElement(required=true) String messageId,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="notificationUrl") @XmlElement(required=true) String notificationUrl );
+							
+	@WebMethod
+	@Oneway
+	public void queryVnfA (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="messageId") @XmlElement(required=true) String messageId,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="notificationUrl") @XmlElement(required=true) String notificationUrl );
+
+	@WebMethod
+	@Oneway
+	public void deleteVnfA (@WebParam(name="cloudSiteId") @XmlElement(required=true) String cloudSiteId,
+							@WebParam(name="tenantId") @XmlElement(required=true) String tenantId,
+							@WebParam(name="vnfName") @XmlElement(required=true) String vnfName,
+							@WebParam(name="messageId") @XmlElement(required=true) String messageId,
+							@WebParam(name="request") MsoRequest msoRequest,
+							@WebParam(name="notificationUrl") @XmlElement(required=true) String notificationUrl );
+
+	@WebMethod
+	@Oneway
+	public void rollbackVnfA (@WebParam(name="rollback") @XmlElement(required=true) VnfRollback rollback,
+						@WebParam(name="messageId") @XmlElement(required=true) String messageId,
+						@WebParam(name="notificationUrl") @XmlElement(required=true) String notificationUrl );
+
+	
+	@WebMethod
+	public void healthCheckA ();
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterAsyncImpl.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterAsyncImpl.java
new file mode 100644
index 0000000..c192eb6
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterAsyncImpl.java
@@ -0,0 +1,652 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jws.WebService;
+import javax.xml.bind.DatatypeConverter;
+import javax.xml.namespace.QName;
+import javax.xml.ws.BindingProvider;
+import javax.xml.ws.Holder;
+import javax.xml.ws.handler.MessageContext;
+
+import org.openecomp.mso.adapters.vnf.async.client.CreateVnfNotification;
+import org.openecomp.mso.adapters.vnf.async.client.QueryVnfNotification;
+import org.openecomp.mso.adapters.vnf.async.client.UpdateVnfNotification;
+import org.openecomp.mso.adapters.vnf.async.client.VnfAdapterNotify;
+import org.openecomp.mso.adapters.vnf.async.client.VnfAdapterNotify_Service;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+import org.openecomp.mso.cloud.CloudConfigFactory;
+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.properties.MsoPropertiesFactory;
+import org.openecomp.mso.openstack.beans.VnfStatus;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+
+@WebService(serviceName = "VnfAdapterAsync", endpointInterface = "org.openecomp.mso.adapters.vnf.MsoVnfAdapterAsync", targetNamespace = "http://com.att.mso/vnfA")
+public class MsoVnfAdapterAsyncImpl implements MsoVnfAdapterAsync {
+
+	MsoPropertiesFactory msoPropertiesFactory = new MsoPropertiesFactory();
+
+	CloudConfigFactory cloudConfigFactory=new CloudConfigFactory();
+
+	public static final String MSO_PROP_VNF_ADAPTER="MSO_PROP_VNF_ADAPTER";
+    private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA);
+    private static MsoAlarmLogger alarmLogger = new MsoAlarmLogger ();
+    private static final String BPEL_AUTH_PROP = "org.openecomp.mso.adapters.vnf.bpelauth";
+    private static final String ENCRYPTION_KEY = "aa3871669d893c7fb8abbcda31b88b4f";
+
+    /**
+     * Health Check web method. Does nothing but return to show the adapter is deployed.
+     */
+    @Override
+    public void healthCheckA () {
+        LOGGER.debug ("Health check call in VNF Adapter");
+    }
+
+    /**
+     * This is the asynchronous "Create VNF" web service implementation.
+     * It will create a new VNF of the requested type in the specified cloud
+     * and tenant. The tenant must exist before this service is called.
+     *
+     * If a VNF with the same name already exists, this can be considered a
+     * success or failure, depending on the value of the 'failIfExists' parameter.
+     *
+     * All VNF types will be defined in the MSO catalog. The caller must request
+     * one of these pre-defined types or an error will be returned. Within the
+     * catalog, each VNF type references (among other things) a Heat template
+     * which is used to deploy the required VNF artifacts (VMs, networks, etc.)
+     * to the cloud.
+     *
+     * Depending on the Heat template, 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 VNF or an error will be thrown.
+     *
+     * The method sends an asynchronous response to the notification URL when
+     * processing completes. The createAsyncResponse contains the vnfId (the
+     * canonical name of the stack), a Map of VNF 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 VNF.
+     * This is useful if a VNF is successfully created but the orchestrator
+     * fails on a subsequent operation.
+     *
+     * Note: this method is implemented by calling the synchronous web method
+     * and translating the response to an asynchronous notification.
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
+     * @param tenantId Openstack tenant identifier
+     * @param vnfType VNF type key, should match a VNF definition in catalog DB
+     * @param vnfName Name to be assigned to the new VNF
+     * @param inputs Map of key=value inputs for VNF stack creation
+     * @param failIfExists Flag whether already existing VNF should be considered
+     *        a success or failure
+     * @param msoRequest Request tracking information for logs
+     * @param notificationURL the target URL for asynchronous response
+     */
+    @Override
+    public void createVnfA (String cloudSiteId,
+                            String tenantId,
+                            String vnfType,
+                            String vnfVersion,
+                            String vnfName,
+                            String requestType,
+                            String volumeGroupHeatStackId,
+                            Map <String, String> inputs,
+                            Boolean failIfExists,
+                            Boolean backout,
+                            String messageId,
+                            MsoRequest msoRequest,
+                            String notificationUrl) {
+        String error;
+        String serviceName = "CreateVnfA";
+        MsoLogger.setLogContext (msoRequest);
+        MsoLogger.setServiceName (serviceName);
+        LOGGER.info (MessageEnum.RA_ASYNC_CREATE_VNF, vnfName, vnfType, cloudSiteId, tenantId, "", "createVnfA");
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+        // Use the synchronous method to perform the actual Create
+        MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory, cloudConfigFactory);
+
+        // Synchronous Web Service Outputs
+        Holder <String> vnfId = new Holder <String> ();
+        Holder <Map <String, String>> outputs = new Holder <Map <String, String>> ();
+        Holder <VnfRollback> vnfRollback = new Holder <VnfRollback> ();
+
+        try {
+            vnfAdapter.createVnf (cloudSiteId,
+                                  tenantId,
+                                  vnfType,
+                                  vnfVersion,
+                                  vnfName,
+                                  requestType,
+                                  volumeGroupHeatStackId,
+                                  inputs,
+                                  failIfExists,
+                                  backout,
+                                  msoRequest,
+                                  vnfId,
+                                  outputs,
+                                  vnfRollback);
+            MsoLogger.setServiceName (serviceName);
+        } catch (VnfException e) {
+        	MsoLogger.setServiceName (serviceName);
+        	LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR,  vnfName, cloudSiteId, tenantId, "", "createVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "VnfException in createVnfA", e);
+            org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory exCat = null;
+            String eMsg = null;
+            try {
+                eMsg = e.getFaultInfo ().getMessage ();
+                exCat = org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory.fromValue (e.getFaultInfo ()
+                                                                                                     .getCategory ()
+                                                                                                     .name ());
+            } catch (Exception e1) {
+                LOGGER.error (MessageEnum.RA_FAULT_INFO_EXC, "", "createVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - Fault info", e1);
+            }
+            // Build and send Asynchronous error response
+            try {
+                VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+                notifyPort.createVnfNotification (messageId, false, exCat, eMsg, null, null, null);
+            } catch (Exception e1) {
+                error = "Error sending createVnf notification " + e1.getMessage ();
+                LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "createVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending createVnf notification", e1);
+                alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+            }
+            LOGGER.info (MessageEnum.RA_ASYNC_CREATE_VNF_COMPLETE, "", "createVnfA", "", "createVnfA");
+            return;
+        }
+        LOGGER.debug ("Async Create VNF: " + vnfName + " VnfId:" + vnfId.value);
+        // Build and send Asynchronous response
+        try {
+            VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+            notifyPort.createVnfNotification (messageId,
+                                              true,
+                                              null,
+                                              null,
+                                              vnfId.value,
+                                              copyCreateOutputs (outputs),
+                                              copyVrb (vnfRollback));
+        } catch (Exception e) {
+            error = "Error sending createVnf notification " + e.getMessage ();
+            LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "createVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending createVnf notification", e);
+            alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+        }
+        LOGGER.info (MessageEnum.RA_ASYNC_CREATE_VNF_COMPLETE, "", "createVnfA");
+        return;
+    }
+
+    @Override
+    public void updateVnfA (String cloudSiteId,
+                            String tenantId,
+                            String vnfType,
+                            String vnfVersion,
+                            String vnfName,
+                            String requestType,
+                            String volumeGroupHeatStackId,
+                            Map <String, String> inputs,
+                            String messageId,
+                            MsoRequest msoRequest,
+                            String notificationUrl) {
+        String error;
+        String serviceName = "UpdateVnfA";
+        MsoLogger.setServiceName (serviceName);
+        MsoLogger.setLogContext (msoRequest);
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+        LOGGER.info (MessageEnum.RA_ASYNC_UPDATE_VNF, vnfName, vnfType, cloudSiteId, tenantId, "", "UpdateVnfA");
+
+        // Use the synchronous method to perform the actual Create
+        MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory,cloudConfigFactory);
+
+        // Synchronous Web Service Outputs
+        Holder <String> vnfId = new Holder <String> ();
+        Holder <Map <String, String>> outputs = new Holder <Map <String, String>> ();
+        Holder <VnfRollback> vnfRollback = new Holder <VnfRollback> ();
+
+        try {
+            vnfAdapter.updateVnf (cloudSiteId, tenantId, vnfType,vnfVersion, vnfName, requestType, volumeGroupHeatStackId, inputs, msoRequest, outputs, vnfRollback);
+            MsoLogger.setServiceName (serviceName);
+        } catch (VnfException e) {
+        	MsoLogger.setServiceName (serviceName);
+        	LOGGER.error (MessageEnum.RA_UPDATE_VNF_ERR,  vnfName, cloudSiteId, tenantId, "", "UpdateVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending updateVnf notification", e);
+            org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory exCat = null;
+            String eMsg = null;
+            try {
+                eMsg = e.getFaultInfo ().getMessage ();
+                exCat = org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory.fromValue (e.getFaultInfo ()
+                                                                                                     .getCategory ()
+                                                                                                     .name ());
+            } catch (Exception e1) {
+            	LOGGER.error (MessageEnum.RA_FAULT_INFO_EXC, "", "UpdateVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - fault info", e1);
+            }
+            // Build and send Asynchronous error response
+            try {
+                VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+                notifyPort.updateVnfNotification (messageId, false, exCat, eMsg, null, null);
+            } catch (Exception e1) {
+                error = "Error sending updateVnf notification " + e1.getMessage ();
+                LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "UpdateVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending updateVnf notification", e1);
+                alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+            }
+            LOGGER.info (MessageEnum.RA_ASYNC_UPDATE_VNF_COMPLETE, "", "UpdateVnfA");
+            return;
+        }
+        LOGGER.debug ("Async Update VNF: " + vnfName + " VnfId:" + vnfId.value);
+        // Build and send Asynchronous response
+        try {
+            VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+            notifyPort.updateVnfNotification (messageId,
+                                              true,
+                                              null,
+                                              null,
+                                              copyUpdateOutputs (outputs),
+                                              copyVrb (vnfRollback));
+        } catch (Exception e) {
+            error = "Error sending updateVnf notification " + e.getMessage ();
+            LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "UpdateVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending updateVnf notification", e);
+            alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+        }
+        LOGGER.info (MessageEnum.RA_ASYNC_UPDATE_VNF_COMPLETE, "", "UpdateVnfA");
+        return;
+    }
+
+    /**
+     * This is the "Query VNF" web service implementation.
+     * It will look up a VNF by name or ID in the specified cloud and tenant.
+     *
+     * The method returns an indicator that the VNF exists, its Openstack internal
+     * ID, its status, and the set of outputs (from when the stack was created).
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to query
+     * @param tenantId Openstack tenant identifier
+     * @param vnfName VNF Name or Openstack ID
+     * @param msoRequest Request tracking information for logs
+     * @param notificationURL the target URL for asynchronous response
+     */
+    @Override
+    public void queryVnfA (String cloudSiteId,
+                           String tenantId,
+                           String vnfName,
+                           String messageId,
+                           MsoRequest msoRequest,
+                           String notificationUrl) {
+        String error;
+        String serviceName = "QueryVnfA";
+        MsoLogger.setServiceName (serviceName);
+        MsoLogger.setLogContext (msoRequest);
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+        LOGGER.info (MessageEnum.RA_ASYNC_QUERY_VNF, vnfName, cloudSiteId, tenantId);
+
+        // Use the synchronous method to perform the actual query
+        MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory,cloudConfigFactory);
+
+        // Synchronous Web Service Outputs
+        Holder <Boolean> vnfExists = new Holder <Boolean> ();
+        Holder <String> vnfId = new Holder <String> ();
+        Holder <VnfStatus> status = new Holder <VnfStatus> ();
+        Holder <Map <String, String>> outputs = new Holder <Map <String, String>> ();
+
+        try {
+            vnfAdapter.queryVnf (cloudSiteId, tenantId, vnfName, msoRequest, vnfExists, vnfId, status, outputs);
+            MsoLogger.setServiceName (serviceName);
+        } catch (VnfException e) {
+        	MsoLogger.setServiceName (serviceName);
+            LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR,  vnfName, cloudSiteId, tenantId, "", "queryVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending queryVnfA notification", e);
+            org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory exCat = null;
+            String eMsg = null;
+            try {
+                eMsg = e.getFaultInfo ().getMessage ();
+                exCat = org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory.fromValue (e.getFaultInfo ()
+                                                                                                     .getCategory ()
+                                                                                                     .name ());
+            } catch (Exception e1) {
+            	LOGGER.error (MessageEnum.RA_FAULT_INFO_EXC, "", "queryVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - fault info", e1);
+            }
+            // Build and send Asynchronous error response
+            try {
+                VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+                notifyPort.queryVnfNotification (messageId, false, exCat, eMsg, null, null, null, null);
+            } catch (Exception e1) {
+                error = "Error sending queryVnf notification " + e1.getMessage ();
+                LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "queryVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending queryVnf notification", e1);
+                alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+            }
+            LOGGER.info (MessageEnum.RA_ASYNC_QUERY_VNF_COMPLETE, "", "queryVnfA");
+            return;
+        }
+
+        if (!vnfExists.value) {
+            LOGGER.debug ("Async Query, VNF not found");
+        } else {
+            LOGGER.debug ("Async Query, VNF=" + vnfId.value + ", status=" + status.value);
+        }
+        // Build and send Asynchronous response
+        try {
+            VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+            org.openecomp.mso.adapters.vnf.async.client.VnfStatus vnfS = org.openecomp.mso.adapters.vnf.async.client.VnfStatus.fromValue (status.value.name ());
+            notifyPort.queryVnfNotification (messageId,
+                                             true,
+                                             null,
+                                             null,
+                                             vnfExists.value,
+                                             vnfId.value,
+                                             vnfS,
+                                             copyQueryOutputs (outputs));
+        } catch (Exception e) {
+            error = "Error sending queryVnf notification " + e.getMessage ();
+            LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "queryVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending queryVnf notification", e);
+            alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+        }
+
+        LOGGER.info (MessageEnum.RA_ASYNC_QUERY_VNF_COMPLETE, "", "queryVnfA");
+        return;
+    }
+
+    /**
+     * This is the Asynchronous "Delete VNF" web service implementation.
+     * It will delete a VNF by name or ID in the specified cloud and tenant.
+     *
+     * The method has no outputs.
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to delete
+     * @param tenantId Openstack tenant identifier
+     * @param vnfName VNF Name or Openstack ID
+     * @param msoRequest Request tracking information for logs
+     * @param notificationURL the target URL for asynchronous response
+     */
+    @Override
+    public void deleteVnfA (String cloudSiteId,
+                            String tenantId,
+                            String vnfName,
+                            String messageId,
+                            MsoRequest msoRequest,
+                            String notificationUrl) {
+        String error;
+        String serviceName = "DeleteVnfA";
+        MsoLogger.setServiceName (serviceName);
+        MsoLogger.setLogContext (msoRequest);
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+        LOGGER.info (MessageEnum.RA_ASYNC_DELETE_VNF, vnfName, cloudSiteId, tenantId);
+
+        // Use the synchronous method to perform the actual delete
+        MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory,cloudConfigFactory);
+
+        try {
+            vnfAdapter.deleteVnf (cloudSiteId, tenantId, vnfName, msoRequest);
+            MsoLogger.setServiceName (serviceName);
+        } catch (VnfException e) {
+        	MsoLogger.setServiceName (serviceName);
+        	LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR,  vnfName, cloudSiteId, tenantId, "", "deleteVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending deleteVnfA notification", e);
+            org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory exCat = null;
+            String eMsg = null;
+            try {
+                eMsg = e.getFaultInfo ().getMessage ();
+                exCat = org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory.fromValue (e.getFaultInfo ()
+                                                                                                     .getCategory ()
+                                                                                                     .name ());
+            } catch (Exception e1) {
+            	LOGGER.error (MessageEnum.RA_FAULT_INFO_EXC, "", "deleteVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - fault info", e1);
+            }
+            // Build and send Asynchronous error response
+            try {
+                VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+                notifyPort.deleteVnfNotification (messageId, false, exCat, eMsg);
+            } catch (Exception e1) {
+                error = "Error sending deleteVnf notification " + e1.getMessage ();
+                LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "deleteVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending deleteVnfA notification", e1);
+                alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+            }
+            LOGGER.info (MessageEnum.RA_ASYNC_DELETE_VNF_COMPLETE, "", "deleteVnfA");
+            return;
+        }
+
+        LOGGER.debug ("Async Delete VNF: " + vnfName);
+        // Build and send Asynchronous response
+        try {
+            VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+            notifyPort.deleteVnfNotification (messageId, true, null, null);
+
+        } catch (Exception e) {
+            error = "Error sending deleteVnf notification " + e.getMessage ();
+            LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "deleteVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending deleteVnfA notification", e);
+            alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+        }
+
+        LOGGER.info (MessageEnum.RA_ASYNC_DELETE_VNF_COMPLETE, "", "deleteVnfA");
+        return;
+    }
+
+    /**
+     * 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.
+     */
+    @Override
+    public void rollbackVnfA (VnfRollback rollback, String messageId, String notificationUrl) {
+        String serviceName = "RollbackVnfA";
+        MsoLogger.setServiceName (serviceName);
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+        String error;
+        // rollback may be null (e.g. if stack already existed when Create was called)
+        if (rollback == null) {
+            error = "Empty Rollback: No action to perform";
+            LOGGER.info (MessageEnum.RA_ROLLBACK_NULL, "", "rollbackVnfA");
+            alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+            return;
+        }
+
+        MsoLogger.setLogContext (rollback.getMsoRequest ());
+        LOGGER.info (MessageEnum.RA_ASYNC_ROLLBACK_VNF, "", "rollbackVnfA");
+
+        // Use the synchronous method to perform the actual rollback
+        MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory,cloudConfigFactory);
+
+        try {
+            vnfAdapter.rollbackVnf (rollback);
+            MsoLogger.setServiceName (serviceName);
+        } catch (VnfException e) {
+        	MsoLogger.setServiceName (serviceName);
+        	LOGGER.error (MessageEnum.RA_ROLLBACK_VNF_ERR, "", "rollbackVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending rollbackVnfA notification", e);
+            org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory exCat = null;
+            String eMsg = null;
+            try {
+                eMsg = e.getFaultInfo ().getMessage ();
+                exCat = org.openecomp.mso.adapters.vnf.async.client.MsoExceptionCategory.fromValue (e.getFaultInfo ()
+                                                                                                     .getCategory ()
+                                                                                                     .name ());
+            } catch (Exception e1) {
+            	LOGGER.error (MessageEnum.RA_FAULT_INFO_EXC, "", "rollbackVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - fault info", e1);
+            }
+            // Build and send Asynchronous error response
+            try {
+                VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+                notifyPort.rollbackVnfNotification (messageId, false, exCat, eMsg);
+            } catch (Exception e1) {
+                error = "Error sending rollbackVnf notification " + e1.getMessage ();
+                LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "rollbackVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending rollbackVnfA notification", e1);
+                alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+            }
+            LOGGER.info (MessageEnum.RA_ASYNC_ROLLBACK_VNF_COMPLETE, "", "rollbackVnfA");
+            return;
+        }
+
+        LOGGER.debug ("Async Rollback VNF:" + rollback.getVnfId ());
+        // Build and send Asynchronous response
+        try {
+            VnfAdapterNotify notifyPort = getNotifyEP (notificationUrl);
+            notifyPort.rollbackVnfNotification (messageId, true, null, null);
+        } catch (Exception e) {
+            error = "Error sending rollbackVnf notification " + e.getMessage ();
+            LOGGER.error (MessageEnum.RA_SEND_VNF_NOTIF_ERR, "", "rollbackVnfA", MsoLogger.ErrorCode.BusinessProcesssError, "Exception sending rollbackVnfA notification", e);
+            alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, error);
+        }
+
+        LOGGER.info (MessageEnum.RA_ASYNC_ROLLBACK_VNF_COMPLETE, "", "rollbackVnfA");
+        return;
+    }
+
+    private org.openecomp.mso.adapters.vnf.async.client.VnfRollback copyVrb (Holder <VnfRollback> hVrb) {
+        org.openecomp.mso.adapters.vnf.async.client.VnfRollback cvrb = new org.openecomp.mso.adapters.vnf.async.client.VnfRollback ();
+
+        if (hVrb != null && hVrb.value != null) {
+            org.openecomp.mso.adapters.vnf.async.client.MsoRequest cmr = new org.openecomp.mso.adapters.vnf.async.client.MsoRequest ();
+
+            cvrb.setCloudSiteId (hVrb.value.getCloudSiteId ());
+            if (hVrb.value.getMsoRequest() != null) {
+            	cmr.setRequestId (hVrb.value.getMsoRequest ().getRequestId ());
+            	cmr.setServiceInstanceId (hVrb.value.getMsoRequest ().getServiceInstanceId ());
+            } else {
+            	cmr.setRequestId (null);
+            	cmr.setServiceInstanceId (null);
+            }
+            cvrb.setMsoRequest (cmr);
+            cvrb.setVnfId (hVrb.value.getVnfId ());
+            cvrb.setTenantId (hVrb.value.getTenantId ());
+            cvrb.setTenantCreated (hVrb.value.getTenantCreated ());
+            cvrb.setVnfCreated (hVrb.value.getVnfCreated ());
+        }
+        return cvrb;
+    }
+
+    private CreateVnfNotification.Outputs copyCreateOutputs (Holder <Map <String, String>> hMap) {
+
+        CreateVnfNotification.Outputs outputs = new CreateVnfNotification.Outputs ();
+
+        if (hMap != null && hMap.value != null) {
+            Map <String, String> sMap = new HashMap <String, String> ();
+            sMap = hMap.value;
+            CreateVnfNotification.Outputs.Entry entry = new CreateVnfNotification.Outputs.Entry ();
+
+            for (String key : sMap.keySet ()) {
+                entry.setKey (key);
+                entry.setValue (sMap.get (key));
+                outputs.getEntry ().add (entry);
+            }
+        }
+        return outputs;
+    }
+
+    private UpdateVnfNotification.Outputs copyUpdateOutputs (Holder <Map <String, String>> hMap) {
+
+        UpdateVnfNotification.Outputs outputs = new UpdateVnfNotification.Outputs ();
+
+        if (hMap != null && hMap.value != null) {
+            Map <String, String> sMap = new HashMap <String, String> ();
+            sMap = hMap.value;
+            UpdateVnfNotification.Outputs.Entry entry = new UpdateVnfNotification.Outputs.Entry ();
+
+            for (String key : sMap.keySet ()) {
+                entry.setKey (key);
+                entry.setValue (sMap.get (key));
+                outputs.getEntry ().add (entry);
+            }
+        }
+        return outputs;
+    }
+
+    private QueryVnfNotification.Outputs copyQueryOutputs (Holder <Map <String, String>> hMap) {
+
+        QueryVnfNotification.Outputs outputs = new QueryVnfNotification.Outputs ();
+
+        if (hMap != null && hMap.value != null) {
+            Map <String, String> sMap = new HashMap <String, String> ();
+            sMap = hMap.value;
+
+            QueryVnfNotification.Outputs.Entry entry = new QueryVnfNotification.Outputs.Entry ();
+
+            for (String key : sMap.keySet ()) {
+                entry.setKey (key);
+                entry.setValue (sMap.get (key));
+                outputs.getEntry ().add (entry);
+            }
+        }
+        return outputs;
+    }
+
+    private VnfAdapterNotify getNotifyEP (String notificationUrl) {
+
+        URL warWsdlLoc = null;
+        try {
+            warWsdlLoc = Thread.currentThread ().getContextClassLoader ().getResource ("VnfAdapterNotify.wsdl");
+        } catch (Exception e) {
+            LOGGER.error (MessageEnum.RA_WSDL_NOT_FOUND, "VnfAdapterNotify.wsdl", "", "getNotifyEP", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - WSDL not found", e);
+        }
+        if (warWsdlLoc == null) {
+        	LOGGER.error (MessageEnum.RA_WSDL_NOT_FOUND, "VnfAdapterNotify.wsdl", "", "getNotifyEP", MsoLogger.ErrorCode.BusinessProcesssError, "WSDL not found");
+        } else {
+            try {
+                LOGGER.debug ("VnfAdpaterNotify.wsdl location:" + warWsdlLoc.toURI ().toString ());
+            } catch (Exception e) {
+                LOGGER.error (MessageEnum.RA_WSDL_URL_CONVENTION_EXC, "VnfAdapterNotify.wsdl", "", "getNotifyEP", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - WSDL URL convention", e);
+            }
+        }
+
+        VnfAdapterNotify_Service notifySvc = new VnfAdapterNotify_Service (warWsdlLoc,
+                                                                           new QName ("http://com.att.mso/vnfNotify",
+                                                                                      "vnfAdapterNotify"));
+
+        VnfAdapterNotify notifyPort = notifySvc.getMsoVnfAdapterAsyncImplPort ();
+
+        BindingProvider bp = (BindingProvider) notifyPort;
+
+        URL epUrl = null;
+        try {
+            epUrl = new URL (notificationUrl);
+        } catch (MalformedURLException e1) {
+            LOGGER.error (MessageEnum.RA_INIT_NOTIF_EXC, "", "getNotifyEP", MsoLogger.ErrorCode.BusinessProcesssError, "MalformedURLException", e1);
+        }
+
+        LOGGER.debug ("Notification Endpoint URL: " + epUrl.toExternalForm ());
+
+        bp.getRequestContext ().put (BindingProvider.ENDPOINT_ADDRESS_PROPERTY, epUrl.toExternalForm ());
+
+        // authentication
+        try {
+            Map <String, Object> req_ctx = bp.getRequestContext ();
+            Map <String, List <String>> headers = new HashMap <String, List <String>> ();
+
+            String userCredentials = msoPropertiesFactory.getMsoJavaProperties (MSO_PROP_VNF_ADAPTER).getEncryptedProperty (BPEL_AUTH_PROP,
+                                                                                             "",
+                                                                                             ENCRYPTION_KEY);
+
+            String basicAuth = "Basic " + DatatypeConverter.printBase64Binary (userCredentials.getBytes ());
+            req_ctx.put (MessageContext.HTTP_REQUEST_HEADERS, headers);
+            headers.put ("Authorization", Collections.singletonList (basicAuth));
+        } catch (Exception e) {
+            LOGGER.error (MessageEnum.RA_SET_CALLBACK_AUTH_EXC, "", "getNotifyEP", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - Unable to set authorization in callback request", e);
+            alarmLogger.sendAlarm ("MsoInternalError", MsoAlarmLogger.CRITICAL, "Unable to set authorization in callback request");
+        }
+
+        return notifyPort;
+    }
+
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterImpl.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterImpl.java
new file mode 100644
index 0000000..757f875
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/MsoVnfAdapterImpl.java
@@ -0,0 +1,2391 @@
+/*-
+ * ============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=========================================================
+ */
+
+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.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.jws.WebService;
+import javax.xml.ws.Holder;
+
+import org.openecomp.mso.adapters.vnf.exceptions.VnfAlreadyExists;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfNotFound;
+import org.openecomp.mso.cloud.CloudConfigFactory;
+import org.openecomp.mso.cloud.CloudConfig;
+import org.openecomp.mso.cloud.CloudSite;
+import org.openecomp.mso.db.catalog.utils.MavenLikeVersioning;
+import org.openecomp.mso.db.catalog.CatalogDatabase;
+import org.openecomp.mso.db.catalog.beans.HeatEnvironment;
+import org.openecomp.mso.db.catalog.beans.HeatFiles;
+import org.openecomp.mso.db.catalog.beans.HeatTemplate;
+import org.openecomp.mso.db.catalog.beans.HeatTemplateParam;
+import org.openecomp.mso.db.catalog.beans.VnfResource;
+import org.openecomp.mso.db.catalog.beans.VfModule;
+import org.openecomp.mso.db.catalog.beans.VnfComponent;
+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.HeatStatus;
+import org.openecomp.mso.openstack.beans.StackInfo;
+import org.openecomp.mso.openstack.beans.VnfStatus;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+import org.openecomp.mso.openstack.exceptions.MsoException;
+import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;
+import org.openecomp.mso.openstack.utils.MsoHeatUtils;
+import org.openecomp.mso.openstack.utils.MsoHeatUtilsWithUpdate;
+import org.openecomp.mso.openstack.utils.MsoHeatEnvironmentEntry;
+import org.openecomp.mso.properties.MsoPropertiesFactory;
+
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.ObjectMapper;
+
+@WebService(serviceName = "VnfAdapter", endpointInterface = "org.openecomp.mso.adapters.vnf.MsoVnfAdapter", targetNamespace = "http://com.att.mso/vnf")
+public class MsoVnfAdapterImpl implements MsoVnfAdapter {
+
+	CloudConfigFactory cloudConfigFactory = new CloudConfigFactory();
+	protected CloudConfig cloudConfig = null;
+
+	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 final String VNF_ADAPTER_SERVICE_NAME = "MSO-BPMN:MSO-VnfAdapter.";
+    private static final String LOG_REPLY_NAME = "MSO-VnfAdapter:MSO-BPMN.";
+    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 String ADD_GET_FILES_ON_VOLUME_REQ = "org.openecomp.mso.adapters.vnf.addGetFilesOnVolumeReq";
+
+    /**
+     * 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 Adapter");
+    }
+
+    /**
+     * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
+     * @see MsoVnfAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
+     */
+    public MsoVnfAdapterImpl() {
+
+    }
+
+    /**
+     * This constructor MUST be used if this class is called with the new operator.
+     * @param msoPropFactory
+     */
+    public MsoVnfAdapterImpl(MsoPropertiesFactory msoPropFactory, CloudConfigFactory cloudConfigFact) {
+    	this.msoPropertiesFactory = msoPropFactory;
+    	this.cloudConfigFactory = cloudConfigFact;
+    }
+
+    /**
+     * This is the "Create VNF" web service implementation.
+     * It will create a new VNF of the requested type in the specified cloud
+     * and tenant. The tenant must exist before this service is called.
+     *
+     * If a VNF with the same name already exists, this can be considered a
+     * success or failure, depending on the value of the 'failIfExists' parameter.
+     *
+     * All VNF types will be defined in the MSO catalog. The caller must request
+     * one of these pre-defined types or an error will be returned. Within the
+     * catalog, each VNF type references (among other things) a Heat template
+     * which is used to deploy the required VNF artifacts (VMs, networks, etc.)
+     * to the cloud.
+     *
+     * Depending on the Heat template, 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 VNF or an error will be thrown.
+     *
+     * The method returns the vnfId (the canonical name), a Map of VNF 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 VNF. This is useful if a VNF is successfully created but the
+     * orchestrator fails on a subsequent operation.
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
+     * @param tenantId Openstack tenant identifier
+     * @param vnfType VNF type key, should match a VNF definition in catalog DB
+     * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
+     * @param vnfName Name to be assigned to the new VNF
+     * @param inputs Map of key=value inputs for VNF stack creation
+     * @param failIfExists Flag whether already existing VNF should be considered
+     *        a success or failure
+     * @param msoRequest Request tracking information for logs
+     * @param vnfId Holder for output VNF Openstack ID
+     * @param outputs Holder for Map of VNF outputs from heat (assigned IPs, etc)
+     * @param rollback Holder for returning VnfRollback object
+     */
+    @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 {
+    	// Create a hook here to catch shortcut createVf requests:
+    	if (requestType != null) {
+    		if (requestType.startsWith("VFMOD")) {
+    			LOGGER.debug("Calling createVfModule from createVnf -- requestType=" + requestType);
+    			String newRequestType = requestType.substring(5);
+    			String vfVolGroupHeatStackId = "";
+    			String vfBaseHeatStackId = "";
+    			try {
+    				if (volumeGroupHeatStackId != null) {
+    					vfVolGroupHeatStackId = volumeGroupHeatStackId.substring(0, volumeGroupHeatStackId.lastIndexOf("|"));
+    					vfBaseHeatStackId = volumeGroupHeatStackId.substring(volumeGroupHeatStackId.lastIndexOf("|")+1);
+    				}
+    			} catch (Exception e) {
+    				// might be ok - both are just blank
+    				LOGGER.debug("ERROR trying to parse the volumeGroupHeatStackId " + volumeGroupHeatStackId);
+    			}
+    			this.createVfModule(cloudSiteId, 
+    					tenantId, 
+    					vnfType, 
+    					vnfVersion, 
+    					vnfName, 
+    					newRequestType, 
+    					vfVolGroupHeatStackId, 
+    					vfBaseHeatStackId, 
+    					inputs, 
+    					failIfExists, 
+    					backout, 
+    					msoRequest, 
+    					vnfId, 
+    					outputs, 
+    					rollback);
+    			return;
+    		}
+    	} 
+    	// createVf will know if the requestType starts with "X" that it's the "old" way
+    	StringBuilder newRequestTypeSb = new StringBuilder("X");
+    	String vfVolGroupHeatStackId = "";
+    	String vfBaseHeatStackId = "";
+    	if (requestType != null) {
+    		newRequestTypeSb.append(requestType);
+    	}
+		this.createVfModule(cloudSiteId, 
+				tenantId, 
+				vnfType, 
+				vnfVersion, 
+				vnfName, 
+				newRequestTypeSb.toString(), 
+				vfVolGroupHeatStackId, 
+				vfBaseHeatStackId, 
+				inputs, 
+				failIfExists, 
+				backout, 
+				msoRequest, 
+				vnfId, 
+				outputs, 
+				rollback);
+    	return;
+    	// End createVf shortcut
+    }
+
+    @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 {
+    	MsoLogger.setLogContext (msoRequest.getRequestId (), msoRequest.getServiceInstanceId ());
+    	MsoLogger.setServiceName ("UpdateVnf");
+    	String requestTypeString = "";
+        if (requestType != null && !requestType.equals("")) {
+        	requestTypeString = requestType;
+        }
+        String nestedStackId = null;
+        if (volumeGroupHeatStackId != null && !volumeGroupHeatStackId.equals("")) {
+        	nestedStackId = volumeGroupHeatStackId;
+        }
+
+        LOGGER.debug ("Updating VNF: " + vnfName + " of type " + vnfType + "in " + cloudSiteId + "/" + tenantId);
+        LOGGER.debug("requestTypeString = " + requestTypeString + ", nestedStackId = " + nestedStackId);
+
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+
+        // Build a default rollback object (no actions performed)
+        VnfRollback vnfRollback = new VnfRollback ();
+        vnfRollback.setCloudSiteId (cloudSiteId);
+        vnfRollback.setTenantId (tenantId);
+        vnfRollback.setMsoRequest (msoRequest);
+        vnfRollback.setRequestType(requestTypeString);
+
+        // First, look up to see if the VNF already exists.
+        MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+        MsoHeatUtilsWithUpdate heatU = new MsoHeatUtilsWithUpdate (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+
+        StackInfo heatStack = null;
+        long queryStackStarttime1 = System.currentTimeMillis ();
+        try {
+            heatStack = heat.queryStack (cloudSiteId, tenantId, vnfName);
+            LOGGER.recordMetricEvent (queryStackStarttime1, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", vnfName);
+        } catch (MsoException me) {
+            // Failed to query the Stack due to an openstack exception.
+            // Convert to a generic VnfException
+            me.addContext ("UpdateVNF");
+            String error = "Update VNF: Query " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+            LOGGER.recordMetricEvent (queryStackStarttime1, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", vnfName);
+            LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.BusinessProcesssError, "Exception in updateVnf", me);
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+            throw new VnfException (me);
+        }
+
+        if (heatStack == null || heatStack.getStatus () == HeatStatus.NOTFOUND) {
+            // Not Found
+            String error = "Update VNF: Stack " + vnfName + " does not exist in " + cloudSiteId + "/" + tenantId;
+            LOGGER.error (MessageEnum.RA_VNF_NOT_EXIST, vnfName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: Stack " + vnfName + " does not exist");
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+            throw new VnfNotFound (cloudSiteId, tenantId, vnfName);
+        } else {
+            LOGGER.debug ("Found Existing stack, status=" + heatStack.getStatus ());
+            // Populate the outputs from the existing stack.
+            outputs.value = copyStringOutputs (heatStack.getOutputs ());
+            rollback.value = vnfRollback; // Default rollback - no updates performed
+        }
+        
+        // 1604 Cinder Volume support - handle a nestedStackId if sent (volumeGroupHeatStackId):
+        StackInfo nestedHeatStack = null;
+        long queryStackStarttime2 = System.currentTimeMillis ();
+        if (nestedStackId != null) {
+        	try {
+        		LOGGER.debug("Querying for nestedStackId = " + nestedStackId);
+        		nestedHeatStack = heat.queryStack(cloudSiteId, tenantId, nestedStackId);
+                LOGGER.recordMetricEvent (queryStackStarttime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", vnfName);
+        	} catch (MsoException me) {
+        	    // Failed to query the Stack due to an openstack exception.
+        	    // Convert to a generic VnfException
+        	    me.addContext ("UpdateVNF");
+        	    String error = "Update VNF: Attached heatStack ID Query " + nestedStackId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+                LOGGER.recordMetricEvent (queryStackStarttime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", vnfName);
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.AvailabilityError, "Exception trying to query nested stack", me);
+        		LOGGER.debug("ERROR trying to query nested stack= " + error);
+        	    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+        	    throw new VnfException (me);
+        	}
+        	if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
+        	    String error = "Update VNF: Attached heatStack ID DOES NOT EXIST " + nestedStackId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, error, "OpenStack", "QueryStack", MsoLogger.ErrorCode.AvailabilityError, "Attached heatStack ID DOES NOT EXIST");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+        	    LOGGER.debug(error);
+        	    throw new VnfException (error, MsoExceptionCategory.USERDATA);
+        	} else {
+        		LOGGER.debug("Found nested heat stack - copying values to inputs");
+        		this.sendMapToDebug(inputs);
+        		heat.copyStringOutputsToInputs(inputs, nestedHeatStack.getOutputs(), false);      
+        		this.sendMapToDebug(inputs);
+        	}
+        }
+
+        // Ready to deploy the new VNF
+
+        try(CatalogDatabase db = new CatalogDatabase ()) {
+            // Retrieve the VNF definition
+            VnfResource vnf;
+            if (vnfVersion != null && !vnfVersion.isEmpty ()) {
+                vnf = db.getVnfResource (vnfType, vnfVersion);
+            } else {
+                vnf = db.getVnfResource (vnfType);
+            }
+            if (vnf == null) {
+                String error = "Update VNF: Unknown VNF Type: " + vnfType;
+                LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "VNF Type", vnfType, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: Unknown VNF Type");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+                throw new VnfException (error, MsoExceptionCategory.USERDATA);
+            }
+            LOGGER.debug ("Got VNF definition from Catalog: " + vnf.toString ());
+
+            // Currently, all VNFs are orchestrated via HEAT
+            if (!"HEAT".equals (vnf.getOrchestrationMode ())) {
+                String error = "Update VNF: Configuration error: VNF=" + vnfType;
+                LOGGER.error (MessageEnum.RA_CONFIG_EXC, " VNF=" + vnfType, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: Configuration error");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
+                // Alarm on this error, configuration must be fixed
+                alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
+                throw new VnfException (error, MsoExceptionCategory.INTERNAL);
+            }
+            
+            //1604 - Need to handle an updateVolume request. 
+            VnfComponent vnfComponent = null;
+            if (requestTypeString != null && !requestTypeString.equals("")) {
+            	LOGGER.debug("About to query for vnfComponent id = " + vnf.getId() + ", type = " + requestTypeString.toUpperCase());
+            	vnfComponent = db.getVnfComponent(vnf.getId(), requestTypeString.toUpperCase());
+            	if (vnfComponent == null) {
+            		String error = "Update VNF: Cannot find VNF Component entry for: " + vnfType + ", type = " + requestTypeString.toUpperCase();
+            		LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "VNF Type", vnfType, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: Cannot find VNF Component entry");
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+            		throw new VnfException (error, MsoExceptionCategory.USERDATA);
+            	}
+            	LOGGER.debug("FOUND VnfComponent: " + vnfComponent.toString());
+            }
+
+            HeatTemplate heatTemplate = db.getHeatTemplate (vnf.getTemplateId ());
+            if (heatTemplate == null) {
+                String error = "Update VNF: undefined Heat Template. VNF=" + vnfType;
+                LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Template ID", String.valueOf(vnf.getTemplateId ()), "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: undefined Heat Template");
+                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);
+            }
+
+            // If this is a component request - get the template associated for volumes
+            // May change this - for now get both templates - but volume will be 2nd, which makes sense
+            // for the rest of the code. Same with envt later
+			if (vnfComponent != null) {
+				LOGGER.debug("Querying db to find component template " + vnfComponent.getHeatTemplateId());
+				heatTemplate = db.getHeatTemplate(vnfComponent
+						.getHeatTemplateId());
+				if (heatTemplate == null) {
+					String error = "Update VNF: undefined Heat Template for Volume Component. VNF="
+							+ vnfType;
+					LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
+							"Heat Template ID",
+							String.valueOf(vnfComponent.getHeatTemplateId()), "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: undefined Heat Template for Volume Component");
+                    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 from DB: " + heatTemplate.toString ());
+            
+            // Add check for any Environment variable
+            HeatEnvironment heatEnvironment = null;
+            String heatEnvironmentString = null;
+
+            if (vnf.getEnvironmentId () != null) {
+                LOGGER.debug ("about to call getHeatEnvironment with :" + vnf.getEnvironmentId () + ":");
+                heatEnvironment = db.getHeatEnvironment (vnf.getEnvironmentId ());
+                if (heatEnvironment == null) {
+
+                    String error = "Create VNF: undefined Heat Environment. VNF=" + vnfType
+                                   + ", Environment ID="
+                                   + vnf.getEnvironmentId ();
+                    LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", String.valueOf(vnf.getEnvironmentId ()), "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Create VNF: 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);
+                } else {
+                    LOGGER.debug ("Got Heat Environment from DB: " + heatEnvironment.toString ());
+                    heatEnvironmentString = heatEnvironment.getEnvironment (); //this.parseEnvironment (heatEnvironment.getEnvironment ());
+                    LOGGER.debug ("After parsing: " + heatEnvironmentString);
+                }
+            } else {
+                LOGGER.debug ("no environment parameter for this VNF " + vnfType);
+            }
+            
+            //1604 - override the VNF environment with the one for the component
+            if(vnfComponent != null) {
+                if (vnfComponent.getHeatEnvironmentId () != null) {
+                    LOGGER.debug ("about to call getHeatEnvironment with :" + vnfComponent.getHeatEnvironmentId () + ":");
+                    heatEnvironment = db.getHeatEnvironment (vnfComponent.getHeatEnvironmentId ());
+                    if (heatEnvironment == null) {
+                        String error = "Update VNF: undefined Heat Environment. VNF=" + vnfType
+                                       + ", Environment ID="
+                                       + vnfComponent.getHeatEnvironmentId ();
+                        LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", String.valueOf(vnfComponent.getHeatEnvironmentId ()), "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: 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);
+                    } else {
+                        LOGGER.debug ("Got Heat Environment from DB: " + heatEnvironment.toString ());
+                        heatEnvironmentString = heatEnvironment.getEnvironment (); //this.parseEnvironment (heatEnvironment.getEnvironment ());
+                        LOGGER.debug ("after parsing: " + heatEnvironmentString);
+                    }
+                } else {
+                    LOGGER.debug ("no environment parameter for this VNF VOLUME component " + vnfType);
+                }
+            }
+            // End 1604
+            
+            
+            LOGGER.debug ("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId="
+                          + heatTemplate.getId ());
+            Map <String, Object> nestedTemplates = db.getNestedTemplates (heatTemplate.getId ());
+            Map <String, Object> nestedTemplatesChecked = new HashMap <String, Object> ();
+            if (nestedTemplates != null) {
+                // for debugging print them out
+                LOGGER.debug ("Contents of nestedTemplates - to be added to files: on stack:");
+                for (String providerResourceFile : nestedTemplates.keySet ()) {
+                    String providerResourceFileChecked = providerResourceFile; //this.enforceFilePrefix (providerResourceFile);
+                    String childTemplateBody = (String) nestedTemplates.get (providerResourceFile);
+                    nestedTemplatesChecked.put (providerResourceFileChecked, childTemplateBody);
+                    LOGGER.debug (providerResourceFileChecked + " -> " + childTemplateBody);
+                }
+            } else {
+                LOGGER.debug ("No nested templates found - nothing to do here");
+                nestedTemplatesChecked = null;
+            }
+
+            // Also add the files: for any get_files associated with this vnf_resource_id
+            // *if* there are any
+            LOGGER.debug ("In MsoVnfAdapterImpl.updateVnf, about to call db.getHeatFiles avec vnfResourceId="
+                          + vnf.getId ());
+            Map <String, HeatFiles> heatFiles = db.getHeatFiles (vnf.getId ());
+            Map <String, Object> heatFilesObjects = new HashMap <String, Object> ();
+            if (heatFiles != null) {
+                // add these to stack - to be done in createStack
+                // here, we will map them to Map<String, Object> from Map<String, HeatFiles>
+                // this will match the nested templates format
+                LOGGER.debug ("Contents of heatFiles - to be added to files: on stack:");
+
+                for (String heatFileName : heatFiles.keySet ()) {
+                    String heatFileBody = heatFiles.get (heatFileName).getFileBody ();
+                    // Remove the file:/// enforcement for get_file:
+                    //String heatFileNameChecked = this.enforceFilePrefix (heatFileName);
+                    String heatFileNameChecked = heatFileName;
+                    LOGGER.debug (heatFileNameChecked + " -> " + heatFileBody);
+                    heatFilesObjects.put (heatFileNameChecked, heatFileBody);
+                }
+            } else {
+                LOGGER.debug ("No heat files found -nothing to do here");
+                heatFilesObjects = null;
+            }
+
+            // Check that required parameters have been supplied
+            String missingParams = null;
+            List <String> paramList = new ArrayList <String> ();
+
+            // New for 1510 - consult the PARAM_ALIAS field to see if we've been
+            // supplied an alias. Only check if we don't find it initially.
+            // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
+            // And also new - add parameter to turn off checking all together if we find we're blocking orders we
+            // shouldn't
+            boolean haveEnvironmentParameters = false;
+            boolean checkRequiredParameters = true;
+            try {
+                String propertyString = msoPropertiesFactory.getMsoJavaProperties (MSO_PROP_VNF_ADAPTER)
+                                                     .getProperty (MsoVnfAdapterImpl.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..."
+                                  + MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
+                }
+            } catch (Exception e) {
+                // No problem - default is true
+                LOGGER.debug ("An exception occured trying to get property " + MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
+            }
+            // 1604 - Add enhanced environment & parameter checking
+            // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
+            // Part 2: only submit to openstack the parameters in the envt that are in the heat template
+            // Note this also removes any comments
+            MsoHeatEnvironmentEntry mhee = null;
+            if (heatEnvironmentString != null && heatEnvironmentString.toLowerCase ().contains ("parameters:")) {
+            	LOGGER.debug("Enhanced environment checking enabled - 1604");
+                haveEnvironmentParameters = true;
+                StringBuilder sb = new StringBuilder(heatEnvironmentString);
+                //LOGGER.debug("About to create MHEE with " + sb);
+                mhee = new MsoHeatEnvironmentEntry(sb);
+                StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
+                for (HeatTemplateParam parm : heatTemplate.getParameters()) {
+                	sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
+                }
+                if (!mhee.isValid()) {
+                	sb2.append("Environment says it's not valid! " + mhee.getErrorString());
+                } else {
+                	sb2.append("\nEnvironment:");
+                	sb2.append(mhee.toFullString());
+                }
+                LOGGER.debug(sb2.toString());
+            } else {
+            	LOGGER.debug("NO ENVIRONMENT for this entry");
+            }
+
+            for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
+                LOGGER.debug ("Parameter:'" + parm.getParamName ()
+                              + "', isRequired="
+                              + parm.isRequired ()
+                              + ", alias="
+                              + parm.getParamAlias ());
+                if (parm.isRequired () && (inputs == null || !inputs.containsKey (parm.getParamName ()))) {
+                    if (inputs.containsKey (parm.getParamAlias ())) {
+                        // They've submitted using an alias name. Remove that from inputs, and add back using real name.
+                        String realParamName = parm.getParamName ();
+                        String alias = parm.getParamAlias ();
+                        String value = inputs.get (alias);
+                        LOGGER.debug ("*Found an Alias: paramName=" + realParamName
+                                      + ",alias="
+                                      + alias
+                                      + ",value="
+                                      + value);
+                        inputs.remove (alias);
+                        inputs.put (realParamName, value);
+                        LOGGER.debug (alias + " entry removed from inputs, added back using " + realParamName);
+                    } 
+                    // enhanced - check if it's in the Environment (note: that method 
+                    else 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 ();
+                        }
+                    }
+                }
+                paramList.add (parm.getParamName ());
+            }
+            if (missingParams != null) {
+                // Problem - missing one or more required parameters
+            	if (checkRequiredParameters) {
+                String error = "Update VNF: Missing Required inputs: " + missingParams;
+                LOGGER.error (MessageEnum.RA_MISSING_PARAM, missingParams, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Update VNF: Missing Required inputs");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataError, error);
+                throw new VnfException (error, MsoExceptionCategory.USERDATA);
+            	} else {
+            		LOGGER.debug ("found missing parameters - but checkRequiredParameters is false - will not block");
+            	}
+            } else {
+                LOGGER.debug ("No missing parameters found - ok to proceed");
+            }
+            
+            // Here - modify heatEnvironmentString
+            StringBuilder parsedEnvironmentString = null; 
+            String newEnvironmentString = null;
+            if (mhee != null) {
+            	LOGGER.debug("Environment before:\n" + heatEnvironmentString);
+            	parsedEnvironmentString = mhee.toFullStringExcludeNonParams(heatTemplate.getParameters());
+            	LOGGER.debug("Environment after:\n" + parsedEnvironmentString.toString());
+            	newEnvironmentString = parsedEnvironmentString.toString();
+            }
+
+            // Remove any extraneous parameters (don't throw an error)
+            if (inputs != null) {
+                List <String> extraParams = new ArrayList <String> ();
+                extraParams.addAll (inputs.keySet ());
+                // This is not a valid parameter for this template
+                extraParams.removeAll (paramList);
+                if (!extraParams.isEmpty ()) {
+                	LOGGER.warn (MessageEnum.RA_VNF_EXTRA_PARAM, vnfType, extraParams.toString(), "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "VNF Extra params");
+                    inputs.keySet ().removeAll (extraParams);
+                }
+            }
+
+            // "Fix" the template if it has CR/LF (getting this from Oracle)
+            String template = heatTemplate.getHeatTemplate ();
+            template = template.replaceAll ("\r\n", "\n");
+
+            // Have the tenant. Now deploy the stack itself
+            // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
+            // because we already checked for those.
+            long updateStackStarttime = System.currentTimeMillis ();
+            try {
+                heatStack = heatU.updateStack (cloudSiteId,
+                                               tenantId,
+                                               vnfName,
+                                               template,
+                                               copyStringInputs (inputs),
+                                               true,
+                                               heatTemplate.getTimeoutMinutes (),
+                                               newEnvironmentString,
+                                               //heatEnvironmentString,
+                                               nestedTemplatesChecked,
+                                               heatFilesObjects);
+                LOGGER.recordMetricEvent (updateStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "UpdateStack", vnfName);
+            } catch (MsoException me) {
+                me.addContext ("UpdateVNF");
+                String error = "Update VNF " + vnfType + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+                LOGGER.recordMetricEvent (updateStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "UpdateStack", vnfName);
+                LOGGER.error (MessageEnum.RA_UPDATE_VNF_ERR, vnfType, cloudSiteId, tenantId, "OpenStack", "updateStack", MsoLogger.ErrorCode.DataError, "Exception - updateStack", me);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+                throw new VnfException (me);
+            }
+        }
+
+        // Reach this point if updateStack is successful.
+        // Populate remaining rollback info and response parameters.
+        vnfRollback.setVnfId (heatStack.getCanonicalName ());
+        vnfRollback.setVnfCreated (true);
+
+        outputs.value = copyStringOutputs (heatStack.getOutputs ());
+        rollback.value = vnfRollback;
+        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully update VNF");
+        return;
+    }
+
+    /**
+     * This is the "Query VNF" web service implementation.
+     * It will look up a VNF by name or ID in the specified cloud and tenant.
+     *
+     * The method returns an indicator that the VNF exists, its Openstack internal
+     * ID, its status, and the set of outputs (from when the stack was created).
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to query
+     * @param tenantId Openstack tenant identifier
+     * @param vnfName VNF Name or Openstack ID
+     * @param msoRequest Request tracking information for logs
+     * @param vnfExists Flag reporting the result of the query
+     * @param vnfId Holder for output VNF Openstack ID
+     * @param outputs Holder for Map of VNF outputs from heat (assigned IPs, etc)
+     */
+    @Override
+    public void queryVnf (String cloudSiteId,
+                          String tenantId,
+                          String vnfName,
+                          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 " + vnfName + " in " + cloudSiteId + "/" + tenantId);
+
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+
+        MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+
+        StackInfo heatStack = null;
+        long subStartTime = System.currentTimeMillis ();
+        try {
+            heatStack = heat.queryStack (cloudSiteId, tenantId, vnfName);
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", vnfName);
+        } catch (MsoException me) {
+            me.addContext ("QueryVNF");
+            // Failed to query the Stack due to an openstack exception.
+            // Convert to a generic VnfException
+            String error = "Query VNF: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", vnfName);
+            LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, "OpenStack", "QueryVNF", MsoLogger.ErrorCode.DataError, "Exception - queryStack", me);
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+            throw new VnfException (me);
+        }
+
+        // Populate the outputs based on the returned Stack information
+        //
+        if (heatStack == null || heatStack.getStatus () == HeatStatus.NOTFOUND) {
+            // Not Found
+            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 " + vnfName + " not found");
+        } else {
+            vnfExists.value = Boolean.TRUE;
+            status.value = stackStatusToVnfStatus (heatStack.getStatus ());
+            vnfId.value = heatStack.getCanonicalName ();
+            outputs.value = copyStringOutputs (heatStack.getOutputs ());
+
+            LOGGER.debug ("VNF " + vnfName + " found, ID = " + vnfId.value);
+        }
+        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully query VNF");
+        return;
+    }
+
+    /**
+     * This is the "Delete VNF" web service implementation.
+     * It will delete a VNF by name or ID in the specified cloud and tenant.
+     *
+     * The method has no outputs.
+     *
+     * @param cloudSiteId CLLI code of the cloud site in which to delete
+     * @param tenantId Openstack tenant identifier
+     * @param vnfName VNF Name or Openstack ID
+     * @param msoRequest Request tracking information for logs
+     */
+    @Override
+    public void deleteVnf (String cloudSiteId,
+                           String tenantId,
+                           String vnfName,
+                           MsoRequest msoRequest) throws VnfException {
+        MsoLogger.setLogContext (msoRequest);
+    	MsoLogger.setServiceName ("DeleteVnf");
+        LOGGER.debug ("Deleting VNF " + vnfName + " in " + cloudSiteId + "/" + tenantId);
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+
+        MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+
+        // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
+        // The possible outcomes of deleteStack are a StackInfo object with status
+        // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
+        // could be thrown.
+        long subStartTime = System.currentTimeMillis ();
+        try {
+            heat.deleteStack (tenantId, cloudSiteId, vnfName, true);
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "DeleteStack", vnfName);
+        } catch (MsoException me) {
+            me.addContext ("DeleteVNF");
+            // Failed to query the Stack due to an openstack exception.
+            // Convert to a generic VnfException
+            String error = "Delete VNF: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "DeleteStack", vnfName);
+            LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vnfName, cloudSiteId, tenantId, "OpenStack", "DeleteVNF", MsoLogger.ErrorCode.DataError, "Exception - DeleteVNF", me);
+            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 VNF");
+        return;
+    }
+
+    /**
+     * 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.
+     */
+    @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;
+        }
+
+        // Get the elements of the VnfRollback object for easier access
+        String cloudSiteId = rollback.getCloudSiteId ();
+        String tenantId = rollback.getTenantId ();
+        String vnfId = rollback.getVnfId ();
+
+        MsoLogger.setLogContext (rollback.getMsoRequest());
+
+        LOGGER.debug ("Rolling Back VNF " + vnfId + " in " + cloudSiteId + "/" + tenantId);
+
+        MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+
+        // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
+        // The possible outcomes of deleteStack are a StackInfo object with status
+        // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
+        // could be thrown.
+        long subStartTime = System.currentTimeMillis ();
+        try {
+            heat.deleteStack (tenantId, cloudSiteId, vnfId, true);
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "DeleteStack", null);
+        } catch (MsoException me) {
+            // Failed to rollback the Stack due to an openstack exception.
+            // Convert to a generic VnfException
+            me.addContext ("RollbackVNF");
+            String error = "Rollback VNF: " + vnfId + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "DeleteStack", null);
+            LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vnfId, cloudSiteId, tenantId, "OpenStack", "DeleteStack", MsoLogger.ErrorCode.DataError, "Exception - DeleteStack", me);
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+            throw new VnfException (me);
+        }
+        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully roll back VNF");
+        return;
+    }
+
+    private VnfStatus stackStatusToVnfStatus (HeatStatus stackStatus) {
+        switch (stackStatus) {
+            case CREATED:
+                return VnfStatus.ACTIVE;
+            case UPDATED:
+                return VnfStatus.ACTIVE;
+            case FAILED:
+                return VnfStatus.FAILED;
+            default:
+                return VnfStatus.UNKNOWN;
+        }
+    }
+
+    private Map <String, String> copyStringOutputs (Map <String, Object> stackOutputs) {
+        Map <String, String> stringOutputs = new HashMap <String, String> ();
+        for (String key : stackOutputs.keySet ()) {
+            if (stackOutputs.get (key) instanceof String) {
+                stringOutputs.put (key, (String) stackOutputs.get (key));
+            }
+        }
+        return stringOutputs;
+    }
+
+    private Map <String, Object> copyStringInputs (Map <String, String> stringInputs) {
+        return new HashMap <String, Object> (stringInputs);
+    }
+
+    /*
+     * a helper method to make sure that any resource_registry entry of the format
+     * "xx::xx" : yyy.yaml (or yyy.template)
+     * has the file name prepended with "file:///"
+     * Return a String of the environment body that's passed in.
+     * Have to be careful not to mess up the original formatting.
+     */
+    private String parseEnvironment (String environment) {
+        StringBuilder sb = new StringBuilder ();
+        try (Scanner scanner = new Scanner (environment)) {
+            scanner.useDelimiter ("\n");
+            String line = null;
+            Pattern resource = Pattern.compile ("\\s*\"\\w+::\\S+\"\\s*:");
+            LOGGER.debug ("regex pattern for finding a resource_registry: \\s*\"\\w+::\\S+\"\\s*:");
+            while (scanner.hasNextLine ()) {
+                line = scanner.nextLine ();
+                if (line.toLowerCase ().contains ("resource_registry")) {
+                    sb.append (line + "\n");
+                    boolean done = false;
+                    // basically keep scanning until EOF or parameters: section
+                    while (scanner.hasNextLine () && !done) {
+                        line = scanner.nextLine ();
+                        if ("parameters:".equalsIgnoreCase (line.trim ())) {
+                            sb.append (line + "\n");
+                            done = true;
+                            break;
+                        }
+                        Matcher m = resource.matcher (line);
+                        if (m.find ()) {
+                            sb.append (m.group ());
+                            String secondPart = line.substring (m.end ()).trim ();
+                            String output = secondPart;
+                            if (secondPart.endsWith (".yaml")
+                                || secondPart.endsWith (".template") && !secondPart.startsWith ("file:///")) {
+                                output = "file:///" + secondPart;
+                                LOGGER.debug ("changed " + secondPart + " to " + output);
+                            } // don't do anything if it's not .yaml or .template
+                            sb.append (" " + output + "\n");
+                        } else {
+                            sb.append (line + "\n");
+                        }
+                    }
+                } else {
+                    sb.append (line + "\n");
+                    continue;
+                }
+            }
+            scanner.close ();
+        } catch (Exception e) {
+            LOGGER.debug ("Error trying to scan " + environment, e);
+            return environment;
+        }
+        return sb.toString ();
+    }
+
+    /*
+     * helper class to add file:/// to the Provider_Resource_File entry in HEAT_NESTED_TEMPLATE
+     * and the File_Name entry in HEAT_FILES if the file:/// part is missing.
+     */
+    private String enforceFilePrefix (String string) {
+        if (string.trim ().startsWith ("file:///")) {
+            // just leave it
+            return string;
+        }
+        if (string.trim ().endsWith (".yaml") || string.trim ().endsWith (".template")) {
+            // only .yaml or .template are valid anyway - otherwise don't bother
+            return "file:///" + string.trim ();
+        } else {
+            LOGGER.debug (string + " is NOT a .yaml or .template file");
+        }
+        return string;
+    }
+    
+    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;
+    }
+
+    public void createVfModule(String cloudSiteId,
+            String tenantId,
+            String vnfType,
+            String vnfVersion,
+            String vnfName,
+            String requestType,
+            String volumeGroupHeatStackId,
+            String baseVfHeatStackId,
+            Map <String, String> inputs,
+            Boolean failIfExists,
+            Boolean backout,
+            MsoRequest msoRequest,
+            Holder <String> vnfId,
+            Holder <Map <String, String>> outputs,
+            Holder <VnfRollback> rollback) throws VnfException {
+    	String vfModuleName = vnfName;
+    	String vfModuleType = vnfType;
+    	String vfVersion = vnfVersion;
+    	MsoLogger.setLogContext (msoRequest);
+    	MsoLogger.setServiceName ("CreateVfModule");
+    	String requestTypeString = "";
+        if (requestType != null && !requestType.equals("")) {
+        	requestTypeString = requestType;
+        }
+        String nestedStackId = null;
+        if (volumeGroupHeatStackId != null && !volumeGroupHeatStackId.equals("")) {
+        	if (!volumeGroupHeatStackId.equalsIgnoreCase("null")) {
+        		nestedStackId = volumeGroupHeatStackId;
+        	}
+        }
+        String nestedBaseStackId = null;
+        if (baseVfHeatStackId != null && !baseVfHeatStackId.equals("")) {
+        	if (!baseVfHeatStackId.equalsIgnoreCase("null")) {
+        		nestedBaseStackId = baseVfHeatStackId;
+        	}
+        }
+        
+        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);
+        }
+        //This method will also handle doing things the "old" way - i.e., just orchestrate a VNF
+        boolean oldWay = false;
+        if (requestTypeString.startsWith("X")) {
+        	oldWay = true;
+        	LOGGER.debug("orchestrating a VNF - *NOT* a module!");
+        	requestTypeString = requestTypeString.substring(1);
+        }
+        
+        // 1607 - let's parse out the request type we're being sent
+        boolean isBaseRequest = false;
+        boolean isVolumeRequest = false;
+        if (requestTypeString.startsWith("VOLUME")) {
+        	isVolumeRequest = true;
+        } 
+
+        LOGGER.debug("requestTypeString = " + requestTypeString + ", nestedStackId = " + nestedStackId + ", nestedBaseStackId = " + nestedBaseStackId);
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+
+        // Build a default rollback object (no actions performed)
+        VnfRollback vfRollback = new VnfRollback();
+        vfRollback.setCloudSiteId(cloudSiteId);
+        vfRollback.setTenantId(tenantId);
+        vfRollback.setMsoRequest(msoRequest);
+        vfRollback.setRequestType(requestTypeString);
+        vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
+        vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
+        vfRollback.setIsBase(isBaseRequest);
+        
+        // First, look up to see if the VF already exists.
+        MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+
+        StackInfo heatStack = null;
+        long subStartTime1 = System.currentTimeMillis ();
+        try {
+            heatStack = heat.queryStack (cloudSiteId, tenantId, vfModuleName);
+            LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", vfModuleName);
+        } catch (MsoException me) {
+            String error = "Create VF Module: Query " + vfModuleName + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+            LOGGER.recordMetricEvent (subStartTime1, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", vfModuleName);
+            LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Exception - queryStack", me);
+            // Failed to query the Stack due to an openstack exception.
+            // Convert to a generic VnfException
+            me.addContext ("CreateVFModule");
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+            throw new VnfException (me);
+        }
+        // New with 1607 - more precise handling/messaging if the stack already exists 
+        if (heatStack != null && !(heatStack.getStatus () == HeatStatus.NOTFOUND)) {
+        	// INIT, CREATED, NOTFOUND, FAILED, BUILDING, DELETING, UNKNOWN, UPDATING, UPDATED
+        	HeatStatus status = heatStack.getStatus();
+        	if (status == HeatStatus.INIT || status == HeatStatus.BUILDING || status == HeatStatus.DELETING || status == HeatStatus.UPDATING) {
+        		// fail - it's in progress - return meaningful error
+                String error = "Create VF: Stack " + 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, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Stack " + vfModuleName + " already exists");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, heatStack.getCanonicalName ());
+        	}
+        	if (status == HeatStatus.FAILED) {
+        		// fail - it exists and is in a FAILED state
+                String error = "Create VF: Stack " + vfModuleName + " already exists and is in FAILED state in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
+                LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Stack " + vfModuleName + " already exists and is in FAILED state");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, heatStack.getCanonicalName ());	
+        	}
+        	if (status == HeatStatus.UNKNOWN || status == HeatStatus.UPDATED) {
+        		// fail - it exists and is in a FAILED state
+                String error = "Create VF: Stack " + vfModuleName + " already exists and has status " + status.toString() + " in " + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
+                LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Stack " + vfModuleName + " already exists and is in UPDATED or UNKNOWN state");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+                throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, heatStack.getCanonicalName ());	
+        	}
+        	if (status == HeatStatus.CREATED) {
+        		// fail - it exists 
+        		if (failIfExists != null && failIfExists) {
+        			String error = "Create VF: Stack " + vfModuleName + " already exists in " + cloudSiteId + "/" + tenantId;
+        			LOGGER.error (MessageEnum.RA_VNF_ALREADY_EXIST, vfModuleName, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Stack " + vfModuleName + " already exists");
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
+        			throw new VnfAlreadyExists (vfModuleName, cloudSiteId, tenantId, heatStack.getCanonicalName ());	
+        		} else {
+        			LOGGER.debug ("Found Existing stack, status=" + heatStack.getStatus ());
+        			// Populate the outputs from the existing stack.
+        			vnfId.value = heatStack.getCanonicalName ();
+        			outputs.value = copyStringOutputs (heatStack.getOutputs ());
+        			rollback.value = vfRollback; // Default rollback - no updates performed
+        		}
+        	}
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully create VF Module");
+            return;
+        	
+        }
+        
+        // handle a nestedStackId if sent- this one would be for the volume - so applies to both Vf and Vnf
+        StackInfo nestedHeatStack = null;
+        long subStartTime2 = System.currentTimeMillis ();
+        if (nestedStackId != null) {
+        	try {
+        		LOGGER.debug("Querying for nestedStackId = " + nestedStackId);
+        		nestedHeatStack = heat.queryStack(cloudSiteId, tenantId, nestedStackId);
+                LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", vfModuleName);
+        	} catch (MsoException me) {
+        	    // Failed to query the Stack due to an openstack exception.
+        	    // Convert to a generic VnfException
+        	    me.addContext ("CreateVFModule");
+        	    String error = "Create VFModule: Attached heatStack ID Query " + nestedStackId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+                LOGGER.recordMetricEvent (subStartTime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", vfModuleName);
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.BusinessProcesssError, "MsoException trying to query nested stack", me);
+        		LOGGER.debug("ERROR trying to query nested stack= " + error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+        	    throw new VnfException (me);
+        	}
+        	if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
+        	    String error = "Create VFModule: Attached heatStack ID DOES NOT EXIST " + nestedStackId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, error, "OpenStack", "queryStack", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: Attached heatStack ID 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 heat stack - copying values to inputs");
+        		this.sendMapToDebug(inputs);
+        		heat.copyStringOutputsToInputs(inputs, nestedHeatStack.getOutputs(), false);      
+        		this.sendMapToDebug(inputs);
+        	}
+        }
+        
+        // handle a nestedBaseStackId if sent- this is the stack ID of the base. Should be null for VNF requests
+        StackInfo nestedBaseHeatStack = null;
+        long subStartTime3 = System.currentTimeMillis ();
+        if (nestedBaseStackId != null) {
+        	try {
+        		LOGGER.debug("Querying for nestedBaseStackId = " + nestedBaseStackId);
+        		nestedBaseHeatStack = heat.queryStack(cloudSiteId, tenantId, nestedBaseStackId);
+                LOGGER.recordMetricEvent (subStartTime3, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", vfModuleName);
+        	} catch (MsoException me) {
+        	    // Failed to query the Stack due to an openstack exception.
+        	    // Convert to a generic VnfException
+        	    me.addContext ("CreateVFModule");
+        	    String error = "Create VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+                LOGGER.recordMetricEvent (subStartTime3, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", vfModuleName);
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.BusinessProcesssError, "MsoException trying to query nested base stack", me);
+        		LOGGER.debug("ERROR trying to query nested base stack= " + error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+        	    throw new VnfException (me);
+        	}
+        	if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
+        	    String error = "Create VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, error, "OpenStack", "QueryStack", MsoLogger.ErrorCode.BusinessProcesssError, "Create VFModule: Attached base heatStack ID 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 base heat stack - copying values to inputs");
+        		this.sendMapToDebug(inputs);
+        		heat.copyStringOutputsToInputs(inputs, nestedBaseHeatStack.getOutputs(), false);      
+        		this.sendMapToDebug(inputs);
+        	}
+        }
+        
+        // Ready to deploy the new VNF
+        
+        try (CatalogDatabase db = new CatalogDatabase()) {
+            // Retrieve the VF 
+        	VfModule vf = null;
+        	VnfResource vnfResource = null;
+        	LOGGER.debug("version: " + vfVersion);
+			if (!oldWay) {
+				// Need to handle old and new schema methods - for a time. Try the new way first.
+				if (vfVersion != null && !vfVersion.isEmpty()) {
+					vf = db.getVfModuleType(vfModuleType, vfVersion);
+					if (vf == null) {
+						LOGGER.debug("Unable to find " + vfModuleType + " and version=" + vfVersion + " in the TYPE column - will try in MODEL_NAME");
+						vf = db.getVfModuleModelName(vfModuleType, vfVersion);
+						if (vf == null) {
+							LOGGER.debug("Unable to find " + vfModuleType + " and version=" + vfVersion + " in the MODEL_NAME field either - ERROR");
+						}
+					}
+				} else {
+					vf = db.getVfModuleType(vfModuleType);
+					if (vf == null) {
+						LOGGER.debug("Unable to find " + vfModuleType + " in the TYPE column - will try in MODEL_NAME");
+						vf = db.getVfModuleModelName(vfModuleType);
+						if (vf == null) {
+							LOGGER.debug("Unable to find " + vfModuleType + " in the MODEL_NAME field either - ERROR");
+						}
+					}
+				}
+				if (vf == null) {
+					String error = "Create VF Module: Unable to determine specific VF Module Type: "
+							+ vfModuleType;
+					if (vfVersion != null && !vfVersion.isEmpty()) {
+						error += " with version = " + vfVersion;
+					}
+					LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
+							"VF Module Type", vfModuleType, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Create VF Module: Unable to determine specific VF Module Type");
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+					throw new VnfException(error, MsoExceptionCategory.USERDATA);
+				}
+				LOGGER.debug("Got VF module definition from Catalog: "
+						+ vf.toString());
+
+				if (vf.isBase()) {
+					isBaseRequest = true;
+					LOGGER.debug("This is a BASE VF request!");
+				} else {
+					LOGGER.debug("This is *not* a BASE VF request!");
+					if (!isVolumeRequest && nestedBaseStackId == null) {
+						LOGGER.debug("DANGER WILL ROBINSON! This is unexpected - no nestedBaseStackId with this non-base request");
+					}
+				}
+			} else {
+				if (vfVersion != null && !vfVersion.isEmpty()) {
+					vnfResource = db.getVnfResource(vnfType, vnfVersion);
+				} else {
+					vnfResource = db.getVnfResource(vnfType);
+				}
+				if (vnfResource == null) {
+					String error = "Create VNF: Unknown VNF Type: " + vnfType;
+					LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM, "VNF Type",
+							vnfType, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Create VNF: Unknown VNF Type");
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+					throw new VnfException(error, MsoExceptionCategory.USERDATA);
+				}
+				LOGGER.debug("Got VNF module definition from Catalog: "
+						+ vnfResource.toString());
+			}
+			// By here - we have either a vf or vnfResource
+
+            //1607 - Add version check
+            // First - see if it's in the VnfResource record
+            // if we have a vf Module - then we have to query to get the VnfResource record.
+            if (!oldWay) {
+            	if (vf.getVnfResourceId() != null) { 
+            		int vnfResourceId = vf.getVnfResourceId();
+            		vnfResource = db.getVnfResourceById(vnfResourceId);
+            		if (vnfResource == null) {
+            			LOGGER.debug("Unable to find vnfResource at " + vnfResourceId + " will not error for now...");
+            		}
+            	}
+            } 
+            String minVersionVnf = null;
+            String maxVersionVnf = null;
+            if (vnfResource != null) {
+            	try {
+            		minVersionVnf = vnfResource.getAicVersionMin();
+            		maxVersionVnf = vnfResource.getAicVersionMax();
+            	} catch (Exception e) {
+            		LOGGER.debug("Unable to pull min/max version for this VNF Resource entry");
+            		minVersionVnf = null;
+            		maxVersionVnf = null;
+            	}
+            	if (minVersionVnf != null && minVersionVnf.equals("")) {
+            		minVersionVnf = null;
+            	}
+            	if (maxVersionVnf != null && maxVersionVnf.equals("")) {
+            		maxVersionVnf = null;
+            	}
+            }
+			if (minVersionVnf != null && maxVersionVnf != null) {
+				MavenLikeVersioning aicV = new MavenLikeVersioning();
+				CloudSite cloudSite = null;
+				String aicVersion = "";
+				if (this.cloudConfig == null) {
+					this.cloudConfig = this.cloudConfigFactory.getCloudConfig();
+				}
+				// double check
+				if (this.cloudConfig != null) {
+					cloudSite = this.cloudConfig.getCloudSite(cloudSiteId);
+					if (cloudSite != null) {
+						aicV.setVersion(cloudSite.getAic_version());
+						if ((aicV.isMoreRecentThan(minVersionVnf) || aicV.isTheSameVersion(minVersionVnf)) // aic >= min
+								&& (aicV.isTheSameVersion(maxVersionVnf) || !(aicV.isMoreRecentThan(maxVersionVnf)))) { //aic <= max
+							LOGGER.debug("VNF Resource " + vnfResource.getVnfType() + " VersionMin=" + minVersionVnf + " VersionMax:" + maxVersionVnf + " supported on Cloud: " + cloudSite.getId() + " with AIC_Version:" + cloudSite.getAic_version());
+						} else {
+							// ERROR
+							String error = "VNF Resource type: " + vnfResource.getVnfType() + " VersionMin=" + minVersionVnf + " VersionMax:" + maxVersionVnf + " NOT supported on Cloud: " + cloudSite.getId() + " with AIC_Version:" + cloudSite.getAic_version();
+							LOGGER.error(MessageEnum.RA_CONFIG_EXC, error, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - setVersion");
+							LOGGER.debug(error);
+							throw new VnfException(error, MsoExceptionCategory.USERDATA);
+						}
+					} // let this error out downstream to avoid introducing uncertainty at this stage
+				} else {
+					LOGGER.debug("cloudConfig is NULL - cannot check cloud site version");
+				}
+
+			} else {
+				LOGGER.debug("AIC Version not set in VNF_Resource - this is expected thru 1607 - do not error here - not checked.");
+			}
+			// End Version check 1607
+
+            // with VF_MODULE - we have both the non-vol and vol template/envs in that object
+            // with VNF_RESOURCE - we use the old methods. 
+            Integer heatTemplateId = null;
+            Integer heatEnvtId = null;
+            
+			if (!oldWay) {
+				if (isVolumeRequest) {
+					heatTemplateId = vf.getVolTemplateId();
+					heatEnvtId = vf.getVolEnvironmentId();
+				} else {
+					heatTemplateId = vf.getTemplateId();
+					heatEnvtId = vf.getEnvironmentId();
+				}
+			} else {
+				if (isVolumeRequest) {
+					VnfComponent vnfComponent = null;
+					vnfComponent = db.getVnfComponent(vnfResource.getId(), "VOLUME");
+	            	if (vnfComponent == null) {
+	            		String error = "Create VNF: Cannot find VNF Component entry for: " + vnfType + ", type = VOLUME";
+	            		LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "VNF Type", vnfType, "OpenStack", "getVnfComponent", MsoLogger.ErrorCode.DataError, "Create VNF: Cannot find VNF Component entry");
+                        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+	            		throw new VnfException (error, MsoExceptionCategory.USERDATA);
+	            	} else {
+	            		heatTemplateId = vnfComponent.getHeatTemplateId();
+	            		heatEnvtId = vnfComponent.getHeatEnvironmentId();
+	            	}
+				} else {
+					heatTemplateId = vnfResource.getTemplateId();
+					heatEnvtId = vnfResource.getEnvironmentId();
+				}
+			}
+			// By the time we get here - heatTemplateId and heatEnvtId should be populated (or null)
+			HeatTemplate heatTemplate = null;
+			if (heatTemplateId == null) {
+				String error = "Create: No Heat Template ID defined in catalog database for " + vnfType + ", reqType=" + requestTypeString;
+				LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Template ID", vnfType, "OpenStack", "", 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);
+			} else {
+				heatTemplate = db.getHeatTemplate(heatTemplateId);
+			}
+			if (heatTemplate == null) {
+				String error = "Create VF/VNF: no entry found for heat template ID = " + heatTemplateId;
+				LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
+						"Heat Template ID",
+						String.valueOf(heatTemplateId), "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Create VF/VNF: no entry found for heat template ID = " + heatTemplateId);
+                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 from DB");
+            
+            HeatEnvironment heatEnvironment = null;
+            String heatEnvironmentString = null;
+
+            if (heatEnvtId != null && heatEnvtId != 0) {
+                LOGGER.debug ("about to call getHeatEnvironment with :" + heatEnvtId + ":");
+                heatEnvironment = db.getHeatEnvironment (heatEnvtId);
+                if (heatEnvironment == null) {
+                    String error = "Create VFModule: undefined Heat Environment. VFModule=" + vfModuleType
+                                   + ", Environment ID="
+                                   + heatEnvtId;
+                    LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", String.valueOf(heatEnvtId), "OpenStack", "getHeatEnvironment", 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);
+                } else {
+                    LOGGER.debug ("Got Heat Environment from DB: " + heatEnvironment.toString ());
+                    heatEnvironmentString = heatEnvironment.getEnvironment (); //this.parseEnvironment (heatEnvironment.getEnvironment ());
+                    LOGGER.debug ("after parsing: " + heatEnvironmentString);
+                }
+            } else {
+                LOGGER.debug ("no environment parameter found for this Type " + vfModuleType);
+            }
+            
+            // 1510 - Add the files: for nested templates *if* there are any
+            LOGGER.debug ("In MsoVnfAdapterImpl, createVfModule about to call db.getNestedTemplates avec templateId="
+                          + heatTemplate.getId ());
+            Map <String, Object> nestedTemplates = db.getNestedTemplates (heatTemplate.getId ());
+            Map <String, Object> nestedTemplatesChecked = new HashMap <String, Object> ();
+            if (nestedTemplates != null) {
+                // for debugging print them out
+                LOGGER.debug ("Contents of nestedTemplates - to be added to files: on stack:");
+                for (String providerResourceFile : nestedTemplates.keySet ()) {
+                    String providerResourceFileChecked = providerResourceFile; //this.enforceFilePrefix (providerResourceFile);
+                    String childTemplateBody = (String) nestedTemplates.get (providerResourceFile);
+                    LOGGER.debug (providerResourceFileChecked + " -> " + childTemplateBody);
+                    nestedTemplatesChecked.put (providerResourceFileChecked, childTemplateBody);
+                }
+            } else {
+                LOGGER.debug ("No nested templates found - nothing to do here");
+                nestedTemplatesChecked = null; // just to make sure
+            }
+
+            // 1510 - Also add the files: for any get_files associated with this vnf_resource_id
+            // *if* there are any
+            Map<String, HeatFiles> heatFiles = null;
+			Map<String, Object> heatFilesObjects = new HashMap<String, Object>();
+
+            // Add ability to turn on adding get_files with volume requests (by property).
+            boolean addGetFilesOnVolumeReq = false;
+            try {
+            	String propertyString = msoPropertiesFactory.getMsoJavaProperties(MSO_PROP_VNF_ADAPTER).getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ, null);
+            	if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
+            		addGetFilesOnVolumeReq = true;
+            		LOGGER.debug("AddGetFilesOnVolumeReq - setting to true! " + propertyString);
+            	}
+            } catch (Exception e) {
+            	LOGGER.debug("An error occured trying to get property " + MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ + " - default to false", e);
+            }
+
+			if (!isVolumeRequest || addGetFilesOnVolumeReq) {
+				if (oldWay) {
+					LOGGER.debug("In MsoVnfAdapterImpl createVfModule, about to call db.getHeatFiles avec vnfResourceId="
+							+ vnfResource.getId());
+					heatFiles = db.getHeatFiles(vnfResource.getId());
+				} else {
+					// 1607 - now use VF_MODULE_TO_HEAT_FILES table
+					LOGGER.debug("In MsoVnfAdapterImpl createVfModule, about to call db.getHeatFilesForVfModule avec vfModuleId="
+							+ vf.getId());
+					heatFiles = db
+							.getHeatFilesForVfModule(vf.getId());
+				}
+				if (heatFiles != null) {
+					// add these to stack - to be done in createStack
+					// here, we will map them to Map<String, Object> from
+					// Map<String, HeatFiles>
+					// this will match the nested templates format
+					LOGGER.debug("Contents of heatFiles - to be added to files: on stack:");
+
+					for (String heatFileName : heatFiles.keySet()) {
+						if (heatFileName.startsWith("_ERROR|")) {
+							// This means there was an invalid entry in VF_MODULE_TO_HEAT_FILES table - the heat file it pointed to could not be found.
+							String heatFileId = heatFileName.substring(heatFileName.lastIndexOf("|")+1);
+							String error = "Create: No HEAT_FILES entry in catalog database for " + vfModuleType + " at HEAT_FILES index=" + heatFileId;
+							LOGGER.debug(error);
+							LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "HEAT_FILES entry not found at " + heatFileId, vfModuleType, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "HEAT_FILES entry not found");
+                            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);
+						}
+						String heatFileBody = heatFiles.get(heatFileName)
+								.getFileBody();
+						String heatFileNameChecked = heatFileName;
+						LOGGER.debug(heatFileNameChecked + " -> "
+								+ heatFileBody);
+						heatFilesObjects.put(heatFileNameChecked, heatFileBody);
+					}
+				} else {
+					LOGGER.debug("No heat files found -nothing to do here");
+					heatFilesObjects = null;
+				}
+			} else {
+					LOGGER.debug("Volume request - DO NOT CHECK for HEAT_FILES");
+			}
+
+            // Check that required parameters have been supplied
+            String missingParams = null;
+            List <String> paramList = new ArrayList <String> ();
+
+            // New for 1510 - consult the PARAM_ALIAS field to see if we've been
+            // supplied an alias. Only check if we don't find it initially.
+            // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
+            // And also new - add parameter to turn off checking all together if we find we're blocking orders we
+            // shouldn't
+            boolean haveEnvironmentParameters = false;
+            boolean checkRequiredParameters = true;
+            try {
+                String propertyString = msoPropertiesFactory.getMsoJavaProperties (MSO_PROP_VNF_ADAPTER)
+                                                     .getProperty (MsoVnfAdapterImpl.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..."
+                                  + MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
+                }
+            } catch (Exception e) {
+                // No problem - default is true
+                LOGGER.debug ("An exception occured trying to get property " + MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
+            }
+            // 1604 - Add enhanced environment & parameter checking
+            // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
+            // Part 2: only submit to openstack the parameters in the envt that are in the heat template
+            // Note this also removes any comments
+            MsoHeatEnvironmentEntry mhee = null;
+            if (heatEnvironmentString != null && heatEnvironmentString.contains ("parameters:")) {
+                //LOGGER.debug ("Have an Environment argument with a parameters: section - will bypass checking for valid params - but will still check for aliases");
+            	LOGGER.debug("Enhanced environment checking enabled - 1604");
+                haveEnvironmentParameters = true;
+                StringBuilder sb = new StringBuilder(heatEnvironmentString);
+                //LOGGER.debug("About to create MHEE with " + sb);
+                mhee = new MsoHeatEnvironmentEntry(sb);
+                StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
+                for (HeatTemplateParam parm : heatTemplate.getParameters()) {
+                	sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
+                }
+                if (!mhee.isValid()) {
+                	sb2.append("Environment says it's not valid! " + mhee.getErrorString());
+                } else {
+                	sb2.append("\nEnvironment:");
+                	sb2.append(mhee.toFullString());
+                }
+                LOGGER.debug(sb2.toString());
+            } else {
+            	LOGGER.debug("NO ENVIRONMENT for this entry");
+            }
+            // This is kind of a mess. inputs is a Map<String, String> --
+            // if one of the parameters is json - we need to pass String, JsonNode -
+            // so we will store off the parameters that are json in its own HashMap
+            // if there are any json params - then we convert inputs to a Map<String, Object>
+            // and pass that to createStack
+            HashMap<String, JsonNode> jsonParams = new HashMap<String, JsonNode>();
+            boolean hasJson = false;
+
+            for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
+                LOGGER.debug ("Parameter:'" + parm.getParamName ()
+                              + "', isRequired="
+                              + parm.isRequired ()
+                              + ", alias="
+                              + parm.getParamAlias ());
+                // New 1607 - support json type
+                String parameterType = parm.getParamType();
+                if (parameterType == null || parameterType.trim().equals("")) {
+                	parameterType = "String";
+                }
+                JsonNode jsonNode = null;
+                if (parameterType.equalsIgnoreCase("json") && inputs != null) {
+                	if (inputs.containsKey(parm.getParamName()) ) {
+                		hasJson = true;
+                		String jsonString = null;
+                		try {
+                			jsonString = inputs.get(parm.getParamName());
+                			jsonNode = new ObjectMapper().readTree(jsonString);
+                		} catch (JsonParseException jpe) {
+                			//TODO - what to do here?
+                			//for now - send the error to debug, but just leave it as a String
+                			String errorMessage = jpe.getMessage();
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " - " + errorMessage);
+                			hasJson = false;
+                			jsonNode = null;
+                		} catch (Exception e) {
+                			// or here?
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " " + e.getMessage());
+                			hasJson = false;
+                			jsonNode = null;
+                		}
+                		if (jsonNode != null) {
+                			jsonParams.put(parm.getParamName(), jsonNode);
+                		}
+                	} else if (inputs.containsKey(parm.getParamAlias())) {
+                		hasJson = true;
+                		String jsonString = null;
+                   		try {
+                			jsonString = inputs.get(parm.getParamAlias());
+                			jsonNode = new ObjectMapper().readTree(jsonString);
+                		} catch (JsonParseException jpe) {
+                			//TODO - what to do here?
+                			//for now - send the error to debug, but just leave it as a String
+                			String errorMessage = jpe.getMessage();
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " - " + errorMessage);
+                			hasJson = false;
+                			jsonNode = null;
+                		} catch (Exception e) {
+                			// or here?
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " " + e.getMessage());
+                			hasJson = false;
+                			jsonNode = null;
+                		}
+                   		if (jsonNode != null) {
+                   			// Notice here - we add it to the jsonParams hashMap with the actual name -
+                   			// then manipulate the inputs so when we check for aliases below - it will not
+                   			// get flagged.
+                   			jsonParams.put(parm.getParamName(), jsonNode);
+                   			inputs.remove(parm.getParamAlias());
+                   			inputs.put(parm.getParamName(), jsonString);
+                   		}
+                	} //TODO add a check for the parameter in the env file
+                }
+                if (parm.isRequired () && (inputs == null || !inputs.containsKey (parm.getParamName ()))) {
+                	// Check if they have an alias
+                	LOGGER.debug("**Parameter " + parm.getParamName() + " is required and not in the inputs...");
+                    if (inputs.containsKey (parm.getParamAlias ())) {
+                        // They've submitted using an alias name. Remove that from inputs, and add back using real name.
+                        String realParamName = parm.getParamName ();
+                        String alias = parm.getParamAlias ();
+                        String value = inputs.get (alias);
+                        LOGGER.debug ("*Found an Alias: paramName=" + realParamName
+                                      + ",alias="
+                                      + alias
+                                      + ",value="
+                                      + value);
+                        inputs.remove (alias);
+                        inputs.put (realParamName, value);
+                        LOGGER.debug (alias + " entry removed from inputs, added back using " + realParamName);
+                    } 
+                    // enhanced - check if it's in the Environment (note: that method 
+                    else 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 ();
+                        }
+                    }
+                }
+                paramList.add (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, "OpenStack", "", 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 - but checkRequiredParameters is false - will not block");
+            	}
+            } else {
+                LOGGER.debug ("No missing parameters found - ok to proceed");
+            }
+            
+            // Here - modify heatEnvironmentString
+            StringBuilder parsedEnvironmentString = null; 
+            String newEnvironmentString = null;
+            if (mhee != null) {
+            	LOGGER.debug("Environment before:\n" + heatEnvironmentString);
+            	parsedEnvironmentString = mhee.toFullStringExcludeNonParams(heatTemplate.getParameters());
+            	LOGGER.debug("Environment after:\n" + parsedEnvironmentString.toString());
+            	newEnvironmentString = parsedEnvironmentString.toString();
+            }
+
+            // Remove any extraneous parameters (don't throw an error)
+            if (inputs != null) {
+                List <String> extraParams = new ArrayList <String> ();
+                extraParams.addAll (inputs.keySet ());
+                extraParams.removeAll (paramList);
+                if (!extraParams.isEmpty ()) {
+                    LOGGER.warn (MessageEnum.RA_VNF_EXTRA_PARAM, vnfType, extraParams.toString(), "OpenStack", "", MsoLogger.ErrorCode.DataError, "Extra params");
+                    inputs.keySet ().removeAll (extraParams);
+                }
+            }
+            // 1607 - when we get here - we have clean inputs. Check if we have
+            Map<String, Object> inputsTwo = null;
+            if (hasJson && jsonParams.size() > 0) {
+            	inputsTwo = new HashMap<String, Object>();
+            	for (String keyParamName : inputs.keySet()) {
+            		if (jsonParams.containsKey(keyParamName)) {
+            			inputsTwo.put(keyParamName, jsonParams.get(keyParamName));
+            		} else {
+            			inputsTwo.put(keyParamName, inputs.get(keyParamName));
+            		}
+            	}
+            }
+
+            // "Fix" the template if it has CR/LF (getting this from Oracle)
+            String template = heatTemplate.getHeatTemplate ();
+            template = template.replaceAll ("\r\n", "\n");
+
+            // Have the tenant. Now deploy the stack itself
+            // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
+            // because we already checked for those.
+            long createStackStarttime = System.currentTimeMillis ();
+            try {
+                // heatStack = heat.createStack(cloudSiteId, tenantId, vnfName, template, inputs, true,
+                // heatTemplate.getTimeoutMinutes());
+            	if (backout == null) {
+            		backout = true;
+            	}
+            	if (heat != null) {
+            		LOGGER.debug("heat is not null!!");
+            	}
+
+            	if (!hasJson) {
+            		heatStack = heat.createStack (cloudSiteId,
+                                              tenantId,
+                                              vfModuleName,
+                                              template,
+                                              inputs,
+                                              true,
+                                              heatTemplate.getTimeoutMinutes (),
+                                              newEnvironmentString,
+                                              //heatEnvironmentString,
+                                              nestedTemplatesChecked,
+                                              heatFilesObjects,
+                                              backout.booleanValue());
+                LOGGER.recordMetricEvent (createStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "CreateStack", vfModuleName);
+            	} else {
+            		heatStack = heat.createStack (cloudSiteId,
+                                              tenantId,
+                                              vfModuleName,
+                                              template,
+                                              inputsTwo,
+                                              true,
+                                              heatTemplate.getTimeoutMinutes (),
+                                              newEnvironmentString,
+                                              //heatEnvironmentString,
+                                              nestedTemplatesChecked,
+                                              heatFilesObjects,
+                                              backout.booleanValue());
+
+            	}
+                LOGGER.recordMetricEvent (createStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "CreateStack", vfModuleName);
+            } catch (MsoException me) {
+                me.addContext ("CreateVFModule");
+                String error = "Create VF Module " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+                LOGGER.recordMetricEvent (createStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "CreateStack", vfModuleName);
+                LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "MsoException - createStack", me);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+                throw new VnfException (me);
+            } catch (NullPointerException npe) {
+                String error = "Create VFModule " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + npe;
+                LOGGER.recordMetricEvent (createStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "CreateStack", vfModuleName);
+                LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "NullPointerException - createStack", npe);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+                LOGGER.debug("NULL POINTER EXCEPTION at heat.createStack");
+                //npe.addContext ("CreateVNF");
+                throw new VnfException ("NullPointerException during heat.createStack");
+            } catch (Exception e) {
+                LOGGER.recordMetricEvent (createStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while creating stack with OpenStack", "OpenStack", "CreateStack", vfModuleName);
+                LOGGER.debug("unhandled exception at heat.createStack");
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while creating stack with OpenStack");
+            	throw new VnfException("Exception during heat.createStack! " + e.getMessage());
+            }
+        } catch (Exception e) {
+        	LOGGER.debug("unhandled exception in create VF");
+        	throw new VnfException("Exception during create VF " + e.getMessage());
+        	
+        }
+
+        // Reach this point if createStack is successful.
+        // Populate remaining rollback info and response parameters.
+        vfRollback.setVnfId (heatStack.getCanonicalName ());
+        vfRollback.setVnfCreated (true);
+
+        vnfId.value = heatStack.getCanonicalName ();
+        outputs.value = copyStringOutputs (heatStack.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 vnfName,
+                           MsoRequest msoRequest) throws VnfException {
+        MsoLogger.setLogContext (msoRequest);
+    	MsoLogger.setServiceName ("DeleteVf");
+        LOGGER.debug ("Deleting VF " + vnfName + " in " + cloudSiteId + "/" + tenantId);
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+
+        MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+
+        // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
+        // The possible outcomes of deleteStack are a StackInfo object with status
+        // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
+        // could be thrown.
+        long subStartTime = System.currentTimeMillis ();
+        try {
+            heat.deleteStack (tenantId, cloudSiteId, vnfName, true);
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "DeleteStack", vnfName);
+        } catch (MsoException me) {
+            me.addContext ("DeleteVNF");
+            // Failed to query the Stack due to an openstack exception.
+            // Convert to a generic VnfException
+            String error = "Delete VF: " + vnfName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+            LOGGER.recordMetricEvent (subStartTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "DeleteStack", vnfName);
+            LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, vnfName, cloudSiteId, tenantId, "OpenStack", "DeleteStack", MsoLogger.ErrorCode.DataError, "Exception - deleteStack", me);
+            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;
+    }
+
+    @Override
+    public void updateVfModule (String cloudSiteId,
+                           String tenantId,
+                           String vnfType,
+                           String vnfVersion,
+                           String vnfName,
+                           String requestType,
+                           String volumeGroupHeatStackId,
+                           String baseVfHeatStackId,
+                           String vfModuleStackId,
+                           Map <String, String> inputs,
+                           MsoRequest msoRequest,
+                           Holder <Map <String, String>> outputs,
+                           Holder <VnfRollback> rollback) throws VnfException {
+    	String vfModuleName = vnfName;
+    	String vfModuleType = vnfType;
+    	String vfVersion = vnfVersion;
+    	String methodName = "updateVfModule";
+    	MsoLogger.setLogContext (msoRequest.getRequestId (), msoRequest.getServiceInstanceId ());
+    	String serviceName = VNF_ADAPTER_SERVICE_NAME + methodName;
+    	MsoLogger.setServiceName (serviceName);
+
+    	String requestTypeString = "";
+        if (requestType != null && !requestType.equals("")) {
+        	requestTypeString = requestType;
+        }
+        String nestedStackId = null;
+        if (volumeGroupHeatStackId != null && !volumeGroupHeatStackId.equals("")) {
+        	if (!volumeGroupHeatStackId.equalsIgnoreCase("null")) {
+        		nestedStackId = volumeGroupHeatStackId;
+        	}
+        }
+        String nestedBaseStackId = null;
+        if (baseVfHeatStackId != null && !baseVfHeatStackId.equals("")) {
+        	if (!baseVfHeatStackId.equalsIgnoreCase("null")) {
+        		nestedBaseStackId = baseVfHeatStackId;
+        	}
+        }
+
+        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);
+        }
+        boolean isBaseRequest = false;
+        boolean isVolumeRequest = false;
+        if (requestTypeString.startsWith("VOLUME")) {
+        	isVolumeRequest = true;
+        }
+        if (vfModuleName == null || vfModuleName.trim().equals("")) {
+        	if (vfModuleStackId != null) {
+        		vfModuleName = this.getVfModuleNameFromModuleStackId(vfModuleStackId);
+        	}
+        }
+
+        LOGGER.debug ("Updating VFModule: " + vfModuleName + " of type " + vfModuleType + "in " + cloudSiteId + "/" + tenantId);
+        LOGGER.debug("requestTypeString = " + requestTypeString + ", nestedStackId = " + nestedStackId + ", nestedBaseStackId = " + nestedBaseStackId);
+
+        // Will capture execution time for metrics
+        long startTime = System.currentTimeMillis ();
+
+        // Build a default rollback object (no actions performed)
+        VnfRollback vfRollback = new VnfRollback ();
+        vfRollback.setCloudSiteId (cloudSiteId);
+        vfRollback.setTenantId (tenantId);
+        vfRollback.setMsoRequest (msoRequest);
+        vfRollback.setRequestType(requestTypeString);
+        vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
+        vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
+        vfRollback.setIsBase(isBaseRequest);
+        vfRollback.setVfModuleStackId(vfModuleStackId);
+
+        // First, look up to see if the VNF already exists.
+        MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+        MsoHeatUtilsWithUpdate heatU = new MsoHeatUtilsWithUpdate (MSO_PROP_VNF_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
+
+        StackInfo heatStack = null;
+        long queryStackStarttime = System.currentTimeMillis ();
+        LOGGER.debug("UpdateVfModule - querying for " + vfModuleName);
+        try {
+            heatStack = heat.queryStack (cloudSiteId, tenantId, vfModuleName);
+            LOGGER.recordMetricEvent (queryStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully receive response from Open Stack", "OpenStack", "QueryStack", null);
+        } catch (MsoException me) {
+            // Failed to query the Stack due to an openstack exception.
+            // Convert to a generic VnfException
+            me.addContext ("UpdateVFModule");
+            String error = "Update VFModule: Query " + vfModuleName + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+            LOGGER.recordMetricEvent (queryStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", null);
+            LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Exception - QueryStack", me);
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+            throw new VnfException (me);
+        }
+
+        //TODO - do we need to check for the other status possibilities?
+        if (heatStack == null || heatStack.getStatus () == HeatStatus.NOTFOUND) {
+            // Not Found
+            String error = "Update VF: Stack " + vfModuleName + " does not exist in " + cloudSiteId + "/" + tenantId;
+            LOGGER.error (MessageEnum.RA_VNF_NOT_EXIST, vfModuleName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, error);
+            LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+            throw new VnfNotFound (cloudSiteId, tenantId, vfModuleName);
+        } else {
+            LOGGER.debug ("Found Existing stack, status=" + heatStack.getStatus ());
+            // Populate the outputs from the existing stack.
+            outputs.value = copyStringOutputs (heatStack.getOutputs ());
+            rollback.value = vfRollback; // Default rollback - no updates performed
+        }
+
+        // 1604 Cinder Volume support - handle a nestedStackId if sent (volumeGroupHeatStackId):
+        StackInfo nestedHeatStack = null;
+        long queryStackStarttime2 = System.currentTimeMillis ();
+        if (nestedStackId != null) {
+        	try {
+        		LOGGER.debug("Querying for nestedStackId = " + nestedStackId);
+        		nestedHeatStack = heat.queryStack(cloudSiteId, tenantId, nestedStackId);
+                LOGGER.recordMetricEvent (queryStackStarttime2, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully receive response from Open Stack", "OpenStack", "QueryStack", null);
+        	} catch (MsoException me) {
+        	    // Failed to query the Stack due to an openstack exception.
+        	    // Convert to a generic VnfException
+        	    me.addContext ("UpdateVFModule");
+        	    String error = "Update VF: Attached heatStack ID Query " + nestedStackId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+                LOGGER.recordMetricEvent (queryStackStarttime2, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", null);
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Exception - " + error, me);
+        		LOGGER.debug("ERROR trying to query nested stack= " + error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+        	    throw new VnfException (me);
+        	}
+        	if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
+        		MsoLogger.setServiceName (serviceName);
+        	    String error = "Update VFModule: Attached volume heatStack ID DOES NOT EXIST " + nestedStackId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vnfName, cloudSiteId, tenantId, error, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, error);
+        	    LOGGER.debug(error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+        	    throw new VnfException (error, MsoExceptionCategory.USERDATA);
+        	} else {
+        		LOGGER.debug("Found nested heat stack - copying values to inputs");
+        		this.sendMapToDebug(inputs);
+        		heat.copyStringOutputsToInputs(inputs, nestedHeatStack.getOutputs(), false);
+        		this.sendMapToDebug(inputs);
+        	}
+        }
+        // handle a nestedBaseStackId if sent - this is the stack ID of the base.
+        StackInfo nestedBaseHeatStack = null;
+        if (nestedBaseStackId != null) {
+            long queryStackStarttime3 = System.currentTimeMillis ();
+        	try {
+        		LOGGER.debug("Querying for nestedBaseStackId = " + nestedBaseStackId);
+        		nestedBaseHeatStack = heat.queryStack(cloudSiteId, tenantId, nestedBaseStackId);
+                LOGGER.recordMetricEvent (queryStackStarttime3, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully receive response from Open Stack", "OpenStack", "QueryStack", null);
+        	} catch (MsoException me) {
+        	    // Failed to query the Stack due to an openstack exception.
+        	    // Convert to a generic VnfException
+        	    me.addContext ("UpdateVfModule");
+        	    String error = "Update VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in " + cloudSiteId + "/" + tenantId + ": " + me ;
+                LOGGER.recordMetricEvent (queryStackStarttime3, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", null);
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, "Exception - " + error, me);
+        		LOGGER.debug("ERROR trying to query nested base stack= " + error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+        	    throw new VnfException (me);
+        	}
+        	if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
+        		MsoLogger.setServiceName (serviceName);
+        	    String error = "Update VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId + " in " + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
+        	    LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR, vfModuleName, cloudSiteId, tenantId, error, "OpenStack", "QueryStack", MsoLogger.ErrorCode.DataError, error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+        	    LOGGER.debug(error);
+        	    throw new VnfException (error, MsoExceptionCategory.USERDATA);
+        	} else {
+        		LOGGER.debug("Found nested base heat stack - copying values to inputs");
+        		this.sendMapToDebug(inputs);
+        		heat.copyStringOutputsToInputs(inputs, nestedBaseHeatStack.getOutputs(), false);
+        		this.sendMapToDebug(inputs);
+        	}
+        }
+
+        // Ready to deploy the new VNF
+
+        try (CatalogDatabase db = new CatalogDatabase ()) {
+            // Retrieve the VF definition
+            //VnfResource vnf;
+        	VfModule vf = null;
+            if (vfVersion != null && !vfVersion.isEmpty ()) {
+            	vf = db.getVfModuleType(vfModuleType, vfVersion);
+            	if (vf == null) {
+            		LOGGER.debug("Unable to find " + vfModuleType + " and version = " + vfVersion + " in the TYPE column - will try in MODEL_NAME");
+            		vf = db.getVfModuleModelName(vfModuleType, vfVersion);
+            		if (vf == null) {
+            			LOGGER.debug("Unable to find " + vfModuleType + " and version = " + vfVersion + " in the MODEL_NAME field either - ERROR");
+            		}
+            	}
+            } else {
+                vf = db.getVfModuleType(vfModuleType);
+            	if (vf == null) {
+            		LOGGER.debug("Unable to find " + vfModuleType + " in the TYPE column - will try in MODEL_NAME");
+            		vf = db.getVfModuleModelName(vfModuleType);
+            		if (vf == null) {
+            			LOGGER.debug("Unable to find " + vfModuleType + " in the MODEL_NAME field either - ERROR");
+            		}
+            	}
+            }
+            if (vf == null) {
+                String error = "Update VFModule: Unknown VF Module Type: " + vfModuleType;
+                if (vfVersion != null && !vfVersion.isEmpty()) {
+                	error += " with version = " + vfVersion;
+                }
+                LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "VF Module Type", vfModuleType, "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataError, error);
+                throw new VnfException (error, MsoExceptionCategory.USERDATA);
+            }
+            LOGGER.debug ("Got VF module definition from Catalog: " + vf.toString ());
+            
+            HeatTemplate heatTemplate = null;
+            Integer heatTemplateId = null;
+            Integer heatEnvtId = null;
+			if (!isVolumeRequest) {
+				heatTemplateId = vf.getTemplateId();
+				heatEnvtId = vf.getEnvironmentId();
+			} else {
+				heatTemplateId = vf.getVolTemplateId();
+				heatEnvtId = vf.getVolEnvironmentId();
+			}
+			if (heatTemplateId == null) {
+				String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType=" + requestTypeString;
+				LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Template ID", vfModuleType, "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
+				alarmLogger.sendAlarm(MSO_CONFIGURATION_ERROR,
+						MsoAlarmLogger.CRITICAL, error);
+				throw new VnfException(error, MsoExceptionCategory.INTERNAL);
+			} else {
+				heatTemplate = db.getHeatTemplate(heatTemplateId);
+			}
+
+			if (heatTemplate == null) {
+				String error = "Update VNF: undefined Heat Template. VF="
+						+ vfModuleType + ", heat template id = " + heatTemplateId;
+				LOGGER.error(MessageEnum.RA_VNF_UNKNOWN_PARAM,
+						"Heat Template ID",
+						String.valueOf(heatTemplateId), "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
+                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 Template from DB: " + heatTemplate.toString ());
+
+            // Add check for any Environment variable
+            HeatEnvironment heatEnvironment = null;
+            String heatEnvironmentString = null;
+
+            if (heatEnvtId != null) {
+                LOGGER.debug ("about to call getHeatEnvironment with :" + heatEnvtId + ":");
+                heatEnvironment = db.getHeatEnvironment (heatEnvtId);
+                if (heatEnvironment == null) {
+
+                    String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType
+                                   + ", Environment ID="
+                                   + heatEnvtId;
+                    LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "Heat Environment ID", String.valueOf(heatEnvtId), "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
+                    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);
+                } else {
+                    LOGGER.debug ("Got Heat Environment from DB: " + heatEnvironment.toString ());
+                    heatEnvironmentString = heatEnvironment.getEnvironment (); //this.parseEnvironment (heatEnvironment.getEnvironment ());
+                    LOGGER.debug ("After parsing: " + heatEnvironmentString);
+                }
+            } else {
+                LOGGER.debug ("no environment parameter for this VFModuleType " + vfModuleType);
+            }
+
+
+            LOGGER.debug ("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId="
+                          + heatTemplate.getId ());
+            Map <String, Object> nestedTemplates = db.getNestedTemplates (heatTemplate.getId ());
+            Map <String, Object> nestedTemplatesChecked = new HashMap <String, Object> ();
+            if (nestedTemplates != null) {
+                // for debugging print them out
+                LOGGER.debug ("Contents of nestedTemplates - to be added to files: on stack:");
+                for (String providerResourceFile : nestedTemplates.keySet ()) {
+                    String providerResourceFileChecked = providerResourceFile; //this.enforceFilePrefix (providerResourceFile);
+                    String childTemplateBody = (String) nestedTemplates.get (providerResourceFile);
+                    nestedTemplatesChecked.put (providerResourceFileChecked, childTemplateBody);
+                    LOGGER.debug (providerResourceFileChecked + " -> " + childTemplateBody);
+                }
+            } else {
+                LOGGER.debug ("No nested templates found - nothing to do here");
+                nestedTemplatesChecked = null;
+            }
+
+            // Also add the files: for any get_files associated with this VfModule
+            // *if* there are any
+            LOGGER.debug ("In MsoVnfAdapterImpl.updateVfModule, about to call db.getHeatFiles avec vfModuleId="
+                          + vf.getId ());
+
+            Map <String, HeatFiles> heatFiles = null;
+//            Map <String, HeatFiles> heatFiles = db.getHeatFiles (vnf.getId ());
+            Map <String, Object> heatFilesObjects = new HashMap <String, Object> ();
+
+            // Add ability to turn on adding get_files with volume requests (by property).
+            boolean addGetFilesOnVolumeReq = false;
+            try {
+            	String propertyString = msoPropertiesFactory.getMsoJavaProperties(MSO_PROP_VNF_ADAPTER).getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ, null);
+            	if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
+            		addGetFilesOnVolumeReq = true;
+            		LOGGER.debug("AddGetFilesOnVolumeReq - setting to true! " + propertyString);
+            	}
+            } catch (Exception e) {
+            	LOGGER.debug("An error occured trying to get property " + MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ + " - default to false", e);
+            }
+            if (!isVolumeRequest || addGetFilesOnVolumeReq) {
+            	heatFiles = db.getHeatFilesForVfModule(vf.getId());
+                if (heatFiles != null) {
+                    // add these to stack - to be done in createStack
+                    // here, we will map them to Map<String, Object> from Map<String, HeatFiles>
+                    // this will match the nested templates format
+                    LOGGER.debug ("Contents of heatFiles - to be added to files: on stack:");
+
+                    for (String heatFileName : heatFiles.keySet ()) {
+						if (heatFileName.startsWith("_ERROR|")) {
+							// This means there was an invalid entry in VF_MODULE_TO_HEAT_FILES table - the heat file it pointed to could not be found.
+							String heatFileId = heatFileName.substring(heatFileName.lastIndexOf("|")+1);
+							String error = "Create: No HEAT_FILES entry in catalog database for " + vfModuleType + " at HEAT_FILES index=" + heatFileId;
+							LOGGER.debug(error);
+							LOGGER.error (MessageEnum.RA_VNF_UNKNOWN_PARAM, "HEAT_FILES entry not found at " + heatFileId, vfModuleType, "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
+                            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);
+						}
+                        String heatFileBody = heatFiles.get (heatFileName).getFileBody ();
+                        LOGGER.debug (heatFileName + " -> " + heatFileBody);
+                        heatFilesObjects.put (heatFileName, heatFileBody);
+                    }
+                } else {
+                    LOGGER.debug ("No heat files found -nothing to do here");
+                    heatFilesObjects = null;
+                }
+            }
+
+            // Check that required parameters have been supplied
+            String missingParams = null;
+            List <String> paramList = new ArrayList <String> ();
+
+            // New for 1510 - consult the PARAM_ALIAS field to see if we've been
+            // supplied an alias. Only check if we don't find it initially.
+            // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
+            // And also new - add parameter to turn off checking all together if we find we're blocking orders we
+            // shouldn't
+            boolean haveEnvironmentParameters = false;
+            boolean checkRequiredParameters = true;
+            try {
+                String propertyString = msoPropertiesFactory.getMsoJavaProperties (MSO_PROP_VNF_ADAPTER)
+                                                     .getProperty (MsoVnfAdapterImpl.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..."
+                                  + MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
+                }
+            } catch (Exception e) {
+                // No problem - default is true
+                LOGGER.debug ("An exception occured trying to get property " + MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
+            }
+            // 1604 - Add enhanced environment & parameter checking
+            // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
+            // Part 2: only submit to openstack the parameters in the envt that are in the heat template
+            // Note this also removes any comments
+            MsoHeatEnvironmentEntry mhee = null;
+            if (heatEnvironmentString != null && heatEnvironmentString.toLowerCase ().contains ("parameters:")) {
+            	LOGGER.debug("Enhanced environment checking enabled - 1604");
+                haveEnvironmentParameters = true;
+                StringBuilder sb = new StringBuilder(heatEnvironmentString);
+                //LOGGER.debug("About to create MHEE with " + sb);
+                mhee = new MsoHeatEnvironmentEntry(sb);
+                StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
+                for (HeatTemplateParam parm : heatTemplate.getParameters()) {
+                	sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
+                }
+                if (!mhee.isValid()) {
+                	sb2.append("Environment says it's not valid! " + mhee.getErrorString());
+                } else {
+                	sb2.append("\nEnvironment:");
+                	sb2.append(mhee.toFullString());
+                }
+                LOGGER.debug(sb2.toString());
+            } else {
+            	LOGGER.debug("NO ENVIRONMENT for this entry");
+            }
+
+            // New for 1607 - support params of json type
+            HashMap<String, JsonNode> jsonParams = new HashMap<String, JsonNode>();
+            boolean hasJson = false;
+            
+            for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
+                LOGGER.debug ("Parameter:'" + parm.getParamName ()
+                              + "', isRequired="
+                              + parm.isRequired ()
+                              + ", alias="
+                              + parm.getParamAlias ());
+                // handle json
+                String parameterType = parm.getParamType();
+                if (parameterType == null || parameterType.trim().equals("")) {
+                	parameterType = "String";
+                }
+                JsonNode jsonNode = null;
+                if (parameterType.equalsIgnoreCase("json") && inputs != null) {
+                	if (inputs.containsKey(parm.getParamName()) ) {
+                		hasJson = true;
+                		String jsonString = null;
+                		try {
+                			jsonString = inputs.get(parm.getParamName());
+                			jsonNode = new ObjectMapper().readTree(jsonString);
+                		} catch (JsonParseException jpe) {
+                			//TODO - what to do here?
+                			//for now - send the error to debug, but just leave it as a String
+                			String errorMessage = jpe.getMessage();
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " - " + errorMessage);
+                			hasJson = false;
+                			jsonNode = null;
+                		} catch (Exception e) {
+                			// or here?
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " " + e.getMessage());
+                			hasJson = false;
+                			jsonNode = null;
+                		}
+                		if (jsonNode != null) {
+                			jsonParams.put(parm.getParamName(), jsonNode);
+                		}
+                	} else if (inputs.containsKey(parm.getParamAlias())) {
+                		hasJson = true;
+                		String jsonString = null;
+                   		try {
+                			jsonString = inputs.get(parm.getParamAlias());
+                			jsonNode = new ObjectMapper().readTree(jsonString);
+                		} catch (JsonParseException jpe) {
+                			//TODO - what to do here?
+                			//for now - send the error to debug, but just leave it as a String
+                			String errorMessage = jpe.getMessage();
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " - " + errorMessage);
+                			hasJson = false;
+                			jsonNode = null;
+                		} catch (Exception e) {
+                			// or here?
+                			LOGGER.debug("Json Error Converting " + parm.getParamName() + " " + e.getMessage());
+                			hasJson = false;
+                			jsonNode = null;
+                		}
+                   		if (jsonNode != null) {
+                   			// Notice here - we add it to the jsonParams hashMap with the actual name -
+                   			// then manipulate the inputs so when we check for aliases below - it will not
+                   			// get flagged.
+                   			jsonParams.put(parm.getParamName(), jsonNode);
+                   			inputs.remove(parm.getParamAlias());
+                   			inputs.put(parm.getParamName(), jsonString);
+                   		}
+                	} //TODO add a check for the parameter in the env file
+                }
+
+                if (parm.isRequired () && (inputs == null || !inputs.containsKey (parm.getParamName ()))) {
+                    if (inputs.containsKey (parm.getParamAlias ())) {
+                        // They've submitted using an alias name. Remove that from inputs, and add back using real name.
+                        String realParamName = parm.getParamName ();
+                        String alias = parm.getParamAlias ();
+                        String value = inputs.get (alias);
+                        LOGGER.debug ("*Found an Alias: paramName=" + realParamName
+                                      + ",alias="
+                                      + alias
+                                      + ",value="
+                                      + value);
+                        inputs.remove (alias);
+                        inputs.put (realParamName, value);
+                        LOGGER.debug (alias + " entry removed from inputs, added back using " + realParamName);
+                    }
+                    // enhanced - check if it's in the Environment (note: that method
+                    else 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 ();
+                        }
+                    }
+                }
+                paramList.add (parm.getParamName ());
+            }
+            if (missingParams != null) {
+                // Problem - missing one or more required parameters
+            	if (checkRequiredParameters) {
+                String error = "Update VNF: Missing Required inputs: " + missingParams;
+                LOGGER.error (MessageEnum.RA_MISSING_PARAM, missingParams, "OpenStack", "", MsoLogger.ErrorCode.DataError, error);
+                    LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
+                throw new VnfException (error, MsoExceptionCategory.USERDATA);
+            	} else {
+            		LOGGER.debug ("found missing parameters - but checkRequiredParameters is false - will not block");
+            	}
+            } else {
+                LOGGER.debug ("No missing parameters found - ok to proceed");
+            }
+
+            // Here - modify heatEnvironmentString
+            StringBuilder parsedEnvironmentString = null;
+            String newEnvironmentString = null;
+            if (mhee != null) {
+            	LOGGER.debug("Environment before:\n" + heatEnvironmentString);
+            	parsedEnvironmentString = mhee.toFullStringExcludeNonParams(heatTemplate.getParameters());
+            	LOGGER.debug("Environment after:\n" + parsedEnvironmentString.toString());
+            	newEnvironmentString = parsedEnvironmentString.toString();
+            }
+
+            // Remove any extraneous parameters (don't throw an error)
+            if (inputs != null) {
+                List <String> extraParams = new ArrayList <String> ();
+                extraParams.addAll (inputs.keySet ());
+                // This is not a valid parameter for this template
+                extraParams.removeAll (paramList);
+                if (!extraParams.isEmpty ()) {
+                	LOGGER.warn (MessageEnum.RA_VNF_EXTRA_PARAM, vnfType, extraParams.toString(), "OpenStack", "", MsoLogger.ErrorCode.DataError, "Extra params");
+                    inputs.keySet ().removeAll (extraParams);
+                }
+            }
+            // 1607 - when we get here - we have clean inputs. Create inputsTwo in case we have json
+            Map<String, Object> inputsTwo = null;
+            if (hasJson && jsonParams.size() > 0) {
+            	inputsTwo = new HashMap<String, Object>();
+            	for (String keyParamName : inputs.keySet()) {
+            		if (jsonParams.containsKey(keyParamName)) {
+            			inputsTwo.put(keyParamName, jsonParams.get(keyParamName));
+            		} else {
+            			inputsTwo.put(keyParamName, inputs.get(keyParamName));
+            		}
+            	}
+            }
+
+            // "Fix" the template if it has CR/LF (getting this from Oracle)
+            String template = heatTemplate.getHeatTemplate ();
+            template = template.replaceAll ("\r\n", "\n");
+
+            // Have the tenant. Now deploy the stack itself
+            // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
+            // because we already checked for those.
+            long updateStackStarttime = System.currentTimeMillis ();
+            try {
+            	if (!hasJson) {
+            		heatStack = heatU.updateStack (cloudSiteId,
+                                               tenantId,
+                                               vfModuleName,
+                                               template,
+                                               copyStringInputs (inputs),
+                                               true,
+                                               heatTemplate.getTimeoutMinutes (),
+                                               newEnvironmentString,
+                                               //heatEnvironmentString,
+                                               nestedTemplatesChecked,
+                                               heatFilesObjects);
+            		LOGGER.recordMetricEvent (updateStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully receive response from Open Stack", "OpenStack", "UpdateStack", null);
+            	} else {
+            		heatStack = heatU.updateStack (cloudSiteId,
+                                               tenantId,
+                                               vfModuleName,
+                                               template,
+                                               inputsTwo,
+                                               true,
+                                               heatTemplate.getTimeoutMinutes (),
+                                               newEnvironmentString,
+                                               //heatEnvironmentString,
+                                               nestedTemplatesChecked,
+                                               heatFilesObjects);
+            		LOGGER.recordMetricEvent (updateStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully receive response from Open Stack", "OpenStack", "UpdateStack", null);
+            		
+            	}
+            } catch (MsoException me) {
+                me.addContext ("UpdateVFModule");
+                String error = "Update VFModule " + vfModuleType + " in " + cloudSiteId + "/" + tenantId + ": " + me;
+                LOGGER.recordMetricEvent (updateStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "UpdateStack", null);
+                LOGGER.error (MessageEnum.RA_UPDATE_VNF_ERR, vfModuleType, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception - " + error, me);
+                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
+                throw new VnfException (me);
+            }
+        }
+
+        // Reach this point if updateStack is successful.
+        // Populate remaining rollback info and response parameters.
+        vfRollback.setVnfId (heatStack.getCanonicalName ());
+        vfRollback.setVnfCreated (true);
+
+        outputs.value = copyStringOutputs (heatStack.getOutputs ());
+        rollback.value = vfRollback;
+        LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully update VF Module");
+        return;
+    }
+
+    private String getVfModuleNameFromModuleStackId(String vfModuleStackId) {
+    	// expected format of vfModuleStackId is "MSOTEST51-vSAMP3_base_module-0/1fc1f86c-7b35-447f-99a6-c23ec176ae24"
+    	// before the "/" is the vfModuleName and after the "/" is the heat stack id in Openstack
+    	if (vfModuleStackId == null)
+    		return null;
+    	int index = vfModuleStackId.lastIndexOf('/');
+    	if (index <= 0) 
+    		return null;
+    	String vfModuleName = null;
+    	try {
+    		vfModuleName = vfModuleStackId.substring(0, index);
+    	} catch (Exception e) {
+    		vfModuleName = null;
+    	}
+    	return vfModuleName;
+    }
+
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VfRollback.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VfRollback.java
new file mode 100644
index 0000000..90ab01b
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VfRollback.java
@@ -0,0 +1,137 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+
+import org.openecomp.mso.entity.MsoRequest;
+
+public class VfRollback {
+	private String vnfId;
+	private String tenantId;
+	private String cloudSiteId;
+	private boolean tenantCreated = false;
+	private boolean vnfCreated = false;
+	private MsoRequest msoRequest;
+	private String volumeGroupName;
+	private String volumeGroupId;
+	private String requestType;
+	private String volumeGroupHeatStackId;
+	private String baseGroupHeatStackId;
+	private boolean isBase = false;
+	private String vfModuleStackId;
+
+
+	public String getVnfId() {
+		return vnfId;
+	}
+	public void setVnfId(String vnfId) {
+		this.vnfId = vnfId;
+	}
+	public String getTenantId() {
+		return tenantId;
+	}
+
+	public void setTenantId(String tenantId) {
+		this.tenantId = tenantId;
+	}
+	public String getCloudSiteId() {
+		return cloudSiteId;
+	}
+	public void setCloudSiteId(String cloudId) {
+		this.cloudSiteId = cloudId;
+	}
+	public boolean getTenantCreated() {
+		return tenantCreated;
+	}
+	public void setTenantCreated(boolean tenantCreated) {
+		this.tenantCreated = tenantCreated;
+	}
+	public boolean getVnfCreated() {
+		return vnfCreated;
+	}
+	public void setVnfCreated(boolean vnfCreated) {
+		this.vnfCreated = vnfCreated;
+	}
+	public MsoRequest getMsoRequest() {
+		return msoRequest;
+	}
+	public void setMsoRequest (MsoRequest msoRequest) {
+		this.msoRequest = msoRequest;
+	}
+	public String getVolumeGroupName() {
+		return this.volumeGroupName;
+	}
+	public void setVolumeGroupName(String volumeGroupName) {
+		this.volumeGroupName = volumeGroupName;
+	}
+	public String getVolumeGroupId() {
+		return this.volumeGroupId;
+	}
+	public void setVolumeGroupId(String volumeGroupId) {
+		this.volumeGroupId = volumeGroupId;
+	}
+	public String getRequestType() {
+		return this.requestType;
+	}
+	public void setRequestType(String requestType) {
+		this.requestType = requestType;
+	}
+	/*
+	private String volumeGroupHeatStackId;
+	private String baseGroupHeatStackId;
+	private boolean isBase = false;
+	*/
+	public String getVolumeGroupHeatStackId() {
+		return this.volumeGroupHeatStackId;
+	}
+	public void setVolumeGroupHeatStackId(String volumeGroupHeatStackId) {
+		this.volumeGroupHeatStackId = volumeGroupHeatStackId;
+	}
+	
+	public String getBaseGroupHeatStackId() {
+		return this.baseGroupHeatStackId;
+	}
+	public void setBaseGroupHeatStackId(String baseGroupHeatStackId) {
+		this.baseGroupHeatStackId = baseGroupHeatStackId;
+	}
+	
+	public boolean isBase() {
+		return this.isBase;
+	}
+	public void setIsBase(boolean isBase) {
+		this.isBase = isBase;
+	}
+	public String getVfModuleStackId() {
+		return this.vfModuleStackId;
+	}
+	public void setVfModuleStackId(String vfModuleStackId) {
+		this.vfModuleStackId = vfModuleStackId;
+	}
+
+	@Override
+    public String toString() {
+		return "VfRollback: cloud=" + cloudSiteId + ", tenant=" + tenantId +
+				", vnf=" + vnfId + ", tenantCreated=" + tenantCreated +
+				", vnfCreated=" + vnfCreated + ", requestType = " + requestType +
+				", volumeGroupHeatStackId = " + this.volumeGroupHeatStackId;
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VnfAdapterRest.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VnfAdapterRest.java
new file mode 100644
index 0000000..6906f81
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VnfAdapterRest.java
@@ -0,0 +1,628 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.xml.ws.Holder;
+
+import org.apache.http.HttpStatus;
+
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+import org.openecomp.mso.cloud.CloudConfigFactory;
+import org.openecomp.mso.entity.MsoRequest;
+import org.openecomp.mso.logger.MessageEnum;
+import org.openecomp.mso.logger.MsoLogger;
+import org.openecomp.mso.openstack.beans.VnfStatus;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;
+import org.openecomp.mso.properties.MsoPropertiesFactory;
+import org.openecomp.mso.adapters.vnfrest.*;
+
+/**
+ * This class services calls to the REST interface for VF Modules (http://host:port/vnfs/rest/v1/vnfs)
+ * Both XML and JSON can be produced/consumed.  Set Accept: and Content-Type: headers appropriately.  XML is the default.
+ * For testing, call with cloudSiteId = ___TESTING___
+ * To test exceptions, also set tenantId = ___TESTING___
+ */
+@Path("/v1/vnfs")
+public class VnfAdapterRest {
+	private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA);
+	private static final String TESTING_KEYWORD = "___TESTING___";
+	private final CloudConfigFactory cloudConfigFactory = new CloudConfigFactory();
+	private final MsoPropertiesFactory msoPropertiesFactory = new MsoPropertiesFactory();
+	private final MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory, cloudConfigFactory);
+	//TODO Logging, SkipAAI, CREATED flags, Integrate with BPEL, Auth,
+
+	@HEAD
+	@GET
+	@Path("/healthcheck")
+	@Produces(MediaType.TEXT_HTML)
+	public Response healthcheck () {
+		String CHECK_HTML = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Health Check</title></head><body>Application ready</body></html>";
+		return Response.ok().entity(CHECK_HTML).build();
+	}
+
+   /*
+	* URL:http://localhost:8080/vnfs/rest/v1/vnfs/<aaivnfid>/vf-modules/<aaimodid>
+	* REQUEST:
+	* {"deleteVfModuleRequest":
+		{"cloudSiteId": "DAN",
+		"tenantId": "214b428a1f554c02935e66330f6a5409",
+		"vnfId": "somevnfid",
+		"vfModuleId": "somemodid",
+		"vfModuleStackId": "4e567676-e266-4594-a3a6-131c8a2baf73",
+		"messageId": "ra.1",
+		"notificationUrl": "http://localhost:8089/vnfmock",
+		"skipAAI": true,
+		"msoRequest": {
+		"requestId": "ra1",
+		"serviceInstanceId": "sa1"
+		}}
+		}
+	*/
+	@DELETE
+	@Path("{aaiVnfId}/vf-modules/{aaiVfModuleId}")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response deleteVfModule (
+   		@PathParam("aaiVnfId") String aaiVnfId,
+		@PathParam("aaiVfModuleId") String aaiVfModuleId,
+		final DeleteVfModuleRequest req)
+	{
+		LOGGER.debug("Delete VfModule enter: " + req.toJsonString());
+		if (aaiVnfId == null || !aaiVnfId.equals(req.getVnfId())) {
+			LOGGER.debug("Req rejected - aaiVnfId not provided or doesn't match URL");
+			return Response
+				.status(HttpStatus.SC_BAD_REQUEST)
+				.type(MediaType.TEXT_PLAIN)
+				.entity("vnfid in URL does not match content")
+				.build();
+		}
+	   	if (aaiVfModuleId == null || !aaiVfModuleId.equals(req.getVfModuleId())) {
+			LOGGER.debug("Req rejected - aaiVfModuleId not provided or doesn't match URL");
+			return Response
+				.status(HttpStatus.SC_BAD_REQUEST)
+				.type(MediaType.TEXT_PLAIN)
+				.entity("vfModuleId in URL does not match content")
+				.build();
+		}
+	   	DeleteVfModuleTask task = new DeleteVfModuleTask(req);
+		if (req.isSynchronous()) {
+   			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+   				.build();
+   		} else {
+			// This is an asynchronous request
+			try {
+				Thread t1 = new Thread(task);
+   				t1.start();
+   			} catch (Exception e) {
+				// problem handling delete, send generic failure as sync resp to caller
+				LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, "", "deleteVfModule", MsoLogger.ErrorCode.BusinessProcesssError, "Exception in deleteVfModule", e);
+				return Response.serverError().build();
+   			}
+   			// send sync response (ACK) to caller
+   			LOGGER.debug ("deleteVNFVolumes exit");
+   			return Response.status(HttpStatus.SC_ACCEPTED).build();
+   		}
+	}
+
+	public class DeleteVfModuleTask implements Runnable {
+		private final DeleteVfModuleRequest req;
+		private DeleteVfModuleResponse response = null;
+		private VfModuleExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public DeleteVfModuleTask(DeleteVfModuleRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<DeleteVfModuleResponse>(response) {}
+				: new GenericEntity<VfModuleExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+
+		@Override
+		public void run() {
+			try {
+				String cloudsite = req.getCloudSiteId();
+				if (cloudsite != null && !cloudsite.equals(TESTING_KEYWORD)) {
+					//vnfAdapter.deleteVnf (req.getCloudSiteId(), req.getTenantId(), req.getVfModuleStackId(), req.getMsoRequest());
+					vnfAdapter.deleteVfModule (req.getCloudSiteId(), req.getTenantId(), req.getVfModuleStackId(), req.getMsoRequest());
+				}
+				response = new DeleteVfModuleResponse(req.getVnfId(), req.getVfModuleId(), Boolean.TRUE, req.getMessageId());
+			} catch (VnfException e) {
+				LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, "", "", MsoLogger.ErrorCode.BusinessProcesssError, "VnfException - Delete VNF Module", e);
+				eresp = new VfModuleExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, Boolean.TRUE, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				BpelRestClient bpelClient = new BpelRestClient();
+				bpelClient.bpelPost(getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug ("Delete vfModule exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+
+	/*
+	 * URL:http://localhost:8080/vnfs/rest/v1/vnfs/<aaiVnfId>/vf-modules/<aaiVfModuleId>?cloudSiteId=DAN&tenantId=vfModule?&skipAAI=TRUE&msoRequest.requestId=ra1&msoRequest.serviceInstanceId=si1&vfModuleName=T2N2S1
+	 * RESP:
+	 * {"queryVfModuleResponse": {
+		   "vfModuleId": "AvfmodId",
+		   "vfModuleOutputs": {"entry": {
+			  "key": "server_private_ip_1",
+			  "value": "10.100.1.25"
+		   }},
+		   "vfModuleStackId": "RaaVnf1/abfa8a6d-feb1-40af-aea3-109403b1cf6b",
+		   "vnfId": "AvnfID",
+		   "vnfStatus": "ACTIVE"
+		}}
+	 */
+	@GET
+	@Path("{aaiVnfId}/vf-modules/{aaiVfModuleId}")
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response queryVfModule(
+		@PathParam("aaiVnfId") String aaiVnfId,
+		@PathParam("aaiVfModuleId") String aaiVfModuleId,
+		@QueryParam("cloudSiteId") String cloudSiteId,
+		@QueryParam("tenantId") String tenantId,
+		@QueryParam("vfModuleName") String vfModuleName, //RAA? Id in doc
+		@QueryParam("skipAAI") Boolean skipAAI,
+		@QueryParam("msoRequest.requestId") String requestId,
+		@QueryParam("msoRequest.serviceInstanceId") String serviceInstanceId)
+	{
+		//This request responds synchronously only
+		LOGGER.debug ("Query vfModule enter:" + vfModuleName);
+		MsoRequest msoRequest = new MsoRequest(requestId, serviceInstanceId);
+
+		try {
+			int respStatus = HttpStatus.SC_OK;
+			QueryVfModuleResponse qryResp = new QueryVfModuleResponse(aaiVnfId, aaiVfModuleId, null, null, null);
+			Holder<Boolean> vnfExists = new Holder<Boolean>();
+			Holder<String> vfModuleId = new Holder<String>();
+			Holder<VnfStatus> status  = new Holder<VnfStatus>();
+			Holder<Map<String, String>> outputs = new Holder <Map <String, String>> ();
+			vnfAdapter.queryVnf (cloudSiteId, tenantId, vfModuleName, msoRequest, vnfExists, vfModuleId, status, outputs);
+			if (!vnfExists.value) {
+				LOGGER.debug ("vfModule not found");
+				respStatus = HttpStatus.SC_NOT_FOUND;
+			} else {
+				LOGGER.debug ("vfModule found" + vfModuleId.value + ", status=" + status.value);
+				qryResp.setVfModuleId(vfModuleId.value);
+				qryResp.setVnfStatus(status.value);
+				qryResp.setVfModuleOutputs(outputs.value);
+			}
+			LOGGER.debug ("Query vfModule exit");
+			return Response
+				.status(respStatus)
+				.entity(new GenericEntity<QueryVfModuleResponse>(qryResp) {})
+				.build();
+		} catch (VnfException e) {
+			LOGGER.error (MessageEnum.RA_QUERY_VNF_ERR,  vfModuleName, "", "queryVfModule", MsoLogger.ErrorCode.BusinessProcesssError, "VnfException - queryVfModule", e);
+			VfModuleExceptionResponse excResp = new VfModuleExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, Boolean.FALSE, null);
+			return Response
+				.status(HttpStatus.SC_INTERNAL_SERVER_ERROR)
+				.entity(new GenericEntity<VfModuleExceptionResponse>(excResp) {})
+				.build();
+		}
+	}
+
+	/*URL: http://localhost:8080/vnfs/rest/v1/vnfs/<aaivnfid>/vf-modules
+	 *REQUEST:
+	 * {"createVfModuleRequest":
+		{"cloudSiteId": "DAN",
+		"tenantId": "214b428a1f554c02935e66330f6a5409",
+		"vnfId": "somevnfid",
+		"vfModuleId": "somemodid",
+		"vfModuleName": "RaaVnf1",
+		"vnfType": "ApacheVnf",
+		"vfModuleParams": {"entry": [
+			{"key": "network_id",
+			"value": "59ed7b41-2983-413f-ba93-e7d437433916"},
+			{"key": "subnet_id",
+			"value": "086c9298-5c57-49b7-bb2b-6fd5730c5d92"},
+			{"key": "server_name_0",
+			"value": "RaaVnf1"}
+			]},
+		"failIfExists": true,
+		"messageId": "ra.1",
+		"notificationUrl": "http://localhost:8089/vnfmock",
+		"skipAAI": true,
+		"msoRequest": {
+		"requestId": "ra1",
+		"serviceInstanceId": "sa1"
+		}}
+		}
+	 */
+	@POST
+	@Path("{aaiVnfId}/vf-modules")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response createVfModule(
+		@PathParam("aaiVnfId") String aaiVnfId,
+		final CreateVfModuleRequest req)
+	{
+		LOGGER.debug("Create VfModule enter inside VnfAdapterRest: " + req.toJsonString());
+		if (aaiVnfId == null || !aaiVnfId.equals(req.getVnfId())) {
+			LOGGER.debug("Req rejected - aaiVnfId not provided or doesn't match URL");
+			return Response
+				.status(HttpStatus.SC_BAD_REQUEST)
+				.type(MediaType.TEXT_PLAIN)
+				.entity("vnfid in URL does not match content")
+				.build();
+		}
+		CreateVfModuleTask task = new CreateVfModuleTask(req);
+		if (req.isSynchronous()) {
+   			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+   				.build();
+   		} else {
+			// This is an asynchronous request
+			try {
+				Thread t1 = new Thread(task);
+   				t1.start();
+   			} catch (Exception e) {
+				// problem handling create, send generic failure as sync resp to caller
+				LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, "", "createVfModule", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - createVfModule", e);
+				return Response.serverError().build();
+   			}
+   			// send sync response (ACK) to caller
+   			LOGGER.debug ("createVfModule exit");
+   			return Response.status(HttpStatus.SC_ACCEPTED).build();
+   		}
+	}
+
+	public class CreateVfModuleTask implements Runnable {
+		private final CreateVfModuleRequest req;
+		private CreateVfModuleResponse response = null;
+		private VfModuleExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public CreateVfModuleTask(CreateVfModuleRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<CreateVfModuleResponse>(response) {}
+				: new GenericEntity<VfModuleExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+
+		@Override
+		public void run() {
+			LOGGER.debug ("CreateVfModuleTask start");
+			try {
+				// Synchronous Web Service Outputs
+				Holder <String> vfModuleStackId = new Holder <String> ();
+				Holder <Map <String, String>> outputs = new Holder <Map <String, String>> ();
+				Holder <VnfRollback> vnfRollback = new Holder <VnfRollback> ();
+				String completeVnfVfModuleType = req.getVnfType() + "::" + req.getVfModuleType();
+				LOGGER.debug("completeVnfVfModuleType=" + completeVnfVfModuleType);
+				String cloudsite = req.getCloudSiteId();
+				if (cloudsite != null && cloudsite.equals(TESTING_KEYWORD)) {
+					String tenant = req.getTenantId();
+					if (tenant != null && tenant.equals(TESTING_KEYWORD)) {
+						throw new VnfException("testing.");
+					}
+					vnfRollback.value = new VnfRollback(req.getVnfId(), tenant, cloudsite,
+							true, false, new MsoRequest("reqid", "svcid"),
+							req.getVolumeGroupId(), req.getVolumeGroupId(), req.getRequestType());
+					vfModuleStackId.value = "479D3D8B-6360-47BC-AB75-21CC91981484";
+					outputs.value = VolumeAdapterRest.testMap();
+				} else {
+//					vnfAdapter.createVnf (createReq.getCloudSiteId(),
+//						createReq.getTenantId(),
+//						createReq.getVnfType(),
+//						createReq.getVnfVersion(),
+//						createReq.getVfModuleName(),
+//						createReq.getRequestType(),
+//						createReq.getVolumeGroupStackId(),
+//						createReq.getVfModuleParams(),
+//						createReq.getFailIfExists(),
+//						createReq.getBackout(),
+//						createReq.getMsoRequest(),
+//						vfModuleStackId,
+//						outputs,
+//						vnfRollback);
+					vnfAdapter.createVfModule(req.getCloudSiteId(),
+						req.getTenantId(),
+						//req.getVnfType(),
+						completeVnfVfModuleType,
+						req.getVnfVersion(),
+						req.getVfModuleName(),
+						req.getRequestType(),
+						req.getVolumeGroupStackId(),
+						req.getBaseVfModuleStackId(),
+						req.getVfModuleParams(),
+						req.getFailIfExists(),
+						req.getBackout(),
+						req.getMsoRequest(),
+						vfModuleStackId,
+						outputs,
+						vnfRollback);
+				}
+				VfModuleRollback modRollback = new VfModuleRollback(vnfRollback.value, req.getVfModuleId(), vfModuleStackId.value, req.getMessageId());
+				response = new CreateVfModuleResponse(req.getVnfId(), req.getVfModuleId(),
+						vfModuleStackId.value, Boolean.TRUE, outputs.value, modRollback, req.getMessageId());
+			} catch (VnfException e) {
+				eresp = new VfModuleExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, Boolean.TRUE, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				BpelRestClient bpelClient = new BpelRestClient();
+				bpelClient.bpelPost(getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug ("CreateVfModuleTask exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+
+	@PUT
+	@Path("{aaiVnfId}/vf-modules/{aaiVfModuleId}")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response updateVfModule(
+			@PathParam("aaiVnfId") String aaiVnfId,
+			@PathParam("aaiVfModuleId") String aaiVfModuleId,
+			final UpdateVfModuleRequest req)
+	{
+		LOGGER.debug("Update VfModule enter: " + req.toJsonString());
+		UpdateVfModulesTask task = new UpdateVfModulesTask(req);
+		if (req.isSynchronous()) {
+			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+				.build();
+		} else {
+			// This is an asynchronous request
+	    	try {
+	    		Thread t1 = new Thread(task);
+	    		t1.start();
+	    	} catch (Exception e) {
+	    		// problem handling create, send generic failure as sync resp to caller
+	    		LOGGER.error (MessageEnum.RA_UPDATE_VNF_ERR, "", "updateVfModule", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - updateVfModule", e);
+	    		return Response.serverError().build();
+	    	}
+	    	// send sync response (ACK) to caller
+	    	LOGGER.debug ("updateVfModules exit");
+	    	return Response.status(HttpStatus.SC_ACCEPTED).build();
+		}
+	}
+
+	public class UpdateVfModulesTask implements Runnable {
+		private final UpdateVfModuleRequest req;
+		private UpdateVfModuleResponse response = null;
+		private VfModuleExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public UpdateVfModulesTask(UpdateVfModuleRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<UpdateVfModuleResponse>(response) {}
+				: new GenericEntity<VfModuleExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+		@Override
+		public void run() {
+			try {
+				//MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl (msoPropertiesFactory, cloudConfigFactory);
+
+				// Synchronous Web Service Outputs
+				Holder <String> vfModuleStackId = new Holder <String> ();
+				Holder <Map <String, String>> outputs = new Holder <Map <String, String>> ();
+				Holder <VnfRollback> vnfRollback = new Holder <VnfRollback> ();
+				String completeVnfVfModuleType = req.getVnfType() + "::" + req.getVfModuleType();
+				LOGGER.debug("in updateVf - completeVnfVfModuleType=" + completeVnfVfModuleType);
+
+				/*
+				vnfAdapter.updateVnf (updateReq.getCloudSiteId(),
+						updateReq.getTenantId(),
+						updateReq.getVnfType(),
+						updateReq.getVnfVersion(),
+						updateReq.getVfModuleName(),
+						updateReq.getRequestType(),
+						updateReq.getVolumeGroupStackId(),
+						updateReq.getVfModuleParams(),
+						updateReq.getMsoRequest(),
+						outputs,
+						vnfRollback); 
+						*/
+				vnfAdapter.updateVfModule (req.getCloudSiteId(),
+						req.getTenantId(),
+						//req.getVnfType(),
+						completeVnfVfModuleType,
+						req.getVnfVersion(),
+						req.getVfModuleName(),
+						req.getRequestType(),
+						req.getVolumeGroupStackId(),
+						req.getBaseVfModuleId(),
+						req.getVfModuleStackId(),
+						req.getVfModuleParams(),
+						req.getMsoRequest(),
+						outputs,
+						vnfRollback); 
+
+				response = new UpdateVfModuleResponse(req.getVnfId(), req.getVfModuleId(),
+						vfModuleStackId.value, outputs.value, req.getMessageId());
+			} catch (VnfException e) {
+				eresp = new VfModuleExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, Boolean.TRUE, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				// This is asynch, so POST response back to caller
+				BpelRestClient bpelClient = new BpelRestClient ();
+				bpelClient.bpelPost (getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug ("Update VfModule exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+	/*
+	 * URL:http://localhost:8080/vnfs/rest/v1/vnfs/<aaivnfid>/vf-modules/<aaimodid>/rollback
+	 * REQUEST:
+	 * {"deleteVfModuleRequest":
+ 		{"cloudSiteId": "DAN",
+ 		"tenantId": "214b428a1f554c02935e66330f6a5409",
+ 		"vnfId": "somevnfid",
+ 		"vfModuleId": "somemodid",
+ 		"vfModuleStackId": "4e567676-e266-4594-a3a6-131c8a2baf73",
+ 		"messageId": "ra.1",
+ 		"notificationUrl": "http://localhost:8089/vnfmock",
+ 		"skipAAI": true,
+ 		"msoRequest": {
+ 		"requestId": "ra1",
+ 		"serviceInstanceId": "sa1"
+ 		}}
+ 		}
+	 */
+	@DELETE
+	@Path("{aaiVnfId}/vf-modules/{aaiVfModuleId}/rollback")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response rollbackVfModule (
+			@PathParam("aaiVnfId") String aaiVnfId,
+			@PathParam("aaiVfModuleId") String aaiVfModuleId,
+			//@QueryParam("rollback") String rollback,
+			final RollbackVfModuleRequest req)
+	{
+		LOGGER.debug("Rollback VfModule enter: " + req.toJsonString());
+		RollbackVfModulesTask task = new RollbackVfModulesTask(req);
+		if (req.isSynchronous()) {
+			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+				.build();
+		} else {
+			// This is an asynchronous request
+	    	try {
+	    		Thread t1 = new Thread(task);
+	    		t1.start();
+	    	} catch (Exception e) {
+	    		// problem handling create, send generic failure as sync resp to caller
+	    		LOGGER.error (MessageEnum.RA_ROLLBACK_VNF_ERR, "", "rollbackVfModule", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - rollbackVfModule", e);
+	    		return Response.serverError().build();
+	    	}
+	    	// send sync response (ACK) to caller
+	    	LOGGER.debug ("rollbackVfModule exit");
+	    	return Response.status(HttpStatus.SC_ACCEPTED).build();
+		}
+	}
+
+	public class RollbackVfModulesTask implements Runnable {
+		private final RollbackVfModuleRequest req;
+		private RollbackVfModuleResponse response = null;
+		private VfModuleExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public RollbackVfModulesTask(RollbackVfModuleRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<RollbackVfModuleResponse>(response) {}
+				: new GenericEntity<VfModuleExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+		@Override
+		public void run() {
+			try {
+				VfModuleRollback vmr = req.getVfModuleRollback();
+				VnfRollback vrb = new VnfRollback(
+						vmr.getVfModuleStackId(), vmr.getTenantId(), vmr.getCloudSiteId(), true, true,
+						vmr.getMsoRequest(), null, null, null);
+				vnfAdapter.rollbackVnf (vrb);
+				response = new RollbackVfModuleResponse(Boolean.TRUE, req.getMessageId());
+			} catch (VnfException e) {
+				LOGGER.error (MessageEnum.RA_ROLLBACK_VNF_ERR, "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - rollbackVfModule", e);
+				eresp = new VfModuleExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, false, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				// This is asynch, so POST response back to caller
+				BpelRestClient bpelClient = new BpelRestClient ();
+				bpelClient.bpelPost (getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug ("RollbackVfModulesTask exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VolumeAdapterRest.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VolumeAdapterRest.java
new file mode 100644
index 0000000..8fa1552
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/VolumeAdapterRest.java
@@ -0,0 +1,576 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.xml.ws.Holder;
+
+import org.apache.http.HttpStatus;
+
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+import org.openecomp.mso.adapters.vnfrest.CreateVolumeGroupRequest;
+import org.openecomp.mso.adapters.vnfrest.CreateVolumeGroupResponse;
+import org.openecomp.mso.adapters.vnfrest.DeleteVolumeGroupRequest;
+import org.openecomp.mso.adapters.vnfrest.DeleteVolumeGroupResponse;
+import org.openecomp.mso.adapters.vnfrest.QueryVolumeGroupResponse;
+import org.openecomp.mso.adapters.vnfrest.RollbackVolumeGroupRequest;
+import org.openecomp.mso.adapters.vnfrest.RollbackVolumeGroupResponse;
+import org.openecomp.mso.adapters.vnfrest.UpdateVolumeGroupRequest;
+import org.openecomp.mso.adapters.vnfrest.UpdateVolumeGroupResponse;
+import org.openecomp.mso.adapters.vnfrest.VolumeGroupExceptionResponse;
+import org.openecomp.mso.adapters.vnfrest.VolumeGroupRollback;
+import org.openecomp.mso.cloud.CloudConfigFactory;
+import org.openecomp.mso.entity.MsoRequest;
+import org.openecomp.mso.logger.MessageEnum;
+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.MsoExceptionCategory;
+import org.openecomp.mso.properties.MsoPropertiesFactory;
+
+/**
+ * This class services calls to the REST interface for VNF Volumes (http://host:port/vnfs/rest/v1/volume-groups)
+ * Both XML and JSON can be produced/consumed.  Set Accept: and Content-Type: headers appropriately.  XML is the default.
+ * For testing, call with cloudSiteId = ___TESTING___
+ * To test exceptions, also set tenantId = ___TESTING___
+ */
+@Path("/v1/volume-groups")
+public class VolumeAdapterRest {
+	private static final MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.RA);
+	private static final String TESTING_KEYWORD = "___TESTING___";
+	private final CloudConfigFactory cloudConfigFactory = new CloudConfigFactory();
+	private final MsoPropertiesFactory msoPropertiesFactory = new MsoPropertiesFactory();
+	private final MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl(msoPropertiesFactory, cloudConfigFactory);
+
+	@POST
+	@Path("")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response createVNFVolumes(final CreateVolumeGroupRequest req) {
+		LOGGER.debug("createVNFVolumes enter: " + req.toJsonString());
+		CreateVNFVolumesTask task = new CreateVNFVolumesTask(req);
+		if (req.isSynchronous()) {
+			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+				.build();
+		} else {
+			// This is an asynchronous request
+			try {
+				Thread t1 = new Thread(task);
+				t1.start();
+			} catch (Exception e) {
+				// problem handling create, send generic failure as sync resp to caller
+				LOGGER.error (MessageEnum.RA_CREATE_VNF_ERR, "", "createVNFVolumes", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - createVNFVolumes", e);
+				return Response.serverError().build();
+			}
+			// send sync response (ACK) to caller
+			LOGGER.debug ("createVNFVolumes exit");
+			return Response.status(HttpStatus.SC_ACCEPTED).build();
+		}
+	}
+
+	public class CreateVNFVolumesTask implements Runnable {
+		private final CreateVolumeGroupRequest req;
+		private CreateVolumeGroupResponse response = null;
+		private VolumeGroupExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public CreateVNFVolumesTask(CreateVolumeGroupRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<CreateVolumeGroupResponse>(response) {}
+				: new GenericEntity<VolumeGroupExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+		@Override
+		public void run() {
+			LOGGER.debug ("CreateVFModule VolumesTask start");
+			try {
+				// Synchronous Web Service Outputs
+				Holder<String> stackId = new Holder<String>();
+				Holder<Map<String, String>> outputs = new Holder<Map<String, String>>();
+				Holder<VnfRollback> vnfRollback = new Holder<VnfRollback>();
+				String completeVnfVfModuleType = req.getVnfType() + "::" + req.getVfModuleType();
+				LOGGER.debug("in createVfModuleVolumes - completeVnfVfModuleType=" + completeVnfVfModuleType);
+
+				String cloudsite = req.getCloudSiteId();
+				if (cloudsite != null && cloudsite.equals(TESTING_KEYWORD)) {
+					String tenant = req.getTenantId();
+					if (tenant != null && tenant.equals(TESTING_KEYWORD)) {
+						throw new VnfException("testing.");
+					}
+					stackId.value = "479D3D8B-6360-47BC-AB75-21CC91981484";
+					outputs.value = testMap();
+				} else {
+//					vnfAdapter.createVnf(
+//							req.getCloudSiteId(),
+//							req.getTenantId(),
+//							req.getVnfType(),
+//							req.getVnfVersion(),
+//							req.getVolumeGroupName(),
+//							"VOLUME",			// request type is VOLUME
+//							null,				// not sure about this
+//							req.getVolumeGroupParams(),
+//							req.getFailIfExists(),
+//							req.getSuppressBackout(),
+//							req.getMsoRequest(),
+//							stackId,
+//							outputs,
+//							vnfRollback);
+					vnfAdapter.createVfModule(
+							req.getCloudSiteId(), //cloudSiteId, 
+							req.getTenantId(), //tenantId, 
+							//req.getVnfType(), //vnfType, 
+							completeVnfVfModuleType,
+							req.getVnfVersion(), //vnfVersion, 
+							req.getVolumeGroupName(), //vnfName, 
+							"VOLUME", //requestType, 
+							null, //volumeGroupHeatStackId, 
+							null, //baseVfHeatStackId, 
+							req.getVolumeGroupParams(), //inputs, 
+							req.getFailIfExists(), //failIfExists, 
+							req.getSuppressBackout(), //backout, 
+							req.getMsoRequest(), // msoRequest, 
+							stackId, 
+							outputs, 
+							vnfRollback);
+				}
+				VolumeGroupRollback rb = new VolumeGroupRollback(
+						req.getVolumeGroupId(),
+						stackId.value,
+						true, 						// TODO boolean volumeGroupCreated, when would it be false?
+						req.getTenantId(),
+						req.getCloudSiteId(),
+						req.getMsoRequest(),
+						req.getMessageId());
+				response = new CreateVolumeGroupResponse(
+						req.getVolumeGroupId(),
+						stackId.value,
+						true, 						// TODO boolean volumeGroupCreated, when would it be false?
+						outputs.value,
+						rb,
+						req.getMessageId());
+			} catch (VnfException e) {
+				eresp = new VolumeGroupExceptionResponse(
+					e.getMessage(), MsoExceptionCategory.INTERNAL, true, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				// This is asynch, so POST response back to caller
+				BpelRestClient bpelClient = new BpelRestClient();
+				bpelClient.bpelPost(getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug ("CreateVFModule VolumesTask exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+
+	@DELETE
+	@Path("{aaiVolumeGroupId}")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response deleteVNFVolumes(
+		@PathParam("aaiVolumeGroupId") String aaiVolumeGroupId,
+		final DeleteVolumeGroupRequest req
+		)
+	{
+		LOGGER.debug("deleteVNFVolumes enter: " + req.toJsonString());
+		if (aaiVolumeGroupId == null || !aaiVolumeGroupId.equals(req.getVolumeGroupId())) {
+			return Response
+				.status(HttpStatus.SC_BAD_REQUEST)
+				.type(MediaType.TEXT_PLAIN)
+				.entity("VolumeGroupId in URL does not match content")
+				.build();
+		}
+		DeleteVNFVolumesTask task = new DeleteVNFVolumesTask(req);
+		if (req.isSynchronous()) {
+			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+				.build();
+		} else {
+			// This is an asynchronous request
+			try {
+				Thread t1 = new Thread(task);
+				t1.start();
+			} catch (Exception e) {
+				// problem handling create, send generic failure as sync resp to caller
+				LOGGER.error (MessageEnum.RA_DELETE_VNF_ERR, "", "deleteVNFVolumes", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - deleteVNFVolumes", e);
+				return Response.serverError().build();
+			}
+			// send sync response (ACK) to caller
+			LOGGER.debug ("deleteVNFVolumes exit");
+			return Response.status(HttpStatus.SC_ACCEPTED).build();
+		}
+	}
+
+	public class DeleteVNFVolumesTask implements Runnable {
+		private final DeleteVolumeGroupRequest req;
+		private DeleteVolumeGroupResponse response = null;
+		private VolumeGroupExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public DeleteVNFVolumesTask(DeleteVolumeGroupRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<DeleteVolumeGroupResponse>(response) {}
+				: new GenericEntity<VolumeGroupExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+		@Override
+		public void run() {
+			LOGGER.debug("DeleteVNFVolumesTask start");
+			try {
+				if (!req.getCloudSiteId().equals(TESTING_KEYWORD)) {
+					vnfAdapter.deleteVnf(req.getCloudSiteId(), req.getTenantId(), req.getVolumeGroupStackId(), req.getMsoRequest());
+				}
+				response = new DeleteVolumeGroupResponse(true, req.getMessageId());
+			} catch (VnfException e) {
+				eresp = new VolumeGroupExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, true, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				// This is asynch, so POST response back to caller
+				BpelRestClient bpelClient = new BpelRestClient();
+				bpelClient.bpelPost(getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug("DeleteVNFVolumesTask exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+
+	@DELETE
+	@Path("{aaiVolumeGroupId}/rollback")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response rollbackVNFVolumes(
+		@PathParam("aaiVolumeGroupId") String aaiVolumeGroupId,
+		final RollbackVolumeGroupRequest req
+		)
+	{
+		LOGGER.debug("rollbackVNFVolumes enter: " + req.toJsonString());
+		if (aaiVolumeGroupId == null || req.getVolumeGroupRollback() == null || !aaiVolumeGroupId.equals(req.getVolumeGroupRollback().getVolumeGroupId())) {
+			return Response
+				.status(HttpStatus.SC_BAD_REQUEST)
+				.type(MediaType.TEXT_PLAIN)
+				.entity("VolumeGroupId in URL does not match content")
+				.build();
+		}
+		RollbackVNFVolumesTask task = new RollbackVNFVolumesTask(req);
+		if (req.isSynchronous()) {
+			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+				.build();
+		} else {
+			// This is an asynchronous request
+			try {
+				Thread t1 = new Thread(task);
+				t1.start();
+			} catch (Exception e) {
+				// problem handling create, send generic failure as sync resp to caller
+				LOGGER.error (MessageEnum.RA_ROLLBACK_VNF_ERR, "", "rollbackVNFVolumes", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - rollbackVNFVolumes", e);
+				return Response.serverError().build();
+			}
+			// send sync response (ACK) to caller
+			LOGGER.debug("rollbackVNFVolumes exit");
+			return Response.status(HttpStatus.SC_ACCEPTED).build();
+		}
+	}
+
+	public class RollbackVNFVolumesTask implements Runnable {
+		private final RollbackVolumeGroupRequest req;
+		private RollbackVolumeGroupResponse response = null;
+		private VolumeGroupExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public RollbackVNFVolumesTask(RollbackVolumeGroupRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<RollbackVolumeGroupResponse>(response) {}
+				: new GenericEntity<VolumeGroupExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+		@Override
+		public void run() {
+			LOGGER.debug("DeleteVNFVolumesTask start");
+			try {
+				VolumeGroupRollback vgr = req.getVolumeGroupRollback();
+				VnfRollback vrb = new VnfRollback(
+						vgr.getVolumeGroupStackId(), vgr.getTenantId(), vgr.getCloudSiteId(), true, true,
+						vgr.getMsoRequest(), null, null, null);
+				vnfAdapter.rollbackVnf(vrb);
+				response = new RollbackVolumeGroupResponse(true, req.getMessageId());
+			} catch (VnfException e) {
+				eresp = new VolumeGroupExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, true, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				// This is asynch, so POST response back to caller
+				BpelRestClient bpelClient = new BpelRestClient();
+				bpelClient.bpelPost(getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug("DeleteVNFVolumesTask exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+
+	@PUT
+	@Path("{aaiVolumeGroupId}")
+	@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response updateVNFVolumes(
+		@PathParam("aaiVolumeGroupId") String aaiVolumeGroupId,
+		final UpdateVolumeGroupRequest req
+		)
+	{
+		LOGGER.debug("updateVNFVolumes enter: " + req.toJsonString());
+		if (aaiVolumeGroupId == null || !aaiVolumeGroupId.equals(req.getVolumeGroupId())) {
+			return Response
+				.status(HttpStatus.SC_BAD_REQUEST)
+				.type(MediaType.TEXT_PLAIN)
+				.entity("VolumeGroupId in URL does not match content")
+				.build();
+		}
+		UpdateVNFVolumesTask task = new UpdateVNFVolumesTask(req);
+		if (req.isSynchronous()) {
+			// This is a synchronous request
+			task.run();
+			return Response
+				.status(task.getStatusCode())
+				.entity(task.getGenericEntityResponse())
+				.build();
+		} else {
+			// This is an asynchronous request
+	    	try {
+	    		Thread t1 = new Thread(task);
+	    		t1.start();
+	    	} catch (Exception e) {
+	    		// problem handling create, send generic failure as sync resp to caller
+	    		LOGGER.error (MessageEnum.RA_UPDATE_VNF_ERR, "", "updateVNFVolumes", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - updateVNFVolumes", e);
+	    		return Response.serverError().build();
+	    	}
+	    	// send sync response (ACK) to caller
+	    	LOGGER.debug ("updateVNFVolumes exit");
+	    	return Response.status(HttpStatus.SC_ACCEPTED).build();
+		}
+	}
+
+	public class UpdateVNFVolumesTask implements Runnable {
+		private final UpdateVolumeGroupRequest req;
+		private UpdateVolumeGroupResponse response = null;
+		private VolumeGroupExceptionResponse eresp = null;
+		private boolean sendxml;
+
+		public UpdateVNFVolumesTask(UpdateVolumeGroupRequest req) {
+			this.req = req;
+			this.sendxml = true; // can be set with a field or header later
+		}
+		public int getStatusCode() {
+			return (response != null) ? HttpStatus.SC_OK : HttpStatus.SC_BAD_REQUEST;
+		}
+		public Object getGenericEntityResponse() {
+			return (response != null)
+				? new GenericEntity<UpdateVolumeGroupResponse>(response) {}
+				: new GenericEntity<VolumeGroupExceptionResponse>(eresp) {};
+		}
+		private String getResponse() {
+			if (response != null) {
+				return sendxml ? response.toXmlString() : response.toJsonString();
+			} else {
+				return sendxml ? eresp.toXmlString() : eresp.toJsonString();
+			}
+		}
+		@Override
+		public void run() {
+			LOGGER.debug("UpdateVNFVolumesTask start");
+			try {
+				@SuppressWarnings("unused")
+				Holder<String> stackId = new Holder<String> ();
+				Holder<Map<String, String>> outputs = new Holder<Map <String, String>> ();
+				Holder<VnfRollback> vnfRollback = new Holder<VnfRollback> ();
+				String completeVnfVfModuleType = req.getVnfType() + "::" + req.getVfModuleType();
+				LOGGER.debug("in updateVfModuleVolume - completeVnfVfModuleType=" + completeVnfVfModuleType);
+
+				if (req.getCloudSiteId().equals(TESTING_KEYWORD)) {
+					outputs.value = testMap();
+				} else {
+					//vnfAdapter.updateVnf(
+					//		req.getCloudSiteId(),
+					//		req.getTenantId(),
+					//		req.getVnfType(),
+					//		req.getVnfVersion(),
+					//		req.getVfModuleType(),
+					//		"VOLUME",			// request type is VOLUME
+					//		req.getVolumeGroupStackId(),
+					//		req.getVolumeGroupParams(),
+					//		req.getMsoRequest(),
+					//		outputs,
+					//		vnfRollback);
+					vnfAdapter.updateVfModule (req.getCloudSiteId(),
+							req.getTenantId(),
+							//req.getVnfType(),
+							completeVnfVfModuleType,
+							req.getVnfVersion(),
+							req.getVolumeGroupStackId(),
+							"VOLUME",
+							null,
+							null,
+							req.getVolumeGroupStackId(),
+							req.getVolumeGroupParams(),
+							req.getMsoRequest(),
+							outputs,
+							vnfRollback); 
+				}
+				response = new UpdateVolumeGroupResponse(
+						req.getVolumeGroupId(), req.getVolumeGroupStackId(),
+						outputs.value, req.getMessageId());
+			} catch (VnfException e) {
+				eresp = new VolumeGroupExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, true, req.getMessageId());
+			}
+			if (!req.isSynchronous()) {
+				// This is asynch, so POST response back to caller
+				BpelRestClient bpelClient = new BpelRestClient();
+				bpelClient.bpelPost(getResponse(), req.getNotificationUrl(), sendxml);
+			}
+			LOGGER.debug("UpdateVNFVolumesTask exit: code=" + getStatusCode() + ", resp="+ getResponse());
+		}
+	}
+
+	@GET
+	@Path("{aaiVolumeGroupId}")
+	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+	public Response queryVNFVolumes(
+		@PathParam("aaiVolumeGroupId") String aaiVolumeGroupId,
+		@QueryParam("cloudSiteId") String cloudSiteId,
+		@QueryParam("tenantId") String tenantId,
+		@QueryParam("volumeGroupStackId") String volumeGroupStackId,
+		@QueryParam("skipAAI") Boolean skipAAI,
+		@QueryParam("msoRequest.requestId") String requestId,
+		@QueryParam("msoRequest.serviceInstanceId") String serviceInstanceId
+		)
+	{
+    	//This request responds synchronously only
+    	LOGGER.debug ("queryVNFVolumes enter:" + aaiVolumeGroupId + " " + volumeGroupStackId);
+    	MsoRequest msoRequest = new MsoRequest(requestId, serviceInstanceId);
+
+    	try {
+        	int respStatus = HttpStatus.SC_OK;
+        	QueryVolumeGroupResponse qryResp = new QueryVolumeGroupResponse(aaiVolumeGroupId, volumeGroupStackId, null, null);
+        	Holder<Boolean> vnfExists = new Holder<Boolean>();
+        	Holder<String> vfModuleId = new Holder<String>();
+        	Holder<VnfStatus> status = new Holder<VnfStatus>();
+        	Holder<Map<String, String>> outputs = new Holder<Map<String, String>>();
+			if (cloudSiteId != null && cloudSiteId.equals(TESTING_KEYWORD)) {
+				if (tenantId != null && tenantId.equals(TESTING_KEYWORD)) {
+					throw new VnfException("testing.");
+				}
+				vnfExists.value = true;
+				vfModuleId.value = TESTING_KEYWORD;
+				status.value = VnfStatus.ACTIVE;
+				outputs.value = testMap();
+			} else {
+				vnfAdapter.queryVnf(cloudSiteId, tenantId, volumeGroupStackId, msoRequest, vnfExists, vfModuleId, status, outputs);
+			}
+    		if (!vnfExists.value) {
+    			LOGGER.debug ("VNFVolumes not found");
+    			qryResp.setVolumeGroupStatus(status.value);
+    			respStatus = HttpStatus.SC_NOT_FOUND;
+    		} else {
+    			LOGGER.debug ("VNFVolumes found " + vfModuleId.value + ", status=" + status.value);
+    			qryResp.setVolumeGroupStatus(status.value);
+    			qryResp.setVolumeGroupOutputs(outputs.value);
+    		}
+        	LOGGER.debug("Query queryVNFVolumes exit");
+    		return Response
+    			.status(respStatus)
+    			.entity(new GenericEntity<QueryVolumeGroupResponse>(qryResp) {})
+    			.build();
+    	} catch (VnfException e) {
+    		LOGGER.error(MessageEnum.RA_QUERY_VNF_ERR,  aaiVolumeGroupId, "", "queryVNFVolumes", MsoLogger.ErrorCode.BusinessProcesssError, "VnfException - queryVNFVolumes", e);
+    		VolumeGroupExceptionResponse excResp = new VolumeGroupExceptionResponse(e.getMessage(), MsoExceptionCategory.INTERNAL, Boolean.FALSE, null);
+        	LOGGER.debug("Query queryVNFVolumes exit");
+    		return Response
+    			.status(HttpStatus.SC_INTERNAL_SERVER_ERROR)
+    			.entity(new GenericEntity<VolumeGroupExceptionResponse>(excResp) {})
+    			.build();
+		}
+	}
+    public static Map<String, String> testMap() {
+		Map<String, String> m = new HashMap<String, String>();
+		m.put("mickey", "7");
+		m.put("clyde", "10");
+		m.put("wayne", "99");
+		return m;
+    }
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfAlreadyExists.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfAlreadyExists.java
new file mode 100644
index 0000000..7cec0cd
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfAlreadyExists.java
@@ -0,0 +1,42 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.exceptions;
+
+
+
+import javax.xml.ws.WebFault;
+
+/**
+ * This class reports an exception when trying to create a VNF when another
+ * VNF of the same name already exists in the target cloud/tenant.  Note that
+ * the createVnf method suppresses this exception by default.
+ * 
+ *
+ */
+@WebFault (name="VnfAlreadyExists", faultBean="org.openecomp.mso.adapters.vnf.exceptions.VnfExceptionBean", targetNamespace="http://com.att.mso/vnf")
+public class VnfAlreadyExists extends VnfException {
+
+	private static final long serialVersionUID = 1L;
+
+	public VnfAlreadyExists (String name, String cloudId, String tenantId, String vnfId) {
+		super("VNF " + name + " already exists in cloud/tenant " + cloudId + "/" + tenantId + " with ID " + vnfId);
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfException.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfException.java
new file mode 100644
index 0000000..ceef76b
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfException.java
@@ -0,0 +1,80 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.exceptions;
+
+
+
+import javax.xml.ws.WebFault;
+
+import org.openecomp.mso.openstack.exceptions.MsoException;
+import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;
+
+/**
+ * This class simply extends Exception (without addition additional functionality)
+ * to provide an identifier for VNF related exceptions on create, delete, query.
+ * 
+ *
+ */
+@WebFault (name="VnfException", faultBean="org.openecomp.mso.adapters.vnf.exceptions.VnfExceptionBean", targetNamespace="http://com.att.mso/vnf")
+public class VnfException extends Exception {
+
+	private static final long serialVersionUID = 1L;
+
+	private VnfExceptionBean faultInfo;
+	
+	public VnfException (String msg) {
+		super(msg);
+		faultInfo = new VnfExceptionBean (msg);
+	}
+	
+	public VnfException (Throwable e) {
+		super(e);
+		faultInfo = new VnfExceptionBean (e.getMessage());
+	}
+	
+	public VnfException (String msg, Throwable e) {
+		super (msg, e);
+		faultInfo = new VnfExceptionBean (msg);
+	}
+
+	public VnfException (String msg, MsoExceptionCategory category) {
+		super(msg);
+		faultInfo = new VnfExceptionBean (msg, category);
+	}
+	
+	public VnfException (String msg, MsoExceptionCategory category, Throwable e) {
+		super (msg, e);
+		faultInfo = new VnfExceptionBean (msg, category);
+	}
+	
+	public VnfException (MsoException e) {
+		super (e);
+		faultInfo = new VnfExceptionBean (e.getContextMessage(), e.getCategory());
+	}
+	
+	public VnfExceptionBean getFaultInfo() {
+		return faultInfo;
+	}
+
+	public void setFaultInfo(VnfExceptionBean faultInfo) {
+		this.faultInfo = faultInfo;
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfExceptionBean.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfExceptionBean.java
new file mode 100644
index 0000000..15918ea
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfExceptionBean.java
@@ -0,0 +1,73 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.exceptions;
+
+
+import java.io.Serializable;
+
+import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;
+
+/**
+ * Jax-WS Fault Bean for Vnf Exception
+ */
+public class VnfExceptionBean implements Serializable {
+
+    private static final long serialVersionUID = -5699310749783790095L;
+
+    private String message;
+	private MsoExceptionCategory category;
+	private Boolean rolledBack;
+
+	public VnfExceptionBean () {}
+
+	public VnfExceptionBean (String message) {
+		this.message = message;
+	}
+
+	public VnfExceptionBean (String message, MsoExceptionCategory category) {
+		this.message = message;
+		this.category = category;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+	public void setMessage(String message) {
+		this.message = message;
+	}
+
+	public MsoExceptionCategory getCategory () {
+		return category;
+	}
+
+	public void setCategory (MsoExceptionCategory category) {
+		this.category = category;
+	}
+
+	public Boolean isRolledBack() {
+		return rolledBack;
+	}
+
+	public void setRolledBack(Boolean rolledBack) {
+		this.rolledBack = rolledBack;
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfNotFound.java b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfNotFound.java
new file mode 100644
index 0000000..3c27d03
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/main/java/org/openecomp/mso/adapters/vnf/exceptions/VnfNotFound.java
@@ -0,0 +1,43 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.exceptions;
+
+
+import javax.xml.ws.WebFault;
+
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+
+/**
+ * This class reports an exception when trying to update a Network that does
+ * not exist in the target cloud/tenant.  Note that deleteNetwork suppresses
+ * this exception (deletion of non-existent network is considered a success).
+ * 
+ *
+ */
+@WebFault (name="VnfNotFound", faultBean="org.openecomp.mso.adapters.vnf.exceptions.VnfExceptionBean", targetNamespace="http://com.att.mso/vnf")
+public class VnfNotFound extends VnfException {
+
+	private static final long serialVersionUID = 1L;
+
+	public VnfNotFound (String cloudId, String tenantId, String vnfName) {
+		super("VNF " + vnfName + " not found in cloud/tenant " + cloudId + "/" + tenantId);
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/QueryTest.java b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/QueryTest.java
new file mode 100644
index 0000000..96e50d0
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/QueryTest.java
@@ -0,0 +1,64 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.test;
+
+
+import java.util.Map;
+
+import javax.xml.ws.Holder;
+
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapter;
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapterImpl;
+import org.openecomp.mso.openstack.beans.VnfStatus;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+
+public class QueryTest {
+	public final static void main (String args[])
+	{
+		MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl();
+		log ("Got a VnfAdapter");
+		
+		String cloudId = "MT";
+		String tenantId = "MSO_Test";
+		String vnfName = "VNF_TEST1";
+		Holder<Boolean> vnfExists = new Holder<Boolean>();
+		Holder<String> vnfId = new Holder<String>();
+		Holder<VnfStatus> status = new Holder<VnfStatus>();
+		Holder<Map<String,String>> outputs = new Holder<Map<String,String>>();
+		
+		try {
+			vnfAdapter.queryVnf(cloudId, tenantId, vnfName, null,
+						vnfExists, vnfId, status, outputs);
+		} catch (VnfException e) {
+			log ("Got an Exception: " + e);
+		}
+		
+		if (! vnfExists.value){
+			log ("VNF Not Found");
+		} else {
+			log ("Found VNF, ID = " + vnfId.value + ", status=" + status.value);
+		}
+	}
+	
+	private static void log (String msg) {
+		System.out.println (msg);
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfCreateTest.java b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfCreateTest.java
new file mode 100644
index 0000000..0269172
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfCreateTest.java
@@ -0,0 +1,87 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.test;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.ws.Holder;
+
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapter;
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapterImpl;
+import org.openecomp.mso.openstack.beans.VnfRollback;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+
+public class VnfCreateTest {
+	public final static void main (String args[])
+	{
+		MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl();
+		log ("Got a VnfAdapter");
+		
+		// Web Service Inputs
+		String cloudId = "MT";
+		String tenantName = "John_Test";
+		String vnfType = "ApacheDemo";
+		String vnfName = "AdapterTest";
+		Map<String,String> inputs = new HashMap<String,String>();
+		inputs.put("vnf_id", "abc");
+		inputs.put("extra", "whocares");
+		inputs.put("private_subnet_gateway", "10.4.1.1");
+		inputs.put("private_subnet_cidr", "10.4.1.0/29");
+		
+		// Web Service Outputs
+		Holder<String> vnfId = new Holder<String>();
+		Holder<Map<String,String>> outputs = new Holder<Map<String,String>>();
+		Holder<VnfRollback> vnfRollback = new Holder<VnfRollback>();
+
+		try {
+			vnfAdapter.createVnf(cloudId, tenantName, vnfType,null, vnfName, null, null, inputs, false, true, null,
+					vnfId, outputs, vnfRollback);
+		} catch (VnfException e) {
+			log ("Got a Create Exception: " + e);
+			System.exit(1);
+		}
+		
+		log ("Created VNF, ID = " + vnfId.value);
+		for (String key : outputs.value.keySet()) {
+			log ("   " + key + " = " + outputs.value.get(key));
+		}
+		if (vnfRollback.value != null)
+			log (vnfRollback.value.toString());
+		
+		try {
+			Thread.sleep(5000);
+		} catch (InterruptedException e) {}
+		
+		log ("Rolling Back VNF");
+		try {
+			vnfAdapter.rollbackVnf(vnfRollback.value);
+		} catch (VnfException e) {
+			log ("Got a Rollback Exception: " + e);
+		}
+		log ("VNF Rolled Back");
+	}
+	
+	private static void log (String msg) {
+		System.out.println (msg);
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfDeleteTest.java b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfDeleteTest.java
new file mode 100644
index 0000000..0c35da7
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfDeleteTest.java
@@ -0,0 +1,52 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.test;
+
+
+
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapter;
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapterImpl;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+
+public class VnfDeleteTest {
+	public final static void main (String args[])
+	{
+		MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl();
+		log ("Got a VnfAdapter");
+		
+		// Web Service Inputs
+		String cloudId = "MT";
+		String tenantName = "MSO_Test";
+		String vnfName = "AdapterTest";
+
+		try {
+			vnfAdapter.deleteVnf(cloudId, tenantName, vnfName, null);
+		} catch (VnfException e) {
+			log ("Got an Exception: " + e);
+		}
+		
+		log ("Deleted VNF");
+	}
+	
+	private static void log (String msg) {
+		System.out.println (msg);
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfQueryTest.java b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfQueryTest.java
new file mode 100644
index 0000000..923c222
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/test/java/org/openecomp/mso/adapters/vnf/test/VnfQueryTest.java
@@ -0,0 +1,64 @@
+/*-
+ * ============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=========================================================
+ */
+
+package org.openecomp.mso.adapters.vnf.test;
+
+
+import java.util.Map;
+
+import javax.xml.ws.Holder;
+
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapter;
+import org.openecomp.mso.adapters.vnf.MsoVnfAdapterImpl;
+import org.openecomp.mso.openstack.beans.VnfStatus;
+import org.openecomp.mso.adapters.vnf.exceptions.VnfException;
+
+public class VnfQueryTest {
+	public final static void main (String args[])
+	{
+		MsoVnfAdapter vnfAdapter = new MsoVnfAdapterImpl();
+		log ("Got a VnfAdapter");
+		
+		String cloudId = "MT";
+		String tenantId = "MSO_Test";
+		String vnfName = "VNF_TEST1";
+		Holder<Boolean> vnfExists = new Holder<Boolean>();
+		Holder<String> vnfId = new Holder<String>();
+		Holder<VnfStatus> status = new Holder<VnfStatus>();
+		Holder<Map<String,String>> outputs = new Holder<Map<String,String>>();
+		
+		try {
+			vnfAdapter.queryVnf(cloudId, tenantId, vnfName, null,
+						vnfExists, vnfId, status, outputs);
+		} catch (VnfException e) {
+			log ("Got an Exception: " + e);
+		}
+		
+		if (! vnfExists.value){
+			log ("VNF Not Found");
+		} else {
+			log ("Found VNF, ID = " + vnfId.value + ", status=" + status.value);
+		}
+	}
+	
+	private static void log (String msg) {
+		System.out.println (msg);
+	}
+}
diff --git a/adapters/mso-vnf-adapter/src/test/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector b/adapters/mso-vnf-adapter/src/test/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector
new file mode 100644
index 0000000..1281d32
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/test/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector
@@ -0,0 +1 @@
+com.woorea.openstack.connector.HttpClientConnector
\ No newline at end of file
diff --git a/adapters/mso-vnf-adapter/src/test/resources/logback-test.xml b/adapters/mso-vnf-adapter/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..238980e
--- /dev/null
+++ b/adapters/mso-vnf-adapter/src/test/resources/logback-test.xml
@@ -0,0 +1,48 @@
+<!--
+  ============LICENSE_START=======================================================
+  ECOMP 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=========================================================
+  -->
+
+<configuration >
+
+  
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{MM/dd-HH:mm:ss.SSS}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}||%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}||%X{Timer}|%msg%n</pattern>
+    </encoder>
+  </appender>
+
+
+  <logger name="com.att.eelf.audit" level="info" additivity="false">
+    <appender-ref ref="STDOUT" />
+  </logger>
+  
+  <logger name="com.att.eelf.metrics" level="info" additivity="false">
+        <appender-ref ref="STDOUT" />
+  </logger>
+
+  <logger name="com.att.eelf.error" level="trace" additivity="false">
+    <appender-ref ref="STDOUT" />
+  </logger> 
+
+  <root level="info">
+    <appender-ref ref="STDOUT" />
+  </root>
+ 
+
+</configuration>