Refactor SOL003 Adapter to organize its modules

based on functions

Change-Id: I10b2376a552272ac3b405b2dae718adcb7e1e489
Issue-ID: SO-2771
Signed-off-by: waqas.ikram <waqas.ikram@est.tech>
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/pom.xml b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/pom.xml
new file mode 100644
index 0000000..560cd87
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/pom.xml
@@ -0,0 +1,109 @@
+<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.onap.so.adapters</groupId>
+    <artifactId>etsi-sol003-lcm</artifactId>
+    <version>1.6.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>etsi-sol003-lcm-adapter</artifactId>
+  <name>ETSI SOL003 Life Cycle Management Adapter</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <systemPropertyVariables>
+            <so.log.level>DEBUG</so.log.level>
+          </systemPropertyVariables>
+          <rerunFailingTestsCount>2</rerunFailingTestsCount>
+          <parallel>suites</parallel>
+          <useUnlimitedThreads>false</useUnlimitedThreads>
+          <threadCount>1</threadCount>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>com.fasterxml.jackson.core</groupId>
+          <artifactId>jackson-databind</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-security</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.springframework.boot</groupId>
+          <artifactId>spring-boot-starter-tomcat</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so</groupId>
+      <artifactId>common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so.adapters</groupId>
+      <artifactId>etsi-sol003-adapter-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so.adapters</groupId>
+      <artifactId>etsi-sol003-lcm-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so.adapters</groupId>
+      <artifactId>etsi-sol003-lcm-ext-clients</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.core</groupId>
+      <artifactId>jersey-client</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.core</groupId>
+      <artifactId>jersey-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.inject</groupId>
+      <artifactId>jersey-hk2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.media</groupId>
+      <artifactId>jersey-media-json-jackson</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.yaml</groupId>
+      <artifactId>snakeyaml</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/LifeCycleManagementConstants.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/LifeCycleManagementConstants.java
new file mode 100644
index 0000000..c812863
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/LifeCycleManagementConstants.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter;
+
+/**
+ * ETSI SOL003 Life Cycle Management Adapter constants
+ *
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ */
+public class LifeCycleManagementConstants {
+
+    public static final String OPERATION_NOTIFICATION_ENDPOINT = "/lcn/VnfLcmOperationOccurrenceNotification";
+
+    private LifeCycleManagementConstants() {}
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/NvfmAdapterUtils.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/NvfmAdapterUtils.java
new file mode 100644
index 0000000..db34cbf
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/NvfmAdapterUtils.java
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.slf4j.Logger;
+import java.util.ArrayList;
+import java.util.Collection;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class NvfmAdapterUtils {
+    private static Logger logger = getLogger(NvfmAdapterUtils.class);
+
+    public static JsonObject child(JsonObject parent, String name) {
+        return childElement(parent, name).getAsJsonObject();
+    }
+
+    public static JsonElement childElement(JsonObject parent, String name) {
+        JsonElement child = parent.get(name);
+        if (child == null) {
+            throw abortOperation("Missing child " + name);
+        }
+        return child;
+    }
+
+    public static Collection<JsonObject> children(JsonObject parent) {
+        ArrayList<JsonObject> childElements = new ArrayList<>();
+        for (String childKey : parent.keySet()) {
+            if (parent.get(childKey).isJsonObject()) {
+                childElements.add(parent.get(childKey).getAsJsonObject());
+            }
+        }
+        return childElements;
+    }
+
+    public static RuntimeException abortOperation(String msg, Exception e) {
+        logger.error(msg, e);
+        return new RuntimeException(msg, e);
+    }
+
+    public static RuntimeException abortOperation(String msg) {
+        logger.error(msg);
+        return new RuntimeException(msg);
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/AbstractServiceProviderConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/AbstractServiceProviderConfiguration.java
new file mode 100644
index 0000000..8f6d853
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/AbstractServiceProviderConfiguration.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients;
+
+import com.google.gson.Gson;
+import java.util.Iterator;
+import org.onap.vnfmadapter.v1.JSON;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * A base class that can be extended by classes for configuring HttpRestServiceProvider classes. Provides common methods
+ * that will be useful to some such classes.
+ *
+ * @author gareth.roper@est.tech
+ */
+public abstract class AbstractServiceProviderConfiguration {
+
+    public void setGsonMessageConverter(final RestTemplate restTemplate) {
+        final Iterator<HttpMessageConverter<?>> iterator = restTemplate.getMessageConverters().iterator();
+        while (iterator.hasNext()) {
+            if (iterator.next() instanceof MappingJackson2HttpMessageConverter) {
+                iterator.remove();
+            }
+        }
+        final Gson gson = new JSON().getGson();
+        restTemplate.getMessageConverters().add(new GsonHttpMessageConverter(gson));
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/SdcPackageProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/SdcPackageProvider.java
new file mode 100644
index 0000000..735e1f9
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/SdcPackageProvider.java
@@ -0,0 +1,195 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ *  Modifications Copyright (c) 2019 Samsung
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients;
+
+import static com.google.common.base.Splitter.on;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.io.ByteStreams.toByteArray;
+import static java.lang.String.format;
+import static org.apache.http.HttpHeaders.ACCEPT;
+import static org.apache.http.HttpHeaders.AUTHORIZATION;
+import static org.onap.so.adapters.vnfmadapter.NvfmAdapterUtils.abortOperation;
+import static org.onap.so.adapters.vnfmadapter.NvfmAdapterUtils.child;
+import static org.onap.so.adapters.vnfmadapter.NvfmAdapterUtils.childElement;
+import static org.onap.so.adapters.vnfmadapter.NvfmAdapterUtils.children;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
+import com.google.common.io.ByteStreams;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import javax.net.ssl.SSLContext;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.onap.so.utils.CryptoUtils;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.Yaml;
+
+@Component
+public class SdcPackageProvider {
+    private static final String GET_PACKAGE_URL = "%s/sdc/v1/catalog/resources/%s/toscaModel";
+    @Value("${sdc.toscametapath:TOSCA-Metadata/TOSCA.meta}")
+    private List<String> toscaMetaPaths;
+    private static final String TOSCA_VNFD_KEY = "Entry-Definitions";
+    private static Logger logger = getLogger(SdcPackageProvider.class);
+
+    @Value("${sdc.username}")
+    private String sdcUsername;
+    @Value("${sdc.password}")
+    private String sdcPassword;
+    @Value("${sdc.key}")
+    private String sdcKey;
+    @Value("${sdc.endpoint}")
+    private String baseUrl;
+
+    public String getVnfdId(final String csarId) {
+        return getVnfNodeProperty(csarId, "descriptor_id");
+    }
+
+    private String getVnfNodeProperty(final String csarId, final String propertyName) {
+        logger.debug("Getting " + propertyName + " from " + csarId);
+        final byte[] onapPackage = getPackage(csarId);
+
+        try {
+            final String vnfdLocation = getVnfdLocation(new ByteArrayInputStream(onapPackage));
+            final String onapVnfdContent = getFileInZip(new ByteArrayInputStream(onapPackage), vnfdLocation).toString();
+            logger.debug("VNFD CONTENTS: " + onapVnfdContent);
+            final JsonObject root = new Gson().toJsonTree(new Yaml().load(onapVnfdContent)).getAsJsonObject();
+
+            final JsonObject topologyTemplates = child(root, "topology_template");
+            final JsonObject nodeTemplates = child(topologyTemplates, "node_templates");
+            for (final JsonObject child : children(nodeTemplates)) {
+                final String type = childElement(child, "type").getAsString();
+                String propertyValue = null;
+                if ("tosca.nodes.nfv.VNF".equals(type)) {
+                    final JsonObject properties = child(child, "properties");
+                    logger.debug("properties: " + properties.toString());
+
+                    propertyValue = properties.get(propertyName).getAsJsonPrimitive().getAsString();
+                }
+                if (propertyValue == null) {
+                    propertyValue = getValueFromNodeTypeDefinition(root, type, propertyName);
+                }
+                return propertyValue;
+            }
+
+        } catch (final Exception e) {
+            throw new IllegalArgumentException("Unable to extract " + propertyName + " from ONAP package", e);
+        }
+        throw new IllegalArgumentException("Unable to extract " + propertyName + " from ONAP package");
+    }
+
+    private String getValueFromNodeTypeDefinition(final JsonObject root, final String nodeTypeName,
+            final String propertyName) {
+        final JsonObject nodeTypes = child(root, "node_types");
+        final JsonObject nodeType = child(nodeTypes, nodeTypeName);
+
+        if ("tosca.nodes.nfv.VNF".equals(childElement(nodeType, "derived_from").getAsString())) {
+            final JsonObject properties = child(nodeType, "properties");
+            logger.debug("properties: " + properties.toString());
+            final JsonObject property = child(properties, propertyName);
+            logger.debug("property: " + property.toString());
+            logger.debug("property default: " + childElement(property, "default").toString());
+            return childElement(property, "default").getAsJsonPrimitive().getAsString();
+        }
+        return null;
+    }
+
+    private byte[] getPackage(final String csarId) {
+        final String SERVICE_NAME = "vnfm-adapter";
+        try (CloseableHttpClient client = HttpClients.custom().setSSLContext(SSLContext.getDefault()).build()) {
+            final HttpGet httpget = new HttpGet(format(GET_PACKAGE_URL, baseUrl, csarId));
+            httpget.setHeader(ACCEPT, APPLICATION_OCTET_STREAM_VALUE);
+            httpget.setHeader("X-ECOMP-InstanceID", SERVICE_NAME);
+            httpget.setHeader("X-FromAppId", SERVICE_NAME);
+            final String auth = sdcUsername + ":" + CryptoUtils.decrypt(sdcPassword, sdcKey);
+            final byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.ISO_8859_1));
+            final String authHeader = "Basic " + new String(encodedAuth);
+            httpget.setHeader(AUTHORIZATION, authHeader);
+            logger.debug("Fetching from SDC: " + httpget);
+            final CloseableHttpResponse response = client.execute(httpget);
+            final HttpEntity entity = response.getEntity();
+            final InputStream is = entity.getContent();
+            return toByteArray(is);
+        } catch (final Exception e) {
+            throw abortOperation("Unable to download " + csarId + " package from SDC", e);
+        }
+    }
+
+    private String getVnfdLocation(final InputStream stream) throws IOException {
+        final Iterator<String> pathIterator = toscaMetaPaths.iterator();
+        while (pathIterator.hasNext()) {
+            final String toscaMetadata = new String(getFileInZip(stream, pathIterator.next()).toByteArray());
+            if (!toscaMetadata.isEmpty()) {
+                final String toscaVnfdLine =
+                        filter(on("\n").split(toscaMetadata), line -> line.contains(TOSCA_VNFD_KEY)).iterator().next();
+                return toscaVnfdLine.replace(TOSCA_VNFD_KEY + ":", "").trim();
+            }
+        }
+        throw abortOperation("Unable to find valid Tosca Path");
+    }
+
+    private static ByteArrayOutputStream getFileInZip(final InputStream zip, final String path) throws IOException {
+        final ZipInputStream zipInputStream = new ZipInputStream(zip);
+        final ByteArrayOutputStream fileContent = getFileInZip(zipInputStream, path);
+        zipInputStream.close();
+        return fileContent;
+    }
+
+    private static ByteArrayOutputStream getFileInZip(final ZipInputStream zipInputStream, final String path)
+            throws IOException {
+        ZipEntry zipEntry;
+        final Set<String> items = new HashSet<>();
+        while ((zipEntry = zipInputStream.getNextEntry()) != null) {
+            items.add(zipEntry.getName());
+            if (zipEntry.getName().matches(path)) {
+                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+                ByteStreams.copy(zipInputStream, byteArrayOutputStream);
+                return byteArrayOutputStream;
+            }
+        }
+        logger.error("Unable to find the {} in archive found: {}", path, items);
+        throw new NoSuchElementException("Unable to find the " + path + " in archive found: " + items);
+    }
+
+    public String getFlavourId(final String csarId) {
+        return getVnfNodeProperty(csarId, "flavour_id");
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiClientProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiClientProvider.java
new file mode 100644
index 0000000..674314d
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiClientProvider.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.aai;
+
+import org.onap.so.client.aai.AAIResourcesClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AaiClientProvider {
+
+    @Bean
+    public AAIResourcesClient getAaiClient() {
+        return new AAIResourcesClient();
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiHelper.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiHelper.java
new file mode 100644
index 0000000..6a7bc6b
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiHelper.java
@@ -0,0 +1,217 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.aai;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.aai.domain.yang.EsrSystemInfoList;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.aai.domain.yang.EsrVnfmList;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.Relationship;
+import org.onap.aai.domain.yang.RelationshipData;
+import org.onap.aai.domain.yang.Vserver;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.TenantNotFoundException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmNotFoundException;
+import org.onap.vnfmadapter.v1.model.Tenant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Provides helper methods for interactions with AAI.
+ */
+@Service
+public class AaiHelper {
+
+    private static final Logger logger = LoggerFactory.getLogger(AaiHelper.class);
+    private final AaiServiceProvider aaiServiceProvider;
+    private final Map<String, OamIpAddressSource> mapOfVnfIdToOamIpAddressHolder = new HashMap<>();
+
+    @Autowired
+    public AaiHelper(final AaiServiceProvider aaiServiceProvider) {
+        this.aaiServiceProvider = aaiServiceProvider;
+    }
+
+    /**
+     * Get the VNFM assigned for use for the given generic VNF.
+     *
+     * @param vnf the generic VNF
+     * @return the VNFM to use, or <code>null</code> if no VNFM has been assigned yet
+     */
+    public EsrVnfm getAssignedVnfm(final GenericVnf vnf) {
+        final String vnfmId = getIdOfAssignedVnfm(vnf);
+        return vnfmId == null ? null : aaiServiceProvider.invokeGetVnfm(vnfmId);
+    }
+
+    /**
+     * Get the ID of the VNFM assigned for use for the given generic VNF.
+     *
+     * @param vnf the generic VNF
+     * @return the ID of the VNFM to use, or <code>null</code> if no VNFM has been assigned yet
+     */
+    public String getIdOfAssignedVnfm(final GenericVnf vnf) {
+        final Relationship relationship = getRelationship(vnf, "esr-vnfm");
+        return getRelationshipData(relationship, "esr-vnfm.vnfm-id");
+    }
+
+    /**
+     * Get the tenant assigned for use for the given generic VNF.
+     *
+     * @param vnf the generic VNF
+     * @return the tenant to use, or <code>null</code> if no tenant has been assigned yet
+     */
+    public Tenant getAssignedTenant(final GenericVnf vnf) {
+        final Relationship relationship = getRelationship(vnf, "tenant");
+        final String cloudOwner = getRelationshipData(relationship, "cloud-region.cloud-owner");
+        final String cloudRegion = getRelationshipData(relationship, "cloud-region.cloud-region-id");
+        final String tenantId = getRelationshipData(relationship, "tenant.tenant-id");
+        if (cloudOwner == null || cloudRegion == null || tenantId == null) {
+            throw new TenantNotFoundException("No matching Tenant found in AAI. VNFID: " + vnf.getVnfId());
+        } else {
+            return new Tenant().cloudOwner(cloudOwner).regionName(cloudRegion).tenantId(tenantId);
+        }
+    }
+
+    private Relationship getRelationship(final GenericVnf vnf, final String relationshipRelatedToValue) {
+        for (final Relationship relationship : vnf.getRelationshipList() == null ? Collections.<Relationship>emptyList()
+                : vnf.getRelationshipList().getRelationship()) {
+            if (relationship.getRelatedTo().equals(relationshipRelatedToValue)) {
+                return relationship;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get the value of the relationship data with the given key in the given relationship.
+     *
+     * @param relationship the relationship
+     * @param relationshipDataKey the key for the relationship data
+     * @return the value of the relationship data for the given key
+     */
+    public String getRelationshipData(final Relationship relationship, final String relationshipDataKey) {
+        if (relationship != null) {
+            for (final RelationshipData relationshipData : relationship.getRelationshipData()) {
+                if (relationshipData.getRelationshipKey().equals(relationshipDataKey)) {
+                    return relationshipData.getRelationshipValue();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Delete from the given VNF the relationship matching the given criteria.
+     *
+     * @param vnf the VNF
+     * @param relationshipRelatedToValue the related-to value for the relationship
+     * @param dataKey the relationship data key to match on
+     * @param dataValue the value the relationship data with the given key must match
+     * @return the deleted relationship or <code>null</code> if none found matching the given criteria
+     */
+    public Relationship deleteRelationshipWithDataValue(final GenericVnf vnf, final String relationshipRelatedToValue,
+            final String dataKey, final String dataValue) {
+        final Iterator<Relationship> relationships =
+                vnf.getRelationshipList() == null ? Collections.<Relationship>emptyList().iterator()
+                        : vnf.getRelationshipList().getRelationship().iterator();
+
+        while (relationships.hasNext()) {
+            final Relationship relationship = relationships.next();
+            if (relationship.getRelatedTo().equals(relationshipRelatedToValue)
+                    && dataValue.equals(getRelationshipData(relationship, dataKey))) {
+                relationships.remove();
+                return relationship;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Select a VNFM to use for the given generic VNF. Should only be used when no VNFM has already been assigned to the
+     * VNF.
+     *
+     * @param vnf the generic VNF
+     * @return the VNFM to use
+     */
+    public EsrVnfm selectVnfm(final GenericVnf vnf) {
+        final EsrVnfmList vnfmsInEsr = aaiServiceProvider.invokeGetVnfms();
+
+        if (vnfmsInEsr == null) {
+            throw new VnfmNotFoundException("No VNFMs found in AAI ESR");
+        }
+        logger.debug("VNFMs in ESR: " + vnfmsInEsr);
+
+        for (final EsrVnfm vnfm : vnfmsInEsr.getEsrVnfm()) {
+            final EsrSystemInfoList systemInfolist =
+                    aaiServiceProvider.invokeGetVnfmEsrSystemInfoList(vnfm.getVnfmId());
+            vnfm.setEsrSystemInfoList(systemInfolist);
+            if (vnfmHasMatchingEsrSystemInfoType(vnfm, vnf.getNfType())) {
+                return vnfm;
+            }
+        }
+        throw new VnfmNotFoundException("No matching VNFM found in AAI ESR");
+    }
+
+    private boolean vnfmHasMatchingEsrSystemInfoType(final EsrVnfm vnfm, final String type) {
+        logger.debug("Checking VNFM ID: " + vnfm + ": " + vnfm.getVnfmId());
+
+        final EsrSystemInfoList systemInfolist = vnfm.getEsrSystemInfoList();
+        if (systemInfolist != null) {
+            for (final EsrSystemInfo esrSystemInfo : systemInfolist.getEsrSystemInfo()) {
+                if (esrSystemInfo.getType().equals(type)) {
+                    logger.debug("Matched VNFM ID: " + vnfm + ", based on type");
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Create a vserver.
+     *
+     * @param vnfc the VNFC to base the vserver on
+     * @return the vserver
+     */
+    public Vserver createVserver(final LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs vnfc) {
+        final Vserver vserver = new Vserver();
+        vserver.setVserverId(vnfc.getComputeResource().getResourceId());
+        vserver.setVserverName(vnfc.getId());
+        vserver.setProvStatus("active");
+        vserver.setVserverSelflink("Not available");
+        return vserver;
+    }
+
+    public void setOamIpAddressSource(final String vnfId, final OamIpAddressSource oamIpAddressSource) {
+        mapOfVnfIdToOamIpAddressHolder.put(vnfId, oamIpAddressSource);
+    }
+
+    public OamIpAddressSource getOamIpAddressSource(final String vnfId) {
+        return mapOfVnfIdToOamIpAddressHolder.get(vnfId);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiPropertiesImpl.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiPropertiesImpl.java
new file mode 100644
index 0000000..cfaad3f
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiPropertiesImpl.java
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.aai;
+
+import org.onap.so.client.aai.AAIProperties;
+import org.onap.so.client.aai.AAIVersion;
+import org.onap.so.spring.SpringContextHelper;
+import org.springframework.context.ApplicationContext;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class AaiPropertiesImpl implements AAIProperties {
+
+    private final String endpoint;
+    private final String encryptedBasicAuth;
+    private final String encrytptionKey;
+
+    public AaiPropertiesImpl() {
+
+        final ApplicationContext context = SpringContextHelper.getAppContext();
+        this.endpoint = context.getEnvironment().getProperty("aai.endpoint");
+        this.encryptedBasicAuth = context.getEnvironment().getProperty("aai.auth");
+        this.encrytptionKey = context.getEnvironment().getProperty("mso.key");
+    }
+
+    @Override
+    public URL getEndpoint() throws MalformedURLException {
+        return new URL(endpoint);
+    }
+
+    @Override
+    public String getSystemName() {
+        return "MSO";
+    }
+
+    @Override
+    public AAIVersion getDefaultVersion() {
+        return AAIVersion.LATEST;
+    }
+
+    @Override
+    public String getAuth() {
+        return encryptedBasicAuth;
+    }
+
+    @Override
+    public String getKey() {
+        return encrytptionKey;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProvider.java
new file mode 100644
index 0000000..d19190d
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProvider.java
@@ -0,0 +1,148 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.aai;
+
+import org.onap.aai.domain.yang.EsrSystemInfoList;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.aai.domain.yang.EsrVnfmList;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.GenericVnfs;
+import org.onap.aai.domain.yang.Vserver;
+import org.onap.vnfmadapter.v1.model.Tenant;
+
+/**
+ * Provides methods for invoking REST calls to AAI.
+ */
+public interface AaiServiceProvider {
+
+    /**
+     * Invoke a get request for a generic VNF.
+     *
+     * @param vnfId the VNF id
+     * @return the generic VNF
+     */
+    GenericVnf invokeGetGenericVnf(final String vnfId);
+
+    /**
+     * Invoke a query for a generic VNF with the given selfLink
+     *
+     * @param selfLink the selfLink
+     * @return the matching generic vnfs
+     */
+    GenericVnfs invokeQueryGenericVnf(final String selfLink);
+
+    /**
+     * Invoke a GET request for the VNFMs.
+     *
+     * @return the VNFMs
+     */
+    EsrVnfmList invokeGetVnfms();
+
+    /**
+     * Invoke a GET request for the esr system info list for a VNFM.
+     *
+     * @return the esr system info list for the VNFM
+     */
+    EsrSystemInfoList invokeGetVnfmEsrSystemInfoList(final String vnfmId);
+
+    /**
+     * Invoke a GET request for the a VNFM.
+     *
+     * @param vnfmId the ID of the VNFM
+     * @return the VNFM
+     */
+    EsrVnfm invokeGetVnfm(final String vnfmId);
+
+    /**
+     * Invoke a PATCH request for a generic vnf.
+     *
+     * @param vnf the generic vnf
+     * @return
+     */
+    void invokePatchGenericVnf(GenericVnf vnf);
+
+    /**
+     * Invoke a PUT request for a relationship from a generic vnf to a VNFM.
+     *
+     * @param vnf the generic vnf
+     * @param vnfmId the ID of the VNFM
+     * @return
+     */
+    void invokePutGenericVnfToVnfmRelationship(GenericVnf vnf, final String vnfmId);
+
+
+    /**
+     * Invoke a PUT request for a vserver.
+     *
+     * @param cloudOwner the cloud owner
+     * @param cloudRegion the cloud region
+     * @param tenantId the ID of the tenant
+     * @param vserver the vserver
+     * @return
+     */
+    void invokePutVserver(final String cloudOwner, final String cloudRegion, final String tenantId,
+            final Vserver vserver);
+
+    /**
+     * Invoke a PUT request for a relationship from a vserver to a generic vnf.
+     *
+     * @param cloudOwner the cloud owner
+     * @param cloudRegion the cloud region the vserver is deployed on
+     * @param tenantId the ID of the tenant the vserver is deployed on
+     * @param vserver the vserver
+     * @param vnfId the ID of the generic vnf
+     * @return
+     */
+    void invokePutVserverToVnfRelationship(final String cloudOwner, final String cloudRegion, final String tenantId,
+            final Vserver vserver, final String vnfId);
+
+    /**
+     * Invoke a DELETE request for a vserver.
+     *
+     * @param cloudOwner the cloud owner
+     * @param cloudRegion the cloud region
+     * @param tenantId the ID of the tenant
+     * @param vserverId the ID of the vserver
+     * @return
+     */
+    void invokeDeleteVserver(final String cloudOwner, final String cloudRegion, final String tenantId,
+            final String vserverId);
+
+    /**
+     * Invoke a GET request for the a tenant.
+     *
+     * @param cloudOwner the cloud owner
+     * @param cloudRegion the cloud region
+     * @param tenantId the ID of the tenant
+     * @return the tenant
+     */
+    Tenant invokeGetTenant(final String cloudOwner, final String cloudRegion, final String tenantId);
+
+    /**
+     * Invoke a GET request for the esr system info list for a cloud region.
+     *
+     * @param cloudOwner the cloud owner
+     * @param cloudRegion the cloud region
+     * @return the esr system info list for the VNFM
+     */
+    EsrSystemInfoList invokeGetCloudRegionEsrSystemInfoList(final String cloudOwner, final String cloudRegion);
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProviderImpl.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProviderImpl.java
new file mode 100644
index 0000000..6dc6020
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProviderImpl.java
@@ -0,0 +1,158 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.aai;
+
+import org.onap.aai.domain.yang.EsrSystemInfoList;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.aai.domain.yang.EsrVnfmList;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.GenericVnfs;
+import org.onap.aai.domain.yang.Vserver;
+import org.onap.so.client.aai.AAIObjectType;
+import org.onap.so.client.aai.entities.uri.AAIUriFactory;
+import org.onap.so.client.graphinventory.entities.uri.Depth;
+import org.onap.vnfmadapter.v1.model.Tenant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AaiServiceProviderImpl implements AaiServiceProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(AaiServiceProviderImpl.class);
+    private final AaiClientProvider aaiClientProvider;
+
+    @Autowired
+    public AaiServiceProviderImpl(final AaiClientProvider aaiClientProvider) {
+        this.aaiClientProvider = aaiClientProvider;
+    }
+
+    @Override
+    public GenericVnf invokeGetGenericVnf(final String vnfId) {
+        return aaiClientProvider.getAaiClient()
+                .get(GenericVnf.class, AAIUriFactory.createResourceUri(AAIObjectType.GENERIC_VNF, vnfId))
+                .orElseGet(() -> {
+                    logger.debug("No vnf found in AAI with ID: {}", vnfId);
+                    return null;
+                });
+    }
+
+    @Override
+    public GenericVnfs invokeQueryGenericVnf(final String selfLink) {
+        return aaiClientProvider.getAaiClient()
+                .get(GenericVnfs.class,
+                        AAIUriFactory.createResourceUri(AAIObjectType.GENERIC_VNFS).queryParam("selflink", selfLink))
+                .orElseGet(() -> {
+                    logger.debug("No vnf found in AAI with selflink: {}", selfLink);
+                    return null;
+                });
+    }
+
+    @Override
+    public EsrVnfmList invokeGetVnfms() {
+        return aaiClientProvider.getAaiClient()
+                .get(EsrVnfmList.class, AAIUriFactory.createResourceUri(AAIObjectType.VNFM_LIST)).orElseGet(() -> {
+                    logger.debug("No VNFMs in AAI");
+                    return null;
+                });
+    }
+
+    @Override
+    public EsrVnfm invokeGetVnfm(final String vnfmId) {
+        return aaiClientProvider.getAaiClient()
+                .get(EsrVnfm.class, AAIUriFactory.createResourceUri(AAIObjectType.VNFM, vnfmId).depth(Depth.ONE))
+                .orElseGet(() -> {
+                    logger.debug("VNFM not found in AAI");
+                    return null;
+                });
+    }
+
+    @Override
+    public EsrSystemInfoList invokeGetVnfmEsrSystemInfoList(final String vnfmId) {
+        return aaiClientProvider.getAaiClient()
+                .get(EsrSystemInfoList.class,
+                        AAIUriFactory.createResourceUri(AAIObjectType.VNFM_ESR_SYSTEM_INFO_LIST, vnfmId))
+                .orElseGet(() -> {
+                    logger.debug("VNFM ESR system info list not found in AAI");
+                    return null;
+                });
+    }
+
+    @Override
+    public void invokePatchGenericVnf(final GenericVnf vnf) {
+        aaiClientProvider.getAaiClient()
+                .update(AAIUriFactory.createResourceUri(AAIObjectType.GENERIC_VNF, vnf.getVnfId()), vnf);
+    }
+
+    @Override
+    public void invokePutGenericVnfToVnfmRelationship(final GenericVnf vnf, final String vnfmId) {
+        aaiClientProvider.getAaiClient().connect(AAIUriFactory.createResourceUri(AAIObjectType.VNFM, vnfmId),
+                AAIUriFactory.createResourceUri(AAIObjectType.GENERIC_VNF, vnf.getVnfId()));
+    }
+
+    @Override
+    public void invokePutVserver(final String cloudOwner, final String cloudRegion, final String tenant,
+            final Vserver vserver) {
+        aaiClientProvider.getAaiClient().create(AAIUriFactory.createResourceUri(AAIObjectType.VSERVER, cloudOwner,
+                cloudRegion, tenant, vserver.getVserverId()), vserver);
+    }
+
+    @Override
+    public void invokePutVserverToVnfRelationship(final String cloudOwner, final String cloudRegion,
+            final String tenant, final Vserver vserver, final String vnfId) {
+        aaiClientProvider.getAaiClient()
+                .connect(
+                        AAIUriFactory.createResourceUri(AAIObjectType.VSERVER, cloudOwner, cloudRegion, tenant,
+                                vserver.getVserverId()),
+                        AAIUriFactory.createResourceUri(AAIObjectType.GENERIC_VNF, vnfId));
+    }
+
+    @Override
+    public void invokeDeleteVserver(final String cloudOwner, final String cloudRegion, final String tenant,
+            final String vserverId) {
+        aaiClientProvider.getAaiClient().delete(
+                AAIUriFactory.createResourceUri(AAIObjectType.VSERVER, cloudOwner, cloudRegion, tenant, vserverId));
+    }
+
+    @Override
+    public Tenant invokeGetTenant(final String cloudOwner, final String cloudRegion, final String tenantId) {
+        return aaiClientProvider.getAaiClient()
+                .get(Tenant.class,
+                        AAIUriFactory.createResourceUri(AAIObjectType.TENANT, cloudOwner, cloudRegion, tenantId))
+                .orElseGet(() -> {
+                    logger.debug("Tenant not found in AAI");
+                    return null;
+                });
+    }
+
+    @Override
+    public EsrSystemInfoList invokeGetCloudRegionEsrSystemInfoList(final String cloudOwner, final String cloudRegion) {
+        return aaiClientProvider
+                .getAaiClient().get(EsrSystemInfoList.class, AAIUriFactory
+                        .createResourceUri(AAIObjectType.CLOUD_ESR_SYSTEM_INFO_LIST, cloudOwner, cloudRegion))
+                .orElseGet(() -> {
+                    logger.debug("Cloud esr system info list not found in AAI");
+                    return null;
+                });
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/OamIpAddressSource.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/OamIpAddressSource.java
new file mode 100644
index 0000000..311c4de
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/OamIpAddressSource.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.aai;
+
+/**
+ * Represents the source of the value to use as the AAI OAM IP address of a VNF
+ */
+public class OamIpAddressSource {
+
+    private final OamIpAddressType type;
+    private final String value;
+
+    public OamIpAddressSource(final OamIpAddressType type, final String value) {
+        this.type = type;
+        this.value = value;
+    }
+
+    public OamIpAddressType getType() {
+        return type;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public enum OamIpAddressType {
+        /**
+         * The value passed in {@link OamIpAddress#OamIpAddress(OamIpAddressType, String)} is to be used directly as the
+         * OAM IP address
+         */
+        LITERAL,
+        /**
+         * The OAM IP address is to be retrieved from the vnfConfigurableProperties returned from the VNFM using the
+         * value passed in {@link OamIpAddress#OamIpAddress(OamIpAddressType, String)} as the name of a property
+         */
+        CONFIGURABLE_PROPERTY
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/AccessInfo.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/AccessInfo.java
new file mode 100644
index 0000000..9ab7618
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/AccessInfo.java
@@ -0,0 +1,107 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vim.model;
+
+import java.util.Objects;
+
+public class AccessInfo {
+
+    protected String projectId;
+    protected String projectName;
+    protected String domainName;
+    protected VimCredentials credentials;
+
+    public String getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(final String value) {
+        projectId = value;
+    }
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public void setProjectName(final String value) {
+        projectName = value;
+    }
+
+    public String getDomainName() {
+        return domainName;
+    }
+
+    public void setDomainName(final String value) {
+        domainName = value;
+    }
+
+    public VimCredentials getCredentials() {
+        return credentials;
+    }
+
+    public void setCredentials(final VimCredentials value) {
+        credentials = value;
+    }
+
+    @Override
+    public boolean equals(final java.lang.Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final AccessInfo accessInfo = (AccessInfo) o;
+        return Objects.equals(this.projectId, accessInfo.projectId)
+                && Objects.equals(this.projectName, accessInfo.projectName)
+                && Objects.equals(this.domainName, accessInfo.domainName)
+                && Objects.equals(this.credentials, accessInfo.credentials);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(projectId, projectName, domainName, credentials);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("class AccessInfo {\n");
+
+        sb.append("    projectId: ").append(toIndentedString(projectId)).append("\n");
+        sb.append("    projectName: ").append(toIndentedString(projectName)).append("\n");
+        sb.append("    domainName: ").append(toIndentedString(domainName)).append("\n");
+        sb.append("    credentials: ").append(toIndentedString(credentials)).append("\n");
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Convert the given object to string with each line indented by 4 spaces (except the first line).
+     */
+    private String toIndentedString(final java.lang.Object o) {
+        if (o == null) {
+            return "null";
+        }
+        return o.toString().replace("\n", "\n    ");
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/InterfaceInfo.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/InterfaceInfo.java
new file mode 100644
index 0000000..ae28ea5
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/InterfaceInfo.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vim.model;
+
+import java.util.Objects;
+
+public class InterfaceInfo {
+
+    protected String identityEndPoint;
+
+    public String getIdentityEndPoint() {
+        return identityEndPoint;
+    }
+
+    public void setIdentityEndPoint(final String value) {
+        identityEndPoint = value;
+    }
+
+    @Override
+    public boolean equals(final java.lang.Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final InterfaceInfo interfaceInfo = (InterfaceInfo) o;
+        return Objects.equals(this.identityEndPoint, interfaceInfo.identityEndPoint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(identityEndPoint);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("class InterfaceInfo {\n");
+
+        sb.append("    identityEndPoint: ").append(toIndentedString(identityEndPoint)).append("\n");
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Convert the given object to string with each line indented by 4 spaces (except the first line).
+     */
+    private String toIndentedString(final java.lang.Object o) {
+        if (o == null) {
+            return "null";
+        }
+        return o.toString().replace("\n", "\n    ");
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/VimCredentials.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/VimCredentials.java
new file mode 100644
index 0000000..63b00b4
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vim/model/VimCredentials.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vim.model;
+
+import java.util.Objects;
+
+public class VimCredentials {
+
+    protected String username;
+
+    protected String password;
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(final String value) {
+        username = value;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(final String password) {
+        this.password = password;
+    }
+
+    @Override
+    public boolean equals(final java.lang.Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final VimCredentials vimCredentials = (VimCredentials) o;
+        return Objects.equals(this.username, vimCredentials.username)
+                && Objects.equals(this.password, vimCredentials.password);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(username, password);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("class AccessInfo {\n");
+
+        sb.append("    username: ").append(toIndentedString(username)).append("\n");
+        sb.append("    password: ").append(toIndentedString(password)).append("\n");
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Convert the given object to string with each line indented by 4 spaces (except the first line).
+     */
+    private String toIndentedString(final java.lang.Object o) {
+        if (o == null) {
+            return "null";
+        }
+        return o.toString().replace("\n", "\n    ");
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java
new file mode 100644
index 0000000..e9f8f2f
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
+
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.so.adapters.vnfmadapter.common.VnfmAdapterUrlProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.vim.model.AccessInfo;
+import org.onap.so.adapters.vnfmadapter.extclients.vim.model.InterfaceInfo;
+import org.onap.so.adapters.vnfmadapter.extclients.vim.model.VimCredentials;
+import org.onap.so.adapters.vnfmadapter.grant.model.InlineResponse201VimConnections;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InstantiateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.LccnSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthentication.AuthTypeEnum;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthenticationParamsBasic;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthenticationParamsOauth2ClientCredentials;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsFilter;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsFilter.NotificationTypesEnum;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsFilterVnfInstanceSubscriptionFilter;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.VnfInstancesvnfInstanceIdinstantiateExtVirtualLinks;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.VnfInstancesvnfInstanceIdinstantiateVimConnectionInfo;
+import org.onap.vnfmadapter.v1.model.CreateVnfRequest;
+import org.onap.vnfmadapter.v1.model.ExternalVirtualLink;
+import org.onap.vnfmadapter.v1.model.Tenant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+/**
+ * Provides helper methods for interactions with VNFM.
+ */
+@Service
+public class VnfmHelper {
+
+    private static final Logger logger = LoggerFactory.getLogger(VnfmHelper.class);
+    private static final String SEPARATOR = "_";
+    private final AaiServiceProvider aaiServiceProvider;
+    private final VnfmAdapterUrlProvider vnfmAdapterUrlProvider;
+
+    @Autowired
+    public VnfmHelper(final AaiServiceProvider aaiServiceProvider,
+            final VnfmAdapterUrlProvider vnfmAdapterUrlProvider) {
+        this.aaiServiceProvider = aaiServiceProvider;
+        this.vnfmAdapterUrlProvider = vnfmAdapterUrlProvider;
+    }
+
+    /**
+     * Create an {@link InstantiateVnfRequest} to send in an instantiation request to a VNFM.
+     *
+     * @param tenant the tenant the request is to be fulfilled on
+     * @param createVnfRequest the request received by the VNFM adapter
+     */
+    public InstantiateVnfRequest createInstantiateRequest(final Tenant tenant, final CreateVnfRequest createVnfRequest,
+            final String flavourId) {
+        final InstantiateVnfRequest instantiateVnfRequest = new InstantiateVnfRequest();
+        instantiateVnfRequest.setFlavourId(flavourId);
+        instantiateVnfRequest.setVimConnectionInfo(getVimConnectionInfos(tenant));
+        instantiateVnfRequest
+                .setAdditionalParams(getAdditionalParametersAsJsonObject(createVnfRequest.getAdditionalParams()));
+        instantiateVnfRequest.setExtVirtualLinks(getExternalVirtualLinks(createVnfRequest.getExternalVirtualLinks()));
+        createVnfRequest.getExternalVirtualLinks();
+        return instantiateVnfRequest;
+    }
+
+    private List<VnfInstancesvnfInstanceIdinstantiateVimConnectionInfo> getVimConnectionInfos(final Tenant tenant) {
+        final List<VnfInstancesvnfInstanceIdinstantiateVimConnectionInfo> connectionInfos = new ArrayList<>();
+        connectionInfos.add(getVimConnectionInfo(tenant));
+        return connectionInfos;
+    }
+
+    private VnfInstancesvnfInstanceIdinstantiateVimConnectionInfo getVimConnectionInfo(final Tenant tenant) {
+        final EsrSystemInfo esrSystemInfo =
+                aaiServiceProvider.invokeGetCloudRegionEsrSystemInfoList(tenant.getCloudOwner(), tenant.getRegionName())
+                        .getEsrSystemInfo().iterator().next();
+
+        final VnfInstancesvnfInstanceIdinstantiateVimConnectionInfo vnfInstancesVimConnectionInfo =
+                new VnfInstancesvnfInstanceIdinstantiateVimConnectionInfo();
+        final String vimId = createVimId(tenant.getCloudOwner(), tenant.getRegionName());
+        vnfInstancesVimConnectionInfo.setId(vimId);
+        vnfInstancesVimConnectionInfo.setVimId(vimId);
+        vnfInstancesVimConnectionInfo.setVimType(esrSystemInfo.getType());
+        vnfInstancesVimConnectionInfo.setInterfaceInfo(getInterfaceInfo(esrSystemInfo.getServiceUrl()));
+        vnfInstancesVimConnectionInfo.setAccessInfo(getAccessInfo(esrSystemInfo, tenant.getTenantId()));
+        return vnfInstancesVimConnectionInfo;
+    }
+
+    private InterfaceInfo getInterfaceInfo(final String url) {
+        final InterfaceInfo interfaceInfo = new InterfaceInfo();
+        interfaceInfo.setIdentityEndPoint(url);
+        return interfaceInfo;
+    }
+
+    private AccessInfo getAccessInfo(final EsrSystemInfo esrSystemInfo, final String tenantId) {
+        final AccessInfo accessInfo = new AccessInfo();
+        accessInfo.setProjectId(tenantId);
+        accessInfo.setDomainName(esrSystemInfo.getCloudDomain());
+
+        final VimCredentials vimCredentials = new VimCredentials();
+        vimCredentials.setUsername(esrSystemInfo.getUserName());
+        vimCredentials.setPassword(esrSystemInfo.getPassword());
+        accessInfo.setCredentials(vimCredentials);
+        return accessInfo;
+    }
+
+    private String createVimId(final String cloudOwner, final String cloudRegion) {
+        return cloudOwner + SEPARATOR + cloudRegion;
+    }
+
+    private JsonObject getAdditionalParametersAsJsonObject(final Map<String, String> additionalParameters) {
+        final JsonObject additionalParametersJsonObject = new JsonObject();
+        if (additionalParameters != null) {
+            for (final Map.Entry<String, JsonElement> item : new Gson().toJsonTree(additionalParameters)
+                    .getAsJsonObject().entrySet()) {
+                additionalParametersJsonObject.add(item.getKey(), item.getValue());
+            }
+        } else {
+            logger.warn("No additional parameters were specified for the operation");
+        }
+        return additionalParametersJsonObject;
+    }
+
+    private List<VnfInstancesvnfInstanceIdinstantiateExtVirtualLinks> getExternalVirtualLinks(
+            final List<ExternalVirtualLink> extVirtualLinks) {
+        if (extVirtualLinks != null) {
+            final String extVirtualLinksJsonObject =
+                    new Gson().toJson(extVirtualLinks, new TypeToken<List<ExternalVirtualLink>>() {}.getType());
+            return new Gson().fromJson(extVirtualLinksJsonObject,
+                    new TypeToken<List<VnfInstancesvnfInstanceIdinstantiateExtVirtualLinks>>() {}.getType());
+        }
+        return null;
+    }
+
+    /**
+     * Create a {@link LccnSubscriptionRequest} to send in an notification subscription request to a VNFM.
+     *
+     * @param the ID of the VNF notifications are required for
+     * @return the request
+     * @throws GeneralSecurityException
+     */
+    public LccnSubscriptionRequest createNotificationSubscriptionRequest(final String vnfId)
+            throws GeneralSecurityException {
+        final LccnSubscriptionRequest lccnSubscriptionRequest = new LccnSubscriptionRequest();
+        lccnSubscriptionRequest.setAuthentication(getSubscriptionsAuthentication());
+        lccnSubscriptionRequest.setCallbackUri(vnfmAdapterUrlProvider.getVnfLcmOperationOccurrenceNotificationUrl());
+        final SubscriptionsFilter filter = new SubscriptionsFilter();
+        filter.addNotificationTypesItem(NotificationTypesEnum.VNFLCMOPERATIONOCCURRENCENOTIFICATION);
+        final SubscriptionsFilterVnfInstanceSubscriptionFilter vnfInstanceSubscriptionFilter =
+                new SubscriptionsFilterVnfInstanceSubscriptionFilter();
+        vnfInstanceSubscriptionFilter.addVnfInstanceIdsItem(vnfId);
+        filter.setVnfInstanceSubscriptionFilter(vnfInstanceSubscriptionFilter);
+        lccnSubscriptionRequest.setFilter(filter);
+        return lccnSubscriptionRequest;
+    }
+
+    private SubscriptionsAuthentication getSubscriptionsAuthentication() throws GeneralSecurityException {
+        final SubscriptionsAuthentication authentication = new SubscriptionsAuthentication();
+
+        final ImmutablePair<String, String> decrypedAuth = vnfmAdapterUrlProvider.getDecryptAuth();
+
+        final SubscriptionsAuthenticationParamsOauth2ClientCredentials oauthParams =
+                new SubscriptionsAuthenticationParamsOauth2ClientCredentials();
+        oauthParams.setTokenEndpoint(vnfmAdapterUrlProvider.getOauthTokenUrl());
+        oauthParams.clientId(decrypedAuth.getLeft());
+        oauthParams.setClientPassword(decrypedAuth.getRight());
+        authentication.addAuthTypeItem(AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS);
+        authentication.paramsOauth2ClientCredentials(oauthParams);
+
+        final SubscriptionsAuthenticationParamsBasic basicAuthParams = new SubscriptionsAuthenticationParamsBasic();
+        basicAuthParams.setUserName(decrypedAuth.getLeft());
+        basicAuthParams.setPassword(decrypedAuth.getRight());
+        authentication.addAuthTypeItem(AuthTypeEnum.BASIC);
+        authentication.paramsBasic(basicAuthParams);
+
+        authentication.addAuthTypeItem(AuthTypeEnum.TLS_CERT);
+        return authentication;
+    }
+
+    /**
+     * Get the VIM connections for a tenant
+     *
+     * @param tenant the tenant
+     * @return the VIM connections
+     */
+    public InlineResponse201VimConnections getVimConnections(final Tenant tenant) {
+        final EsrSystemInfo esrSystemInfo =
+                aaiServiceProvider.invokeGetCloudRegionEsrSystemInfoList(tenant.getCloudOwner(), tenant.getRegionName())
+                        .getEsrSystemInfo().iterator().next();
+
+        final InlineResponse201VimConnections vimConnection = new InlineResponse201VimConnections();
+        vimConnection.setId(createVimId(tenant.getCloudOwner(), tenant.getRegionName()));
+        vimConnection.setVimId(vimConnection.getId());
+        vimConnection.setVimType(esrSystemInfo.getType());
+        vimConnection.setInterfaceInfo(getInterfaceInfo(esrSystemInfo.getServiceUrl()));
+        vimConnection.setAccessInfo(getAccessInfo(esrSystemInfo, tenant.getTenantId()));
+        return vimConnection;
+    }
+
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java
new file mode 100644
index 0000000..cb8c7c4
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
+
+import com.google.common.base.Optional;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.CreateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse2001;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InstantiateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.LccnSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.TerminateVnfRequest;
+
+/**
+ * Provides methods for invoking REST calls to a VNFM.
+ */
+public interface VnfmServiceProvider {
+
+    /**
+     * Invoke a get request for a VNF.
+     *
+     * @param vnfm the VNFM in AAI
+     * @param vnfSelfLink the link to the VNF in the VNFM
+     * @return the VNF from the VNFM
+     */
+    Optional<InlineResponse201> getVnf(final EsrVnfm vnfm, final String vnfSelfLink);
+
+    /**
+     * Invoke an instantiate request for a VNF.
+     *
+     * @param vnfm the VNFM in AAI
+     * @param vnfSelfLink the link to he VNF on the VNFM
+     * @param instantiateVnfRequest the instantiate request
+     * @return the operation ID of the instantiation operation
+     */
+    String instantiateVnf(final EsrVnfm vnfm, final String vnfSelfLink,
+            final InstantiateVnfRequest instantiateVnfRequest);
+
+    /**
+     * Invoke a notification subscription request to a VNFM.
+     *
+     * @param vnfm the VNFM in AAI
+     * @param subscriptionRequest
+     * @return the response to the subscription request
+     */
+    InlineResponse2001 subscribeForNotifications(final EsrVnfm vnfm, final LccnSubscriptionRequest subscriptionRequest);
+
+    /**
+     * Invoke a terminate request for a VNF.
+     *
+     * @param vnfm the VNFM in AAI
+     * @param vnfSelfLink the link to he VNF on the VNFM
+     * @param terminateVnfRequest the terminate request
+     * @return the operation ID of the termination operation
+     */
+    String terminateVnf(final EsrVnfm vnfm, final String vnfSelfLink, final TerminateVnfRequest terminateVnfRequest);
+
+    /**
+     * Invoke a delete request for a VNF.
+     *
+     * @param vnfm the VNFM in AAI
+     * @param vnfSelfLink the link to he VNF on the VNFM
+     * @return the operation ID of the instantiation operation
+     */
+    void deleteVnf(final EsrVnfm vnfm, final String vnfSelfLink);
+
+    /**
+     * Invoke a get request for a VNFM operation.
+     *
+     * @param vnfm the VNFM in AAI
+     * @param operationId the id of the operation on the VNFM
+     * @return the operation from the VNFM
+     */
+    Optional<InlineResponse200> getOperation(final EsrVnfm vnfm, final String operationId);
+
+    /**
+     * Invoke a create request to a VNFM
+     *
+     * @param vnfm the VNFM in AAI
+     * @param createVnfRequest the parameters for creating a VNF
+     * @return the newly created VNF
+     */
+    Optional<InlineResponse201> createVnf(final EsrVnfm vnfm, final CreateVnfRequest createVnfRequest);
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java
new file mode 100644
index 0000000..eaaa8d8
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java
@@ -0,0 +1,154 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
+
+import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.net.ssl.SSLContext;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.so.adapters.vnfmadapter.extclients.AbstractServiceProviderConfiguration;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.onap.so.rest.service.HttpRestServiceProviderImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.http.client.BufferingClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.security.oauth2.client.OAuth2RestTemplate;
+import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Configures the HttpRestServiceProvider for REST call to a VNFM.
+ */
+@Configuration
+public class VnfmServiceProviderConfiguration extends AbstractServiceProviderConfiguration {
+
+    private static final Logger logger = LoggerFactory.getLogger(VnfmServiceProviderConfiguration.class);
+    private Map<String, HttpRestServiceProvider> mapOfVnfmIdToHttpRestServiceProvider = new ConcurrentHashMap<>();
+
+    @Value("${http.client.ssl.trust-store:#{null}}")
+    private Resource trustStore;
+    @Value("${http.client.ssl.trust-store-password:#{null}}")
+    private String trustStorePassword;
+
+    @Value("${server.ssl.key-store:#{null}}")
+    private Resource keyStoreResource;
+    @Value("${server.ssl.key--store-password:#{null}}")
+    private String keyStorePassword;
+
+    /**
+     * This property is only intended to be temporary until the AAI schema is updated to support setting the endpoint
+     */
+    @Value("${vnfmadapter.temp.vnfm.oauth.endpoint:#{null}}")
+    private String oauthEndpoint;
+
+    @Qualifier(CONFIGURABLE_REST_TEMPLATE)
+    @Autowired()
+    private RestTemplate defaultRestTemplate;
+
+    public HttpRestServiceProvider getHttpRestServiceProvider(final EsrVnfm vnfm) {
+        if (!mapOfVnfmIdToHttpRestServiceProvider.containsKey(vnfm.getVnfmId())) {
+            mapOfVnfmIdToHttpRestServiceProvider.put(vnfm.getVnfmId(), createHttpRestServiceProvider(vnfm));
+        }
+        return mapOfVnfmIdToHttpRestServiceProvider.get(vnfm.getVnfmId());
+    }
+
+    private HttpRestServiceProvider createHttpRestServiceProvider(final EsrVnfm vnfm) {
+        final RestTemplate restTemplate = createRestTemplate(vnfm);
+        setGsonMessageConverter(restTemplate);
+        if (trustStore != null) {
+            setTrustStore(restTemplate);
+        }
+        return new HttpRestServiceProviderImpl(restTemplate, new BasicHttpHeadersProvider().getHttpHeaders());
+    }
+
+    private RestTemplate createRestTemplate(final EsrVnfm vnfm) {
+        if (vnfm != null) {
+            for (final EsrSystemInfo esrSystemInfo : vnfm.getEsrSystemInfoList().getEsrSystemInfo()) {
+                if (!StringUtils.isEmpty(esrSystemInfo.getUserName())
+                        && !StringUtils.isEmpty(esrSystemInfo.getPassword())) {
+                    return createOAuth2RestTemplate(esrSystemInfo);
+                }
+            }
+        }
+        return defaultRestTemplate;
+    }
+
+    private OAuth2RestTemplate createOAuth2RestTemplate(final EsrSystemInfo esrSystemInfo) {
+        logger.debug("Getting OAuth2RestTemplate ...");
+        final ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
+        resourceDetails.setId(UUID.randomUUID().toString());
+        resourceDetails.setClientId(esrSystemInfo.getUserName());
+        resourceDetails.setClientSecret(esrSystemInfo.getPassword());
+        resourceDetails.setAccessTokenUri(
+                oauthEndpoint == null ? esrSystemInfo.getServiceUrl().replace("vnflcm/v1", "oauth/token")
+                        : oauthEndpoint);
+        resourceDetails.setGrantType("client_credentials");
+        return new OAuth2RestTemplate(resourceDetails);
+    }
+
+    private void setTrustStore(final RestTemplate restTemplate) {
+        SSLContext sslContext;
+        try {
+            if (keyStoreResource != null) {
+                KeyStore keystore = KeyStore.getInstance("pkcs12");
+                keystore.load(keyStoreResource.getInputStream(), keyStorePassword.toCharArray());
+                sslContext =
+                        new SSLContextBuilder().loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray())
+                                .loadKeyMaterial(keystore, keyStorePassword.toCharArray()).build();
+            } else {
+                sslContext = new SSLContextBuilder()
+                        .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray()).build();
+            }
+            logger.info("Setting truststore: {}", trustStore.getURL());
+            final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
+            final HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
+            final HttpComponentsClientHttpRequestFactory factory =
+                    new HttpComponentsClientHttpRequestFactory(httpClient);
+            restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(factory));
+        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
+                | IOException | UnrecoverableKeyException exception) {
+            logger.error("Error reading truststore, TLS connection to VNFM will fail.", exception);
+        }
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java
new file mode 100644
index 0000000..d898de3
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java
@@ -0,0 +1,194 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
+
+import com.google.common.base.Optional;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.CreateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse2001;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201.InstantiationStateEnum;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InstantiateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.LccnSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.TerminateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.jobmanagement.JobManager;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmRequestFailureException;
+import org.onap.so.rest.exceptions.RestProcessingException;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+@Service
+public class VnfmServiceProviderImpl implements VnfmServiceProvider {
+    private static final Logger logger = LoggerFactory.getLogger(VnfmServiceProviderImpl.class);
+
+    private final VnfmServiceProviderConfiguration vnfmServiceProviderConfiguration;
+    private final VnfmUrlProvider urlProvider;
+
+    @Autowired
+    public VnfmServiceProviderImpl(final VnfmUrlProvider urlProvider,
+            VnfmServiceProviderConfiguration vnfmServiceProviderConfiguration) {
+        this.vnfmServiceProviderConfiguration = vnfmServiceProviderConfiguration;
+        this.urlProvider = urlProvider;
+    }
+
+    @Override
+    public Optional<InlineResponse201> getVnf(final EsrVnfm vnfm, final String vnfSelfLink) {
+        return getHttpServiceProvider(vnfm).get(vnfSelfLink, InlineResponse201.class);
+    }
+
+    @Override
+    public String instantiateVnf(final EsrVnfm vnfm, final String vnfSelfLink,
+            final InstantiateVnfRequest instantiateVnfRequest) {
+        logger.debug("Sending instantiate request " + instantiateVnfRequest + " to : " + vnfSelfLink);
+
+        ResponseEntity<Void> response = null;
+        try {
+            response = getHttpServiceProvider(vnfm).postHttpRequest(instantiateVnfRequest, vnfSelfLink + "/instantiate",
+                    Void.class);
+        } catch (final Exception exception) {
+            final String errorMessage =
+                    "Instantiate request to " + vnfSelfLink + " resulted in exception" + instantiateVnfRequest;
+            logger.error(errorMessage, exception);
+            throw new VnfmRequestFailureException(errorMessage, exception);
+        }
+        if (response.getStatusCode() != HttpStatus.ACCEPTED) {
+            final String errorMessage = "Instantiate request to " + vnfSelfLink + " returned status code: "
+                    + response.getStatusCode() + ", request: " + instantiateVnfRequest;
+            logger.error(errorMessage);
+            throw new VnfmRequestFailureException(errorMessage);
+        }
+        final String locationHeader = response.getHeaders().get("Location").iterator().next();
+        return locationHeader.substring(locationHeader.lastIndexOf("/") + 1);
+    }
+
+    @Override
+    public InlineResponse2001 subscribeForNotifications(final EsrVnfm vnfm,
+            final LccnSubscriptionRequest subscriptionRequest) {
+        logger.info("Subscribing for notifications {}", subscriptionRequest);
+        final String url = urlProvider.getSubscriptionsUrl(vnfm.getVnfmId());
+        ResponseEntity<InlineResponse2001> response = null;
+        try {
+            response = getHttpServiceProvider(vnfm).postHttpRequest(subscriptionRequest, url, InlineResponse2001.class);
+            logger.info("Subscribing for notifications response {}", response);
+        } catch (final Exception exception) {
+            final String errorMessage =
+                    "Subscription to VNFM " + vnfm.getVnfmId() + " resulted in exception" + subscriptionRequest;
+            logger.error(errorMessage, exception);
+            throw new VnfmRequestFailureException(errorMessage, exception);
+        }
+        if (response.getStatusCode() != HttpStatus.CREATED) {
+            final String errorMessage = "Subscription to VNFM " + vnfm.getVnfmId() + " returned status code: "
+                    + response.getStatusCode() + ", request: " + subscriptionRequest;
+            logger.error(errorMessage);
+            throw new VnfmRequestFailureException(errorMessage);
+        }
+        return response.getBody();
+    }
+
+    @Override
+    public String terminateVnf(final EsrVnfm vnfm, final String vnfSelfLink,
+            final TerminateVnfRequest terminateVnfRequest) {
+        logger.debug("Sending terminate request " + terminateVnfRequest + " to : " + vnfSelfLink);
+
+        ResponseEntity<Void> response = null;
+        try {
+            response = getHttpServiceProvider(vnfm).postHttpRequest(terminateVnfRequest, vnfSelfLink + "/terminate",
+                    Void.class);
+        } catch (final RestProcessingException restProcessingException) {
+            if (restProcessingException.getStatusCode() == HttpStatus.CONFLICT.value()) {
+                InlineResponse201 vnf = getVnf(vnfm, vnfSelfLink).get();
+                if (vnf.getInstantiationState().equals(InstantiationStateEnum.NOT_INSTANTIATED)) {
+                    return JobManager.ALREADY_COMPLETED_OPERATION_ID;
+                } else {
+                    final String errorMessage =
+                            "Terminate request to " + vnfSelfLink + " resulted in exception" + terminateVnfRequest;
+                    logger.error(errorMessage, restProcessingException);
+                    throw new VnfmRequestFailureException(errorMessage, restProcessingException);
+                }
+            }
+        } catch (final Exception exception) {
+            final String errorMessage =
+                    "Terminate request to " + vnfSelfLink + " resulted in exception" + terminateVnfRequest;
+            logger.error(errorMessage, exception);
+            throw new VnfmRequestFailureException(errorMessage, exception);
+        }
+        checkIfResponseIsAcceptable(response, vnfSelfLink, terminateVnfRequest);
+        final String locationHeader = response.getHeaders().get("Location").iterator().next();
+        return locationHeader.substring(locationHeader.lastIndexOf("/") + 1);
+    }
+
+    @Override
+    public void deleteVnf(final EsrVnfm vnfm, final String vnfSelfLink) {
+        logger.debug("Sending delete request to : " + vnfSelfLink);
+        final ResponseEntity<Void> response = getHttpServiceProvider(vnfm).deleteHttpRequest(vnfSelfLink, Void.class);
+        if (response.getStatusCode() != HttpStatus.NO_CONTENT) {
+            throw new VnfmRequestFailureException(
+                    "Delete request to " + vnfSelfLink + " return status code: " + response.getStatusCode());
+        }
+    }
+
+    @Override
+    public Optional<InlineResponse200> getOperation(final EsrVnfm vnfm, final String operationId) {
+        final String url = urlProvider.getOperationUrl(vnfm.getVnfmId(), operationId);
+        return getHttpServiceProvider(vnfm).get(url, InlineResponse200.class);
+    }
+
+    @Override
+    public Optional<InlineResponse201> createVnf(final EsrVnfm vnfm, final CreateVnfRequest createVnfRequest) {
+        final String url = urlProvider.getCreationUrl(vnfm.getVnfmId());
+        logger.debug("Sending create request {} to : {}", createVnfRequest, url);
+        try {
+            return getHttpServiceProvider(vnfm).post(createVnfRequest, url, InlineResponse201.class);
+        } catch (final Exception exception) {
+            final String errorMessage =
+                    "Create request to vnfm:" + vnfm.getVnfmId() + " resulted in exception" + createVnfRequest;
+            logger.error(errorMessage, exception);
+            throw new VnfmRequestFailureException(errorMessage, exception);
+        }
+    }
+
+    private void checkIfResponseIsAcceptable(ResponseEntity<Void> response, String vnfSelfLink,
+            TerminateVnfRequest terminateVnfRequest) {
+        if (response == null) {
+            final String errorMessage =
+                    "Terminate request to " + vnfSelfLink + ", response is null, " + "request: " + terminateVnfRequest;
+            logger.error(errorMessage);
+            throw new VnfmRequestFailureException(errorMessage);
+        }
+        if (response.getStatusCode() != HttpStatus.ACCEPTED) {
+            final String errorMessage = "Terminate request to " + vnfSelfLink + ", returned status code: "
+                    + response.getStatusCode() + ", request: " + terminateVnfRequest;
+            logger.error(errorMessage);
+            throw new VnfmRequestFailureException(errorMessage);
+        }
+    }
+
+    private HttpRestServiceProvider getHttpServiceProvider(final EsrVnfm vnfm) {
+        return vnfmServiceProviderConfiguration.getHttpRestServiceProvider(vnfm);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java
new file mode 100644
index 0000000..9c760b4
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import java.net.URI;
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.aai.domain.yang.EsrSystemInfoList;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmNotFoundException;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * Provides URLs for REST calls to a VNFM.
+ */
+@Service
+public class VnfmUrlProvider {
+
+    private static Logger logger = getLogger(VnfmUrlProvider.class);
+    private final AaiServiceProvider aaiServiceProvider;
+
+    @Autowired
+    public VnfmUrlProvider(final AaiServiceProvider aaiServiceProvider) {
+        this.aaiServiceProvider = aaiServiceProvider;
+    }
+
+    /**
+     * Get the URL for an operation on a VNFM.
+     *
+     * @param vnfmId The ID of the VNFM
+     * @return the URL of the operation
+     */
+    public String getOperationUrl(final String vnfmId, final String operationId) {
+        final String url = UriComponentsBuilder.fromUri(getBaseUri(vnfmId)).pathSegment("vnf_lcm_op_occs")
+                .pathSegment(operationId).build().toString();
+        logger.debug("getOperationUrl:" + url);
+
+        return url;
+    }
+
+    /**
+     * Get the URL for the subscriptions on a VNFM.
+     *
+     * @param vnfmId The ID of the VNFM
+     * @return the URL of the subscriptions
+     */
+    public String getSubscriptionsUrl(final String vnfmId) {
+        final String url =
+                UriComponentsBuilder.fromUri(getBaseUri(vnfmId)).pathSegment("subscriptions").build().toString();
+        logger.debug("getSubscriptionUrl:" + url);
+
+        return url;
+    }
+
+    public String getCreationUrl(final String vnfmId) {
+        final String url =
+                UriComponentsBuilder.fromUri(getBaseUri(vnfmId)).pathSegment("vnf_instances").build().toString();
+        logger.debug("getCreationUrl:" + url);
+
+        return url;
+    }
+
+    private URI getBaseUri(final String vnfmId) {
+        final EsrSystemInfoList vnfmEsrSystemInfoList = aaiServiceProvider.invokeGetVnfmEsrSystemInfoList(vnfmId);
+
+        if (vnfmEsrSystemInfoList != null) {
+            for (final EsrSystemInfo esrSystemInfo : vnfmEsrSystemInfoList.getEsrSystemInfo()) {
+                return UriComponentsBuilder.fromHttpUrl(esrSystemInfo.getServiceUrl()).build().toUri();
+            }
+        }
+
+        throw new VnfmNotFoundException("VNFM, or Service URL for VNFM, not found for VNFM " + vnfmId);
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java
new file mode 100644
index 0000000..3de94eb
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java
@@ -0,0 +1,174 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.jobmanagement;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.UUID;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.JobNotFoundException;
+import org.onap.so.rest.exceptions.HttpResouceNotFoundException;
+import org.onap.vnfmadapter.v1.model.OperationEnum;
+import org.onap.vnfmadapter.v1.model.OperationStateEnum;
+import org.onap.vnfmadapter.v1.model.OperationStatusRetrievalStatusEnum;
+import org.onap.vnfmadapter.v1.model.QueryJobResponse;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Manages jobs enabling the status of jobs to be queried. A job is associated with an operation on a VNFM.
+ */
+@Component
+public class JobManager {
+    public static final String ALREADY_COMPLETED_OPERATION_ID = "alreadyCompleted";
+    private static final String SEPARATOR = "_";
+    private static Logger logger = getLogger(JobManager.class);
+    private final Map<String, VnfmOperation> mapOfJobIdToVnfmOperation = Maps.newConcurrentMap();
+    private final VnfmServiceProvider vnfmServiceProvider;
+    private final AaiServiceProvider aaiServiceProvider;
+
+    @Autowired
+    JobManager(final VnfmServiceProvider vnfmServiceProvider, final AaiServiceProvider aaiServiceProvider) {
+        this.vnfmServiceProvider = vnfmServiceProvider;
+        this.aaiServiceProvider = aaiServiceProvider;
+    }
+
+    /**
+     * Create a job associated with an operation on a VNFM.
+     *
+     * @param vnfmId the VNFM the operation relates to
+     * @param operationId the ID of the associated VNFM operation
+     * @param waitForNotificationForSuccess if set to <code>true</code> the {@link QueryJobResponse#getOperationState()}
+     *        shall not return {@link org.onap.vnfmadapter.v1.model.OperationStateEnum#COMPLETED} unless a required
+     *        notification has been processed
+     * @return the ID of the job. Can be used to query the job using {@link #getVnfmOperation(String)}
+     */
+    public String createJob(final String vnfmId, final String operationId,
+            final boolean waitForNotificationForSuccess) {
+        final String jobId = vnfmId + SEPARATOR + UUID.randomUUID().toString();
+        final VnfmOperation vnfmOperation = new VnfmOperation(vnfmId, operationId, waitForNotificationForSuccess);
+        mapOfJobIdToVnfmOperation.put(jobId, vnfmOperation);
+        return jobId;
+    }
+
+    /**
+     * Get the operation, associated with the given job ID, from the VNFM.
+     *
+     * @param jobId the job ID
+     * @return the associated operation from the VNFM, or <code>null</code> of no operation is associated with the given
+     *         job ID
+     */
+    public QueryJobResponse getVnfmOperation(final String jobId) {
+        final VnfmOperation vnfmOperation = mapOfJobIdToVnfmOperation.get(jobId);
+        final QueryJobResponse response = new QueryJobResponse();
+
+        if (vnfmOperation == null) {
+            throw new JobNotFoundException("No job found with ID: " + jobId);
+        }
+
+        if (vnfmOperation.getOperationId().equals(ALREADY_COMPLETED_OPERATION_ID)) {
+            response.setOperationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.STATUS_FOUND);
+            return response.operationState(OperationStateEnum.COMPLETED);
+        }
+
+        if (vnfmOperation.isVnfDeleted()) {
+            response.setOperationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.STATUS_FOUND);
+            return response.operationState(getOperationState(vnfmOperation, null));
+        }
+
+        try {
+            final Optional<InlineResponse200> operationOptional = vnfmServiceProvider.getOperation(
+                    aaiServiceProvider.invokeGetVnfm(vnfmOperation.getVnfmId()), vnfmOperation.getOperationId());
+
+            if (!operationOptional.isPresent()) {
+                return response.operationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.OPERATION_NOT_FOUND);
+            }
+            final InlineResponse200 operation = operationOptional.get();
+
+            logger.debug("Job Id: {} operationId: {} operation details: {} ", jobId, operation.getId(), operation);
+
+            if (operation.getOperationState() == null) {
+                return response.operationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.WAITING_FOR_STATUS);
+            }
+
+            response.setOperationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.STATUS_FOUND);
+            response.setId(operation.getId());
+            response.setOperation(OperationEnum.fromValue(operation.getOperation().getValue()));
+            response.setOperationState(getOperationState(vnfmOperation, operation));
+            response.setStartTime(operation.getStartTime());
+            response.setStateEnteredTime(operation.getStateEnteredTime());
+            response.setVnfInstanceId(operation.getVnfInstanceId());
+
+            return response;
+        } catch (final HttpResouceNotFoundException exception) {
+            logger.error("Exception encountered trying to get operation status for operation id "
+                    + vnfmOperation.getOperationId(), exception);
+            return response.operationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.WAITING_FOR_STATUS);
+        }
+    }
+
+    private OperationStateEnum getOperationState(final VnfmOperation vnfmOperation,
+            final InlineResponse200 operationResponse) {
+        switch (vnfmOperation.getNotificationStatus()) {
+            case NOTIFICATION_PROCESSING_PENDING:
+                return org.onap.vnfmadapter.v1.model.OperationStateEnum.PROCESSING;
+            case NOTIFICATION_PROCEESING_SUCCESSFUL:
+                return org.onap.vnfmadapter.v1.model.OperationStateEnum.COMPLETED;
+            case NOTIFICATION_PROCESSING_FAILED:
+                return org.onap.vnfmadapter.v1.model.OperationStateEnum.FAILED;
+            default:
+                if (operationResponse == null || operationResponse.getOperationState() == null)
+                    return null;
+                return OperationStateEnum.fromValue(operationResponse.getOperationState().getValue());
+        }
+    }
+
+    public void notificationProcessedForOperation(final String operationId,
+            final boolean notificationProcessingWasSuccessful) {
+        logger.debug("Notification processed for operation ID {} success?: {}", operationId,
+                notificationProcessingWasSuccessful);
+        final java.util.Optional<VnfmOperation> relatedOperation = mapOfJobIdToVnfmOperation.values().stream()
+                .filter(operation -> operation.getOperationId().equals(operationId)).findFirst();
+        if (relatedOperation.isPresent()) {
+            relatedOperation.get().setNotificationProcessed(notificationProcessingWasSuccessful);
+        } else {
+            logger.debug("No operation found for operation ID {} ", operationId);
+
+        }
+    }
+
+    public void vnfDeleted(final String operationId) {
+        logger.debug("VNF deleyed for operation ID {}", operationId);
+        final java.util.Optional<VnfmOperation> relatedOperation = mapOfJobIdToVnfmOperation.values().stream()
+                .filter(operation -> operation.getOperationId().equals(operationId)).findFirst();
+        if (relatedOperation.isPresent()) {
+            relatedOperation.get().setVnfDeleted();
+        } else {
+            logger.debug("No operation found for operation ID {} ", operationId);
+        }
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java
new file mode 100644
index 0000000..3bfe48a
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.jobmanagement;
+
+/**
+ * Represents an operation on a VNFM.
+ */
+public class VnfmOperation {
+
+    private final String vnfmId;
+    private final String operationId;
+    private NotificationStatus notificationStatus;
+    private boolean vnfDeleted;
+
+    public VnfmOperation(final String vnfmId, final String operationId, final boolean waitForNotificationForSuccess) {
+        this.vnfmId = vnfmId;
+        this.operationId = operationId;
+        this.notificationStatus = waitForNotificationForSuccess ? NotificationStatus.NOTIFICATION_PROCESSING_PENDING
+                : NotificationStatus.NOTIFICATION_PROCESSING_NOT_REQUIRED;
+    }
+
+    /**
+     * Get the ID of the operation on the VNFM.
+     *
+     * @return the ID of the operation on the VNFM
+     */
+    public String getOperationId() {
+        return operationId;
+    }
+
+    /**
+     * Get the ID of the VNFM the operation is carried out by.
+     *
+     * @return the ID of the VNFM
+     */
+    public String getVnfmId() {
+        return vnfmId;
+    }
+
+    /**
+     * Set the required notification has been processed for the operation.
+     *
+     * @param notificationProcessingWasSuccessful <code>true</code> if the notification processing was successful,
+     *        <code>false<code> otherwise
+     */
+    public void setNotificationProcessed(final boolean notificationProcessingWasSuccessful) {
+        this.notificationStatus =
+                notificationProcessingWasSuccessful ? NotificationStatus.NOTIFICATION_PROCEESING_SUCCESSFUL
+                        : NotificationStatus.NOTIFICATION_PROCESSING_FAILED;
+    }
+
+    /**
+     * Get the notification status for the operation.
+     *
+     * @return the notification status
+     */
+    public NotificationStatus getNotificationStatus() {
+        return notificationStatus;
+    }
+
+    /**
+     * Set the VNF has been deleted from the VNFM.
+     */
+    public void setVnfDeleted() {
+        this.vnfDeleted = true;
+    }
+
+    /**
+     * Check if the VNF has been deleted from the VNFM
+     *
+     * @return <code>true</code> of the VNF has been deleted from the VNFM, <code>false</code> otherwise
+     */
+    public boolean isVnfDeleted() {
+        return vnfDeleted;
+    }
+
+
+    public enum NotificationStatus {
+        /**
+         * No notification handling is required to determine the status of the operation
+         */
+        NOTIFICATION_PROCESSING_NOT_REQUIRED,
+        /**
+         * A notification must be processed before the notification can be considered to be completed
+         */
+        NOTIFICATION_PROCESSING_PENDING,
+        /**
+         * A notification has been successfully handled for the operation
+         */
+        NOTIFICATION_PROCEESING_SUCCESSFUL,
+        /**
+         * An error occurred processing a notification for the operation
+         */
+        NOTIFICATION_PROCESSING_FAILED;
+    }
+
+    @Override
+    public String toString() {
+        return "VnfmOperation [vnfmId=" + vnfmId + ", operationId=" + operationId + ", notificationStatus="
+                + notificationStatus + "]";
+    }
+
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java
new file mode 100644
index 0000000..a885721
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java
@@ -0,0 +1,265 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.lifecycle;
+
+import com.google.common.base.Optional;
+import java.util.Map;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.Relationship;
+import org.onap.so.adapters.vnfmadapter.extclients.SdcPackageProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiHelper;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.OamIpAddressSource;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.OamIpAddressSource.OamIpAddressType;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmHelper;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InstantiateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.LccnSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.TerminateVnfRequest;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.TerminateVnfRequest.TerminationTypeEnum;
+import org.onap.so.adapters.vnfmadapter.jobmanagement.JobManager;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfNotFoundException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmNotFoundException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmRequestFailureException;
+import org.onap.vnfmadapter.v1.model.CreateVnfRequest;
+import org.onap.vnfmadapter.v1.model.CreateVnfResponse;
+import org.onap.vnfmadapter.v1.model.DeleteVnfResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Manages lifecycle operations towards the VNFMs.
+ */
+@Component
+public class LifecycleManager {
+    private static final Logger logger = LoggerFactory.getLogger(LifecycleManager.class);
+    private final AaiServiceProvider aaiServiceProvider;
+    private final VnfmServiceProvider vnfmServiceProvider;
+    private final AaiHelper aaiHelper;
+    private final VnfmHelper vnfmHelper;
+    private final JobManager jobManager;
+    private final SdcPackageProvider packageProvider;
+
+    @Autowired
+    LifecycleManager(final AaiServiceProvider aaiServiceProvider, final AaiHelper aaiHelper,
+            final VnfmHelper vnfmHelper, final VnfmServiceProvider vnfmServiceProvider, final JobManager jobManager,
+            final SdcPackageProvider packageProvider) {
+        this.aaiServiceProvider = aaiServiceProvider;
+        this.vnfmServiceProvider = vnfmServiceProvider;
+        this.aaiHelper = aaiHelper;
+        this.vnfmHelper = vnfmHelper;
+        this.jobManager = jobManager;
+        this.packageProvider = packageProvider;
+    }
+
+    /**
+     * Create a VNF on a VNFM.
+     *
+     * @param vnfIdInAai the ID of the VNF in AAI
+     * @param request the create request
+     * @return the response to the request
+     */
+    public CreateVnfResponse createVnf(final String vnfIdInAai, final CreateVnfRequest request) {
+        GenericVnf genericVnf = getGenericVnfFromAai(vnfIdInAai);
+        EsrVnfm vnfm = aaiHelper.getAssignedVnfm(genericVnf);
+        checkIfVnfAlreadyExistsInVnfm(vnfm, genericVnf);
+
+        if (vnfm == null) {
+            vnfm = aaiHelper.selectVnfm(genericVnf);
+            aaiServiceProvider.invokePutGenericVnfToVnfmRelationship(genericVnf, vnfm.getVnfmId());
+        }
+        final InlineResponse201 vnfmResponse = sendCreateRequestToVnfm(request, genericVnf, vnfIdInAai, vnfm);
+
+        logger.info("Create response: {}", vnfmResponse);
+
+        genericVnf.setSelflink(getSelfLink(vnfmResponse, vnfm));
+
+        GenericVnf genericVnfPatch = new GenericVnf();
+        genericVnfPatch.setVnfId(genericVnf.getVnfId());
+        genericVnfPatch.setSelflink(genericVnf.getSelflink());
+        aaiServiceProvider.invokePatchGenericVnf(genericVnfPatch);
+
+        final String vnfIdInVnfm = vnfmResponse.getId();
+
+        final OamIpAddressSource oamIpAddressSource = extractOamIpAddressSource(request);
+        aaiHelper.setOamIpAddressSource(vnfIdInVnfm, oamIpAddressSource);
+
+        createNotificationSubscription(vnfm, vnfIdInVnfm);
+        final String operationId = sendInstantiateRequestToVnfm(vnfm, genericVnf, request);
+
+        final String jobId = jobManager.createJob(vnfm.getVnfmId(), operationId, false);
+        final CreateVnfResponse response = new CreateVnfResponse();
+        response.setJobId(jobId);
+        return response;
+    }
+
+    private String getSelfLink(final InlineResponse201 vnfmResponse, final EsrVnfm vnfm) {
+        if (vnfmResponse.getLinks() != null && vnfmResponse.getLinks().getSelf() != null
+                && vnfmResponse.getLinks().getSelf().getHref() != null) {
+            return vnfmResponse.getLinks().getSelf().getHref();
+        }
+        return vnfm.getEsrSystemInfoList().getEsrSystemInfo().iterator().next().getServiceUrl() + "/vnf_instances/"
+                + vnfmResponse.getId();
+    }
+
+    private OamIpAddressSource extractOamIpAddressSource(final CreateVnfRequest request) {
+        final Map<String, String> additionalParams = request.getAdditionalParams();
+        try {
+            final String sourceType = additionalParams.remove("oamIpAddressSourceType");
+            final String sourceValue = additionalParams.remove("oamIpAddressSourceValue");
+            final OamIpAddressType oamIpAddressType = OamIpAddressType.valueOf(sourceType.toUpperCase());
+            return new OamIpAddressSource(oamIpAddressType, sourceValue);
+        } catch (final NullPointerException | IllegalArgumentException exception) {
+            logger.debug("Additional Params not set for OAM IP address source", exception);
+            return null;
+        }
+    }
+
+    private void checkIfVnfAlreadyExistsInVnfm(final EsrVnfm vnfm, final GenericVnf genericVnf) {
+        if (genericVnf.getSelflink() != null && !genericVnf.getSelflink().isEmpty() && vnfm != null) {
+            Optional<InlineResponse201> response = Optional.absent();
+            try {
+                response = vnfmServiceProvider.getVnf(vnfm, genericVnf.getSelflink());
+            } catch (final Exception exception) {
+                logger.debug("Ignoring invalid self link in generic vnf", exception);
+            }
+            if (response.isPresent()) {
+                throw new IllegalArgumentException("VNF " + genericVnf.getVnfId()
+                        + " is already defined on the VNFM, self link: " + genericVnf.getSelflink());
+            }
+        }
+    }
+
+    private InlineResponse201 sendCreateRequestToVnfm(final CreateVnfRequest aaiRequest, final GenericVnf genericVnf,
+            final String vnfIdInAai, final EsrVnfm vnfm) {
+        logger.debug("Sending a create request to SVNFM " + aaiRequest);
+        final org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.CreateVnfRequest vnfmRequest =
+                new org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.CreateVnfRequest();
+
+        final String vnfdId = packageProvider.getVnfdId(genericVnf.getModelVersionId());
+        vnfmRequest.setVnfdId(vnfdId);
+        vnfmRequest.setVnfInstanceName(aaiRequest.getName().replaceAll(" ", "_"));
+        vnfmRequest.setVnfInstanceDescription(vnfIdInAai);
+
+        final Optional<InlineResponse201> optionalResponse = vnfmServiceProvider.createVnf(vnfm, vnfmRequest);
+
+        try {
+            return optionalResponse.get();
+        } catch (final Exception exception) {
+            final String errorMessage = "Unable to return response from VNFM";
+            logger.error(errorMessage, exception);
+            throw new VnfmRequestFailureException(errorMessage, exception);
+        }
+    }
+
+    private void createNotificationSubscription(final EsrVnfm vnfm, final String vnfId) {
+        try {
+            final LccnSubscriptionRequest subscriptionRequest = vnfmHelper.createNotificationSubscriptionRequest(vnfId);
+            vnfmServiceProvider.subscribeForNotifications(vnfm, subscriptionRequest);
+        } catch (final Exception exception) {
+            logger.warn("Subscription for notifications to VNFM: " + vnfm.getVnfmId() + " for VNF " + vnfId
+                    + " failed. AAI will not be updated unless the VNFM is configured by other means to send notifications relating to this VNF",
+                    exception);
+        }
+    }
+
+    private String sendInstantiateRequestToVnfm(final EsrVnfm vnfm, final GenericVnf genericVnf,
+            final CreateVnfRequest createVnfRequest) {
+
+        final InstantiateVnfRequest instantiateVnfRequest =
+                vnfmHelper.createInstantiateRequest(createVnfRequest.getTenant(), createVnfRequest,
+                        packageProvider.getFlavourId(genericVnf.getModelVersionId()));
+        final String jobId = vnfmServiceProvider.instantiateVnf(vnfm, genericVnf.getSelflink(), instantiateVnfRequest);
+
+        logger.info("Instantiate VNF request successfully sent to " + genericVnf.getSelflink());
+        return jobId;
+    }
+
+    /**
+     * Delete a VNF on a VNFM.
+     *
+     * @param vnfIdInAai the ID of the VNF in AAI
+     * @return the response to the request
+     */
+    public DeleteVnfResponse deleteVnf(final String vnfIdInAai) {
+        final GenericVnf genericVnf = getGenericVnfFromAai(vnfIdInAai);
+        final EsrVnfm vnfm = getAssignedVnfm(genericVnf);
+
+        final String operationId = sendTerminateRequestToVnfm(vnfm, genericVnf);
+
+        if (operationId.equals(JobManager.ALREADY_COMPLETED_OPERATION_ID)) {
+            sendDeleteRequestToVnfm(genericVnf);
+        }
+        final String jobId = jobManager.createJob(vnfm.getVnfmId(), operationId, true);
+
+        return new DeleteVnfResponse().jobId(jobId);
+    }
+
+    private String sendTerminateRequestToVnfm(final EsrVnfm vnfm, final GenericVnf genericVnf) {
+        final TerminateVnfRequest terminateVnfRequest = new TerminateVnfRequest();
+        terminateVnfRequest.setTerminationType(TerminationTypeEnum.FORCEFUL);
+        return vnfmServiceProvider.terminateVnf(vnfm, genericVnf.getSelflink(), terminateVnfRequest);
+    }
+
+    private GenericVnf getGenericVnfFromAai(final String vnfIdInAai) {
+        final GenericVnf genericVnf = aaiServiceProvider.invokeGetGenericVnf(vnfIdInAai);
+        if (genericVnf == null) {
+            throw new VnfNotFoundException("VNF not found in AAI: " + vnfIdInAai);
+        }
+        logger.debug("Retrieved generic VNF from AAI: " + genericVnf);
+        return genericVnf;
+    }
+
+    private EsrVnfm getAssignedVnfm(final GenericVnf genericVnf) {
+        final EsrVnfm vnfm = aaiHelper.getAssignedVnfm(genericVnf);
+        if (vnfm == null) {
+            throw new VnfmNotFoundException("No VNFM found in AAI for VNF " + genericVnf.getVnfId());
+        }
+        return vnfm;
+    }
+
+    private void sendDeleteRequestToVnfm(final GenericVnf genericVnf) {
+
+        vnfmServiceProvider.deleteVnf(aaiHelper.getAssignedVnfm(genericVnf), genericVnf.getSelflink());
+
+        final GenericVnf genericVnfPatch = new GenericVnf();
+        genericVnfPatch.setVnfId(genericVnf.getVnfId());
+        genericVnfPatch.setOrchestrationStatus("Assigned");
+        genericVnfPatch.setSelflink("");
+        aaiServiceProvider.invokePatchGenericVnf(genericVnfPatch);
+
+        for (final Relationship relationship : genericVnf.getRelationshipList().getRelationship()) {
+            if (relationship.getRelatedTo().equals("vserver")) {
+                aaiServiceProvider.invokeDeleteVserver(
+                        aaiHelper.getRelationshipData(relationship, "cloud-region.cloud-owner"),
+                        aaiHelper.getRelationshipData(relationship, "cloud-region.cloud-region-id"),
+                        aaiHelper.getRelationshipData(relationship, "tenant.tenant-id"),
+                        aaiHelper.getRelationshipData(relationship, "vserver.vserver-id"));
+            }
+        }
+
+
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/notificationhandling/NotificationHandler.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/notificationhandling/NotificationHandler.java
new file mode 100644
index 0000000..09722d4
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/notificationhandling/NotificationHandler.java
@@ -0,0 +1,276 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.notificationhandling;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.Relationship;
+import org.onap.aai.domain.yang.Vserver;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiHelper;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.OamIpAddressSource;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.OamIpAddressSource.OamIpAddressType;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmServiceProvider;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs.ChangeTypeEnum;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification.OperationStateEnum;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201VimConnectionInfo;
+import org.onap.so.adapters.vnfmadapter.jobmanagement.JobManager;
+import org.slf4j.Logger;
+
+/**
+ * Performs updates to AAI based on a received notification. The updates are executed in a separate thread so as the
+ * notification response to the VNFM is not delayed.
+ */
+public class NotificationHandler implements Runnable {
+    private static Logger logger = getLogger(NotificationHandler.class);
+    private final VnfLcmOperationOccurrenceNotification vnfLcmOperationOccurrenceNotification;
+    private final AaiHelper aaiHelper;
+    private final AaiServiceProvider aaiServiceProvider;
+    private final VnfmServiceProvider vnfmServiceProvider;
+    private final JobManager jobManager;
+    private final InlineResponse201 vnfInstance;
+
+    public NotificationHandler(final VnfLcmOperationOccurrenceNotification vnfLcmOperationOccurrenceNotification,
+            final AaiHelper aaiHelper, final AaiServiceProvider aaiServiceProvider,
+            final VnfmServiceProvider vnfmServiceProvider, final JobManager jobManager,
+            final InlineResponse201 vnfInstance) {
+        this.vnfLcmOperationOccurrenceNotification = vnfLcmOperationOccurrenceNotification;
+        this.aaiHelper = aaiHelper;
+        this.aaiServiceProvider = aaiServiceProvider;
+        this.vnfmServiceProvider = vnfmServiceProvider;
+        this.jobManager = jobManager;
+        this.vnfInstance = vnfInstance;
+    }
+
+    @Override
+    public void run() {
+        try {
+            if (vnfLcmOperationOccurrenceNotification.getOperationState().equals(OperationStateEnum.COMPLETED)) {
+                switch (vnfLcmOperationOccurrenceNotification.getOperation()) {
+                    case INSTANTIATE:
+                        handleVnfInstantiate();
+                        break;
+                    case TERMINATE:
+                        handleVnfTerminate();
+                        break;
+                    default:
+                }
+            }
+        } catch (final Exception exception) {
+            logger.error("Error encountered handling notification, AAI may not be updated correctly "
+                    + vnfLcmOperationOccurrenceNotification, exception);
+        }
+    }
+
+    private void handleVnfInstantiate() {
+        if (vnfLcmOperationOccurrenceNotification.getOperationState().equals(OperationStateEnum.COMPLETED)) {
+            handleVnfInstantiateCompleted();
+        }
+    }
+
+    private void handleVnfInstantiateCompleted() {
+        final GenericVnf genericVnf = aaiServiceProvider
+                .invokeQueryGenericVnf(vnfInstance.getLinks().getSelf().getHref()).getGenericVnf().get(0);
+
+        final GenericVnf genericVnfPatch = new GenericVnf();
+        genericVnfPatch.setVnfId(genericVnf.getVnfId());
+        setOamIpAddress(genericVnfPatch, vnfInstance);
+        genericVnfPatch.setOrchestrationStatus("Created");
+        aaiServiceProvider.invokePatchGenericVnf(genericVnfPatch);
+
+        addVservers(vnfLcmOperationOccurrenceNotification, genericVnf.getVnfId(), vnfInstance.getVimConnectionInfo());
+
+        logger.debug("Finished handling notification for vnfm: " + vnfInstance.getId());
+    }
+
+    private void setOamIpAddress(final GenericVnf genericVnf, final InlineResponse201 vnfInstance) {
+        final OamIpAddressSource oamIpAddressSource = aaiHelper.getOamIpAddressSource(vnfInstance.getId());
+        if (oamIpAddressSource == null) {
+            logger.warn("No source indicated for OAM IP address, no value will be set in AAI");
+            return;
+        }
+        if (oamIpAddressSource.getType().equals(OamIpAddressType.LITERAL)) {
+            genericVnf.setIpv4OamAddress(oamIpAddressSource.getValue());
+        } else {
+            try {
+                logger.debug("ConfigurableProperties: " + vnfInstance.getVnfConfigurableProperties());
+                if (vnfInstance.getVnfConfigurableProperties() == null) {
+                    logger.warn("No ConfigurableProperties, cannot set OAM IP Address");
+                }
+                final JSONObject properties = new JSONObject((Map) vnfInstance.getVnfConfigurableProperties());
+                genericVnf.setIpv4OamAddress(properties.get(oamIpAddressSource.getValue()).toString());
+            } catch (final JSONException jsonException) {
+                logger.error("Error getting vnfIpAddress", jsonException);
+            }
+        }
+    }
+
+    private void handleVnfTerminate() {
+        switch (vnfLcmOperationOccurrenceNotification.getOperationState()) {
+            case COMPLETED:
+                handleVnfTerminateCompleted();
+                break;
+            case FAILED:
+            case ROLLING_BACK:
+                handleVnfTerminateFailed();
+                break;
+            default:
+        }
+    }
+
+    private void handleVnfTerminateFailed() {
+        try {
+            final GenericVnf genericVnf = aaiServiceProvider
+                    .invokeQueryGenericVnf(vnfInstance.getLinks().getSelf().getHref()).getGenericVnf().get(0);
+            deleteVserversFromAai(vnfLcmOperationOccurrenceNotification, genericVnf);
+        } finally {
+            jobManager.notificationProcessedForOperation(vnfLcmOperationOccurrenceNotification.getVnfLcmOpOccId(),
+                    false);
+        }
+    }
+
+    private void handleVnfTerminateCompleted() {
+        GenericVnf genericVnf = null;
+        boolean vServersDeletedFromAai = false;
+        boolean identifierDeletedFromVnfm = false;
+        boolean genericVnfUpdated = false;
+        try {
+            genericVnf = aaiServiceProvider.invokeQueryGenericVnf(vnfInstance.getLinks().getSelf().getHref())
+                    .getGenericVnf().get(0);
+            vServersDeletedFromAai = deleteVserversFromAai(vnfLcmOperationOccurrenceNotification, genericVnf);
+            identifierDeletedFromVnfm = deleteVnfIdentifierOnVnfm(genericVnf);
+            genericVnfUpdated = patchVnfInAai(genericVnf.getVnfId(), "Assigned", identifierDeletedFromVnfm ? "" : null);
+        } finally {
+            jobManager.notificationProcessedForOperation(vnfLcmOperationOccurrenceNotification.getVnfLcmOpOccId(),
+                    vServersDeletedFromAai && identifierDeletedFromVnfm && genericVnfUpdated);
+            jobManager.vnfDeleted(vnfLcmOperationOccurrenceNotification.getVnfLcmOpOccId());
+        }
+    }
+
+    private void addVservers(final VnfLcmOperationOccurrenceNotification notification, final String vnfId,
+            final List<InlineResponse201VimConnectionInfo> vnfInstancesVimConnectionInfo) {
+        final Map<String, InlineResponse201VimConnectionInfo> vimConnectionIdToVimConnectionInfo = new HashMap<>();
+        for (final InlineResponse201VimConnectionInfo vimConnectionInfo : vnfInstancesVimConnectionInfo) {
+            vimConnectionIdToVimConnectionInfo.put(vimConnectionInfo.getId(), vimConnectionInfo);
+        }
+
+        for (final LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs vnfc : notification.getAffectedVnfcs()) {
+            final InlineResponse201VimConnectionInfo vimConnectionInfo =
+                    getVimConnectionInfo(vimConnectionIdToVimConnectionInfo, vnfc);
+            if (ChangeTypeEnum.ADDED.equals(vnfc.getChangeType())) {
+                final Vserver vserver = aaiHelper.createVserver(vnfc);
+                aaiServiceProvider.invokePutVserver(getCloudOwner(vimConnectionInfo), getCloudRegion(vimConnectionInfo),
+                        getTenant(vimConnectionInfo), vserver);
+
+                aaiServiceProvider.invokePutVserverToVnfRelationship(getCloudOwner(vimConnectionInfo),
+                        getCloudRegion(vimConnectionInfo), getTenant(vimConnectionInfo), vserver, vnfId);
+            }
+        }
+    }
+
+    private boolean deleteVserversFromAai(final VnfLcmOperationOccurrenceNotification notification,
+            final GenericVnf vnf) {
+        try {
+            for (final LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs vnfc : notification.getAffectedVnfcs()) {
+                if (ChangeTypeEnum.REMOVED.equals(vnfc.getChangeType())) {
+
+                    final Relationship relationshipToVserver = aaiHelper.deleteRelationshipWithDataValue(vnf, "vserver",
+                            "vserver.vserver-id", vnfc.getComputeResource().getResourceId());
+
+                    aaiServiceProvider.invokeDeleteVserver(
+                            aaiHelper.getRelationshipData(relationshipToVserver, "cloud-region.cloud-owner"),
+                            aaiHelper.getRelationshipData(relationshipToVserver, "cloud-region.cloud-region-id"),
+                            aaiHelper.getRelationshipData(relationshipToVserver, "tenant.tenant-id"),
+                            vnfc.getComputeResource().getResourceId());
+                }
+            }
+            return true;
+        } catch (final Exception exception) {
+            logger.error(
+                    "Error encountered deleting vservers based on received notification, AAI may not be updated correctly "
+                            + vnfLcmOperationOccurrenceNotification,
+                    exception);
+            return false;
+        }
+    }
+
+    private boolean deleteVnfIdentifierOnVnfm(GenericVnf genericVnf) {
+        try {
+            vnfmServiceProvider.deleteVnf(aaiHelper.getAssignedVnfm(genericVnf), genericVnf.getSelflink());
+            return true;
+        } catch (Exception exception) {
+            logger.error("Exception deleting the identifier " + genericVnf.getSelflink()
+                    + " from the VNFM. The VNF has been terminated successfully but the identifier will remain on the VNFM.",
+                    exception);
+            return false;
+        }
+    }
+
+    private boolean patchVnfInAai(final String vnfId, final String orchestrationStatus, final String selfLink) {
+        try {
+            final GenericVnf genericVnfPatch = new GenericVnf();
+            genericVnfPatch.setVnfId(vnfId);
+            genericVnfPatch.setOrchestrationStatus(orchestrationStatus);
+            if (selfLink != null) {
+                genericVnfPatch.setSelflink(selfLink);
+            }
+            aaiServiceProvider.invokePatchGenericVnf(genericVnfPatch);
+            return true;
+        } catch (final Exception exception) {
+            logger.error(
+                    "Error encountered setting orchestration status and/or self link based on received notification, AAI may not be updated correctly "
+                            + vnfLcmOperationOccurrenceNotification,
+                    exception);
+            return false;
+        }
+    }
+
+    private InlineResponse201VimConnectionInfo getVimConnectionInfo(
+            final Map<String, InlineResponse201VimConnectionInfo> vimConnectionIdToVimConnectionInfo,
+            final LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs vnfc) {
+        final String vimConnectionId = vnfc.getComputeResource().getVimConnectionId();
+        return vimConnectionIdToVimConnectionInfo.get(vimConnectionId);
+    }
+
+    private String getCloudOwner(final InlineResponse201VimConnectionInfo vimConnectionInfo) {
+        final String vimId = vimConnectionInfo.getVimId();
+        return vimId.substring(0, vimId.indexOf("_"));
+    }
+
+    private String getCloudRegion(final InlineResponse201VimConnectionInfo vimConnectionInfo) {
+        final String vimId = vimConnectionInfo.getVimId();
+        return vimId.substring(vimId.indexOf("_") + 1);
+    }
+
+    private String getTenant(final InlineResponse201VimConnectionInfo vimConnectionInfo) {
+        final JSONObject vimConnectionJsonObject = new JSONObject(vimConnectionInfo);
+        return vimConnectionJsonObject.getJSONObject("accessInfo").get("projectId").toString();
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/AuthorizationServerConfig.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/AuthorizationServerConfig.java
new file mode 100644
index 0000000..7f71b2e
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/AuthorizationServerConfig.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.oauth;
+
+import org.onap.so.utils.CryptoUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+
+@Configuration
+@EnableAuthorizationServer
+/**
+ * Configures the authorization server for oauth token based authentication.
+ */
+public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
+
+    private static final int ONE_DAY = 60 * 60 * 24;
+
+    @Value("${vnfmadapter.auth:E39823AAB2739CC654C4E92B52C05BC34149342D0A46451B00CA508C8EDC62242CE4E9DA9445D3C01A3F13}")
+    private String vnfmAdapterAuth;
+
+    @Value("${mso.key}")
+    private String msoEncryptionKey;
+
+    @Override
+    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
+        final String[] decrypedAuth = CryptoUtils.decrypt(vnfmAdapterAuth, msoEncryptionKey).split(":");
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        clients.inMemory().withClient(decrypedAuth[0]).secret(passwordEncoder.encode(decrypedAuth[1]))
+                .authorizedGrantTypes("client_credentials").scopes("write").accessTokenValiditySeconds(ONE_DAY)
+                .refreshTokenValiditySeconds(ONE_DAY);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2AccessTokenAdapter.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2AccessTokenAdapter.java
new file mode 100644
index 0000000..2f51406
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2AccessTokenAdapter.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.oauth;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+
+public class OAuth2AccessTokenAdapter implements JsonSerializer<OAuth2AccessToken> {
+
+    @Override
+    public JsonElement serialize(final OAuth2AccessToken src, final Type typeOfSrc,
+            final JsonSerializationContext context) {
+        final JsonObject obj = new JsonObject();
+        obj.addProperty(OAuth2AccessToken.ACCESS_TOKEN, src.getValue());
+        obj.addProperty(OAuth2AccessToken.TOKEN_TYPE, src.getTokenType());
+        if (src.getRefreshToken() != null) {
+            obj.addProperty(OAuth2AccessToken.REFRESH_TOKEN, src.getRefreshToken().getValue());
+        }
+        obj.addProperty(OAuth2AccessToken.EXPIRES_IN, src.getExpiresIn());
+        final JsonArray scopeObj = new JsonArray();
+        for (final String scope : src.getScope()) {
+            scopeObj.add(scope);
+        }
+        obj.add(OAuth2AccessToken.SCOPE, scopeObj);
+
+        return obj;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2ResourceServer.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2ResourceServer.java
new file mode 100644
index 0000000..23533e9
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2ResourceServer.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.oauth;
+
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.BASE_URL;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+@Configuration
+@EnableResourceServer
+/**
+ * Enforces oauth token based authentication when a token is provided in the request.
+ */
+public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
+
+    @Override
+    public void configure(final HttpSecurity http) throws Exception {
+        http.requestMatcher(new OAuth2ResourceServerRequestMatcher()).authorizeRequests()
+                .antMatchers(BASE_URL + "/grants/**", BASE_URL + "/lcn/**").authenticated();
+    }
+
+    private static class OAuth2ResourceServerRequestMatcher implements RequestMatcher {
+        @Override
+        public boolean matches(HttpServletRequest request) {
+            String auth = request.getHeader("Authorization");
+            String uri = request.getRequestURI();
+            return (auth != null && auth.startsWith("Bearer") && (uri.contains("/grants") || uri.contains("/lcn/")));
+        }
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003GrantController.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003GrantController.java
new file mode 100644
index 0000000..a485b1d
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003GrantController.java
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.BASE_URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import javax.ws.rs.core.MediaType;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiHelper;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmHelper;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantRequest;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantsAddResources;
+import org.onap.so.adapters.vnfmadapter.grant.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.grant.model.InlineResponse201AddResources;
+import org.onap.so.adapters.vnfmadapter.grant.model.InlineResponse201VimConnections;
+import org.onap.vnfmadapter.v1.model.Tenant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+@RequestMapping(value = BASE_URL, produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
+public class Sol003GrantController {
+
+    private static final Logger logger = LoggerFactory.getLogger(Sol003GrantController.class);
+    public final AaiServiceProvider aaiServiceProvider;
+    public final AaiHelper aaiHelper;
+    public final VnfmHelper vnfmHelper;
+
+    @Autowired
+    public Sol003GrantController(final AaiServiceProvider aaiServiceProvider, final AaiHelper aaiHelper,
+            final VnfmHelper vnfmHelper) {
+        this.aaiServiceProvider = aaiServiceProvider;
+        this.aaiHelper = aaiHelper;
+        this.vnfmHelper = vnfmHelper;
+    }
+
+    @GetMapping(value = "/grants/{grantId}")
+    public ResponseEntity<InlineResponse201> grantsGrantIdGet(@PathVariable("grantId") final String grantId) {
+        logger.info("Get grant received from VNFM, grant id: " + grantId);
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @PostMapping(value = "/grants")
+    public ResponseEntity<InlineResponse201> grantsPost(@RequestBody final GrantRequest grantRequest) {
+        logger.info("Grant request received from VNFM: " + grantRequest);
+
+        final InlineResponse201 grantResponse = createGrantResponse(grantRequest);
+        logger.info("Grant request returning to VNFM: " + grantResponse);
+        return new ResponseEntity<>(grantResponse, HttpStatus.CREATED);
+    }
+
+    private InlineResponse201 createGrantResponse(final GrantRequest grantRequest) {
+        final InlineResponse201 grantResponse = new InlineResponse201();
+        grantResponse.setId(UUID.randomUUID().toString());
+        grantResponse.setVnfInstanceId(grantRequest.getVnfInstanceId());
+        grantResponse.setVnfLcmOpOccId(grantRequest.getVnfLcmOpOccId());
+        final String vnfSelfLink = grantRequest.getLinks().getVnfInstance().getHref();
+        final Tenant tenant = aaiHelper
+                .getAssignedTenant(aaiServiceProvider.invokeQueryGenericVnf(vnfSelfLink).getGenericVnf().get(0));
+
+        String vimConnectionId = "";
+        final InlineResponse201VimConnections vimConnection = vnfmHelper.getVimConnections(tenant);
+        grantResponse.addVimConnectionsItem(vimConnection);
+        vimConnectionId = vimConnection.getId();
+
+        if (grantRequest.getOperation().equals(GrantRequest.OperationEnum.INSTANTIATE)) {
+            grantResponse.addResources(getResources(grantRequest.getAddResources(), vimConnectionId));
+        } else if (grantRequest.getOperation().equals(GrantRequest.OperationEnum.TERMINATE)) {
+            grantResponse.removeResources(getResources(grantRequest.getRemoveResources(), vimConnectionId));
+        }
+        return grantResponse;
+    }
+
+    private List<InlineResponse201AddResources> getResources(final List<GrantsAddResources> requestResources,
+            final String vimId) {
+        final List<InlineResponse201AddResources> resources = new ArrayList<>();
+        for (final GrantsAddResources requestResource : requestResources) {
+            final InlineResponse201AddResources responseResource = new InlineResponse201AddResources();
+            responseResource.setResourceDefinitionId(requestResource.getId());
+            responseResource.setVimConnectionId(vimId);
+            resources.add(responseResource);
+        }
+        return resources;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnContoller.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnContoller.java
new file mode 100644
index 0000000..49bad20
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnContoller.java
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.onap.so.adapters.vnfmadapter.LifeCycleManagementConstants.OPERATION_NOTIFICATION_ENDPOINT;
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.BASE_URL;
+import static org.slf4j.LoggerFactory.getLogger;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import javax.ws.rs.core.MediaType;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiHelper;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmServiceProvider;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfIdentifierCreationNotification;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfIdentifierDeletionNotification;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification.OperationEnum;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification.OperationStateEnum;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.jobmanagement.JobManager;
+import org.onap.so.adapters.vnfmadapter.notificationhandling.NotificationHandler;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Controller for handling notifications from the VNFM (Virtual Network Function Manager).
+ */
+@Controller
+@RequestMapping(value = BASE_URL, produces = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML},
+        consumes = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+public class Sol003LcnContoller {
+    private static Logger logger = getLogger(Sol003LcnContoller.class);
+    private static final String LOG_LCN_RECEIVED = "LCN received from VNFM: ";
+    private final AaiServiceProvider aaiServiceProvider;
+    private final AaiHelper aaiHelper;
+    private final VnfmServiceProvider vnfmServiceProvider;
+    private final JobManager jobManager;
+    private final ExecutorService executor = Executors.newCachedThreadPool();
+
+    @Autowired
+    Sol003LcnContoller(final AaiServiceProvider aaiServiceProvider, final AaiHelper aaiHelper,
+            final VnfmServiceProvider vnfmServiceProvider, final JobManager jobManager) {
+        this.aaiServiceProvider = aaiServiceProvider;
+        this.aaiHelper = aaiHelper;
+        this.vnfmServiceProvider = vnfmServiceProvider;
+        this.jobManager = jobManager;
+    }
+
+    @PostMapping(value = "/lcn/VnfIdentifierCreationNotification")
+    public ResponseEntity<Void> lcnVnfIdentifierCreationNotificationPost(
+            @RequestBody final VnfIdentifierCreationNotification vnfIdentifierCreationNotification) {
+        logger.info(LOG_LCN_RECEIVED + vnfIdentifierCreationNotification);
+        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+    }
+
+    @PostMapping(value = "/lcn/VnfIdentifierDeletionNotification")
+    public ResponseEntity<Void> lcnVnfIdentifierDeletionNotificationPost(
+            @RequestBody final VnfIdentifierDeletionNotification vnfIdentifierDeletionNotification) {
+        logger.info(LOG_LCN_RECEIVED + vnfIdentifierDeletionNotification);
+        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+    }
+
+    @PostMapping(value = OPERATION_NOTIFICATION_ENDPOINT)
+    public ResponseEntity<Void> lcnVnfLcmOperationOccurrenceNotificationPost(
+            @RequestBody final VnfLcmOperationOccurrenceNotification vnfLcmOperationOccurrenceNotification) {
+        logger.info(LOG_LCN_RECEIVED + vnfLcmOperationOccurrenceNotification);
+
+        if (isANotificationOfInterest(vnfLcmOperationOccurrenceNotification)) {
+            final InlineResponse201 vnfInstance = getVnfInstance(vnfLcmOperationOccurrenceNotification);
+            final NotificationHandler handler = new NotificationHandler(vnfLcmOperationOccurrenceNotification,
+                    aaiHelper, aaiServiceProvider, vnfmServiceProvider, jobManager, vnfInstance);
+            executor.execute(handler);
+        }
+
+        logger.info("Sending notification response");
+        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+    }
+
+    private boolean isANotificationOfInterest(final VnfLcmOperationOccurrenceNotification notification) {
+        return isInstanitiateCompleted(notification) || isTerminateTerminalState(notification);
+    }
+
+    private boolean isInstanitiateCompleted(final VnfLcmOperationOccurrenceNotification notification) {
+        return notification.getOperation().equals(OperationEnum.INSTANTIATE)
+                && notification.getOperationState().equals(OperationStateEnum.COMPLETED);
+    }
+
+    private boolean isTerminateTerminalState(final VnfLcmOperationOccurrenceNotification notification) {
+        return notification.getOperation().equals(OperationEnum.TERMINATE)
+                && (notification.getOperationState().equals(OperationStateEnum.COMPLETED)
+                        || notification.getOperationState().equals(OperationStateEnum.FAILED)
+                        || notification.getOperationState().equals(OperationStateEnum.ROLLED_BACK));
+    }
+
+    private InlineResponse201 getVnfInstance(
+            final VnfLcmOperationOccurrenceNotification vnfLcmOperationOccurrenceNotification) {
+        GenericVnf vnfInAai = aaiServiceProvider
+                .invokeQueryGenericVnf(vnfLcmOperationOccurrenceNotification.getLinks().getVnfInstance().getHref())
+                .getGenericVnf().get(0);
+        EsrVnfm vnfm = aaiHelper.getAssignedVnfm(vnfInAai);
+        return vnfmServiceProvider
+                .getVnf(vnfm, vnfLcmOperationOccurrenceNotification.getLinks().getVnfInstance().getHref()).get();
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java
new file mode 100644
index 0000000..33ee881
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java
@@ -0,0 +1,164 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.BASE_URL;
+import javax.validation.Valid;
+import javax.ws.rs.core.MediaType;
+import org.onap.logging.ref.slf4j.ONAPLogConstants;
+import org.onap.so.adapters.vnfmadapter.common.CommonConstants;
+import org.onap.so.adapters.vnfmadapter.jobmanagement.JobManager;
+import org.onap.so.adapters.vnfmadapter.lifecycle.LifecycleManager;
+import org.onap.vnfmadapter.v1.model.CreateVnfRequest;
+import org.onap.vnfmadapter.v1.model.CreateVnfResponse;
+import org.onap.vnfmadapter.v1.model.DeleteVnfResponse;
+import org.onap.vnfmadapter.v1.model.QueryJobResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import io.swagger.annotations.ApiParam;
+
+/**
+ * Controller for handling requests to the VNFM (Virtual Network Function Manager) adapter REST API.
+ */
+@Controller
+@RequestMapping(value = BASE_URL, produces = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML},
+        consumes = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+public class VnfmAdapterController {
+
+    private static final Logger logger = LoggerFactory.getLogger(VnfmAdapterController.class);
+    private final LifecycleManager lifecycleManager;
+    private final JobManager jobManager;
+
+    @Autowired
+    VnfmAdapterController(final LifecycleManager lifecycleManager, final JobManager jobManager) {
+        this.lifecycleManager = lifecycleManager;
+        this.jobManager = jobManager;
+    }
+
+    @PostMapping(value = "/vnfs/{vnfId}")
+    public ResponseEntity<CreateVnfResponse> vnfCreate(
+            @ApiParam(value = "The identifier of the VNF. This must be the vnf-id of an existing generic-vnf in AAI.",
+                    required = true) @PathVariable("vnfId") final String vnfId,
+            @ApiParam(value = "VNF creation parameters",
+                    required = true) @Valid @RequestBody final CreateVnfRequest createVnfRequest,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies a single top level invocation of ONAP",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.REQUEST_ID,
+                            required = false) final String requestId,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies the client application user agent or user invoking the API",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.PARTNER_NAME,
+                            required = false) final String partnerName,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies a single invocation of a single component",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.INVOCATION_ID,
+                            required = false) final String invocationId) {
+
+        setLoggingMDCs(requestId, partnerName, invocationId);
+
+        logger.info("REST request vnfCreate with body: {}", createVnfRequest);
+
+        try {
+            final CreateVnfResponse createVnfResponse = lifecycleManager.createVnf(vnfId, createVnfRequest);
+            return new ResponseEntity<>(createVnfResponse, HttpStatus.ACCEPTED);
+        } finally {
+            clearLoggingMDCs();
+        }
+    }
+
+    @DeleteMapping(value = "/vnfs/{vnfId}")
+    public ResponseEntity<DeleteVnfResponse> vnfDelete(
+            @ApiParam(value = "The identifier of the VNF. This must be the vnf-id of an existing generic-vnf in AAI.",
+                    required = true) @PathVariable("vnfId") final String vnfId,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies a single top level invocation of ONAP",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.REQUEST_ID,
+                            required = false) final String requestId,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies the client application user agent or user invoking the API",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.PARTNER_NAME,
+                            required = false) final String partnerName,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies a single invocation of a single component",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.INVOCATION_ID,
+                            required = false) final String invocationId) {
+
+        setLoggingMDCs(requestId, partnerName, invocationId);
+
+        logger.info("REST request vnfDelete for VNF: {}", vnfId);
+
+        try {
+            final DeleteVnfResponse response = lifecycleManager.deleteVnf(vnfId);
+            return new ResponseEntity<>(response, HttpStatus.ACCEPTED);
+        } finally {
+            clearLoggingMDCs();
+        }
+    }
+
+    @GetMapping(value = "/jobs/{jobId}")
+    public ResponseEntity<QueryJobResponse> jobQuery(
+            @ApiParam(value = "The identifier of the Job.", required = true) @PathVariable("jobId") final String jobId,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies a single top level invocation of ONAP",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.REQUEST_ID,
+                            required = false) final String requestId,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies the client application user agent or user invoking the API",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.PARTNER_NAME,
+                            required = false) final String partnerName,
+            @ApiParam(
+                    value = "Used to track REST requests for logging purposes. Identifies a single invocation of a single component",
+                    required = false) @RequestHeader(value = ONAPLogConstants.Headers.INVOCATION_ID,
+                            required = false) final String invocationId) {
+
+        setLoggingMDCs(requestId, partnerName, invocationId);
+
+        try {
+            final QueryJobResponse response = jobManager.getVnfmOperation(jobId);
+            return new ResponseEntity<>(response, HttpStatus.OK);
+        } finally {
+            clearLoggingMDCs();
+        }
+    }
+
+    private void setLoggingMDCs(final String requestId, final String partnerName, final String invocationId) {
+        MDC.put(ONAPLogConstants.MDCs.REQUEST_ID, requestId);
+        MDC.put(ONAPLogConstants.MDCs.PARTNER_NAME, partnerName);
+        MDC.put(ONAPLogConstants.MDCs.INVOCATION_ID, invocationId);
+    }
+
+    private void clearLoggingMDCs() {
+        MDC.clear();
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/JobNotFoundException.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/JobNotFoundException.java
new file mode 100644
index 0000000..bc7c569
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/JobNotFoundException.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for job not found.
+ */
+@ResponseStatus(HttpStatus.NOT_FOUND)
+public class JobNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = 6398018034431666933L;
+
+    public JobNotFoundException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/TenantNotFoundException.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/TenantNotFoundException.java
new file mode 100644
index 0000000..215e68a
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/TenantNotFoundException.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+/**
+ * Exception for Tenant not found.
+ */
+
+public class TenantNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = 6398018034431666933L;
+
+    public TenantNotFoundException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfNotFoundException.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfNotFoundException.java
new file mode 100644
index 0000000..9b73293
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfNotFoundException.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for VNF not found.
+ */
+@ResponseStatus(HttpStatus.NOT_FOUND)
+public class VnfNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = 6398018034431666933L;
+
+    public VnfNotFoundException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfmNotFoundException.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfmNotFoundException.java
new file mode 100644
index 0000000..4e494c1
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfmNotFoundException.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for VNFM not found.
+ */
+@ResponseStatus(HttpStatus.BAD_REQUEST)
+public class VnfmNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = 6398018034431666933L;
+
+    public VnfmNotFoundException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfmRequestFailureException.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfmRequestFailureException.java
new file mode 100644
index 0000000..9f50a2c
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfmRequestFailureException.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+/**
+ * Exception indicating a request to a VNFM failed.
+ */
+public class VnfmRequestFailureException extends RuntimeException {
+
+    private static final long serialVersionUID = 6398018034431666933L;
+
+    public VnfmRequestFailureException(final String message) {
+        super(message);
+    }
+
+    public VnfmRequestFailureException(final String message, final Throwable throwable) {
+        super(message, throwable);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/resources/META-INF/services/org.onap.so.client.RestProperties b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/resources/META-INF/services/org.onap.so.client.RestProperties
new file mode 100644
index 0000000..86cc3f0
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/main/resources/META-INF/services/org.onap.so.client.RestProperties
@@ -0,0 +1 @@
+org.onap.so.adapters.vnfmadapter.extclients.aai.AaiPropertiesImpl
\ No newline at end of file
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/HealthCheckTest.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/HealthCheckTest.java
new file mode 100644
index 0000000..01749fa
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/HealthCheckTest.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.junit.Assert.assertEquals;
+import java.net.URI;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+public class HealthCheckTest {
+
+    @LocalServerPort
+    private int port;
+
+    private final TestRestTemplate restTemplate = new TestRestTemplate("test", "test");
+
+    @Test
+    public void testHealthcheck() throws Exception {
+        final RequestEntity<Void> request =
+                RequestEntity.get(new URI("http://localhost:" + port + "/manage/health")).build();
+        final ResponseEntity<Void> response = restTemplate.exchange(request, Void.class);
+        assertEquals(200, response.getStatusCode().value());
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003GrantControllerTest.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003GrantControllerTest.java
new file mode 100644
index 0000000..5a2c5d0
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003GrantControllerTest.java
@@ -0,0 +1,252 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.hamcrest.MockitoHamcrest;
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.aai.domain.yang.EsrSystemInfoList;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.GenericVnfs;
+import org.onap.aai.domain.yang.Relationship;
+import org.onap.aai.domain.yang.RelationshipData;
+import org.onap.aai.domain.yang.RelationshipList;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantRequest;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantRequest.OperationEnum;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantsAddResources;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantsAddResources.TypeEnum;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantsLinks;
+import org.onap.so.adapters.vnfmadapter.grant.model.GrantsLinksVnfLcmOpOcc;
+import org.onap.so.adapters.vnfmadapter.grant.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.grant.model.InlineResponse201VimConnections;
+import org.onap.so.client.aai.AAIResourcesClient;
+import org.onap.so.client.aai.entities.uri.AAIResourceUri;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+public class Sol003GrantControllerTest {
+
+    private static final String CLOUD_OWNER = "myTestCloudOwner";
+    private static final String REGION = "myTestRegion";
+    private static final String TENANT_ID = "myTestTenantId";
+    private static final String SEPARATOR = "_";
+    private static final String vimConnectionId = CLOUD_OWNER + SEPARATOR + REGION;
+
+    @LocalServerPort
+    private int port;
+    @Autowired
+    @Qualifier(CONFIGURABLE_REST_TEMPLATE)
+    private RestTemplate testRestTemplate;
+
+    @MockBean
+    private AAIResourcesClient aaiResourcesClient;
+
+    @Autowired
+    private Sol003GrantController controller;
+
+    @Before
+    public void setUp() throws Exception {
+        setUpVimInMockAai();
+    }
+
+    @Test
+    public void grantRequest_ValidRequestInstantiate_GrantApproved() {
+        final GrantRequest grantRequest = createGrantRequest("INSTANTIATE");
+        setUpGenericVnfWithVnfmRelationshipInMockAai("vnfmType", "vnfm1");
+        final ResponseEntity<InlineResponse201> response = controller.grantsPost(grantRequest);
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        assertEquals(1, response.getBody().getAddResources().size());
+        assertNull(response.getBody().getRemoveResources());
+
+        assertEquals(vimConnectionId, response.getBody().getAddResources().get(0).getVimConnectionId());
+        assertEquals("myTestVnfIdOnVnfm", response.getBody().getVnfInstanceId());
+        assertEquals("123456", response.getBody().getVnfLcmOpOccId());
+
+        final InlineResponse201VimConnections vimConnections = response.getBody().getVimConnections().get(0);
+        assertEquals(vimConnectionId, vimConnections.getVimId());
+        assertEquals("OPENSTACK", vimConnections.getVimType());
+        assertNotNull(vimConnections.getAccessInfo());
+        assertNotNull(vimConnections.getInterfaceInfo());
+        assertEquals("INSTANTIATE", grantRequest.getOperation().toString());
+    }
+
+    @Test
+    public void getGrant_notSupported_returns501() {
+        final ResponseEntity<InlineResponse201> response2 = controller.grantsGrantIdGet("myTestGrantId");
+        assertEquals(HttpStatus.NOT_IMPLEMENTED, response2.getStatusCode());
+    }
+
+    @Test
+    public void grantRequest_ValidRequestTerminate_GrantApproved() {
+        final GrantRequest grantRequest = createGrantRequest("TERMINATE");
+        setUpGenericVnfWithVnfmRelationshipInMockAai("vnfmType", "vnfm1");
+        final ResponseEntity<InlineResponse201> response = controller.grantsPost(grantRequest);
+
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        assertNull(response.getBody().getAddResources());
+        assertEquals(1, response.getBody().getRemoveResources().size());
+        assertEquals(vimConnectionId, response.getBody().getRemoveResources().get(0).getVimConnectionId());
+        assertEquals("myTestVnfIdOnVnfm", response.getBody().getVnfInstanceId());
+        assertEquals("123456", response.getBody().getVnfLcmOpOccId());
+
+        final InlineResponse201VimConnections vimConnections = response.getBody().getVimConnections().get(0);
+        assertEquals(vimConnectionId, vimConnections.getVimId());
+        assertEquals("OPENSTACK", vimConnections.getVimType());
+        assertNotNull(vimConnections.getAccessInfo());
+        assertNotNull(vimConnections.getInterfaceInfo());
+        assertEquals("TERMINATE", grantRequest.getOperation().toString());
+
+    }
+
+    private GrantRequest createGrantRequest(final String operation) {
+        final GrantRequest grantRequest = new GrantRequest();
+        grantRequest.setVnfInstanceId("myTestVnfIdOnVnfm");
+        grantRequest.setVnfLcmOpOccId("123456");
+        grantRequest.links(new GrantsLinks()
+                .vnfInstance(new GrantsLinksVnfLcmOpOcc().href("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm")));
+        if (operation == "INSTANTIATE") {
+            grantRequest.setOperation(OperationEnum.INSTANTIATE);
+            final GrantsAddResources resource = new GrantsAddResources();
+            resource.setId("123");
+            resource.setType(TypeEnum.COMPUTE);
+            grantRequest.addAddResourcesItem(resource);
+        } else if (operation == "TERMINATE") {
+            grantRequest.setOperation(OperationEnum.TERMINATE);
+            final GrantsAddResources resource = new GrantsAddResources();
+            resource.setId("123");
+            resource.setType(TypeEnum.COMPUTE);
+            grantRequest.addRemoveResourcesItem(resource);
+        }
+
+        return grantRequest;
+    }
+
+    private void setUpVimInMockAai() {
+        final EsrSystemInfo esrSystemInfo = new EsrSystemInfo();
+        esrSystemInfo.setServiceUrl("http://myVim:8080");
+        esrSystemInfo.setType("OPENSTACK");
+        esrSystemInfo.setSystemType("VIM");
+        esrSystemInfo.setCloudDomain("myDomain");
+        esrSystemInfo.setUserName("myUser");
+        esrSystemInfo.setPassword("myPassword");
+
+        final EsrSystemInfoList esrSystemInfoList = new EsrSystemInfoList();
+        esrSystemInfoList.getEsrSystemInfo().add(esrSystemInfo);
+
+        doReturn(Optional.of(esrSystemInfoList)).when(aaiResourcesClient).get(eq(EsrSystemInfoList.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        + CLOUD_OWNER + "/" + REGION + "/esr-system-info-list")));
+    }
+
+    private GenericVnf createGenericVnf(final String type) {
+        final GenericVnf genericVnf = new GenericVnf();
+        genericVnf.setVnfId("myTestVnfId");
+        genericVnf.setNfType(type);
+        return genericVnf;
+    }
+
+    private void setUpGenericVnfWithVnfmRelationshipInMockAai(final String type, final String vnfmId) {
+        final GenericVnf genericVnf = createGenericVnf(type);
+
+        final Relationship relationshipToVnfm = new Relationship();
+        relationshipToVnfm.setRelatedTo("tenant");
+        final RelationshipData relationshipData1 = new RelationshipData();
+        final RelationshipData relationshipData2 = new RelationshipData();
+        final RelationshipData relationshipData3 = new RelationshipData();
+
+        relationshipData1.setRelationshipKey("cloud-region.cloud-owner");
+        relationshipData1.setRelationshipValue(CLOUD_OWNER);
+        relationshipData2.setRelationshipKey("cloud-region.cloud-region-id");
+        relationshipData2.setRelationshipValue(REGION);
+        relationshipData3.setRelationshipKey("tenant.tenant-id");
+        relationshipData3.setRelationshipValue(TENANT_ID);
+
+        relationshipToVnfm.getRelationshipData().add(relationshipData1);
+        relationshipToVnfm.getRelationshipData().add(relationshipData2);
+        relationshipToVnfm.getRelationshipData().add(relationshipData3);
+
+        final RelationshipList relationshipList = new RelationshipList();
+        relationshipList.getRelationship().add(relationshipToVnfm);
+        genericVnf.setRelationshipList(relationshipList);
+
+        doReturn(Optional.of(genericVnf)).when(aaiResourcesClient).get(eq(GenericVnf.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher("/network/generic-vnfs/generic-vnf/myTestVnfId")));
+
+        final List<GenericVnf> listOfGenericVnfs = new ArrayList<>();
+        listOfGenericVnfs.add(genericVnf);
+        final GenericVnfs genericVnfs = new GenericVnfs();
+        genericVnfs.getGenericVnf().addAll(listOfGenericVnfs);
+        doReturn(Optional.of(genericVnfs)).when(aaiResourcesClient).get(eq(GenericVnfs.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher(
+                        "/network/generic-vnfs?selflink=http%3A%2F%2Fvnfm%3A8080%2Fvnfs%2FmyTestVnfIdOnVnfm")));
+    }
+
+    private class AaiResourceUriMatcher extends BaseMatcher<AAIResourceUri> {
+
+        final String uriAsString;
+
+        public AaiResourceUriMatcher(final String uriAsString) {
+            this.uriAsString = uriAsString;
+        }
+
+        @Override
+        public boolean matches(final Object item) {
+            if (item instanceof AAIResourceUri) {
+                if (uriAsString.endsWith("...")) {
+                    return ((AAIResourceUri) item).build().toString()
+                            .startsWith(uriAsString.substring(0, uriAsString.indexOf("...")));
+                }
+                return ((AAIResourceUri) item).build().toString().equals(uriAsString);
+            }
+            return false;
+        }
+
+        @Override
+        public void describeTo(final Description description) {}
+
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnControllerTest.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnControllerTest.java
new file mode 100644
index 0000000..f44bf87
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnControllerTest.java
@@ -0,0 +1,413 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.hamcrest.MockitoHamcrest;
+import org.onap.aai.domain.yang.EsrSystemInfoList;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.GenericVnfs;
+import org.onap.aai.domain.yang.Relationship;
+import org.onap.aai.domain.yang.RelationshipData;
+import org.onap.aai.domain.yang.RelationshipList;
+import org.onap.aai.domain.yang.Vserver;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiHelper;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.OamIpAddressSource;
+import org.onap.so.adapters.vnfmadapter.extclients.aai.OamIpAddressSource.OamIpAddressType;
+import org.onap.so.adapters.vnfmadapter.extclients.vim.model.AccessInfo;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201Links;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201LinksSelf;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201VimConnectionInfo;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs.ChangeTypeEnum;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationComputeResource;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationLinks;
+import org.onap.so.adapters.vnfmadapter.lcn.model.LcnVnfLcmOperationOccurrenceNotificationLinksVnfInstance;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfIdentifierCreationNotification;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification.OperationEnum;
+import org.onap.so.adapters.vnfmadapter.lcn.model.VnfLcmOperationOccurrenceNotification.OperationStateEnum;
+import org.onap.so.client.aai.AAIResourcesClient;
+import org.onap.so.client.aai.entities.uri.AAIResourceUri;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+import com.google.gson.Gson;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+public class Sol003LcnControllerTest {
+
+    private static final String CLOUD_OWNER = "myTestCloudOwner";
+    private static final String REGION = "myTestRegion";
+    private static final String TENANT_ID = "myTestTenantId";
+
+    @LocalServerPort
+    private int port;
+    @Autowired
+    @Qualifier(CONFIGURABLE_REST_TEMPLATE)
+    private RestTemplate testRestTemplate;
+    private MockRestServiceServer mockRestServer;
+
+    @MockBean
+    private AAIResourcesClient aaiResourcesClient;
+
+    @Autowired
+    private Sol003LcnContoller controller;
+    private final Gson gson = new Gson();
+
+    @Inject
+    private AaiHelper aaiHelper;
+
+    @Before
+    public void setUp() throws Exception {
+        mockRestServer = MockRestServiceServer.bindTo(testRestTemplate).build();
+    }
+
+    @Test
+    public void lcnNotification_IdentifierCreated_Returns204() throws URISyntaxException, InterruptedException {
+        final VnfIdentifierCreationNotification vnfIdentifierCreationNotification =
+                new VnfIdentifierCreationNotification();
+        final ResponseEntity<Void> response =
+                controller.lcnVnfIdentifierCreationNotificationPost(vnfIdentifierCreationNotification);
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+    }
+
+    @Test
+    public void lcnNotification_IdentifierDeleted_Returns204() throws URISyntaxException, InterruptedException {
+        final VnfIdentifierCreationNotification vnfIdentifierCreationNotification =
+                new VnfIdentifierCreationNotification();
+        final ResponseEntity<Void> response =
+                controller.lcnVnfIdentifierCreationNotificationPost(vnfIdentifierCreationNotification);
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+    }
+
+    @Test
+    public void lcnNotification_InstantiateStartingOrProcessing_NoAction()
+            throws URISyntaxException, InterruptedException {
+        final VnfLcmOperationOccurrenceNotification startingNotification = new VnfLcmOperationOccurrenceNotification();
+        startingNotification.setOperation(OperationEnum.INSTANTIATE);
+        startingNotification.setOperationState(OperationStateEnum.STARTING);
+
+        ResponseEntity<Void> response = controller.lcnVnfLcmOperationOccurrenceNotificationPost(startingNotification);
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+
+        verifyZeroInteractions(aaiResourcesClient);
+
+        final VnfLcmOperationOccurrenceNotification processingNotification =
+                new VnfLcmOperationOccurrenceNotification();
+        processingNotification.setOperation(OperationEnum.INSTANTIATE);
+        processingNotification.setOperationState(OperationStateEnum.STARTING);
+
+        response = controller.lcnVnfLcmOperationOccurrenceNotificationPost(processingNotification);
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+
+        verifyZeroInteractions(aaiResourcesClient);
+    }
+
+    @Test
+    public void lcnNotification_InstantiateCompleted_AaiUpdated() throws URISyntaxException, InterruptedException {
+        final VnfLcmOperationOccurrenceNotification vnfLcmOperationOccurrenceNotification =
+                createNotification(OperationEnum.INSTANTIATE);
+        addVnfcsToNotification(vnfLcmOperationOccurrenceNotification, ChangeTypeEnum.ADDED);
+        final InlineResponse201 vnfInstance = createVnfInstance();
+
+        mockRestServer.expect(requestTo(new URI("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm")))
+                .andRespond(withSuccess(gson.toJson(vnfInstance), MediaType.APPLICATION_JSON));
+
+        final GenericVnf genericVnf = createGenericVnf("vnfmType1");
+        addRelationshipFromGenericVnfToVnfm(genericVnf, "vnfm1");
+        final List<GenericVnf> listOfGenericVnfs = new ArrayList<>();
+        listOfGenericVnfs.add(genericVnf);
+        final GenericVnfs genericVnfs = new GenericVnfs();
+        genericVnfs.getGenericVnf().addAll(listOfGenericVnfs);
+        doReturn(Optional.of(genericVnfs)).when(aaiResourcesClient).get(eq(GenericVnfs.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher(
+                        "/network/generic-vnfs?selflink=http%3A%2F%2Fvnfm%3A8080%2Fvnfs%2FmyTestVnfIdOnVnfm")));
+        EsrVnfm vnfm = new EsrVnfm();
+        vnfm.setVnfmId("vnfm1");
+        final EsrSystemInfoList esrSystemInfoList = new EsrSystemInfoList();
+        vnfm.setEsrSystemInfoList(esrSystemInfoList);
+        doReturn(Optional.of(vnfm)).when(aaiResourcesClient).get(eq(EsrVnfm.class), MockitoHamcrest
+                .argThat(new AaiResourceUriMatcher("/external-system/esr-vnfm-list/esr-vnfm/vnfm1?depth=1")));
+
+        final ResponseEntity<Void> response =
+                controller.lcnVnfLcmOperationOccurrenceNotificationPost(vnfLcmOperationOccurrenceNotification);
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+
+        final ArgumentCaptor<Object> bodyArgument1 = ArgumentCaptor.forClass(Object.class);
+        final ArgumentCaptor<AAIResourceUri> uriArgument1 = ArgumentCaptor.forClass(AAIResourceUri.class);
+
+        verify(aaiResourcesClient, timeout(1000)).update(uriArgument1.capture(), bodyArgument1.capture());
+
+        assertEquals("/network/generic-vnfs/generic-vnf/myTestVnfId",
+                uriArgument1.getAllValues().get(0).build().toString());
+        final GenericVnf updatedGenericVnf = (GenericVnf) bodyArgument1.getAllValues().get(0);
+        assertEquals("10.10.10.10", updatedGenericVnf.getIpv4OamAddress());
+        assertEquals("Created", updatedGenericVnf.getOrchestrationStatus());
+
+        final ArgumentCaptor<Object> bodyArgument2 = ArgumentCaptor.forClass(Object.class);
+        final ArgumentCaptor<AAIResourceUri> uriArgument2 = ArgumentCaptor.forClass(AAIResourceUri.class);
+        verify(aaiResourcesClient, timeout(1000)).create(uriArgument2.capture(), bodyArgument2.capture());
+
+        assertEquals(
+                "/cloud-infrastructure/cloud-regions/cloud-region/" + CLOUD_OWNER + "/" + REGION + "/tenants/tenant/"
+                        + TENANT_ID + "/vservers/vserver/myVnfc1",
+                uriArgument2.getAllValues().get(0).build().toString());
+
+        final Vserver vserver = (Vserver) bodyArgument2.getAllValues().get(0);
+        assertEquals("myVnfc1", vserver.getVserverId());
+
+        final ArgumentCaptor<AAIResourceUri> uriArgument1Connect = ArgumentCaptor.forClass(AAIResourceUri.class);
+        final ArgumentCaptor<AAIResourceUri> uriArgument2Connect = ArgumentCaptor.forClass(AAIResourceUri.class);
+        verify(aaiResourcesClient, timeout(1000)).connect(uriArgument1Connect.capture(), uriArgument2Connect.capture());
+        assertEquals(
+                "/cloud-infrastructure/cloud-regions/cloud-region/" + CLOUD_OWNER + "/" + REGION + "/tenants/tenant/"
+                        + TENANT_ID + "/vservers/vserver/myVnfc1",
+                uriArgument1Connect.getAllValues().get(0).build().toString());
+        assertEquals("/network/generic-vnfs/generic-vnf/myTestVnfId",
+                uriArgument2Connect.getAllValues().get(0).build().toString());
+    }
+
+    @Test
+    public void lcnNotification_TerminateCompleted_AaiUpdated() throws URISyntaxException, InterruptedException {
+        final VnfLcmOperationOccurrenceNotification vnfLcmOperationOccurrenceNotification =
+                createNotification(OperationEnum.TERMINATE);
+        addVnfcsToNotification(vnfLcmOperationOccurrenceNotification, ChangeTypeEnum.REMOVED);
+
+        final InlineResponse201 vnfInstance = createVnfInstance();
+
+        mockRestServer.expect(requestTo(new URI("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm")))
+                .andRespond(withSuccess(gson.toJson(vnfInstance), MediaType.APPLICATION_JSON));
+
+        mockRestServer.expect(requestTo(new URI("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm")))
+                .andRespond(withStatus(HttpStatus.NO_CONTENT).contentType(MediaType.APPLICATION_JSON));
+
+        final GenericVnf genericVnf = createGenericVnf("vnfmType1");
+        addRelationshipFromGenericVnfToVnfm(genericVnf, "vnfm1");
+        genericVnf.setSelflink("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm");
+        final List<GenericVnf> listOfGenericVnfs = new ArrayList<>();
+        listOfGenericVnfs.add(genericVnf);
+        final GenericVnfs genericVnfs = new GenericVnfs();
+        genericVnfs.getGenericVnf().addAll(listOfGenericVnfs);
+        addRelationshipFromGenericVnfToVserver(genericVnf, "myVnfc1");
+
+        doReturn(Optional.of(genericVnfs)).when(aaiResourcesClient).get(eq(GenericVnfs.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher(
+                        "/network/generic-vnfs?selflink=http%3A%2F%2Fvnfm%3A8080%2Fvnfs%2FmyTestVnfIdOnVnfm")));
+        EsrVnfm vnfm = new EsrVnfm();
+        vnfm.setVnfmId("vnfm1");
+        final EsrSystemInfoList esrSystemInfoList = new EsrSystemInfoList();
+        vnfm.setEsrSystemInfoList(esrSystemInfoList);
+        doReturn(Optional.of(vnfm)).when(aaiResourcesClient).get(eq(EsrVnfm.class), MockitoHamcrest
+                .argThat(new AaiResourceUriMatcher("/external-system/esr-vnfm-list/esr-vnfm/vnfm1?depth=1")));
+
+        final ResponseEntity<Void> response =
+                controller.lcnVnfLcmOperationOccurrenceNotificationPost(vnfLcmOperationOccurrenceNotification);
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+
+        final ArgumentCaptor<GenericVnf> genericVnfArgument = ArgumentCaptor.forClass(GenericVnf.class);
+        final ArgumentCaptor<AAIResourceUri> updateUriArgument = ArgumentCaptor.forClass(AAIResourceUri.class);
+        verify(aaiResourcesClient, timeout(10000000)).update(updateUriArgument.capture(), genericVnfArgument.capture());
+        assertEquals("/network/generic-vnfs/generic-vnf/myTestVnfId", updateUriArgument.getValue().build().toString());
+        assertEquals("Assigned", genericVnfArgument.getValue().getOrchestrationStatus());
+
+        final ArgumentCaptor<AAIResourceUri> deleteUriArgument = ArgumentCaptor.forClass(AAIResourceUri.class);
+
+        verify(aaiResourcesClient, timeout(10000000)).delete(deleteUriArgument.capture());
+
+        assertEquals(
+                "/cloud-infrastructure/cloud-regions/cloud-region/" + CLOUD_OWNER + "/" + REGION + "/tenants/tenant/"
+                        + TENANT_ID + "/vservers/vserver/myVnfc1",
+                deleteUriArgument.getAllValues().get(0).build().toString());
+    }
+
+    private VnfLcmOperationOccurrenceNotification createNotification(final OperationEnum operation) {
+        final VnfLcmOperationOccurrenceNotification notification = new VnfLcmOperationOccurrenceNotification();
+        notification.setOperation(operation);
+        notification.setOperationState(OperationStateEnum.COMPLETED);
+
+        final LcnVnfLcmOperationOccurrenceNotificationLinksVnfInstance linkToVnfInstance =
+                new LcnVnfLcmOperationOccurrenceNotificationLinksVnfInstance()
+                        .href("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm");
+        final LcnVnfLcmOperationOccurrenceNotificationLinks operationLinks =
+                new LcnVnfLcmOperationOccurrenceNotificationLinks().vnfInstance(linkToVnfInstance);
+        notification.setLinks(operationLinks);
+
+        return notification;
+    }
+
+    private void addVnfcsToNotification(final VnfLcmOperationOccurrenceNotification notification,
+            final ChangeTypeEnum changeType) {
+        final List<LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs> affectedVnfcs = new ArrayList<>();;
+        final LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs vnfc =
+                new LcnVnfLcmOperationOccurrenceNotificationAffectedVnfcs();
+        vnfc.changeType(changeType);
+        final LcnVnfLcmOperationOccurrenceNotificationComputeResource computeResource =
+                new LcnVnfLcmOperationOccurrenceNotificationComputeResource();
+        computeResource.setResourceId("myVnfc1");
+        computeResource.setVimConnectionId(CLOUD_OWNER + "_" + REGION);
+        vnfc.setComputeResource(computeResource);
+        affectedVnfcs.add(vnfc);
+        notification.setAffectedVnfcs(affectedVnfcs);
+    }
+
+    private InlineResponse201 createVnfInstance() {
+        final InlineResponse201 vnfInstance = new InlineResponse201();
+        vnfInstance.setId("myTestVnfIdOnVnfm");
+        final InlineResponse201LinksSelf selfLink = new InlineResponse201LinksSelf();
+        selfLink.setHref("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm");
+        final InlineResponse201Links VnfInstancelinks = new InlineResponse201Links();
+        VnfInstancelinks.setSelf(selfLink);
+        vnfInstance.setLinks(VnfInstancelinks);
+
+        final Map<String, String> vnfConfigurableProperties = new HashMap<>();
+        vnfConfigurableProperties.put("vnfIpAddress", "10.10.10.10");
+        vnfInstance.setVnfConfigurableProperties(vnfConfigurableProperties);
+
+        final List<InlineResponse201VimConnectionInfo> vimConnectionInfo = new ArrayList<>();;
+        final InlineResponse201VimConnectionInfo vimConnection = new InlineResponse201VimConnectionInfo();
+        vimConnection.setVimId(CLOUD_OWNER + "_" + REGION);
+        vimConnection.setId(CLOUD_OWNER + "_" + REGION);
+        final AccessInfo accessInfo = new AccessInfo();
+        accessInfo.setProjectId(TENANT_ID);
+        vimConnection.setAccessInfo(accessInfo);
+        vimConnectionInfo.add(vimConnection);
+        vnfInstance.setVimConnectionInfo(vimConnectionInfo);
+
+        final OamIpAddressSource oamIpAddressSource =
+                new OamIpAddressSource(OamIpAddressType.CONFIGURABLE_PROPERTY, "vnfIpAddress");
+        aaiHelper.setOamIpAddressSource("myTestVnfIdOnVnfm", oamIpAddressSource);
+        return vnfInstance;
+    }
+
+    private GenericVnf createGenericVnf(final String type) {
+        final GenericVnf genericVnf = new GenericVnf();
+        genericVnf.setVnfId("myTestVnfId");
+        genericVnf.setNfType(type);
+        return genericVnf;
+    }
+
+    private void addRelationshipFromGenericVnfToVnfm(final GenericVnf genericVnf, final String vnfmId) {
+        final Relationship relationshipToVnfm = new Relationship();
+        relationshipToVnfm.setRelatedLink("/aai/v15/external-system/esr-vnfm-list/esr-vnfm/" + vnfmId);
+        relationshipToVnfm.setRelatedTo("esr-vnfm");
+        final RelationshipData relationshipData = new RelationshipData();
+        relationshipData.setRelationshipKey("esr-vnfm.vnfm-id");
+        relationshipData.setRelationshipValue(vnfmId);
+        relationshipToVnfm.getRelationshipData().add(relationshipData);
+
+        if (genericVnf.getRelationshipList() == null) {
+            final RelationshipList relationshipList = new RelationshipList();
+            genericVnf.setRelationshipList(relationshipList);
+        }
+        genericVnf.getRelationshipList().getRelationship().add(relationshipToVnfm);
+    }
+
+    private void addRelationshipFromGenericVnfToVserver(final GenericVnf genericVnf, final String vserverId) {
+        final Relationship relationshipToVserver = new Relationship();
+        relationshipToVserver.setRelatedTo("vserver");
+        final RelationshipData relationshipData1 = new RelationshipData();
+        relationshipData1.setRelationshipKey("vserver.vserver-id");
+        relationshipData1.setRelationshipValue(vserverId);
+        relationshipToVserver.getRelationshipData().add(relationshipData1);
+        final RelationshipData relationshipData2 = new RelationshipData();
+        relationshipData2.setRelationshipKey("cloud-region.cloud-owner");
+        relationshipData2.setRelationshipValue(CLOUD_OWNER);
+        relationshipToVserver.getRelationshipData().add(relationshipData2);
+        final RelationshipData relationshipData3 = new RelationshipData();
+        relationshipData3.setRelationshipKey("cloud-region.cloud-region-id");
+        relationshipData3.setRelationshipValue(REGION);
+        relationshipToVserver.getRelationshipData().add(relationshipData3);
+        final RelationshipData relationshipData4 = new RelationshipData();
+        relationshipData4.setRelationshipKey("tenant.tenant-id");
+        relationshipData4.setRelationshipValue(TENANT_ID);
+        relationshipToVserver.getRelationshipData().add(relationshipData4);
+
+        if (genericVnf.getRelationshipList() == null) {
+            final RelationshipList relationshipList = new RelationshipList();
+            genericVnf.setRelationshipList(relationshipList);
+        }
+        genericVnf.getRelationshipList().getRelationship().add(relationshipToVserver);
+    }
+
+    private class AaiResourceUriMatcher extends BaseMatcher<AAIResourceUri> {
+
+        final String uriAsString;
+
+        public AaiResourceUriMatcher(final String uriAsString) {
+            this.uriAsString = uriAsString;
+        }
+
+        @Override
+        public boolean matches(final Object item) {
+            if (item instanceof AAIResourceUri) {
+                if (uriAsString.endsWith("...")) {
+                    return ((AAIResourceUri) item).build().toString()
+                            .startsWith(uriAsString.substring(0, uriAsString.indexOf("...")));
+                }
+                return ((AAIResourceUri) item).build().toString().equals(uriAsString);
+            }
+            return false;
+        }
+
+        @Override
+        public void describeTo(final Description description) {}
+
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/TestApplication.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/TestApplication.java
new file mode 100755
index 0000000..152d313
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/TestApplication.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.cache.annotation.EnableCaching;
+
+@EnableCaching
+@SpringBootApplication(scanBasePackages = {"org.onap.so"})
+@EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class})
+public class TestApplication {
+
+    public static void main(final String[] args) {
+        new SpringApplication(TestApplication.class).run(args);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java
new file mode 100644
index 0000000..9fbea38
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java
@@ -0,0 +1,562 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withBadRequest;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import com.google.gson.Gson;
+import java.net.URI;
+import java.util.Optional;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.hamcrest.MockitoHamcrest;
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.aai.domain.yang.EsrSystemInfoList;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.aai.domain.yang.EsrVnfmList;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.Relationship;
+import org.onap.aai.domain.yang.RelationshipData;
+import org.onap.aai.domain.yang.RelationshipList;
+import org.onap.so.adapters.vnfmadapter.extclients.SdcPackageProvider;
+import org.onap.so.adapters.vnfmadapter.lcn.JSON;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse2001;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201.InstantiationStateEnum;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201Links;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201LinksSelf;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmNotFoundException;
+import org.onap.so.client.aai.AAIResourcesClient;
+import org.onap.so.client.aai.AAIVersion;
+import org.onap.so.client.aai.entities.uri.AAIResourceUri;
+import org.onap.vnfmadapter.v1.model.CreateVnfRequest;
+import org.onap.vnfmadapter.v1.model.CreateVnfResponse;
+import org.onap.vnfmadapter.v1.model.DeleteVnfResponse;
+import org.onap.vnfmadapter.v1.model.OperationEnum;
+import org.onap.vnfmadapter.v1.model.OperationStateEnum;
+import org.onap.vnfmadapter.v1.model.QueryJobResponse;
+import org.onap.vnfmadapter.v1.model.Tenant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+import org.threeten.bp.LocalDateTime;
+import org.threeten.bp.OffsetDateTime;
+import org.threeten.bp.ZoneOffset;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+
+public class VnfmAdapterControllerTest {
+
+    private static final OffsetDateTime JAN_1_2019_12_00 =
+            OffsetDateTime.of(LocalDateTime.of(2019, 1, 1, 12, 0), ZoneOffset.UTC);
+    private static final OffsetDateTime JAN_1_2019_1_00 =
+            OffsetDateTime.of(LocalDateTime.of(2019, 1, 1, 1, 0), ZoneOffset.UTC);
+    private static final String CLOUD_OWNER = "myTestCloudOwner";
+    private static final String REGION = "myTestRegion";
+    private static final String TENANT_ID = "myTestTenantId";
+
+    @LocalServerPort
+    private int port;
+    @Autowired
+    @Qualifier(CONFIGURABLE_REST_TEMPLATE)
+    private RestTemplate testRestTemplate;
+    private MockRestServiceServer mockRestServer;
+
+    @MockBean
+    AAIResourcesClient aaiResourcesClient;
+
+    @MockBean
+    SdcPackageProvider sdcPackageProvider;
+
+    @Autowired
+    VnfmAdapterController controller;
+    Gson gson = new JSON().getGson();
+
+    @Before
+    public void setUp() throws Exception {
+        mockRestServer = MockRestServiceServer.bindTo(testRestTemplate).build();
+    }
+
+    @Test
+    public void createVnf_ValidRequest_Returns202AndJobId() throws Exception {
+        final Tenant tenant = new Tenant().cloudOwner(CLOUD_OWNER).regionName(REGION).tenantId(TENANT_ID);
+        final CreateVnfRequest createVnfRequest = new CreateVnfRequest().name("myTestName").tenant(tenant);
+
+        setUpGenericVnfInMockAai("vnfmType2");
+        setUpVnfmsInMockAai();
+        setUpVimInMockAai();
+
+        final String expectedsubscriptionRequest =
+                "{\"filter\":{\"vnfInstanceSubscriptionFilter\":{\"vnfInstanceIds\":[\"vnfId\"]},\"notificationTypes\":[\"VnfLcmOperationOccurrenceNotification\"]},\"callbackUri\":\"https://so-vnfm-adapter.onap:30406/so/vnfm-adapter/v1/lcn/VnfLcmOperationOccurrenceNotification\",\"authentication\":{\"authType\":[\"OAUTH2_CLIENT_CREDENTIALS\", \"BASIC\", \"TLS_CERT\"],\"paramsOauth2ClientCredentials\":{\"clientId\":\"vnfm\",\"clientPassword\":\"password1$\",\"tokenEndpoint\":\"https://so-vnfm-adapter.onap:30406/oauth/token\"},\"paramsBasic\":{\"userName\":\"vnfm\",\"password\":\"password1$\"}}}";
+        final InlineResponse2001 subscriptionResponse = new InlineResponse2001();
+
+        final InlineResponse201 createResponse = createCreateResponse();
+        mockRestServer.expect(requestTo("http://vnfm2:8080/vnf_instances"))
+                .andRespond(withSuccess(gson.toJson(createResponse), MediaType.APPLICATION_JSON));
+
+        mockRestServer.expect(requestTo("http://vnfm2:8080/subscriptions"))
+                .andExpect(content().json(expectedsubscriptionRequest))
+                .andRespond(withSuccess(gson.toJson(subscriptionResponse), MediaType.APPLICATION_JSON));
+
+        mockRestServer.expect(requestTo("http://vnfm2:8080/vnf_instances/vnfId/instantiate"))
+                .andRespond(withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
+                        .location(new URI("http://vnfm2:8080/vnf_lcm_op_occs/123456")));
+
+        final InlineResponse200 firstOperationQueryResponse = createOperationQueryResponse(
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.INSTANTIATE,
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.PROCESSING);
+        mockRestServer.expect(requestTo("http://vnfm2:8080/vnf_lcm_op_occs/123456"))
+                .andRespond(withSuccess(gson.toJson(firstOperationQueryResponse), MediaType.APPLICATION_JSON));
+
+        final InlineResponse200 secondOperationQueryReponse = createOperationQueryResponse(
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.INSTANTIATE,
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.COMPLETED);
+        mockRestServer.expect(requestTo("http://vnfm2:8080/vnf_lcm_op_occs/123456"))
+                .andRespond(withSuccess(gson.toJson(secondOperationQueryReponse), MediaType.APPLICATION_JSON));
+
+        // Invoke the create request
+
+        final ResponseEntity<CreateVnfResponse> createVnfResponse =
+                controller.vnfCreate("myTestVnfId", createVnfRequest, "asadas", "so", "1213");
+        assertEquals(HttpStatus.ACCEPTED, createVnfResponse.getStatusCode());
+        assertNotNull(createVnfResponse.getBody().getJobId());
+
+        final ArgumentCaptor<GenericVnf> genericVnfArgument = ArgumentCaptor.forClass(GenericVnf.class);
+        final ArgumentCaptor<AAIResourceUri> uriArgument = ArgumentCaptor.forClass(AAIResourceUri.class);
+
+        verify(aaiResourcesClient).update(uriArgument.capture(), genericVnfArgument.capture());
+
+        assertEquals("/network/generic-vnfs/generic-vnf/myTestVnfId", uriArgument.getValue().build().toString());
+
+        assertEquals("myTestVnfId", genericVnfArgument.getValue().getVnfId());
+
+        final ArgumentCaptor<AAIResourceUri> uriArgument1Connect = ArgumentCaptor.forClass(AAIResourceUri.class);
+        final ArgumentCaptor<AAIResourceUri> uriArgument2Connect = ArgumentCaptor.forClass(AAIResourceUri.class);
+        verify(aaiResourcesClient, timeout(1000)).connect(uriArgument1Connect.capture(), uriArgument2Connect.capture());
+        assertEquals("/external-system/esr-vnfm-list/esr-vnfm/vnfm2",
+                uriArgument1Connect.getAllValues().get(0).build().toString());
+        assertEquals("/network/generic-vnfs/generic-vnf/myTestVnfId",
+                uriArgument2Connect.getAllValues().get(0).build().toString());
+
+        // check the job status
+
+        final ResponseEntity<QueryJobResponse> firstJobQueryResponse =
+                controller.jobQuery(createVnfResponse.getBody().getJobId(), "", "so", "1213");
+        assertEquals(OperationEnum.INSTANTIATE, firstJobQueryResponse.getBody().getOperation());
+        assertEquals(OperationStateEnum.PROCESSING, firstJobQueryResponse.getBody().getOperationState());
+        assertEquals(JAN_1_2019_12_00, firstJobQueryResponse.getBody().getStartTime());
+        assertEquals(JAN_1_2019_1_00, firstJobQueryResponse.getBody().getStateEnteredTime());
+
+        final ResponseEntity<QueryJobResponse> secondJobQueryResponse =
+                controller.jobQuery(createVnfResponse.getBody().getJobId(), "", "so", "1213");
+        assertEquals(OperationEnum.INSTANTIATE, secondJobQueryResponse.getBody().getOperation());
+        assertEquals(OperationStateEnum.COMPLETED, secondJobQueryResponse.getBody().getOperationState());
+        assertEquals(JAN_1_2019_12_00, secondJobQueryResponse.getBody().getStartTime());
+        assertEquals(JAN_1_2019_1_00, secondJobQueryResponse.getBody().getStateEnteredTime());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createVnf_VnfAlreadyExistsOnVnfm_ThrowsIllegalArgumentException() throws Exception {
+        final Tenant tenant = new Tenant().cloudOwner(CLOUD_OWNER).regionName(REGION).tenantId(TENANT_ID);
+        final CreateVnfRequest createVnfRequest = new CreateVnfRequest().name("myTestName").tenant(tenant);
+
+        final GenericVnf genericVnf = setUpGenericVnfInMockAai("vnfmType1");
+        addSelfLinkToGenericVnf(genericVnf);
+        addRelationshipFromGenericVnfToVnfm(genericVnf, "vnfm1");
+        setUpVnfmsInMockAai();
+
+        final InlineResponse201 reponse = new InlineResponse201();
+        mockRestServer.expect(requestTo(new URI("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm")))
+                .andRespond(withSuccess(gson.toJson(reponse), MediaType.APPLICATION_JSON));
+
+        controller.vnfCreate("myTestVnfId", createVnfRequest, "asadas", "so", "1213");
+    }
+
+    @Test(expected = VnfmNotFoundException.class)
+    public void createVnf_NoMatchingVnfmFound_ThrowsException() throws Exception {
+        final Tenant tenant = new Tenant().cloudOwner(CLOUD_OWNER).regionName(REGION).tenantId(TENANT_ID);
+        final CreateVnfRequest createVnfRequest = new CreateVnfRequest().name("myTestName").tenant(tenant);
+
+        setUpGenericVnfInMockAai("anotherType");
+        setUpVnfmsInMockAai();
+
+        controller.vnfCreate("myTestVnfId", createVnfRequest, "asadas", "so", "1213");
+    }
+
+    @Test
+    public void createVnf_VnfmAlreadyAssociatedWithVnf_Returns202AndJobId() throws Exception {
+        final Tenant tenant = new Tenant().cloudOwner(CLOUD_OWNER).regionName(REGION).tenantId(TENANT_ID);
+        final CreateVnfRequest createVnfRequest = new CreateVnfRequest().name("myTestName").tenant(tenant);
+
+        final GenericVnf genericVnf = setUpGenericVnfInMockAai("vnfmType2");
+        addRelationshipFromGenericVnfToVnfm(genericVnf, "vnfm2");
+        setUpVnfmsInMockAai();
+        setUpVimInMockAai();
+
+        final InlineResponse201 createResponse = createCreateResponse();
+        mockRestServer.expect(requestTo("http://vnfm2:8080/vnf_instances"))
+                .andRespond(withSuccess(gson.toJson(createResponse), MediaType.APPLICATION_JSON));
+
+        mockRestServer.expect(requestTo("http://vnfm2:8080/subscriptions")).andRespond(withBadRequest());
+
+        mockRestServer.expect(requestTo("http://vnfm2:8080/vnf_instances/vnfId/instantiate"))
+                .andRespond(withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
+                        .location(new URI("http://vnfm2:8080/vnf_lcm_op_occs/123456")));
+
+        final ResponseEntity<CreateVnfResponse> response =
+                controller.vnfCreate("myTestVnfId", createVnfRequest, "asadas", "so", "1213");
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        assertNotNull(response.getBody().getJobId());
+    }
+
+    @Test
+    @Ignore
+    public void createVnf_UnauthorizedUser_Returns401() throws Exception {
+        final TestRestTemplate restTemplateWrongPassword = new TestRestTemplate("test", "wrongPassword");
+        final Tenant tenant = new Tenant().cloudOwner(CLOUD_OWNER).regionName(REGION).tenantId(TENANT_ID);
+        final CreateVnfRequest createVnfRequest = new CreateVnfRequest().name("myTestName").tenant(tenant);
+
+        final RequestEntity<CreateVnfRequest> request =
+                RequestEntity.post(new URI("http://localhost:" + port + "/so/vnfm-adapter/v1/vnfs/myVnfId"))
+                        .accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON)
+                        .header("X-ONAP-RequestId", "myRequestId").header("X-ONAP-InvocationID", "myInvocationId")
+                        .body(createVnfRequest);
+        final ResponseEntity<CreateVnfResponse> response =
+                restTemplateWrongPassword.exchange(request, CreateVnfResponse.class);
+        assertEquals(401, response.getStatusCode().value());
+    }
+
+    @Test
+    public void deleteVnf_ValidRequest_Returns202AndJobId() throws Exception {
+        final TestRestTemplate restTemplate = new TestRestTemplate("test", "test");
+
+        final GenericVnf genericVnf = setUpGenericVnfInMockAai("vnfmType1");
+        addSelfLinkToGenericVnf(genericVnf);
+        addRelationshipFromGenericVnfToVnfm(genericVnf, "vnfm1");
+        setUpVnfmsInMockAai();
+
+        mockRestServer.expect(requestTo("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm/terminate"))
+                .andRespond(withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
+                        .location(new URI("http://vnfm1:8080/vnf_lcm_op_occs/1234567")));
+
+        final InlineResponse200 firstOperationQueryResponse = createOperationQueryResponse(
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.TERMINATE,
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.PROCESSING);
+        mockRestServer.expect(requestTo("http://vnfm1:8080/vnf_lcm_op_occs/1234567"))
+                .andRespond(withSuccess(gson.toJson(firstOperationQueryResponse), MediaType.APPLICATION_JSON));
+
+        final InlineResponse200 secondOperationQueryReponse = createOperationQueryResponse(
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.TERMINATE,
+                org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.COMPLETED);
+        mockRestServer.expect(requestTo("http://vnfm1:8080/vnf_lcm_op_occs/1234567"))
+                .andRespond(withSuccess(gson.toJson(secondOperationQueryReponse), MediaType.APPLICATION_JSON));
+
+        final RequestEntity<Void> request = RequestEntity
+                .delete(new URI("http://localhost:" + port + "/so/vnfm-adapter/v1/vnfs/myTestVnfId"))
+                .accept(MediaType.APPLICATION_JSON).header("X-ONAP-RequestId", "myRequestId")
+                .header("X-ONAP-InvocationID", "myInvocationId").header("Content-Type", "application/json").build();
+        final ResponseEntity<DeleteVnfResponse> deleteVnfResponse =
+                restTemplate.exchange(request, DeleteVnfResponse.class);
+        assertEquals(202, deleteVnfResponse.getStatusCode().value());
+        assertNotNull(deleteVnfResponse.getBody().getJobId());
+
+        final ResponseEntity<QueryJobResponse> firstJobQueryResponse =
+                controller.jobQuery(deleteVnfResponse.getBody().getJobId(), "", "so", "1213");
+        assertEquals(OperationEnum.TERMINATE, firstJobQueryResponse.getBody().getOperation());
+        assertEquals(OperationStateEnum.PROCESSING, firstJobQueryResponse.getBody().getOperationState());
+        assertEquals(JAN_1_2019_12_00, firstJobQueryResponse.getBody().getStartTime());
+        assertEquals(JAN_1_2019_1_00, firstJobQueryResponse.getBody().getStateEnteredTime());
+
+        final ResponseEntity<QueryJobResponse> secondJobQueryResponse =
+                controller.jobQuery(deleteVnfResponse.getBody().getJobId(), "", "so", "1213");
+        assertEquals(OperationEnum.TERMINATE, secondJobQueryResponse.getBody().getOperation());
+        assertEquals(OperationStateEnum.PROCESSING, secondJobQueryResponse.getBody().getOperationState());
+        assertEquals(JAN_1_2019_12_00, secondJobQueryResponse.getBody().getStartTime());
+        assertEquals(JAN_1_2019_1_00, secondJobQueryResponse.getBody().getStateEnteredTime());
+    }
+
+    @Test
+    public void deleteVnf_VnfAlreadyTerminated_Returns202AndJobId() throws Exception {
+        final TestRestTemplate restTemplate = new TestRestTemplate("test", "test");
+
+        final GenericVnf genericVnf = setUpGenericVnfInMockAai("vnfmType1");
+        addSelfLinkToGenericVnf(genericVnf);
+        addRelationshipFromGenericVnfToVnfm(genericVnf, "vnfm1");
+        setUpVnfmsInMockAai();
+
+        mockRestServer.expect(requestTo("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm/terminate"))
+                .andRespond(withStatus(HttpStatus.CONFLICT).contentType(MediaType.APPLICATION_JSON));
+
+        final InlineResponse201 reponse = new InlineResponse201();
+        reponse.setInstantiationState(InstantiationStateEnum.NOT_INSTANTIATED);
+        mockRestServer.expect(requestTo(new URI("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm")))
+                .andRespond(withSuccess(gson.toJson(reponse), MediaType.APPLICATION_JSON));
+
+        mockRestServer.expect(requestTo("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm"))
+                .andRespond(withStatus(HttpStatus.NO_CONTENT).contentType(MediaType.APPLICATION_JSON));
+
+        final RequestEntity<Void> request = RequestEntity
+                .delete(new URI("http://localhost:" + port + "/so/vnfm-adapter/v1/vnfs/myTestVnfId"))
+                .accept(MediaType.APPLICATION_JSON).header("X-ONAP-RequestId", "myRequestId")
+                .header("X-ONAP-InvocationID", "myInvocationId").header("Content-Type", "application/json").build();
+        final ResponseEntity<DeleteVnfResponse> deleteVnfResponse =
+                restTemplate.exchange(request, DeleteVnfResponse.class);
+        assertEquals(202, deleteVnfResponse.getStatusCode().value());
+        assertNotNull(deleteVnfResponse.getBody().getJobId());
+
+        final ResponseEntity<QueryJobResponse> jobQueryResponse =
+                controller.jobQuery(deleteVnfResponse.getBody().getJobId(), "", "so", "1213");
+        assertEquals(OperationStateEnum.COMPLETED, jobQueryResponse.getBody().getOperationState());
+    }
+
+    @Test
+    public void deleteVnf_GenericVnfNotFound_Returns404() throws Exception {
+        final TestRestTemplate restTemplate = new TestRestTemplate("test", "test");
+
+        final RequestEntity<Void> request = RequestEntity
+                .delete(new URI("http://localhost:" + port + "/so/vnfm-adapter/v1/vnfs/myNonExistingVnfId"))
+                .accept(MediaType.APPLICATION_JSON).header("X-ONAP-RequestId", "myRequestId")
+                .header("X-ONAP-InvocationID", "myInvocationId").header("Content-Type", "application/json").build();
+        final ResponseEntity<DeleteVnfResponse> deleteVnfResponse =
+                restTemplate.exchange(request, DeleteVnfResponse.class);
+        assertEquals(404, deleteVnfResponse.getStatusCode().value());
+        assertNull(deleteVnfResponse.getBody().getJobId());
+    }
+
+    @Test
+    public void deleteVnf_NoAssignedVnfm_Returns400() throws Exception {
+        final TestRestTemplate restTemplate = new TestRestTemplate("test", "test");
+
+        setUpGenericVnfInMockAai("vnfmType");
+
+        final RequestEntity<Void> request = RequestEntity
+                .delete(new URI("http://localhost:" + port + "/so/vnfm-adapter/v1/vnfs/myTestVnfId"))
+                .accept(MediaType.APPLICATION_JSON).header("X-ONAP-RequestId", "myRequestId")
+                .header("X-ONAP-InvocationID", "myInvocationId").header("Content-Type", "application/json").build();
+        final ResponseEntity<DeleteVnfResponse> deleteVnfResponse =
+                restTemplate.exchange(request, DeleteVnfResponse.class);
+        assertEquals(400, deleteVnfResponse.getStatusCode().value());
+        assertNull(deleteVnfResponse.getBody().getJobId());
+    }
+
+    @Test
+    public void deleteVnf_ErrorStatusCodeFromVnfm_Returns500() throws Exception {
+        final TestRestTemplate restTemplate = new TestRestTemplate("test", "test");
+
+        final GenericVnf genericVnf = setUpGenericVnfInMockAai("vnfmType1");
+        addSelfLinkToGenericVnf(genericVnf);
+        addRelationshipFromGenericVnfToVnfm(genericVnf, "vnfm1");
+        setUpVnfmsInMockAai();
+
+        mockRestServer.expect(requestTo("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm/terminate"))
+                .andRespond(withStatus(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON));
+
+        final RequestEntity<Void> request = RequestEntity
+                .delete(new URI("http://localhost:" + port + "/so/vnfm-adapter/v1/vnfs/myTestVnfId"))
+                .accept(MediaType.APPLICATION_JSON).header("X-ONAP-RequestId", "myRequestId")
+                .header("X-ONAP-InvocationID", "myInvocationId").header("Content-Type", "application/json").build();
+        final ResponseEntity<DeleteVnfResponse> deleteVnfResponse =
+                restTemplate.exchange(request, DeleteVnfResponse.class);
+        assertEquals(500, deleteVnfResponse.getStatusCode().value());
+        assertNull(deleteVnfResponse.getBody().getJobId());
+
+    }
+
+    private InlineResponse200 createOperationQueryResponse(
+            final org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum operation,
+            final org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum operationState) {
+        final InlineResponse200 response = new InlineResponse200();
+        response.setId("9876");
+        response.setOperation(operation);
+        response.setOperationState(operationState);
+        response.setStartTime(JAN_1_2019_12_00);
+        response.setStateEnteredTime(JAN_1_2019_1_00);
+        response.setVnfInstanceId("myVnfInstanceId");
+        return response;
+    }
+
+    private GenericVnf createGenericVnf(final String type) {
+        final GenericVnf genericVnf = new GenericVnf();
+        genericVnf.setVnfId("myTestVnfId");
+        genericVnf.setNfType(type);
+        return genericVnf;
+    }
+
+    private GenericVnf setUpGenericVnfInMockAai(final String type) {
+        final GenericVnf genericVnf = createGenericVnf(type);
+
+        doReturn(Optional.of(genericVnf)).when(aaiResourcesClient).get(eq(GenericVnf.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher("/network/generic-vnfs/generic-vnf/myTestVnfId")));
+        return genericVnf;
+    }
+
+    private void addSelfLinkToGenericVnf(final GenericVnf vnf) {
+        vnf.setSelflink("http://vnfm:8080/vnfs/myTestVnfIdOnVnfm");
+    }
+
+    private void addRelationshipFromGenericVnfToVnfm(final GenericVnf genericVnf, final String vnfmId) {
+        final Relationship relationshipToVnfm = new Relationship();
+        relationshipToVnfm
+                .setRelatedLink("/aai/" + AAIVersion.LATEST + "/external-system/esr-vnfm-list/esr-vnfm/" + vnfmId);
+        relationshipToVnfm.setRelatedTo("esr-vnfm");
+        final RelationshipData relationshipData = new RelationshipData();
+        relationshipData.setRelationshipKey("esr-vnfm.vnfm-id");
+        relationshipData.setRelationshipValue(vnfmId);
+        relationshipToVnfm.getRelationshipData().add(relationshipData);
+
+        final RelationshipList relationshipList = new RelationshipList();
+        relationshipList.getRelationship().add(relationshipToVnfm);
+        genericVnf.setRelationshipList(relationshipList);
+    }
+
+    private void setUpVnfmsInMockAai() {
+        final EsrSystemInfo esrSystemInfo1 = new EsrSystemInfo();
+        esrSystemInfo1.setServiceUrl("http://vnfm1:8080");
+        esrSystemInfo1.setType("vnfmType1");
+        esrSystemInfo1.setSystemType("VNFM");
+        final EsrSystemInfoList esrSystemInfoList1 = new EsrSystemInfoList();
+        esrSystemInfoList1.getEsrSystemInfo().add(esrSystemInfo1);
+
+        final EsrVnfm esrVnfm1 = new EsrVnfm();
+        esrVnfm1.setVnfmId("vnfm1");
+        esrVnfm1.setEsrSystemInfoList(esrSystemInfoList1);
+        esrVnfm1.setResourceVersion("1234");
+
+        final EsrSystemInfo esrSystemInfo2 = new EsrSystemInfo();
+        esrSystemInfo2.setServiceUrl("http://vnfm2:8080");
+        esrSystemInfo2.setType("vnfmType2");
+        esrSystemInfo2.setSystemType("VNFM");
+        final EsrSystemInfoList esrSystemInfoList2 = new EsrSystemInfoList();
+        esrSystemInfoList2.getEsrSystemInfo().add(esrSystemInfo2);
+
+        final EsrVnfm esrVnfm2 = new EsrVnfm();
+        esrVnfm2.setVnfmId("vnfm2");
+        esrVnfm2.setEsrSystemInfoList(esrSystemInfoList2);
+        esrVnfm2.setResourceVersion("1234");
+
+        final EsrVnfmList esrVnfmList = new EsrVnfmList();
+        esrVnfmList.getEsrVnfm().add(esrVnfm1);
+        esrVnfmList.getEsrVnfm().add(esrVnfm2);
+
+        doReturn(Optional.of(esrVnfm1)).when(aaiResourcesClient).get(eq(EsrVnfm.class), MockitoHamcrest
+                .argThat(new AaiResourceUriMatcher("/external-system/esr-vnfm-list/esr-vnfm/vnfm1?depth=1")));
+
+        doReturn(Optional.of(esrVnfm2)).when(aaiResourcesClient).get(eq(EsrVnfm.class), MockitoHamcrest
+                .argThat(new AaiResourceUriMatcher("/external-system/esr-vnfm-list/esr-vnfm/vnfm2?depth=1")));
+
+        doReturn(Optional.of(esrVnfmList)).when(aaiResourcesClient).get(eq(EsrVnfmList.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher("/external-system/esr-vnfm-list")));
+
+        doReturn(Optional.of(esrSystemInfoList1)).when(aaiResourcesClient).get(eq(EsrSystemInfoList.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher(
+                        "/external-system/esr-vnfm-list/esr-vnfm/vnfm1/esr-system-info-list")));
+        doReturn(Optional.of(esrSystemInfoList2)).when(aaiResourcesClient).get(eq(EsrSystemInfoList.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher(
+                        "/external-system/esr-vnfm-list/esr-vnfm/vnfm2/esr-system-info-list")));
+    }
+
+    private void setUpVimInMockAai() {
+        final EsrSystemInfo esrSystemInfo = new EsrSystemInfo();
+        esrSystemInfo.setServiceUrl("http://myVim:8080");
+        esrSystemInfo.setType("openstack");
+        esrSystemInfo.setSystemType("VIM");
+        esrSystemInfo.setCloudDomain("myDomain");
+        esrSystemInfo.setUserName("myUser");
+        esrSystemInfo.setPassword("myPassword");
+
+        final EsrSystemInfoList esrSystemInfoList = new EsrSystemInfoList();
+        esrSystemInfoList.getEsrSystemInfo().add(esrSystemInfo);
+
+        doReturn(Optional.of(esrSystemInfoList)).when(aaiResourcesClient).get(eq(EsrSystemInfoList.class),
+                MockitoHamcrest.argThat(new AaiResourceUriMatcher("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        + CLOUD_OWNER + "/" + REGION + "/esr-system-info-list")));
+    }
+
+    private InlineResponse201 createCreateResponse() {
+        final InlineResponse201 createResponse = new InlineResponse201();
+        createResponse.setVnfdId("myTestVnfd");
+        final InlineResponse201Links links = new InlineResponse201Links();
+        final InlineResponse201LinksSelf self = new InlineResponse201LinksSelf();
+        self.setHref("http://vnfm2:8080/vnf_instances/vnfId");
+        links.setSelf(self);
+        createResponse.setLinks(links);
+        createResponse.setId("vnfId");
+        return createResponse;
+    }
+
+
+    private class AaiResourceUriMatcher extends BaseMatcher<AAIResourceUri> {
+
+        final String uriAsString;
+
+        public AaiResourceUriMatcher(final String uriAsString) {
+            this.uriAsString = uriAsString;
+        }
+
+        @Override
+        public boolean matches(final Object item) {
+            if (item instanceof AAIResourceUri) {
+                if (uriAsString.endsWith("...")) {
+                    return ((AAIResourceUri) item).build().toString()
+                            .startsWith(uriAsString.substring(0, uriAsString.indexOf("...")));
+                }
+                return ((AAIResourceUri) item).build().toString().equals(uriAsString);
+            }
+            return false;
+        }
+
+        @Override
+        public void describeTo(final Description description) {}
+
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/resources/application.yaml b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/resources/application.yaml
new file mode 100644
index 0000000..cdb6662
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-lcm/etsi-sol003-lcm-adapter/src/test/resources/application.yaml
@@ -0,0 +1,61 @@
+# Copyright © 2019 Nordix Foundation
+#
+# 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.
+spring:
+  security:
+    usercredentials:
+      - username: test
+        password: '$2a$12$Zi3AuYcZoZO/gBQyUtST2.F5N6HqcTtaNci2Et.ufsQhski56srIu'
+        role: BPEL-Client
+      - username: vnfm
+        password: '$2a$10$Fh9ffgPw2vnmsghsRD3ZauBL1aKXebigbq3BB1RPWtE62UDILsjke'
+        role: BPEL-Client
+  main:
+    allow-bean-definition-overriding: true
+
+mso:
+  key: 07a7159d3bf51a0e53be7a8f89699be7
+
+aai:
+  auth: 2A11B07DB6214A839394AA1EC5844695F5114FC407FF5422625FB00175A3DCB8A1FF745F22867EFA72D5369D599BBD88DA8BED4233CF5586
+  endpoint: https://aai.onap:8443
+  version: v15
+
+sdc:
+  username: sdcUser
+  password: sdcPassword
+  key: adadadadad
+  endpoint: http://sdc.onap/1234A
+
+
+vnfmadapter:
+  endpoint: https://so-vnfm-adapter.onap:30406
+
+  
+#Actuator
+management:
+  endpoints:
+    web:
+      base-path: /manage
+      exposure:
+        include: "*"
+  metrics:
+    se-global-registry: false
+    export:
+      prometheus:
+        enabled: true # Whether exporting of metrics to Prometheus is enabled.
+        step: 1m # Step size (i.e. reporting frequency) to use.
+
+etsi-catalog-manager:
+   vnfpkgm:
+      endpoint: http://msb-iag.onap:80/api/vnfpkgm/v1