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
9 * http://www.apache.org/licenses/LICENSE-2.0
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=====================================================
18 package org.onap.sdnc.apps.pomba.networkdiscovery.service;
20 import com.bazaarvoice.jolt.JsonUtils;
21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
23 import java.io.IOException;
24 import java.text.MessageFormat;
25 import java.util.ArrayList;
26 import java.util.List;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
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;
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;
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;
68 private ExecutorService executor = Executors.newFixedThreadPool(
69 Integer.getInteger("discovery.threads", DEFAULT_WORKER_THREADS),
70 new ThreadFactoryBuilder().setNameFormat("discovery-worker-%d").build());
73 private RestClient openstackClient;
76 private String openstackIdentityUrl;
79 private String openstackIdentityUser;
82 private String openstackIdentityPassword;
85 private String openstackApiMicroversion;
87 @javax.annotation.Resource
88 private Client callbackClient;
90 @javax.annotation.Resource
91 private Map<String, String> openstackTypeURLs;
93 public SpringServiceImpl() throws ParserConfigurationException {
97 public NetworkDiscoveryResponse findbyResourceIdAndType(String transactionId, String requestId, String resourceType,
98 List<String> resourceIds, String notificationURL, String notificationAuthorization, ONAPLogAdapter adapter)
99 throws ApplicationException {
101 NetworkDiscoveryResponse response = new NetworkDiscoveryResponse();
102 response.setRequestId(requestId);
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);
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);
119 response.setCode(Status.ACCEPTED.getStatusCode());
120 response.setMessage(Status.ACCEPTED.getReasonPhrase());
121 response.setAckFinalIndicator(false);
126 public void shutdown() {
127 this.executor.shutdown();
130 private void sendNotification(String url, String transactionId, String authorization, Object notification,
131 ONAPLogAdapter adapter) {
133 Invocation.Builder request = this.callbackClient.target(url).request().accept(MediaType.TEXT_PLAIN_TYPE);
135 if (authorization != null) {
136 request.header(HttpHeaders.AUTHORIZATION, authorization);
137 request.header(ONAPLogConstants.Headers.REQUEST_ID, transactionId);
139 Logger log = adapter.unwrap();
140 adapter.invoke(new RequestBuilderWrapper(request), InvocationMode.SYNCHRONOUS);
142 log.info("Posting notfication to url = {} , payload: {}", url,
143 JsonUtils.toJsonString(Entity.json(notification).getEntity()));
145 Response result = request.post(Entity.json(notification));
147 StatusType status = result.getStatusInfo();
149 if (status.getFamily().equals(Family.SUCCESSFUL)) {
150 log.info("request at url = {} resulted in http status code {}",
151 url, status.getStatusCode());
153 log.error("request at url = {} resulted in http status code {}, reason: {}",
154 url, status.getStatusCode(), status.getReasonPhrase());
158 } catch (Exception x) {
159 log.error("Error during {} operation to endpoint at url = {} with error = {}", "POST", url,
160 x.getLocalizedMessage());
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;
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;
189 runResourceDiscoveryTask();
190 } catch (Exception e) {
191 Logger log = adapter.unwrap();
192 log.error("Failure in resource task", e);
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);
201 // call client back with resource details
202 sendNotification(this.notificationURL, this.transactionId, this.notificationAuthorization, notification,
207 private void runResourceDiscoveryTask() throws IOException, ApplicationException {
209 String token = OSAuthentication.getToken(openstackIdentityUrl, openstackIdentityUser,
210 openstackIdentityPassword, openstackClient, adapter);
212 NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
213 notification.setRequestId(this.requestId);
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);
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);
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);
233 resources.add(resource);
235 Logger log = adapter.unwrap();
237 if (result.wasSuccessful()) {
238 log.info("Openstack GET result code: {}", result.getResultCode());
240 String transformedOutput = TransformationUtil.transform(result.getResult(), this.resourceType);
242 log.debug("Jolt transformed output: {}", transformedOutput);
244 resource.setDataQuality(DataQuality.ok());
245 List<Attribute> attributeList = TransformationUtil.toAttributeList(transformedOutput);
246 resource.setAttributeList(attributeList);
248 log.error("Openstack GET result code: {}. Failure cause: {}",
249 result.getResultCode(), result.getFailureCause());
250 resource.setDataQuality(DataQuality.error(result.getFailureCause()));
253 notification.setCode(Status.OK.getStatusCode());
254 notification.setMessage(Status.OK.getReasonPhrase());
255 notification.setAckFinalIndicator(true);
257 // call client back with resource details
258 sendNotification(this.notificationURL, this.transactionId, this.notificationAuthorization, notification,
264 private static class RequestBuilderWrapper implements RequestBuilder<RequestBuilderWrapper> {
265 private Invocation.Builder builder;
267 private RequestBuilderWrapper(Invocation.Builder builder) {
268 this.builder = builder;
272 public RequestBuilderWrapper setHeader(String name, String value) {
273 this.builder.header(name, value);