summaryrefslogtreecommitdiffstats
path: root/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/SpringServiceImpl.java
blob: 1be8fbcb5272886415ad795506571467f5d109d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/*
 * ============LICENSE_START===================================================
 * Copyright (c) 2018 Amdocs
 * ============================================================================
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============LICENSE_END=====================================================
 */
package org.onap.sdnc.apps.pomba.networkdiscovery.service;

import com.bazaarvoice.jolt.JsonUtils;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.PreDestroy;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.xml.parsers.ParserConfigurationException;

import org.onap.aai.restclient.client.OperationResult;
import org.onap.aai.restclient.client.RestClient;
import org.onap.logging.ref.slf4j.ONAPLogAdapter;
import org.onap.logging.ref.slf4j.ONAPLogAdapter.RequestBuilder;
import org.onap.logging.ref.slf4j.ONAPLogConstants;
import org.onap.logging.ref.slf4j.ONAPLogConstants.InvocationMode;
import org.onap.pomba.common.datatypes.DataQuality;
import org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException;
import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute;
import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryNotification;
import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse;
import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Resource;
import org.onap.sdnc.apps.pomba.networkdiscovery.service.util.TransformationUtil;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SpringServiceImpl implements SpringService {
    private static final String OPENSTACK_HEADER_TOKEN = "X-Auth-Token";
    private static final String OPENSTACK_HEADER_API_VERSION = "X-OpenStack-Nova-API-Version";
    private static final int DEFAULT_WORKER_THREADS = 3;

    private ExecutorService executor = Executors.newFixedThreadPool(
            Integer.getInteger("discovery.threads", DEFAULT_WORKER_THREADS),
            new ThreadFactoryBuilder().setNameFormat("discovery-worker-%d").build());

    @Autowired
    private RestClient openstackClient;

    @Autowired
    private String openstackIdentityUrl;

    @Autowired
    private String openstackIdentityUser;

    @Autowired
    private String openstackIdentityPassword;

    @Autowired
    private String openstackApiMicroversion;

    @javax.annotation.Resource
    private Client callbackClient;

    @javax.annotation.Resource
    private Map<String, String> openstackTypeURLs;

    public SpringServiceImpl() throws ParserConfigurationException {
    }

    @Override
    public NetworkDiscoveryResponse findbyResourceIdAndType(String transactionId, String requestId, String resourceType,
            List<String> resourceIds, String notificationURL, String notificationAuthorization, ONAPLogAdapter adapter)
            throws ApplicationException {

        NetworkDiscoveryResponse response = new NetworkDiscoveryResponse();
        response.setRequestId(requestId);

        String openstackURL = this.openstackTypeURLs.get(resourceType);
        // check if resourceType is supported
        if (openstackURL == null) {
            // don't know what to do with this - return empty response
            response.setCode(Status.NO_CONTENT.getStatusCode());
            response.setMessage("Unsupported resourceType " + resourceType);
            response.setAckFinalIndicator(true);
            return response;
        }

        // schedule discovery of specified resources
        Runnable task = new ResourceTask(transactionId, requestId, resourceType, resourceIds, notificationURL,
                notificationAuthorization, openstackURL, adapter);
        this.executor.submit(task);

        response.setCode(Status.ACCEPTED.getStatusCode());
        response.setMessage(Status.ACCEPTED.getReasonPhrase());
        response.setAckFinalIndicator(false);
        return response;
    }

    @PreDestroy
    public void shutdown() {
        this.executor.shutdown();
    }

    private void sendNotification(String url, String transactionId, String authorization, Object notification,
            ONAPLogAdapter adapter) {

        Invocation.Builder request = this.callbackClient.target(url).request().accept(MediaType.TEXT_PLAIN_TYPE);

        if (authorization != null) {
            request.header(HttpHeaders.AUTHORIZATION, authorization);
            request.header(ONAPLogConstants.Headers.REQUEST_ID, transactionId);
        }
        Logger log = adapter.unwrap();
        adapter.invoke(new RequestBuilderWrapper(request), InvocationMode.SYNCHRONOUS);
        try {
            adapter.unwrap().info("Posting notfication to url = {} , payload: {}", url,
                    JsonUtils.toJsonString(Entity.json(notification).getEntity()));

            Response result = request.post(Entity.json(notification));

            adapter.unwrap().info("request at url = {} resulted in http status {} and response: {}", url,
                    result.getStatus(), result.getEntity());

        } catch (Exception x) {
            log.error("Error during {} operation to endpoint at url = {} with error = {}", "POST", url,
                    x.getLocalizedMessage());
        }
    }

    private class ResourceTask implements Runnable {
        private final String transactionId;
        private final String requestId;
        private final String resourceType;
        private final List<String> resourceIds;
        private final String notificationURL;
        private final String notificationAuthorization;
        private final String resourceURL;
        private final ONAPLogAdapter adapter;

        public ResourceTask(String transactionId, String requestId, String resourceType, List<String> resourceIds,
                String notificationURL, String notificationAuthorization, String resourceURL, ONAPLogAdapter adapter) {
            this.transactionId = transactionId;
            this.requestId = requestId;
            this.resourceType = resourceType;
            this.resourceIds = resourceIds;
            this.notificationURL = notificationURL;
            this.notificationAuthorization = notificationAuthorization;
            this.resourceURL = resourceURL;
            this.adapter = adapter;
        }

        @Override
        public void run() {
            try {
                runResourceDiscoveryTask();
            } catch (Exception e) {
                Logger log = adapter.unwrap();
                log.error("Failure in resource task", e);

                // Try to send out a notification of the failure:
                NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
                notification.setRequestId(this.requestId);
                notification.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
                notification.setMessage(e.getMessage());
                notification.setAckFinalIndicator(true);

                // call client back with resource details
                sendNotification(this.notificationURL, this.transactionId, this.notificationAuthorization, notification,
                        adapter);
            }
        }

        private void runResourceDiscoveryTask() throws IOException, ApplicationException {

            String token = OSAuthentication.getToken(openstackIdentityUrl, openstackIdentityUser,
                    openstackIdentityPassword, openstackClient, adapter);

            NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
            notification.setRequestId(this.requestId);

            List<Resource> resources = null;
            MessageFormat format = new MessageFormat(this.resourceURL);
            MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
            headers.add(OPENSTACK_HEADER_TOKEN, token);
            headers.add(OPENSTACK_HEADER_API_VERSION, openstackApiMicroversion);

            for (String resourceId : this.resourceIds) {
                String url = format.format(new Object[] { resourceId });
                OperationResult result = SpringServiceImpl.this.openstackClient.get(url, headers,
                        MediaType.APPLICATION_JSON_TYPE);

                adapter.unwrap().info("Openstack GET result: {}", result.getResult());

                Resource resource = new Resource();
                resource.setType(this.resourceType);
                resource.setId(resourceId);
                if (resources == null) {
                    resources = new ArrayList<>();
                    notification.setResources(resources);
                }
                resources.add(resource);

                if (result.wasSuccessful()) {
                    String transformedOutput = TransformationUtil.transform(result.getResult(), this.resourceType);

                    adapter.unwrap().info("Jolt transformed output: {}", transformedOutput);

                    resource.setDataQuality(DataQuality.ok());
                    List<Attribute> attributeList = TransformationUtil.toAttributeList(transformedOutput);
                    resource.setAttributeList(attributeList);
                } else {
                    resource.setDataQuality(DataQuality.error(result.getFailureCause()));
                }
            }
            notification.setCode(Status.OK.getStatusCode());
            notification.setMessage(Status.OK.getReasonPhrase());
            notification.setAckFinalIndicator(true);

            // call client back with resource details
            sendNotification(this.notificationURL, this.transactionId, this.notificationAuthorization, notification,
                    adapter);
        }

    }

    private static class RequestBuilderWrapper implements RequestBuilder<RequestBuilderWrapper> {
        private Invocation.Builder builder;

        private RequestBuilderWrapper(Invocation.Builder builder) {
            this.builder = builder;
        }

        @Override
        public RequestBuilderWrapper setHeader(String name, String value) {
            this.builder.header(name, value);
            return this;
        }

    }
}