2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2020 Nordix Foundation.
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.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.gui.pdp.monitoring.rest;
23 import com.google.gson.Gson;
24 import com.google.gson.reflect.TypeToken;
25 import java.util.ArrayList;
26 import java.util.Date;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
31 import java.util.Objects;
32 import javax.ws.rs.Consumes;
33 import javax.ws.rs.GET;
34 import javax.ws.rs.Path;
35 import javax.ws.rs.Produces;
36 import javax.ws.rs.QueryParam;
37 import javax.ws.rs.core.MediaType;
38 import javax.ws.rs.core.Response;
39 import lombok.EqualsAndHashCode;
41 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
42 import org.onap.policy.common.endpoints.http.client.HttpClient;
43 import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
44 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
45 import org.onap.policy.common.utils.coder.CoderException;
46 import org.onap.policy.common.utils.coder.StandardCoder;
47 import org.onap.policy.models.pdp.concepts.Pdp;
48 import org.onap.policy.models.pdp.concepts.PdpEngineWorkerStatistics;
49 import org.onap.policy.models.pdp.concepts.PdpGroups;
50 import org.onap.policy.models.pdp.concepts.PdpStatistics;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 * The class represents the root resource exposed at the base URL<br>
56 * The url to access this resource would be in the form {@code <baseURL>/rest/....} <br>
57 * For example: a GET request to the following URL
58 * {@code http://localhost:18989/papservices/rest/?hostName=localhost&port=12345}
60 * <b>Note:</b> An allocated {@code hostName} and {@code port} query parameter must be included in
61 * all requests. Datasets for different {@code hostName} are completely isolated from one another.
63 * @author Yehui Wang (yehui.wang@est.tech)
66 @Produces({MediaType.APPLICATION_JSON})
67 @Consumes({MediaType.APPLICATION_JSON})
68 public class PdpMonitoringRestResource {
69 // Get a reference to the logger
70 private static final Logger LOGGER = LoggerFactory.getLogger(PdpMonitoringRestResource.class);
71 // Set up a map separated by host and engine for the data
72 private static final Map<String, HashMap<String, List<Counter>>> cache = new HashMap<>();
74 // Set the maximum number of stored data entries to be stored for each engine
75 private static final int MAX_CACHED_ENTITIES = 50;
77 private static Gson gson = new Gson();
82 * @param useHttps use Http or not
83 * @param hostname hostname the host name of the engine service to connect to.
84 * @param port port the port number of the engine service to connect to.
85 * @param username user name
86 * @param password password
87 * @return a Response object containing Pdps in JSON
88 * @throws HttpClientConfigException exception
91 public Response getPdps(@QueryParam("useHttps") final String useHttps,
92 @QueryParam("hostname") final String hostname, @QueryParam("port") final int port,
93 @QueryParam("username") final String username, @QueryParam("password") final String password)
94 throws HttpClientConfigException {
97 .ok(getHttpClient(useHttps, hostname, port, username, password, "policy/pap/v1/pdps").get().getEntity(),
98 MediaType.APPLICATION_JSON)
103 * Query Pdp statistics.
105 * @param useHttps use Http or not
106 * @param hostname the host name of the engine service to connect to.
107 * @param port the port number of the engine service to connect to.
108 * @param username user name
109 * @param password password
110 * @param id PdpGroupName/PdpSubGroup/PdpIntanceID
111 * @return a Response object containing the Pdp status and context data in JSON
112 * @throws HttpClientConfigException exception
113 * @throws CoderException Coder exception
117 public Response getStatistics(@QueryParam("useHttps") final String useHttps,
118 @QueryParam("hostname") final String hostname, @QueryParam("port") final int port,
119 @QueryParam("username") final String username, @QueryParam("password") final String password,
120 @QueryParam("id") final String id) throws HttpClientConfigException, CoderException {
122 PdpGroups pdpGroups = getHttpClient(useHttps, hostname, port, username, password, "policy/pap/v1/pdps").get()
123 .readEntity(PdpGroups.class);
127 String[] idArray = id.split("/");
128 if (idArray.length == 3) {
129 groupName = idArray[0];
130 subGroup = idArray[1];
131 instanceId = idArray[2];
133 throw new IllegalArgumentException("Cannot parse groupName, subGroup and instanceId from " + id);
136 Pdp pdp = pdpGroups.getGroups().stream().filter(group -> group.getName().equals(groupName))
137 .flatMap(group -> group.getPdpSubgroups().stream().filter(sub -> sub.getPdpType().equals(subGroup)))
138 .flatMap(sub -> sub.getPdpInstances().stream()
139 .filter(instance -> instance.getInstanceId().equals(instanceId)))
140 .filter(Objects::nonNull).findFirst().orElseThrow();
142 final StatisticsResponse responseObject = new StatisticsResponse();
144 // Engine Service data
145 responseObject.setEngineId(pdp.getInstanceId());
146 responseObject.setServer(hostname);
147 responseObject.setPort(Integer.toString(port));
148 responseObject.setHealthStatus(pdp.getHealthy().name());
149 responseObject.setPdpState(pdp.getPdpState().name());
151 String statisticsEntity = getHttpClient(useHttps, hostname, port, username, password,
152 "policy/pap/v1/pdps/statistics/" + id + "?recordCount=1").get().readEntity(String.class);
153 Map<String, Map<String, List<PdpStatistics>>> pdpStats = gson.fromJson(statisticsEntity,
154 new TypeToken<Map<String, Map<String, List<PdpStatistics>>>>() {}.getType());
156 final List<EngineStatus> engineStatusList = new ArrayList<>();
158 if (!pdpStats.isEmpty()) {
159 PdpStatistics pdpStatistics = pdpStats.get(groupName).get(subGroup).get(0);
160 responseObject.setTimeStamp(pdpStatistics.getTimeStamp().toString());
161 responseObject.setPolicyDeployCount(pdpStatistics.getPolicyDeployCount());
162 responseObject.setPolicyDeploySuccessCount(pdpStatistics.getPolicyDeploySuccessCount());
163 responseObject.setPolicyDeployFailCount(pdpStatistics.getPolicyDeployFailCount());
164 responseObject.setPolicyExecutedCount(pdpStatistics.getPolicyExecutedCount());
165 responseObject.setPolicyExecutedSuccessCount(pdpStatistics.getPolicyExecutedSuccessCount());
166 responseObject.setPolicyExecutedFailCount(pdpStatistics.getPolicyExecutedFailCount());
168 // Engine Status data
169 for (final PdpEngineWorkerStatistics engineStats : pdpStatistics.getEngineStats()) {
171 final EngineStatus engineStatusObject = new EngineStatus();
172 engineStatusObject.setTimestamp(pdpStatistics.getTimeStamp().toString());
173 engineStatusObject.setId(engineStats.getEngineId());
174 engineStatusObject.setStatus(engineStats.getEngineWorkerState().name());
175 engineStatusObject.setLastMessage(new Date(engineStats.getEngineTimeStamp()).toString());
176 engineStatusObject.setUpTime(engineStats.getUpTime());
177 engineStatusObject.setPolicyExecutions(engineStats.getEventCount());
178 engineStatusObject.setLastPolicyDuration(gson.toJson(
179 getValuesFromCache(id, engineStats.getEngineId() + "_last_policy_duration",
180 pdpStatistics.getTimeStamp().getTime(), engineStats.getLastExecutionTime()),
182 engineStatusObject.setAveragePolicyDuration(
183 gson.toJson(getValuesFromCache(id, engineStats.getEngineId() + "_average_policy_duration",
184 pdpStatistics.getTimeStamp().getTime(),
185 (long) engineStats.getAverageExecutionTime()), List.class));
186 engineStatusList.add(engineStatusObject);
187 } catch (final RuntimeException e) {
188 LOGGER.warn("Error getting status of engine with ID " + engineStats.getEngineId() + "<br>", e);
192 responseObject.setTimeStamp("N/A");
193 responseObject.setPolicyDeployCount("N/A");
194 responseObject.setPolicyDeploySuccessCount("N/A");
195 responseObject.setPolicyDeployFailCount("N/A");
196 responseObject.setPolicyExecutedCount("N/A");
197 responseObject.setPolicyExecutedSuccessCount("N/A");
198 responseObject.setPolicyExecutedFailCount("N/A");
201 responseObject.setStatus(engineStatusList);
202 return Response.ok(new StandardCoder().encode(responseObject), MediaType.APPLICATION_JSON).build();
205 private HttpClient getHttpClient(String useHttps, String hostname, int port, String username, String password,
206 String basePath) throws HttpClientConfigException {
207 BusTopicParams busParams = new BusTopicParams();
208 busParams.setClientName("pdp-monitoring");
209 busParams.setHostname(hostname);
210 busParams.setManaged(false);
211 busParams.setPassword(password);
212 busParams.setPort(port);
213 busParams.setUseHttps(useHttps.equals("https"));
214 busParams.setUserName(username);
215 busParams.setBasePath(basePath);
216 return HttpClientFactoryInstance.getClientFactory().build(busParams);
220 * This method takes in the latest data entry for an engine, adds it to an existing data set and
221 * returns the full map for that host and engine.
223 * @param uri the pdp uri
224 * @param id the engines id
225 * @param timestamp the timestamp of the latest data entry
226 * @param latestValue the value of the latest data entry
227 * @return a list of {@code Counter} objects for that engine
229 private synchronized List<Counter> getValuesFromCache(final String uri, final String id, final long timestamp,
230 final long latestValue) {
232 Map<String, List<Counter>> engineStatus = cache.computeIfAbsent(uri, k -> new HashMap<>());
234 List<Counter> valueList = engineStatus.computeIfAbsent(id, k -> new SlidingWindowList<>(MAX_CACHED_ENTITIES));
236 valueList.add(new Counter(timestamp, latestValue));
242 * A list of values that uses a FIFO sliding window of a fixed size.
244 @EqualsAndHashCode(callSuper = true)
245 public class SlidingWindowList<V> extends LinkedList<V> {
246 private static final long serialVersionUID = -7187277916025957447L;
248 private final int maxEntries;
250 public SlidingWindowList(final int maxEntries) {
251 this.maxEntries = maxEntries;
255 public boolean add(final V elm) {
256 if (this.size() > (maxEntries - 1)) {
259 return super.add(elm);
264 * A class used to storing a single data entry for an engine.
267 public class Counter {
268 private final long timestamp;
269 private final long value;
271 public Counter(final long timestamp, final long value) {
272 this.timestamp = timestamp;