f9de2bed54fbd5740dc55a300aa2a1785661d397
[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 package org.onap.sdnc.apps.pomba.networkdiscovery.service;
19
20 import com.bazaarvoice.jolt.JsonUtils;
21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
22
23 import java.io.IOException;
24 import java.text.MessageFormat;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30
31 import javax.annotation.PreDestroy;
32 import javax.ws.rs.client.Client;
33 import javax.ws.rs.client.Entity;
34 import javax.ws.rs.client.Invocation;
35 import javax.ws.rs.core.HttpHeaders;
36 import javax.ws.rs.core.MediaType;
37 import javax.ws.rs.core.MultivaluedHashMap;
38 import javax.ws.rs.core.MultivaluedMap;
39 import javax.ws.rs.core.Response;
40 import javax.ws.rs.core.Response.StatusType;
41 import javax.ws.rs.core.Response.Status;
42 import javax.ws.rs.core.Response.Status.Family;
43 import javax.xml.parsers.ParserConfigurationException;
44
45 import org.onap.aai.restclient.client.OperationResult;
46 import org.onap.aai.restclient.client.RestClient;
47 import org.onap.logging.ref.slf4j.ONAPLogAdapter;
48 import org.onap.logging.ref.slf4j.ONAPLogAdapter.RequestBuilder;
49 import org.onap.logging.ref.slf4j.ONAPLogConstants;
50 import org.onap.logging.ref.slf4j.ONAPLogConstants.InvocationMode;
51 import org.onap.pomba.common.datatypes.DataQuality;
52 import org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException;
53 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute;
54 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryNotification;
55 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse;
56 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Resource;
57 import org.onap.sdnc.apps.pomba.networkdiscovery.service.util.TransformationUtil;
58 import org.slf4j.Logger;
59 import org.springframework.beans.factory.annotation.Autowired;
60 import org.springframework.stereotype.Service;
61
62 @Service
63 public class SpringServiceImpl implements SpringService {
64     private static final String OPENSTACK_HEADER_TOKEN = "X-Auth-Token";
65     private static final String OPENSTACK_HEADER_API_VERSION = "X-OpenStack-Nova-API-Version";
66     private static final int DEFAULT_WORKER_THREADS = 3;
67
68     private ExecutorService executor = Executors.newFixedThreadPool(
69             Integer.getInteger("discovery.threads", DEFAULT_WORKER_THREADS),
70             new ThreadFactoryBuilder().setNameFormat("discovery-worker-%d").build());
71
72     @Autowired
73     private RestClient openstackClient;
74
75     @Autowired
76     private String openstackIdentityUrl;
77
78     @Autowired
79     private String openstackIdentityUser;
80
81     @Autowired
82     private String openstackIdentityPassword;
83
84     @Autowired
85     private String openstackApiMicroversion;
86
87     @javax.annotation.Resource
88     private Client callbackClient;
89
90     @javax.annotation.Resource
91     private Map<String, String> openstackTypeURLs;
92
93     public SpringServiceImpl() throws ParserConfigurationException {
94     }
95
96     @Override
97     public NetworkDiscoveryResponse findbyResourceIdAndType(String transactionId, String requestId, String resourceType,
98             List<String> resourceIds, String notificationURL, String notificationAuthorization, ONAPLogAdapter adapter)
99             throws ApplicationException {
100
101         NetworkDiscoveryResponse response = new NetworkDiscoveryResponse();
102         response.setRequestId(requestId);
103
104         String openstackURL = this.openstackTypeURLs.get(resourceType);
105         // check if resourceType is supported
106         if (openstackURL == null) {
107             // don't know what to do with this - return empty response
108             response.setCode(Status.NO_CONTENT.getStatusCode());
109             response.setMessage("Unsupported resourceType " + resourceType);
110             response.setAckFinalIndicator(true);
111             return response;
112         }
113
114         // schedule discovery of specified resources
115         Runnable task = new ResourceTask(transactionId, requestId, resourceType, resourceIds, notificationURL,
116                 notificationAuthorization, openstackURL, adapter);
117         this.executor.submit(task);
118
119         response.setCode(Status.ACCEPTED.getStatusCode());
120         response.setMessage(Status.ACCEPTED.getReasonPhrase());
121         response.setAckFinalIndicator(false);
122         return response;
123     }
124
125     @PreDestroy
126     public void shutdown() {
127         this.executor.shutdown();
128     }
129
130     private void sendNotification(String url, String transactionId, String authorization, Object notification,
131             ONAPLogAdapter adapter) {
132
133         Invocation.Builder request = this.callbackClient.target(url).request().accept(MediaType.TEXT_PLAIN_TYPE);
134
135         if (authorization != null) {
136             request.header(HttpHeaders.AUTHORIZATION, authorization);
137             request.header(ONAPLogConstants.Headers.REQUEST_ID, transactionId);
138         }
139         Logger log = adapter.unwrap();
140         adapter.invoke(new RequestBuilderWrapper(request), InvocationMode.SYNCHRONOUS);
141         try {
142             log.info("Posting notfication to url = {} , payload: {}", url,
143                     JsonUtils.toJsonString(Entity.json(notification).getEntity()));
144
145             Response result = request.post(Entity.json(notification));
146
147             StatusType status = result.getStatusInfo();
148
149             if (status.getFamily().equals(Family.SUCCESSFUL)) {
150                 log.info("request at url = {} resulted in http status code {}", 
151                     url, status.getStatusCode());
152             } else {
153                 log.error("request at url = {} resulted in http status code {}, reason: {}",
154                     url, status.getStatusCode(), status.getReasonPhrase());
155             }
156
157
158         } catch (Exception x) {
159             log.error("Error during {} operation to endpoint at url = {} with error = {}", "POST", url,
160                     x.getLocalizedMessage());
161         }
162     }
163
164     private class ResourceTask implements Runnable {
165         private final String transactionId;
166         private final String requestId;
167         private final String resourceType;
168         private final List<String> resourceIds;
169         private final String notificationURL;
170         private final String notificationAuthorization;
171         private final String resourceURL;
172         private final ONAPLogAdapter adapter;
173
174         public ResourceTask(String transactionId, String requestId, String resourceType, List<String> resourceIds,
175                 String notificationURL, String notificationAuthorization, String resourceURL, ONAPLogAdapter adapter) {
176             this.transactionId = transactionId;
177             this.requestId = requestId;
178             this.resourceType = resourceType;
179             this.resourceIds = resourceIds;
180             this.notificationURL = notificationURL;
181             this.notificationAuthorization = notificationAuthorization;
182             this.resourceURL = resourceURL;
183             this.adapter = adapter;
184         }
185
186         @Override
187         public void run() {
188             try {
189                 runResourceDiscoveryTask();
190             } catch (Exception e) {
191                 Logger log = adapter.unwrap();
192                 log.error("Failure in resource task", e);
193
194                 // Try to send out a notification of the failure:
195                 NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
196                 notification.setRequestId(this.requestId);
197                 notification.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
198                 notification.setMessage(e.getMessage());
199                 notification.setAckFinalIndicator(true);
200
201                 // call client back with resource details
202                 sendNotification(this.notificationURL, this.transactionId, this.notificationAuthorization, notification,
203                         adapter);
204             }
205         }
206
207         private void runResourceDiscoveryTask() throws IOException, ApplicationException {
208
209             String token = OSAuthentication.getToken(openstackIdentityUrl, openstackIdentityUser,
210                     openstackIdentityPassword, openstackClient, adapter);
211
212             NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
213             notification.setRequestId(this.requestId);
214
215             List<Resource> resources = null;
216             MessageFormat format = new MessageFormat(this.resourceURL);
217             MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
218             headers.add(OPENSTACK_HEADER_TOKEN, token);
219             headers.add(OPENSTACK_HEADER_API_VERSION, openstackApiMicroversion);
220
221             for (String resourceId : this.resourceIds) {
222                 String url = format.format(new Object[] { resourceId });
223                 OperationResult result = SpringServiceImpl.this.openstackClient.get(url, headers,
224                         MediaType.APPLICATION_JSON_TYPE);
225
226                 Resource resource = new Resource();
227                 resource.setType(this.resourceType);
228                 resource.setId(resourceId);
229                 if (resources == null) {
230                     resources = new ArrayList<>();
231                     notification.setResources(resources);
232                 }
233                 resources.add(resource);
234
235                 Logger log = adapter.unwrap();
236
237                 if (result.wasSuccessful()) {
238                     log.info("Openstack GET result code: {}", result.getResultCode());
239
240                     String transformedOutput = TransformationUtil.transform(result.getResult(), this.resourceType);
241
242                     log.debug("Jolt transformed output: {}", transformedOutput);
243
244                     resource.setDataQuality(DataQuality.ok());
245                     List<Attribute> attributeList = TransformationUtil.toAttributeList(transformedOutput);
246                     resource.setAttributeList(attributeList);
247                 } else {
248                     log.error("Openstack GET result code: {}.  Failure cause: {}", 
249                         result.getResultCode(), result.getFailureCause());
250                     resource.setDataQuality(DataQuality.error(result.getFailureCause()));
251                 }
252             }
253             notification.setCode(Status.OK.getStatusCode());
254             notification.setMessage(Status.OK.getReasonPhrase());
255             notification.setAckFinalIndicator(true);
256
257             // call client back with resource details
258             sendNotification(this.notificationURL, this.transactionId, this.notificationAuthorization, notification,
259                     adapter);
260         }
261
262     }
263
264     private static class RequestBuilderWrapper implements RequestBuilder<RequestBuilderWrapper> {
265         private Invocation.Builder builder;
266
267         private RequestBuilderWrapper(Invocation.Builder builder) {
268             this.builder = builder;
269         }
270
271         @Override
272         public RequestBuilderWrapper setHeader(String name, String value) {
273             this.builder.header(name, value);
274             return this;
275         }
276
277     }
278 }