cd65b2540b1257ad9f05a0230a4b79bdf7a117c6
[sdnc/apps.git] /
1 /*
2  *  ============LICENSE_START===================================================
3  * Copyright (c) 2018 Amdocs
4  * ============================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  * ============LICENSE_END=====================================================
17  */
18
19 package org.onap.sdnc.apps.pomba.networkdiscovery.unittest.service;
20
21 import static com.github.tomakehurst.wiremock.client.WireMock.get;
22 import static com.github.tomakehurst.wiremock.client.WireMock.ok;
23 import static com.github.tomakehurst.wiremock.client.WireMock.okTextXml;
24 import static com.github.tomakehurst.wiremock.client.WireMock.post;
25 import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertNull;
28 import static org.junit.Assert.assertTrue;
29 import static org.junit.Assert.fail;
30
31 import com.fasterxml.jackson.databind.AnnotationIntrospector;
32 import com.fasterxml.jackson.databind.ObjectMapper;
33 import com.fasterxml.jackson.databind.type.TypeFactory;
34 import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
35 import com.github.tomakehurst.wiremock.junit.WireMockRule;
36 import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
37 import com.github.tomakehurst.wiremock.verification.LoggedRequest;
38 import java.net.URISyntaxException;
39 import java.text.MessageFormat;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Base64;
43 import java.util.List;
44 import java.util.UUID;
45 import javax.servlet.http.HttpServletRequest;
46 import javax.ws.rs.core.HttpHeaders;
47 import javax.ws.rs.core.Response;
48 import javax.ws.rs.core.Response.Status;
49 import org.junit.After;
50 import org.junit.Before;
51 import org.junit.Rule;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import org.onap.logging.ref.slf4j.ONAPLogConstants;
55 import org.onap.sdnc.apps.pomba.networkdiscovery.Application;
56 import org.onap.sdnc.apps.pomba.networkdiscovery.PropertyPasswordConfiguration;
57 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute;
58 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.DataQuality;
59 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryNotification;
60 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse;
61 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Resource;
62 import org.onap.sdnc.apps.pomba.networkdiscovery.service.rs.RestService;
63 import org.springframework.beans.factory.annotation.Autowired;
64 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
65 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
66 import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
67 import org.springframework.boot.test.context.SpringBootTest;
68 import org.springframework.core.env.Environment;
69 import org.springframework.test.context.ContextConfiguration;
70 import org.springframework.test.context.TestPropertySource;
71 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
72 import org.springframework.test.context.web.WebAppConfiguration;
73
74 @RunWith(SpringJUnit4ClassRunner.class)
75 @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
76 @WebAppConfiguration
77 @SpringBootTest
78 @TestPropertySource(properties = {
79         "enricher.url=http://localhost:9505",
80         "basicAuth.username=admin",
81         "basicAuth.password=password(OBF:1u2a1toa1w8v1tok1u30)"
82 })
83 @ContextConfiguration(initializers = PropertyPasswordConfiguration.class, classes = Application.class)
84 public class NetworkDiscoveryTest {
85     private static final String V1 = "v1";
86     private static final String APP = "junit";
87
88     private static final String RESOURCE_TYPE_VSERVER = "vserver";
89     private static final String CALLBACK_PATH = "/callback";
90
91     private static final String AUTH = "Basic " + Base64.getEncoder().encodeToString(("admin:admin").getBytes());
92     @Autowired
93     private Environment environment;
94
95     @Rule
96     public WireMockRule enricherRule = new WireMockRule(wireMockConfig().port(9505));
97
98     @Rule
99     public WireMockRule callbackRule = new WireMockRule(wireMockConfig().dynamicPort());
100
101     @Autowired
102     private RestService service;
103
104     private String transactionId = UUID.randomUUID().toString();
105     private String requestId = UUID.randomUUID().toString();
106     private HttpServletRequest httpRequest = new TestHttpServletRequest();
107
108     public NetworkDiscoveryTest() throws URISyntaxException {
109
110     }
111
112     @Before
113     public void setUp() throws Exception {
114     }
115
116     @After
117     public void tearDown() throws Exception {
118     }
119
120     @Test
121     public void testNoAuthHeader() throws Exception {
122         // no Authorization header
123         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
124         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, null, APP, this.transactionId,
125                 this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
126         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
127         // should get WWW-Authenticate header in response
128         assertTrue(response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE).startsWith("Basic realm"));
129     }
130
131     @Test
132     public void testUnauthorized() throws Exception {
133         String authorization = "Basic " + Base64.getEncoder().encodeToString("aaa:bbb".getBytes());
134         // bad Authorization header
135         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
136         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, authorization, APP, this.transactionId,
137                 this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
138         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
139         // should not get WWW-Authenticate header in response
140         assertNull(response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE));
141     }
142
143     @Test
144     public void testVerifyAppId() throws Exception {
145         // no X-FromAppId header
146         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
147         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, null, this.transactionId,
148                 this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
149         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
150         assertTrue(((String)response.getEntity()).contains(ONAPLogConstants.Headers.PARTNER_NAME));
151     }
152
153     @Test
154     public void testVerifyRequestId() throws Exception {
155         // no X-FromAppId header
156         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
157         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
158                 null, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
159         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
160         assertTrue(((String)response.getEntity()).contains("requestId"));
161     }
162
163     @Test
164     public void testVerifyNotificationUrl() throws Exception {
165         // no X-FromAppId header
166         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
167         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
168                 this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, null);
169         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
170         assertTrue(((String)response.getEntity()).contains("notificationURL"));
171     }
172
173     @Test
174     public void testVerifyResourceIds() throws Exception {
175         // no resourceIds list
176         {
177             List<String> resourceIds = null;
178             Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
179                     this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
180             assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
181             assertTrue(((String)response.getEntity()).contains("resourceIds"));
182         }
183
184         // empty resourceId list
185         {
186             List<String> resourceIds = new ArrayList<>();
187             Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
188                     this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
189             assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
190             assertTrue(((String)response.getEntity()).contains("resourceIds"));
191         }
192     }
193
194     @Test
195     public void testVerifyResourceType() throws Exception {
196         // no resource type
197         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
198         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
199                 this.requestId, null, resourceIds, getCallbackUrl());
200         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
201         assertTrue(((String)response.getEntity()).contains("resourceType"));
202     }
203
204     @Test
205     public void testDiscoverVserver() throws Exception {
206         String vserverId = UUID.randomUUID().toString();
207
208         String resourcePath = MessageFormat.format(
209                 this.environment.getProperty("enricher.type.vserver.url"),
210                 new Object[] { vserverId });
211
212         String enricherPayload = String.format(
213                 "<vserver xmlns=\"http://org.onap.aai.inventory/v11\">\r\n"
214                 + "   <vserver-id>%s</vserver-id>\r\n"
215                 + "   <power-state>1</power-state>\r\n"
216                 + "   <vm-state>active</vm-state>\r\n"
217                 + "   <status>ACTIVE</status>\r\n"
218                 + "   <host-status>UNKNOWN</host-status>\r\n"
219                 + "   <updated>2017-11-20T04:26:13Z</updated>\r\n"
220                 + "   <disk-allocation-gb>.010</disk-allocation-gb>\r\n"
221                 + "   <memory-usage-mb>null</memory-usage-mb>\r\n"
222                 + "   <cpu-util-percent>.043</cpu-util-percent>\r\n"
223                 + "   <retrieval-timestamp>2018-06-27 19:41:49 +0000</retrieval-timestamp>\r\n"
224                 + "</vserver>", vserverId);
225
226         this.enricherRule.stubFor(get(resourcePath).willReturn(okTextXml(enricherPayload)));
227
228         this.callbackRule.stubFor(post(CALLBACK_PATH).willReturn(ok("Acknowledged")));
229
230         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, null, this.requestId,
231                 RESOURCE_TYPE_VSERVER, Arrays.asList(vserverId), getCallbackUrl());
232
233         assertEquals(Status.OK.getStatusCode(), response.getStatus());
234         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
235         assertEquals(requestId, entity.getRequestId());
236         assertEquals(Status.ACCEPTED.getStatusCode(), entity.getCode().intValue());
237         assertEquals(Boolean.FALSE, entity.getAckFinalIndicator());
238
239         List<ServeEvent> events = waitForRequests(this.callbackRule, 1, 10);
240         LoggedRequest notificationRequest = events.get(0).getRequest();
241         assertEquals(AUTH, notificationRequest.getHeader(HttpHeaders.AUTHORIZATION));
242         String notificationJson = notificationRequest.getBodyAsString();
243
244         ObjectMapper mapper = new ObjectMapper();
245         AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
246         mapper.setAnnotationIntrospector(introspector);
247         NetworkDiscoveryNotification notification =
248                 mapper.readValue(notificationJson, NetworkDiscoveryNotification.class);
249
250         assertEquals(requestId, notification.getRequestId());
251         assertEquals(Status.OK.getStatusCode(), notification.getCode().intValue());
252         assertEquals(Boolean.TRUE, notification.getAckFinalIndicator());
253
254         assertEquals(1, notification.getResources().size());
255         Resource vserver = notification.getResources().get(0);
256         assertEquals(vserverId, vserver.getId());
257         assertEquals("vserver", vserver.getType());
258         assertEquals(DataQuality.Status.ok, vserver.getDataQuality().getStatus());
259
260         verifyAttribute(vserver.getAttributeList(), "power-state", "1");
261         verifyAttribute(vserver.getAttributeList(), "vm-state", "active");
262         verifyAttribute(vserver.getAttributeList(), "status", "ACTIVE");
263         verifyAttribute(vserver.getAttributeList(), "host-status", "UNKNOWN");
264         verifyAttribute(vserver.getAttributeList(), "updated", "2017-11-20T04:26:13Z");
265         verifyAttribute(vserver.getAttributeList(), "disk-allocation-gb", ".010");
266         verifyAttribute(vserver.getAttributeList(), "memory-usage-mb", "null");
267         verifyAttribute(vserver.getAttributeList(), "cpu-util-percent", ".043");
268         verifyAttribute(vserver.getAttributeList(), "retrieval-timestamp", "2018-06-27 19:41:49 +0000");
269     }
270
271     /**
272      * Verify API returns a final response indicating no discovery possible.
273      */
274     @Test
275     public void testUnsupportedResourceType() throws Exception {
276
277         String resourceType = "unsupported";
278         List<String> resourceIds = Arrays.asList("dummyId");
279         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
280                 this.requestId, resourceType, resourceIds, getCallbackUrl());
281         assertEquals(Status.OK.getStatusCode(), response.getStatus());
282
283         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
284         assertEquals(Boolean.TRUE, entity.getAckFinalIndicator());
285         assertEquals(Status.NO_CONTENT.getStatusCode(), entity.getCode().intValue());
286     }
287
288     private void verifyAttribute(List<Attribute> attributeList, String attrName, String attrValue) {
289         for (Attribute attr : attributeList) {
290             if (attr.getName().equals(attrName)) {
291                 assertEquals("Unexpected value for attribute " + attrName, attrValue, attr.getValue());
292                 return;
293             }
294         }
295         fail("Attribute " + attrName + " not found");
296     }
297
298     private List<ServeEvent> waitForRequests(WireMockRule service, int minRequests, long timeoutSeconds)
299             throws InterruptedException {
300
301         long remaining = timeoutSeconds * 1000L;
302         long retryInterval = Math.min(remaining / 5, 1000);
303         while (true) {
304             List<ServeEvent> events = service.getAllServeEvents();
305             if (events.size() >= minRequests) {
306                 return events;
307             }
308             if (remaining <= 0) {
309                 fail("Timeout waiting for " + minRequests + " requests");
310             }
311             Thread.sleep(retryInterval);
312             remaining -= retryInterval;
313         }
314     }
315
316     private String getCallbackUrl() {
317         return "http://localhost:" + this.callbackRule.port() + CALLBACK_PATH;
318     }
319 }