<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.springframework.retry</groupId>
+ <artifactId>spring-retry</artifactId>
+ </dependency>
<dependency>
<groupId>org.onap.so.adapters</groupId>
<artifactId>mso-vnfm-adapter-ext-clients</artifactId>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
import java.util.List;
import java.util.Optional;
+import org.apache.logging.log4j.util.Strings;
import org.onap.aai.domain.yang.EsrSystemInfo;
import org.onap.aai.domain.yang.EsrVnfm;
import org.onap.aai.domain.yang.EsrVnfmList;
+import org.onap.so.adapters.vevnfm.exception.VeVnfmException;
import org.onap.so.client.aai.AAIObjectType;
import org.onap.so.client.aai.AAIResourcesClient;
+import org.onap.so.client.aai.entities.uri.AAIResourceUri;
import org.onap.so.client.aai.entities.uri.AAIUriFactory;
import org.onap.so.client.graphinventory.entities.uri.Depth;
import org.slf4j.Logger;
private static final int FIRST_INDEX = 0;
- public EsrSystemInfo receiveVnfm() {
+ private static void isValid(final EsrSystemInfo info) throws VeVnfmException {
+ if (info == null || Strings.isBlank(info.getServiceUrl())) {
+ throw new VeVnfmException("No 'url' field in VNFM info");
+ }
+ }
+
+ public EsrSystemInfo receiveVnfm() throws VeVnfmException {
+ EsrSystemInfo info;
+
+ try {
+ info = receiveVnfmInternal();
+ } catch (Exception e) {
+ throw new VeVnfmException(e);
+ }
+
+ isValid(info);
+
+ return info;
+ }
+
+ private EsrSystemInfo receiveVnfmInternal() {
final AAIResourcesClient resourcesClient = new AAIResourcesClient();
- final Optional<EsrVnfmList> response =
- resourcesClient.get(EsrVnfmList.class, AAIUriFactory.createResourceUri(AAIObjectType.VNFM_LIST));
+ final AAIResourceUri resourceUri = AAIUriFactory.createResourceUri(AAIObjectType.VNFM_LIST);
+ final Optional<EsrVnfmList> response = resourcesClient.get(EsrVnfmList.class, resourceUri);
if (response.isPresent()) {
final EsrVnfmList esrVnfmList = response.get();
package org.onap.so.adapters.vevnfm.configuration;
+import org.onap.aai.domain.yang.EsrSystemInfo;
import org.onap.so.adapters.vevnfm.service.StartupService;
+import org.onap.so.adapters.vevnfm.service.SubscriptionScheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Configuration;
@Autowired
private StartupService startupService;
+ @Autowired
+ private SubscriptionScheduler subscriptionScheduler;
+
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReadyEvent() throws Exception {
if (!environment.acceptsProfiles(Profiles.of(TEST_PROFILE))) {
- startupService.run();
+ final EsrSystemInfo info = startupService.receiveVnfm();
+ subscriptionScheduler.setInfo(info);
}
}
}
@Autowired
private DmaapService dmaapService;
- @PostMapping("${notification.url}")
+ @PostMapping("${vnfm.notification}")
public ResponseEntity receiveNotification(@RequestBody final VnfLcmOperationOccurrenceNotification notification) {
logger.info("Notification received {}", notification);
dmaapService.send(notification);
public VeVnfmException(final String message) {
super(message);
}
+
+ public VeVnfmException(final Throwable cause) {
+ super(cause);
+ }
}
package org.onap.so.adapters.vevnfm.provider;
+import org.apache.logging.log4j.util.Strings;
import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
public class AuthorizationHeadersProvider extends BasicHttpHeadersProvider {
public void addAuthorization(final String authorization) {
+ if (Strings.isBlank(authorization)) {
+ return;
+ }
+
getHttpHeaders().set(AUTHORIZATION_HEADER, authorization);
}
package org.onap.so.adapters.vevnfm.service;
-import org.apache.logging.log4j.util.Strings;
import org.onap.aai.domain.yang.EsrSystemInfo;
import org.onap.so.adapters.vevnfm.aai.AaiConnection;
import org.onap.so.adapters.vevnfm.exception.VeVnfmException;
+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.retry.annotation.Backoff;
+import org.springframework.retry.annotation.EnableRetry;
+import org.springframework.retry.annotation.Recover;
+import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
+@EnableRetry
public class StartupService {
- @Autowired
- private AaiConnection aaiConnection;
+ private static final Logger logger = LoggerFactory.getLogger(StartupService.class);
+
+ @Value("${vnfm.default-endpoint}")
+ private String vnfmDefaultEndpoint;
@Autowired
- private SubscriberService subscriberService;
+ private AaiConnection aaiConnection;
- private static void isValid(final EsrSystemInfo info) throws VeVnfmException {
- if (Strings.isBlank(info.getServiceUrl())) {
- throw new VeVnfmException("No 'url' field in VNFM info");
- }
+ @Retryable(value = {VeVnfmException.class}, maxAttempts = 5, backoff = @Backoff(delay = 5000, multiplier = 10))
+ public EsrSystemInfo receiveVnfm() throws VeVnfmException {
+ return aaiConnection.receiveVnfm();
}
- public void run() throws Exception {
- final EsrSystemInfo info = aaiConnection.receiveVnfm();
- isValid(info);
- final boolean done = subscriberService.subscribe(info);
+ @Recover
+ public EsrSystemInfo recoverReceiveVnfm(final Throwable e) {
+ logger.warn("Connection to AAI failed");
+ final EsrSystemInfo info = new EsrSystemInfo();
+ info.setServiceUrl(vnfmDefaultEndpoint);
+ logger.warn("This EsrSystemInfo is used by default: {}", info);
- if (!done) {
- throw new VeVnfmException("Could not subscribe to VNFM");
- }
+ return info;
}
}
* ============LICENSE_END=========================================================
*/
-package org.onap.so.adapters.vevnfm.subscription;
+package org.onap.so.adapters.vevnfm.service;
+import com.fasterxml.jackson.annotation.JsonProperty;
import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.so.adapters.vevnfm.exception.VeVnfmException;
import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.LccnSubscriptionRequest;
import org.onap.so.rest.service.HttpRestServiceProvider;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+import lombok.ToString;
-@Component
+@Service
public class SubscribeSender {
+ public static final String SLASH = "/";
+
private static final Logger logger = LoggerFactory.getLogger(SubscribeSender.class);
@Value("${vnfm.subscription}")
@Autowired
private HttpRestServiceProvider restProvider;
- public boolean send(final EsrSystemInfo info, final LccnSubscriptionRequest request) {
- final ResponseEntity<String> response = restProvider.postHttpRequest(request, getUrl(info), String.class);
+ public String send(final EsrSystemInfo info, final LccnSubscriptionRequest request) throws VeVnfmException {
+ final ResponseEntity<SubscribeToManoResponse> response =
+ restProvider.postHttpRequest(request, getUrl(info), SubscribeToManoResponse.class);
final HttpStatus statusCode = response.getStatusCode();
- final String body = response.getBody();
+ final SubscribeToManoResponse body = response.getBody();
logger.info("The VNFM replied with the code {} and the body {}", statusCode, body);
- return HttpStatus.CREATED == statusCode;
+ if (HttpStatus.CREATED != statusCode) {
+ throw new VeVnfmException("The status code was different than " + HttpStatus.CREATED);
+ }
+
+ return body.id;
+ }
+
+ public boolean check(final EsrSystemInfo info, final String id) {
+ final ResponseEntity<SubscribeToManoResponse> response =
+ restProvider.getHttpResponse(getUrl(info) + SLASH + id, SubscribeToManoResponse.class);
+ return response.getBody() != null && response.getBody().id.equals(id);
}
private String getUrl(final EsrSystemInfo info) {
return info.getServiceUrl() + vnfmSubscription;
}
+
+ @ToString
+ static class SubscribeToManoResponse {
+ @JsonProperty("id")
+ String id;
+ @JsonProperty("callbackUri")
+ String callbackUri;
+ }
}
import com.squareup.okhttp.Credentials;
import java.util.Collections;
+import org.apache.logging.log4j.util.Strings;
import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.so.adapters.vevnfm.exception.VeVnfmException;
import org.onap.so.adapters.vevnfm.provider.AuthorizationHeadersProvider;
-import org.onap.so.adapters.vevnfm.subscription.SubscribeSender;
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.SubscriptionsAuthenticationParamsBasic;
@Service
public class SubscriberService {
- private static final char COLON = ':';
+ @Value("${vevnfmadapter.endpoint}")
+ private String endpoint;
- @Value("${system.url}")
- private String systemUrl;
-
- @Value("${server.port}")
- private String serverPort;
-
- @Value("${notification.url}")
- private String notificationUrl;
+ @Value("${vnfm.notification}")
+ private String notification;
@Value("${spring.security.usercredentials[0].username}")
private String username;
private SubscribeSender sender;
private static String getAuthorization(final EsrSystemInfo info) {
- return Credentials.basic(info.getUserName(), info.getPassword());
+ if (info == null) {
+ return null;
+ }
+
+ final String userName = info.getUserName();
+
+ if (Strings.isBlank(userName)) {
+ return null;
+ }
+
+ final String password = info.getPassword();
+ return Credentials.basic(userName, password);
}
- public boolean subscribe(final EsrSystemInfo info) {
+ public String subscribe(final EsrSystemInfo info) throws VeVnfmException {
try {
headersProvider.addAuthorization(getAuthorization(info));
final LccnSubscriptionRequest request = createRequest();
return sender.send(info, request);
+ } catch (Exception e) {
+ throw new VeVnfmException(e);
} finally {
headersProvider.removeAuthorization();
}
}
+ public boolean checkSubscription(final EsrSystemInfo info, final String id) throws VeVnfmException {
+ try {
+ return sender.check(info, id);
+ } catch (Exception e) {
+ throw new VeVnfmException(e);
+ }
+ }
+
private LccnSubscriptionRequest createRequest() {
final LccnSubscriptionRequest request = new LccnSubscriptionRequest();
request.callbackUri(getCallbackUri());
}
private String getCallbackUri() {
- return systemUrl + COLON + serverPort + notificationUrl;
+ return endpoint + notification;
}
}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * SO
+ * ================================================================================
+ * Copyright (C) 2020 Samsung. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vevnfm.service;
+
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.so.adapters.vevnfm.exception.VeVnfmException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+@Service
+@EnableScheduling
+public class SubscriptionScheduler {
+
+ private static final Logger logger = LoggerFactory.getLogger(SubscriptionScheduler.class);
+
+ @Autowired
+ private SubscriberService subscriberService;
+
+ private String subscribedId;
+
+ private EsrSystemInfo info;
+
+ public void setInfo(final EsrSystemInfo info) {
+ this.info = info;
+ }
+
+ @Scheduled(fixedRate = 5000, initialDelay = 2000)
+ void subscribeTask() throws VeVnfmException {
+ if (info != null) {
+ if (subscribedId == null) {
+ logger.info("Starting subscribe task");
+ subscribedId = subscriberService.subscribe(info);
+ }
+ }
+ }
+
+ @Scheduled(fixedRate = 20000)
+ void checkSubscribeTask() throws VeVnfmException {
+ if (info != null) {
+ if (subscribedId != null) {
+ logger.info("Checking subscription: {}", subscribedId);
+ if (!subscriberService.checkSubscription(info, subscribedId)) {
+ logger.info("Subscription {} not available", subscribedId);
+ subscribedId = null;
+ }
+ }
+ }
+ }
+}
# limitations under the License.
server:
- port: 8080
+ port: 9098
-system:
- url: http://localhost
-
-notification:
- url: /lcm/v1/vnf/instances/notifications
+vevnfmadapter:
+ endpoint: http://so-ve-vnfm-adapter.onap:9098
mso:
key: 07a7159d3bf51a0e53be7a8f89699be7
auth: 75C4483F9C05E2C33A8602635FA532397EC44AB667A2B64DED4FEE08DD932F2E3C1FEE
vnfm:
+ default-endpoint: https://so-vnfm-simulator.onap:9093
subscription: /vnflcm/v1/subscriptions
+ notification: /lcm/v1/vnf/instances/notifications
dmaap:
- endpoint: http://message-router:30227
+ endpoint: http://message-router.onap:30227
topic: /events/unauthenticated.DCAE_CL_OUTPUT
spring:
private static final String MINIMAL_JSON_CONTENT = "{}";
private static final int ZERO = 0;
- @Value("${notification.url}")
- private String notificationUrl;
+ @Value("${vnfm.notification}")
+ private String notification;
@Autowired
private WebApplicationContext webApplicationContext;
@Test
public void testReceiveNotification() throws Exception {
// given
- final MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post(notificationUrl)
+ final MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post(notification)
.contentType(MediaType.APPLICATION_JSON).content(MINIMAL_JSON_CONTENT);
mockRestServer.expect(once(), anything()).andRespond(withSuccess());
public class AuthorizationHeadersProviderTest {
private static final String AUTHORIZATION_EXAMPLE = "authorization";
+ private static final String BLANK_EXAMPLE = "\t\n";
+ private static final String EMPTY = "";
private final AuthorizationHeadersProvider provider = new AuthorizationHeadersProvider();
assertEquals(size, headers.size());
assertFalse(headers.containsKey(AUTHORIZATION_HEADER));
}
+
+ @Test
+ public void testBlankAuthorization() {
+ final HttpHeaders headers = provider.getHttpHeaders();
+ final int size = headers.size();
+
+ provider.addAuthorization(BLANK_EXAMPLE);
+ assertEquals(size, headers.size());
+ }
+
+ @Test
+ public void testEmptyAuthorization() {
+ final HttpHeaders headers = provider.getHttpHeaders();
+ final int size = headers.size();
+
+ provider.addAuthorization(EMPTY);
+ assertEquals(size, headers.size());
+ }
+
+ @Test
+ public void testNullAuthorization() {
+ final HttpHeaders headers = provider.getHttpHeaders();
+ final int size = headers.size();
+
+ provider.addAuthorization(null);
+ assertEquals(size, headers.size());
+ }
+
+ @Test
+ public void testRemoveAuthorization() {
+ final HttpHeaders headers = provider.getHttpHeaders();
+ final int size = headers.size();
+
+ provider.removeAuthorization();
+ provider.removeAuthorization();
+ assertEquals(size, headers.size());
+ }
}
package org.onap.so.adapters.vevnfm.service;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.junit.MockitoJUnitRunner;
import org.onap.aai.domain.yang.EsrSystemInfo;
import org.onap.so.adapters.vevnfm.aai.AaiConnection;
-import org.onap.so.adapters.vevnfm.exception.VeVnfmException;
@RunWith(MockitoJUnitRunner.class)
public class StartupServiceTest {
- @Mock
- private AaiConnection aaiConnection;
+ private static final String URL = "rt";
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
@Mock
- private SubscriberService subscriberService;
+ private AaiConnection aaiConnection;
@InjectMocks
private StartupService startupService;
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
@Test
public void testSuccess() throws Exception {
// given
final EsrSystemInfo info = new EsrSystemInfo();
- info.setServiceUrl("lh");
- when(aaiConnection.receiveVnfm()).thenReturn(info);
- when(subscriberService.subscribe(info)).thenReturn(true);
-
- // when
- startupService.run();
-
- // then
- verify(aaiConnection, times(1)).receiveVnfm();
- verify(subscriberService, times(1)).subscribe(info);
- }
+ info.setServiceUrl(URL);
- @Test
- public void testFailureAai() throws Exception {
- // given
- final EsrSystemInfo info = new EsrSystemInfo();
when(aaiConnection.receiveVnfm()).thenReturn(info);
- thrown.expect(VeVnfmException.class);
-
// when
- startupService.run();
- }
-
- @Test
- public void testFailureSubscriber() throws Exception {
- // given
- final EsrSystemInfo info = new EsrSystemInfo();
- info.setServiceUrl("lh");
- when(aaiConnection.receiveVnfm()).thenReturn(info);
- when(subscriberService.subscribe(info)).thenReturn(false);
+ final EsrSystemInfo systemInfo = startupService.receiveVnfm();
- thrown.expect(VeVnfmException.class);
-
- // when
- startupService.run();
+ // then
+ verify(aaiConnection).receiveVnfm();
+ assertEquals(info, systemInfo);
}
}
* ============LICENSE_END=========================================================
*/
-package org.onap.so.adapters.vevnfm.subscription;
+package org.onap.so.adapters.vevnfm.service;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.onap.so.adapters.vevnfm.service.SubscribeSender.SLASH;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.test.web.client.ExpectedCount.once;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import org.junit.runner.RunWith;
import org.onap.aai.domain.yang.EsrSystemInfo;
import org.onap.so.adapters.vevnfm.configuration.StartupConfiguration;
+import org.onap.so.adapters.vevnfm.exception.VeVnfmException;
import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.LccnSubscriptionRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ActiveProfiles(StartupConfiguration.TEST_PROFILE)
public class SubscribeSenderTest {
- private static final String SLASH = "/";
- private static final String MINIMAL_JSON_CONTENT = "{}";
+ private static final String URL = "lh";
+ private static final String ID = "1a2s3d4f";
+ private static final String JSON = "{\"id\":\"" + ID + "\"}";
private static final Gson GSON;
}
@Test
- public void testSuccess() {
+ public void testSuccess() throws VeVnfmException {
// given
final EsrSystemInfo info = new EsrSystemInfo();
- info.setServiceUrl("lh");
+ info.setServiceUrl(URL);
final LccnSubscriptionRequest request = new LccnSubscriptionRequest();
mockRestServer.expect(once(), requestTo(SLASH + info.getServiceUrl() + vnfmSubscription))
.andExpect(header(CONTENT_TYPE, CoreMatchers.containsString(MediaType.APPLICATION_JSON_VALUE)))
.andExpect(method(HttpMethod.POST)).andExpect(content().json(GSON.toJson(request)))
- .andRespond(withStatus(HttpStatus.CREATED).body(MINIMAL_JSON_CONTENT));
+ .andRespond(withStatus(HttpStatus.CREATED).body(JSON).contentType(MediaType.APPLICATION_JSON));
// when
- final boolean done = sender.send(info, request);
+ final String id = sender.send(info, request);
// then
- assertTrue(done);
mockRestServer.verify();
+ assertEquals(ID, id);
}
}