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