18a0a04f80a91b20264bd0d49e5a7e015682827d
[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.okJson;
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.bazaarvoice.jolt.JsonUtils;
32 import com.fasterxml.jackson.databind.AnnotationIntrospector;
33 import com.fasterxml.jackson.databind.ObjectMapper;
34 import com.fasterxml.jackson.databind.type.TypeFactory;
35 import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
36 import com.github.tomakehurst.wiremock.client.WireMock;
37 import com.github.tomakehurst.wiremock.junit.WireMockRule;
38 import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
39 import com.github.tomakehurst.wiremock.verification.LoggedRequest;
40
41 import java.net.URISyntaxException;
42 import java.text.MessageFormat;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Base64;
46 import java.util.List;
47 import java.util.UUID;
48
49 import javax.servlet.http.HttpServletRequest;
50 import javax.ws.rs.core.HttpHeaders;
51 import javax.ws.rs.core.Response;
52 import javax.ws.rs.core.Response.Status;
53
54 import org.eclipse.jetty.util.security.Password;
55 import org.junit.After;
56 import org.junit.Before;
57 import org.junit.Rule;
58 import org.junit.Test;
59 import org.junit.runner.RunWith;
60 import org.mockito.Mockito;
61 import org.onap.logging.ref.slf4j.ONAPLogConstants;
62 import org.onap.pomba.common.datatypes.DataQuality;
63 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute;
64 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryNotification;
65 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse;
66 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Resource;
67 import org.onap.sdnc.apps.pomba.networkdiscovery.service.rs.RestService;
68 import org.springframework.beans.factory.annotation.Autowired;
69 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
70 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
71 import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
72 import org.springframework.boot.test.context.SpringBootTest;
73 import org.springframework.test.context.TestPropertySource;
74 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
75 import org.springframework.test.context.web.WebAppConfiguration;
76
77 @RunWith(SpringJUnit4ClassRunner.class)
78 @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
79 @WebAppConfiguration
80 @SpringBootTest
81 @TestPropertySource(properties = { "openstack.type.vserver.url=http://localhost:8774/v2.1/servers/{0}",
82         "openstack.identity.url=http://localhost:5000/v3/auth/tokens",
83         "enricher.keyStorePath=src/test/resources/client-cert-onap.p12",
84         "enricher.keyStorePassword=OBF:1y0q1uvc1uum1uvg1pil1pjl1uuq1uvk1uuu1y10",
85         "basicAuth.username=admin",
86         "basicAuth.password=OBF:1u2a1toa1w8v1tok1u30" })
87
88 public class NetworkDiscoveryTest {
89     private static final String V1 = "v1";
90     private static final String APP = "junit";
91
92     private static final String RESOURCE_TYPE_VSERVER = "vserver";
93     private static final String CALLBACK_PATH = "/callback";
94
95     private static final String AUTH = "Basic " + Base64.getEncoder().encodeToString((
96             "admin:" + Password.deobfuscate("OBF:1u2a1toa1w8v1tok1u30")).getBytes());
97
98     @Rule
99     public WireMockRule identityRule = new WireMockRule(wireMockConfig().port(5000));
100
101     @Rule
102     public WireMockRule openstackRule = new WireMockRule(wireMockConfig().port(8774));
103
104     @Rule
105     public WireMockRule callbackRule = new WireMockRule(wireMockConfig().dynamicPort());
106
107     @Autowired
108     private RestService service;
109
110     private String transactionId = UUID.randomUUID().toString();
111     private String requestId = UUID.randomUUID().toString();
112     private HttpServletRequest httpRequest = Mockito.mock(HttpServletRequest.class);
113     
114     private static final String TEST_RESOURCES = "src/test/resources/jolt/";
115
116
117     public NetworkDiscoveryTest() throws URISyntaxException {
118
119     }
120
121     @Before
122     public void setUp() throws Exception {
123     }
124
125     @After
126     public void tearDown() throws Exception {
127     }
128
129     @Test
130     public void testNoAuthHeader() throws Exception {
131         // no Authorization header
132         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
133         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, null, APP, this.transactionId,
134                         this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
135         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
136         // should get WWW-Authenticate header in response
137         assertTrue(response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE).startsWith("Basic realm"));
138     }
139
140     @Test
141     public void testUnauthorized() throws Exception {
142         String authorization = "Basic " + Base64.getEncoder().encodeToString("aaa:bbb".getBytes());
143         // bad Authorization header
144         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
145         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, authorization, APP,
146                         this.transactionId, this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
147         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
148         // should not get WWW-Authenticate header in response
149         assertNull(response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE));
150     }
151
152     @Test
153     public void testNoVersion() throws Exception {
154         // no Authorization header
155         String authorization = "Basic " + Base64.getEncoder().encodeToString("aaa:bbb".getBytes());
156         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
157         Response response = this.service.findbyResourceIdAndType(this.httpRequest, null, authorization, APP,
158                 this.transactionId, this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
159         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
160         // should get WWW-Authenticate header in response
161         assertTrue(((String) response.getEntity()).contains("version"));
162     }
163
164     @Test
165     public void testVerifyAppId() throws Exception {
166         // no X-FromAppId header
167         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
168         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, null, this.transactionId,
169                         this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
170         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
171         assertTrue(((String) response.getEntity()).contains(ONAPLogConstants.Headers.PARTNER_NAME));
172     }
173
174     @Test
175     public void testVerifyRequestId() throws Exception {
176         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
177         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
178                         null, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
179         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
180         assertTrue(((String) response.getEntity()).contains("requestId"));
181     }
182
183     @Test
184     public void testVerifyNotificationUrl() throws Exception {
185         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
186         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
187                         this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, null);
188         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
189         assertTrue(((String) response.getEntity()).contains("notificationURL"));
190     }
191
192     @Test
193     public void testVerifyResourceIds() throws Exception {
194         // no resourceIds list
195         {
196             List<String> resourceIds = null;
197             Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP,
198                             this.transactionId, this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
199             assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
200             assertTrue(((String) response.getEntity()).contains("resourceIds"));
201         }
202
203         // empty resourceId list
204         {
205             List<String> resourceIds = new ArrayList<>();
206             Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP,
207                             this.transactionId, this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
208             assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
209             assertTrue(((String) response.getEntity()).contains("resourceIds"));
210         }
211     }
212
213     @Test
214     public void testVerifyResourceType() throws Exception {
215         // no resource type
216         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
217         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
218                         this.requestId, null, resourceIds, getCallbackUrl());
219         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
220         assertTrue(((String) response.getEntity()).contains("resourceType"));
221     }
222
223     @Test
224     public void testVerifyInternalError() throws Exception {
225         // no request
226         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
227         Response response = this.service.findbyResourceIdAndType(null, V1, AUTH, APP, this.transactionId,
228                         this.requestId, null, resourceIds, getCallbackUrl());
229         assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
230     }
231
232     @Test
233     public void testDiscoverVserver() throws Exception {
234         String vserverId = UUID.randomUUID().toString();
235
236         String resourcePath = MessageFormat.format("/v2.1/servers/{0}",
237                 new Object[] { vserverId });
238
239         String identityPath = "/v3/auth/tokens";
240       
241         Object sourceObject = JsonUtils.filepathToObject(TEST_RESOURCES + "vserver-input.json");
242
243         String openstackApiResponse = JsonUtils.toJsonString(sourceObject);
244
245         this.openstackRule.stubFor(get(resourcePath).willReturn(okJson(openstackApiResponse)));
246
247         this.identityRule.stubFor(post(identityPath).willReturn(okJson("{}").withHeader("X-Subject-Token", "tokenId")));
248         
249         this.callbackRule.stubFor(post(CALLBACK_PATH).willReturn(ok("Acknowledged")));
250
251         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, null, this.requestId,
252                         RESOURCE_TYPE_VSERVER, Arrays.asList(vserverId), getCallbackUrl());
253
254         assertEquals(Status.OK.getStatusCode(), response.getStatus());
255         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
256         assertEquals(requestId, entity.getRequestId());
257         assertEquals(Status.ACCEPTED.getStatusCode(), entity.getCode().intValue());
258         assertEquals(Boolean.FALSE, entity.getAckFinalIndicator());
259
260         List<ServeEvent> events = waitForRequests(this.callbackRule, 1, 10);
261         LoggedRequest notificationRequest = events.get(0).getRequest();
262         assertEquals(AUTH, notificationRequest.getHeader(HttpHeaders.AUTHORIZATION));
263         String notificationJson = notificationRequest.getBodyAsString();
264
265         ObjectMapper mapper = new ObjectMapper();
266         AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
267         mapper.setAnnotationIntrospector(introspector);
268         NetworkDiscoveryNotification notification = mapper.readValue(notificationJson,
269                         NetworkDiscoveryNotification.class);
270
271         assertEquals(requestId, notification.getRequestId());
272         assertEquals(Status.OK.getStatusCode(), notification.getCode().intValue());
273         assertEquals(Boolean.TRUE, notification.getAckFinalIndicator());
274
275         assertEquals(1, notification.getResources().size());
276         Resource vserver = notification.getResources().get(0);
277         assertEquals(vserverId, vserver.getId());
278         assertEquals("vserver", vserver.getType());
279         assertEquals(DataQuality.Status.ok, vserver.getDataQuality().getStatus());
280
281         verifyAttribute(vserver.getAttributeList(), "status", "ACTIVE");
282         verifyAttribute(vserver.getAttributeList(), "inMaintenance", "true");
283         verifyAttribute(vserver.getAttributeList(), "hostname", "norm-bouygues");
284         verifyAttribute(vserver.getAttributeList(), "vmState", "active");
285     }
286     
287     @Test
288     public void testDiscoverVserverFailure() throws Exception {
289         String vserverId = UUID.randomUUID().toString();
290
291         String resourcePath = MessageFormat.format("/v2.1/servers/{0}",
292                 new Object[] { vserverId });
293
294         String identityPath = "/v3/auth/tokens";
295  
296         this.openstackRule.stubFor(get(resourcePath).willReturn(WireMock.notFound()));
297
298         this.identityRule.stubFor(post(identityPath).willReturn(okJson("{}").withHeader("X-Subject-Token", "tokenId")));
299         
300         this.callbackRule.stubFor(post(CALLBACK_PATH).willReturn(ok("Acknowledged")));
301
302         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, null, this.requestId,
303                         RESOURCE_TYPE_VSERVER, Arrays.asList(vserverId), getCallbackUrl());
304
305         assertEquals(Status.OK.getStatusCode(), response.getStatus());
306         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
307         assertEquals(requestId, entity.getRequestId());
308         assertEquals(Status.ACCEPTED.getStatusCode(), entity.getCode().intValue());
309         assertEquals(Boolean.FALSE, entity.getAckFinalIndicator());
310
311         List<ServeEvent> events = waitForRequests(this.callbackRule, 1, 10);
312         LoggedRequest notificationRequest = events.get(0).getRequest();
313         assertEquals(AUTH, notificationRequest.getHeader(HttpHeaders.AUTHORIZATION));
314         String notificationJson = notificationRequest.getBodyAsString();
315
316         ObjectMapper mapper = new ObjectMapper();
317         AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
318         mapper.setAnnotationIntrospector(introspector);
319         NetworkDiscoveryNotification notification = mapper.readValue(notificationJson,
320                         NetworkDiscoveryNotification.class);
321
322         assertEquals(requestId, notification.getRequestId());
323         assertEquals(Status.OK.getStatusCode(), notification.getCode().intValue());
324         assertEquals(Boolean.TRUE, notification.getAckFinalIndicator());
325
326         assertEquals(1, notification.getResources().size());
327         Resource vserver = notification.getResources().get(0);
328         assertEquals(vserverId, vserver.getId());
329         assertEquals("vserver", vserver.getType());
330         assertEquals(DataQuality.Status.error, vserver.getDataQuality().getStatus());
331         assertNull(vserver.getAttributeList());
332     }
333
334
335     /**
336      * Verify API returns a final response indicating no discovery possible.
337      */
338     @Test
339     public void testUnsupportedResourceType() throws Exception {
340
341         String resourceType = "unsupported";
342         List<String> resourceIds = Arrays.asList("dummyId");
343         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
344                         this.requestId, resourceType, resourceIds, getCallbackUrl());
345         assertEquals(Status.OK.getStatusCode(), response.getStatus());
346
347         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
348         System.err.println("entity:" + entity);
349         assertEquals(Boolean.TRUE, entity.getAckFinalIndicator());
350         assertEquals(Status.NO_CONTENT.getStatusCode(), entity.getCode().intValue());
351     }
352     
353     @Test
354     public void testLoginFailure() throws Exception {
355         String vserverId = UUID.randomUUID().toString();
356
357         String identityPath = "/v3/auth/tokens";
358       
359         this.identityRule.stubFor(post(identityPath).willReturn(WireMock.unauthorized()));
360         
361         this.callbackRule.stubFor(post(CALLBACK_PATH).willReturn(ok("Acknowledged")));
362
363         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, null, this.requestId,
364                         RESOURCE_TYPE_VSERVER, Arrays.asList(vserverId), getCallbackUrl());
365
366         assertEquals(Status.OK.getStatusCode(), response.getStatus());
367         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
368         assertEquals(requestId, entity.getRequestId());
369         assertEquals(Status.ACCEPTED.getStatusCode(), entity.getCode().intValue());
370         assertEquals(Boolean.FALSE, entity.getAckFinalIndicator());
371
372         List<ServeEvent> events = waitForRequests(this.callbackRule, 1, 10);
373         LoggedRequest notificationRequest = events.get(0).getRequest();
374         assertEquals(AUTH, notificationRequest.getHeader(HttpHeaders.AUTHORIZATION));
375         String notificationJson = notificationRequest.getBodyAsString();
376
377         ObjectMapper mapper = new ObjectMapper();
378         AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
379         mapper.setAnnotationIntrospector(introspector);
380         NetworkDiscoveryNotification notification = mapper.readValue(notificationJson,
381                         NetworkDiscoveryNotification.class);
382
383         assertEquals(requestId, notification.getRequestId());
384         assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), notification.getCode().intValue());
385         assertEquals(Boolean.TRUE, notification.getAckFinalIndicator());
386         assertNull(notification.getResources());
387     }
388
389
390     private void verifyAttribute(List<Attribute> attributeList, String attrName, String attrValue) {
391         for (Attribute attr : attributeList) {
392             if (attr.getName().equals(attrName)) {
393                 assertEquals("Unexpected value for attribute " + attrName, attrValue, attr.getValue());
394                 return;
395             }
396         }
397         fail("Attribute " + attrName + " not found");
398     }
399
400     private List<ServeEvent> waitForRequests(WireMockRule service, int minRequests, long timeoutSeconds)
401                     throws InterruptedException {
402
403         long remaining = timeoutSeconds * 1000L;
404         long retryInterval = Math.min(remaining / 5, 1000);
405         while (true) {
406             List<ServeEvent> events = service.getAllServeEvents();
407             if (events.size() >= minRequests) {
408                 return events;
409             }
410             if (remaining <= 0) {
411                 fail("Timeout waiting for " + minRequests + " requests");
412             }
413             Thread.sleep(retryInterval);
414             remaining -= retryInterval;
415         }
416     }
417
418     private String getCallbackUrl() {
419         return "http://localhost:" + this.callbackRule.port() + CALLBACK_PATH;
420     }
421 }