939fc5c19eb02012217e8baed5b7cb925d0a66d7
[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.eclipse.jetty.util.security.Password;
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Rule;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.onap.logging.ref.slf4j.ONAPLogConstants;
56 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute;
57 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.DataQuality;
58 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryNotification;
59 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse;
60 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Resource;
61 import org.onap.sdnc.apps.pomba.networkdiscovery.service.rs.RestService;
62 import org.springframework.beans.factory.annotation.Autowired;
63 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
64 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
65 import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
66 import org.springframework.boot.test.context.SpringBootTest;
67 import org.springframework.core.env.Environment;
68 import org.springframework.test.context.TestPropertySource;
69 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
70 import org.springframework.test.context.web.WebAppConfiguration;
71
72 @RunWith(SpringJUnit4ClassRunner.class)
73 @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
74 @WebAppConfiguration
75 @SpringBootTest
76 @TestPropertySource(properties = { "enricher.url=http://localhost:9505",
77         "basicAuth.username=admin",
78         "basicAuth.password=OBF:1u2a1toa1w8v1tok1u30" })
79 public class NetworkDiscoveryTest {
80     private static final String V1 = "v1";
81     private static final String APP = "junit";
82
83     private static final String RESOURCE_TYPE_VSERVER = "vserver";
84     private static final String CALLBACK_PATH = "/callback";
85
86     private static final String AUTH = "Basic " + Base64.getEncoder().encodeToString((
87             "admin:" + Password.deobfuscate("OBF:1u2a1toa1w8v1tok1u30")).getBytes());
88     @Autowired
89     private Environment environment;
90
91     @Rule
92     public WireMockRule enricherRule = new WireMockRule(wireMockConfig().port(9505));
93
94     @Rule
95     public WireMockRule callbackRule = new WireMockRule(wireMockConfig().dynamicPort());
96
97     @Autowired
98     private RestService service;
99
100     private String transactionId = UUID.randomUUID().toString();
101     private String requestId = UUID.randomUUID().toString();
102     private HttpServletRequest httpRequest = new TestHttpServletRequest();
103
104     public NetworkDiscoveryTest() throws URISyntaxException {
105
106     }
107
108     @Before
109     public void setUp() throws Exception {
110     }
111
112     @After
113     public void tearDown() throws Exception {
114     }
115
116     @Test
117     public void testNoAuthHeader() throws Exception {
118         // no Authorization header
119         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
120         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, null, APP, this.transactionId,
121                         this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
122         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
123         // should get WWW-Authenticate header in response
124         assertTrue(response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE).startsWith("Basic realm"));
125     }
126
127     @Test
128     public void testUnauthorized() throws Exception {
129         String authorization = "Basic " + Base64.getEncoder().encodeToString("aaa:bbb".getBytes());
130         // bad Authorization header
131         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
132         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, authorization, APP,
133                         this.transactionId, this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
134         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
135         // should not get WWW-Authenticate header in response
136         assertNull(response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE));
137     }
138
139     @Test
140     public void testVerifyAppId() throws Exception {
141         // no X-FromAppId header
142         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
143         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, null, this.transactionId,
144                         this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
145         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
146         assertTrue(((String) response.getEntity()).contains(ONAPLogConstants.Headers.PARTNER_NAME));
147     }
148
149     @Test
150     public void testVerifyRequestId() throws Exception {
151         // no X-FromAppId header
152         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
153         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
154                         null, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
155         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
156         assertTrue(((String) response.getEntity()).contains("requestId"));
157     }
158
159     @Test
160     public void testVerifyNotificationUrl() throws Exception {
161         // no X-FromAppId header
162         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
163         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
164                         this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, null);
165         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
166         assertTrue(((String) response.getEntity()).contains("notificationURL"));
167     }
168
169     @Test
170     public void testVerifyResourceIds() throws Exception {
171         // no resourceIds list
172         {
173             List<String> resourceIds = null;
174             Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP,
175                             this.transactionId, this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
176             assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
177             assertTrue(((String) response.getEntity()).contains("resourceIds"));
178         }
179
180         // empty resourceId list
181         {
182             List<String> resourceIds = new ArrayList<>();
183             Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP,
184                             this.transactionId, this.requestId, RESOURCE_TYPE_VSERVER, resourceIds, getCallbackUrl());
185             assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
186             assertTrue(((String) response.getEntity()).contains("resourceIds"));
187         }
188     }
189
190     @Test
191     public void testVerifyResourceType() throws Exception {
192         // no resource type
193         List<String> resourceIds = Arrays.asList(UUID.randomUUID().toString());
194         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
195                         this.requestId, null, resourceIds, getCallbackUrl());
196         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
197         assertTrue(((String) response.getEntity()).contains("resourceType"));
198     }
199
200     @Test
201     public void testDiscoverVserver() throws Exception {
202         String vserverId = UUID.randomUUID().toString();
203
204         String resourcePath = MessageFormat.format(
205                         this.environment.getProperty("enricher.type.vserver.url"),
206                         new Object[] { vserverId });
207
208         String enricherPayload = String.format("<vserver xmlns=\"http://org.onap.aai.inventory/v11\">\r\n"
209                         + "   <vserver-id>%s</vserver-id>\r\n"
210                         + "   <power-state>1</power-state>\r\n"
211                         + "   <locked>true</locked>\r\n"
212                         + "   <hostname>10.147.112.48</hostname>\r\n"
213                         + "   <vm-state>active</vm-state>\r\n"
214                         + "   <status>ACTIVE</status>\r\n"
215                         + "   <host-status>UNKNOWN</host-status>\r\n"
216                         + "   <updated>2017-11-20T04:26:13Z</updated>\r\n"
217                         + "   <disk-allocation-gb>.010</disk-allocation-gb>\r\n"
218                         + "   <memory-usage-mb>null</memory-usage-mb>\r\n"
219                         + "   <cpu-util-percent>.043</cpu-util-percent>\r\n"
220                         + "   <retrieval-timestamp>2018-06-27 19:41:49 +0000</retrieval-timestamp>\r\n"
221                         + "</vserver>",
222                         vserverId);
223
224         this.enricherRule.stubFor(get(resourcePath).willReturn(okTextXml(enricherPayload)));
225
226         this.callbackRule.stubFor(post(CALLBACK_PATH).willReturn(ok("Acknowledged")));
227
228         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, null, this.requestId,
229                         RESOURCE_TYPE_VSERVER, Arrays.asList(vserverId), getCallbackUrl());
230
231         assertEquals(Status.OK.getStatusCode(), response.getStatus());
232         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
233         assertEquals(requestId, entity.getRequestId());
234         assertEquals(Status.ACCEPTED.getStatusCode(), entity.getCode().intValue());
235         assertEquals(Boolean.FALSE, entity.getAckFinalIndicator());
236
237         List<ServeEvent> events = waitForRequests(this.callbackRule, 1, 10);
238         LoggedRequest notificationRequest = events.get(0).getRequest();
239         assertEquals(AUTH, notificationRequest.getHeader(HttpHeaders.AUTHORIZATION));
240         String notificationJson = notificationRequest.getBodyAsString();
241
242         ObjectMapper mapper = new ObjectMapper();
243         AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
244         mapper.setAnnotationIntrospector(introspector);
245         NetworkDiscoveryNotification notification = mapper.readValue(notificationJson,
246                         NetworkDiscoveryNotification.class);
247
248         assertEquals(requestId, notification.getRequestId());
249         assertEquals(Status.OK.getStatusCode(), notification.getCode().intValue());
250         assertEquals(Boolean.TRUE, notification.getAckFinalIndicator());
251
252         assertEquals(1, notification.getResources().size());
253         Resource vserver = notification.getResources().get(0);
254         assertEquals(vserverId, vserver.getId());
255         assertEquals("vserver", vserver.getType());
256         assertEquals(DataQuality.Status.ok, vserver.getDataQuality().getStatus());
257
258         verifyAttribute(vserver.getAttributeList(), "status", "ACTIVE");
259         verifyAttribute(vserver.getAttributeList(), "inMaintenance", "true");
260         verifyAttribute(vserver.getAttributeList(), "hostname", "10.147.112.48");
261         verifyAttribute(vserver.getAttributeList(), "vmState", "active");
262     }
263
264     /**
265      * Verify API returns a final response indicating no discovery possible.
266      */
267     @Test
268     public void testUnsupportedResourceType() throws Exception {
269
270         String resourceType = "unsupported";
271         List<String> resourceIds = Arrays.asList("dummyId");
272         Response response = this.service.findbyResourceIdAndType(this.httpRequest, V1, AUTH, APP, this.transactionId,
273                         this.requestId, resourceType, resourceIds, getCallbackUrl());
274         assertEquals(Status.OK.getStatusCode(), response.getStatus());
275
276         NetworkDiscoveryResponse entity = (NetworkDiscoveryResponse) response.getEntity();
277         assertEquals(Boolean.TRUE, entity.getAckFinalIndicator());
278         assertEquals(Status.NO_CONTENT.getStatusCode(), entity.getCode().intValue());
279     }
280
281     private void verifyAttribute(List<Attribute> attributeList, String attrName, String attrValue) {
282         for (Attribute attr : attributeList) {
283             if (attr.getName().equals(attrName)) {
284                 assertEquals("Unexpected value for attribute " + attrName, attrValue, attr.getValue());
285                 return;
286             }
287         }
288         fail("Attribute " + attrName + " not found");
289     }
290
291     private List<ServeEvent> waitForRequests(WireMockRule service, int minRequests, long timeoutSeconds)
292                     throws InterruptedException {
293
294         long remaining = timeoutSeconds * 1000L;
295         long retryInterval = Math.min(remaining / 5, 1000);
296         while (true) {
297             List<ServeEvent> events = service.getAllServeEvents();
298             if (events.size() >= minRequests) {
299                 return events;
300             }
301             if (remaining <= 0) {
302                 fail("Timeout waiting for " + minRequests + " requests");
303             }
304             Thread.sleep(retryInterval);
305             remaining -= retryInterval;
306         }
307     }
308
309     private String getCallbackUrl() {
310         return "http://localhost:" + this.callbackRule.port() + CALLBACK_PATH;
311     }
312 }