419fe04112e1e1beb7d1d4f27bbdb074853f5f4b
[policy/gui.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2020-2021 Nordix Foundation.
4  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.gui.pdp.monitoring.rest;
23
24 import com.google.gson.Gson;
25 import com.google.gson.reflect.TypeToken;
26 import java.util.ArrayList;
27 import java.util.Date;
28 import java.util.HashMap;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Objects;
33 import javax.ws.rs.Consumes;
34 import javax.ws.rs.GET;
35 import javax.ws.rs.Path;
36 import javax.ws.rs.Produces;
37 import javax.ws.rs.QueryParam;
38 import javax.ws.rs.core.MediaType;
39 import javax.ws.rs.core.Response;
40 import lombok.EqualsAndHashCode;
41 import lombok.Getter;
42 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
43 import org.onap.policy.common.endpoints.http.client.HttpClient;
44 import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
45 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
46 import org.onap.policy.common.utils.coder.CoderException;
47 import org.onap.policy.common.utils.coder.StandardCoder;
48 import org.onap.policy.models.pdp.concepts.Pdp;
49 import org.onap.policy.models.pdp.concepts.PdpEngineWorkerStatistics;
50 import org.onap.policy.models.pdp.concepts.PdpGroups;
51 import org.onap.policy.models.pdp.concepts.PdpStatistics;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * The class represents the root resource exposed at the base URL<br>
57  * The url to access this resource would be in the form {@code <baseURL>/rest/....} <br>
58  * For example: a GET request to the following URL
59  * {@code http://localhost:18989/papservices/rest/?hostName=localhost&port=12345}
60  *
61  * <b>Note:</b> An allocated {@code hostName} and {@code port} query parameter must be included in
62  * all requests. Datasets for different {@code hostName} are completely isolated from one another.
63  *
64  * @author Yehui Wang (yehui.wang@est.tech)
65  */
66 @Path("monitoring/")
67 @Produces({MediaType.APPLICATION_JSON})
68 @Consumes({MediaType.APPLICATION_JSON})
69 public class PdpMonitoringRestResource {
70     // Get a reference to the logger
71     private static final Logger LOGGER = LoggerFactory.getLogger(PdpMonitoringRestResource.class);
72     // Set up a map separated by host and engine for the data
73     private static final Map<String, HashMap<String, List<Counter>>> cache = new HashMap<>();
74
75     // Set the maximum number of stored data entries to be stored for each engine
76     private static final int MAX_CACHED_ENTITIES = 50;
77
78     private static Gson gson = new Gson();
79
80     /**
81      * Query Pdps.
82      *
83      * @param useHttps use Http or not
84      * @param hostname hostname the host name of the engine service to connect to.
85      * @param port port the port number of the engine service to connect to.
86      * @param username user name
87      * @param password password
88      * @return a Response object containing Pdps in JSON
89      * @throws HttpClientConfigException exception
90      */
91     @GET
92     public Response getPdps(@QueryParam("useHttps") final String useHttps,
93             @QueryParam("hostname") final String hostname, @QueryParam("port") final int port,
94             @QueryParam("username") final String username, @QueryParam("password") final String password)
95             throws HttpClientConfigException {
96
97         return Response
98                 .ok(getHttpClient(useHttps, hostname, port, username, password, "policy/pap/v1/pdps").get().getEntity(),
99                         MediaType.APPLICATION_JSON)
100                 .build();
101     }
102
103     /**
104      * Query Pdp statistics.
105      *
106      * @param useHttps use Http or not
107      * @param hostname the host name of the engine service to connect to.
108      * @param port the port number of the engine service to connect to.
109      * @param username user name
110      * @param password password
111      * @param id PdpGroupName/PdpSubGroup/PdpIntanceID
112      * @return a Response object containing the Pdp status and context data in JSON
113      * @throws HttpClientConfigException exception
114      * @throws CoderException Coder exception
115      */
116     @GET
117     @Path("statistics/")
118     public Response getStatistics(@QueryParam("useHttps") final String useHttps,
119             @QueryParam("hostname") final String hostname, @QueryParam("port") final int port,
120             @QueryParam("username") final String username, @QueryParam("password") final String password,
121             @QueryParam("id") final String id) throws HttpClientConfigException, CoderException {
122
123         var pdpGroups = getHttpClient(useHttps, hostname, port, username, password, "policy/pap/v1/pdps").get()
124                 .readEntity(PdpGroups.class);
125         String groupName;
126         String subGroup;
127         String instanceId;
128         String[] idArray = id.split("/");
129         if (idArray.length == 3) {
130             groupName = idArray[0];
131             subGroup = idArray[1];
132             instanceId = idArray[2];
133         } else {
134             throw new IllegalArgumentException("Cannot parse groupName, subGroup and instanceId from " + id);
135         }
136
137         Pdp pdp = pdpGroups.getGroups().stream().filter(group -> group.getName().equals(groupName))
138                 .flatMap(group -> group.getPdpSubgroups().stream().filter(sub -> sub.getPdpType().equals(subGroup)))
139                 .flatMap(sub -> sub.getPdpInstances().stream()
140                         .filter(instance -> instance.getInstanceId().equals(instanceId)))
141                 .filter(Objects::nonNull).findFirst().orElseThrow();
142
143         final var responseObject = new StatisticsResponse();
144
145         // Engine Service data
146         responseObject.setEngineId(pdp.getInstanceId());
147         responseObject.setServer(hostname);
148         responseObject.setPort(Integer.toString(port));
149         responseObject.setHealthStatus(pdp.getHealthy().name());
150         responseObject.setPdpState(pdp.getPdpState().name());
151
152         String statisticsEntity = getHttpClient(useHttps, hostname, port, username, password,
153                 "policy/pap/v1/pdps/statistics/" + id + "?recordCount=1").get().readEntity(String.class);
154         Map<String, Map<String, List<PdpStatistics>>> pdpStats = gson.fromJson(statisticsEntity,
155                 new TypeToken<Map<String, Map<String, List<PdpStatistics>>>>() {}.getType());
156
157         final List<EngineStatus> engineStatusList = new ArrayList<>();
158
159         if (!pdpStats.isEmpty()) {
160             PdpStatistics pdpStatistics = pdpStats.get(groupName).get(subGroup).get(0);
161             responseObject.setTimeStamp(pdpStatistics.getTimeStamp().toString());
162             responseObject.setPolicyDeployCount(pdpStatistics.getPolicyDeployCount());
163             responseObject.setPolicyDeploySuccessCount(pdpStatistics.getPolicyDeploySuccessCount());
164             responseObject.setPolicyDeployFailCount(pdpStatistics.getPolicyDeployFailCount());
165             responseObject.setPolicyExecutedCount(pdpStatistics.getPolicyExecutedCount());
166             responseObject.setPolicyExecutedSuccessCount(pdpStatistics.getPolicyExecutedSuccessCount());
167             responseObject.setPolicyExecutedFailCount(pdpStatistics.getPolicyExecutedFailCount());
168
169             // Engine Status data
170             for (final PdpEngineWorkerStatistics engineStats : pdpStatistics.getEngineStats()) {
171                 try {
172                     final EngineStatus engineStatusObject = new EngineStatus();
173                     engineStatusObject.setTimestamp(pdpStatistics.getTimeStamp().toString());
174                     engineStatusObject.setId(engineStats.getEngineId());
175                     engineStatusObject.setStatus(engineStats.getEngineWorkerState().name());
176                     engineStatusObject.setLastMessage(new Date(engineStats.getEngineTimeStamp()).toString());
177                     engineStatusObject.setUpTime(engineStats.getUpTime());
178                     engineStatusObject.setPolicyExecutions(engineStats.getEventCount());
179                     engineStatusObject.setLastPolicyDuration(gson.toJson(
180                             getValuesFromCache(id, engineStats.getEngineId() + "_last_policy_duration",
181                                     pdpStatistics.getTimeStamp().getEpochSecond(), engineStats.getLastExecutionTime()),
182                             List.class));
183                     engineStatusObject.setAveragePolicyDuration(
184                             gson.toJson(getValuesFromCache(id, engineStats.getEngineId() + "_average_policy_duration",
185                                     pdpStatistics.getTimeStamp().getEpochSecond(),
186                                     (long) engineStats.getAverageExecutionTime()), List.class));
187                     engineStatusList.add(engineStatusObject);
188                 } catch (final RuntimeException e) {
189                     LOGGER.warn("Error getting status of engine with ID " + engineStats.getEngineId() + "<br>", e);
190                 }
191             }
192         } else {
193             responseObject.setTimeStamp("N/A");
194             responseObject.setPolicyDeployCount("N/A");
195             responseObject.setPolicyDeploySuccessCount("N/A");
196             responseObject.setPolicyDeployFailCount("N/A");
197             responseObject.setPolicyExecutedCount("N/A");
198             responseObject.setPolicyExecutedSuccessCount("N/A");
199             responseObject.setPolicyExecutedFailCount("N/A");
200         }
201
202         responseObject.setStatus(engineStatusList);
203         return Response.ok(new StandardCoder().encode(responseObject), MediaType.APPLICATION_JSON).build();
204     }
205
206     private HttpClient getHttpClient(String useHttps, String hostname, int port, String username, String password,
207             String basePath) throws HttpClientConfigException {
208         var busParams = new BusTopicParams();
209         busParams.setClientName("pdp-monitoring");
210         busParams.setHostname(hostname);
211         busParams.setManaged(false);
212         busParams.setPassword(password);
213         busParams.setPort(port);
214         busParams.setUseHttps(useHttps.equals("https"));
215         busParams.setUserName(username);
216         busParams.setBasePath(basePath);
217         return HttpClientFactoryInstance.getClientFactory().build(busParams);
218     }
219
220     /**
221      * This method takes in the latest data entry for an engine, adds it to an existing data set and
222      * returns the full map for that host and engine.
223      *
224      * @param uri the pdp uri
225      * @param id the engines id
226      * @param timestamp the timestamp of the latest data entry
227      * @param latestValue the value of the latest data entry
228      * @return a list of {@code Counter} objects for that engine
229      */
230     private synchronized List<Counter> getValuesFromCache(final String uri, final String id, final long timestamp,
231             final long latestValue) {
232
233         Map<String, List<Counter>> engineStatus = cache.computeIfAbsent(uri, k -> new HashMap<>());
234
235         List<Counter> valueList = engineStatus.computeIfAbsent(id, k -> new SlidingWindowList<>(MAX_CACHED_ENTITIES));
236
237         valueList.add(new Counter(timestamp, latestValue));
238
239         return valueList;
240     }
241
242     /**
243      * A list of values that uses a FIFO sliding window of a fixed size.
244      */
245     @EqualsAndHashCode(callSuper = true)
246     public class SlidingWindowList<V> extends LinkedList<V> {
247         private static final long serialVersionUID = -7187277916025957447L;
248
249         private final int maxEntries;
250
251         public SlidingWindowList(final int maxEntries) {
252             this.maxEntries = maxEntries;
253         }
254
255         @Override
256         public boolean add(final V elm) {
257             if (this.size() > (maxEntries - 1)) {
258                 this.removeFirst();
259             }
260             return super.add(elm);
261         }
262     }
263
264     /**
265      * A class used to storing a single data entry for an engine.
266      */
267     @Getter
268     public class Counter {
269         private final long timestamp;
270         private final long value;
271
272         public Counter(final long timestamp, final long value) {
273             this.timestamp = timestamp;
274             this.value = value;
275         }
276     }
277
278 }