Merge "Add subscription for notifications"
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/Constants.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/Constants.java
index cdf7de8..1695b39 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/Constants.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/Constants.java
@@ -28,6 +28,7 @@
     public static final String SERVICE_NAME = "vnfm-adapter";
     public static final String SERVICE_VERSION = "v1";
     public static final String BASE_URL = "/so/" + SERVICE_NAME + "/" + SERVICE_VERSION;
+    public static final String OPERATION_NOTIFICATION_ENDPOINT = "/lcn/VnfLcmOperationOccurrenceNotification";
 
     private Constants() {}
 }
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java
index 3b2b87f..2e23318 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java
@@ -20,6 +20,8 @@
 
 package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
 
+import static org.onap.so.adapters.vnfmadapter.Constants.BASE_URL;
+import static org.onap.so.adapters.vnfmadapter.Constants.OPERATION_NOTIFICATION_ENDPOINT;
 import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
@@ -33,14 +35,23 @@
 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.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.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.so.security.WebSecurityConfig;
 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.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 /**
@@ -52,10 +63,15 @@
     private static final Logger logger = LoggerFactory.getLogger(VnfmHelper.class);
     private static final String SEPARATOR = "_";
     private final AaiServiceProvider aaiServiceProvider;
+    private final WebSecurityConfig webSecurityConfig;
+
+    @Value("${vnfmadapter.endpoint}")
+    private String vnfmAdapterEndoint;
 
     @Autowired
-    public VnfmHelper(final AaiServiceProvider aaiServiceProvider) {
+    public VnfmHelper(final AaiServiceProvider aaiServiceProvider, final WebSecurityConfig webSecurityConfig) {
         this.aaiServiceProvider = aaiServiceProvider;
+        this.webSecurityConfig = webSecurityConfig;
     }
 
     /**
@@ -149,4 +165,39 @@
         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
+     */
+    public LccnSubscriptionRequest createNotificationSubscriptionRequest(final String vnfId) {
+        final LccnSubscriptionRequest lccnSubscriptionRequest = new LccnSubscriptionRequest();
+        lccnSubscriptionRequest.setAuthentication(getSubscriptionsAuthentication());
+        lccnSubscriptionRequest.setCallbackUri(vnfmAdapterEndoint + BASE_URL + OPERATION_NOTIFICATION_ENDPOINT);
+        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() {
+        final SubscriptionsAuthenticationParamsBasic basicAuthParams = new SubscriptionsAuthenticationParamsBasic();
+        basicAuthParams.setUserName("vnfm");
+        basicAuthParams.setPassword(webSecurityConfig.getUsercredentials().stream()
+                .filter(userCredentials -> "vnfm".equals(userCredentials.getUsername())).findFirst().get()
+                .getPassword());
+
+        final SubscriptionsAuthentication authentication = new SubscriptionsAuthentication();
+        authentication.addAuthTypeItem(AuthTypeEnum.BASIC);
+        authentication.paramsBasic(basicAuthParams);
+        return authentication;
+    }
+
 }
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java
index 320715d..472a8b8 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java
@@ -22,8 +22,10 @@
 
 import com.google.common.base.Optional;
 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;
 
 /**
@@ -49,6 +51,16 @@
     String instantiateVnf(final String vnfSelfLink, final InstantiateVnfRequest instantiateVnfRequest);
 
     /**
+     * Invoke a notification subscription request to a VNFM.
+     *
+     * @param vnfmId the ID of the VNFM
+     * @param subscriptionRequest
+     * @return the response to the subscription request
+     */
+    InlineResponse2001 subscribeForNotifications(final String vnfmId,
+            final LccnSubscriptionRequest subscriptionRequest);
+
+    /**
      * Invoke a terminate request for a VNF.
      *
      * @param vnfSelfLink the link to he VNF on the VNFM
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java
index 246444f..711069b 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java
@@ -22,8 +22,10 @@
 
 import com.google.common.base.Optional;
 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;
 import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmRequestFailureException;
 import org.onap.so.rest.service.HttpRestServiceProvider;
@@ -76,7 +78,29 @@
         }
         final String locationHeader = response.getHeaders().get("Location").iterator().next();
         return locationHeader.substring(locationHeader.lastIndexOf("/") + 1);
+    }
 
+
+    @Override
+    public InlineResponse2001 subscribeForNotifications(final String vnfmId,
+            final LccnSubscriptionRequest subscriptionRequest) {
+        final String url = urlProvider.getSubscriptionsUrl(vnfmId);
+        ResponseEntity<InlineResponse2001> response = null;
+        try {
+            response = httpServiceProvider.postHttpRequest(subscriptionRequest, url, InlineResponse2001.class);
+        } catch (final Exception exception) {
+            final String errorMessage =
+                    "Subscription to VNFM " + vnfmId + " resulted in exception" + subscriptionRequest;
+            logger.error(errorMessage, exception);
+            throw new VnfmRequestFailureException(errorMessage, exception);
+        }
+        if (response.getStatusCode() != HttpStatus.OK) {
+            final String errorMessage = "Subscription to VNFM " + vnfmId + " returned status code: "
+                    + response.getStatusCode() + ", request: " + subscriptionRequest;
+            logger.error(errorMessage);
+            throw new VnfmRequestFailureException(errorMessage);
+        }
+        return response.getBody();
     }
 
     @Override
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java
index f0280d6..2eaaa81 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java
@@ -59,6 +59,20 @@
         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("getOperationUrl:" + url);
+
+        return url;
+    }
+
     private URI getBaseUri(final String vnfmId) {
         final EsrSystemInfoList vnfmEsrSystemInfoList = aaiServiceProvider.invokeGetVnfmEsrSystemInfoList(vnfmId);
 
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java
index 9249b9e..7f0f9e3 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java
@@ -29,6 +29,7 @@
 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;
@@ -82,6 +83,7 @@
         }
 
         final String vnfIdInVnfm = sendCreateRequestToVnfm(genericVnf);
+        createNotificationSubscription(vnfm.getVnfmId(), vnfIdInVnfm);
         final String operationId = sendInstantiateRequestToVnfm(vnfm, genericVnf, request, vnfIdInAai, vnfIdInVnfm);
 
         final String jobId = jobManager.createJob(vnfm.getVnfmId(), operationId, false);
@@ -111,6 +113,17 @@
         return "vnfId";
     }
 
+    private void createNotificationSubscription(final String vnfmId, final String vnfId) {
+        try {
+            final LccnSubscriptionRequest subscriptionRequest = vnfmHelper.createNotificationSubscriptionRequest(vnfId);
+            vnfmServiceProvider.subscribeForNotifications(vnfmId, subscriptionRequest);
+        } catch (final Exception exception) {
+            logger.warn("Subscription for notifications to VNFM: " + vnfmId + " 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 String vnfIdInAai, final String vnfIdInVnfm) {
 
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnContoller.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnContoller.java
index f0958cb..0441342 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnContoller.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003LcnContoller.java
@@ -21,6 +21,7 @@
 package org.onap.so.adapters.vnfmadapter.rest;
 
 import static org.onap.so.adapters.vnfmadapter.Constants.BASE_URL;
+import static org.onap.so.adapters.vnfmadapter.Constants.OPERATION_NOTIFICATION_ENDPOINT;
 import static org.slf4j.LoggerFactory.getLogger;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -83,7 +84,7 @@
         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
     }
 
-    @PostMapping(value = "/lcn/VnfLcmOperationOccurrenceNotification")
+    @PostMapping(value = OPERATION_NOTIFICATION_ENDPOINT)
     public ResponseEntity<Void> lcnVnfLcmOperationOccurrenceNotificationPost(
             @RequestBody final VnfLcmOperationOccurrenceNotification vnfLcmOperationOccurrenceNotification) {
         logger.info(LOG_LCN_RECEIVED + vnfLcmOperationOccurrenceNotification);
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java
index 64ce25c..78b2116 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java
@@ -27,7 +27,9 @@
 import static org.mockito.Mockito.doReturn;
 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;
@@ -50,6 +52,7 @@
 import org.onap.aai.domain.yang.RelationshipList;
 import org.onap.so.adapters.vnfmadapter.VnfmAdapterApplication;
 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.rest.exceptions.VnfmNotFoundException;
 import org.onap.so.client.aai.AAIResourcesClient;
@@ -123,6 +126,13 @@
         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\":[\"BASIC\"],\"paramsBasic\":{\"userName\":\"vnfm\",\"password\":\"$2a$10$Fh9ffgPw2vnmsghsRD3ZauBL1aKXebigbq3BB1RPWtE62UDILsjke\"}}}";
+        final InlineResponse2001 subscriptionResponse = new InlineResponse2001();
+        mockRestServer.expect(requestTo("http://vnfm2:8080/subscriptions"))
+                .andExpect(content().json(expectedsubscriptionRequest))
+                .andRespond(withSuccess(gson.toJson(subscriptionResponse), MediaType.APPLICATION_JSON));
+
         mockRestServer.expect(requestTo("http://dummy.value/until/create/implememted/vnfId/instantiate"))
                 .andRespond(withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
                         .location(new URI("http://vnfm2:8080/vnf_lcm_op_occs/123456")));
@@ -215,6 +225,8 @@
         setUpVnfmsInMockAai();
         setUpVimInMockAai();
 
+        mockRestServer.expect(requestTo("http://vnfm2:8080/subscriptions")).andRespond(withBadRequest());
+
         mockRestServer.expect(requestTo("http://dummy.value/until/create/implememted/vnfId/instantiate"))
                 .andRespond(withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
                         .location(new URI("http://vnfm2:8080/vnf_lcm_op_occs/123456")));
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/resources/application-test.yaml b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/resources/application-test.yaml
index 2b51181..e69e901 100644
--- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/resources/application-test.yaml
+++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/resources/application-test.yaml
@@ -17,6 +17,9 @@
     - username: test
       password: '$2a$12$Zi3AuYcZoZO/gBQyUtST2.F5N6HqcTtaNci2Et.ufsQhski56srIu'
       role: BPEL-Client
+    - username: vnfm
+      password: '$2a$10$Fh9ffgPw2vnmsghsRD3ZauBL1aKXebigbq3BB1RPWtE62UDILsjke'
+      role: BPEL-Client
       
 mso:
   key: 07a7159d3bf51a0e53be7a8f89699be7
@@ -24,4 +27,7 @@
 aai:
   auth: 2A11B07DB6214A839394AA1EC5844695F5114FC407FF5422625FB00175A3DCB8A1FF745F22867EFA72D5369D599BBD88DA8BED4233CF5586
   endpoint: https://aai.onap:8443
-  version: v15
\ No newline at end of file
+  version: v15
+  
+vnfmadapter:
+  endpoint: https://so-vnfm-adapter.onap:30406
\ No newline at end of file