1 /*******************************************************************************
2 * ============LICENSE_START==================================================
4 * * ===========================================================================
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6 * * ===========================================================================
7 * * Licensed under the Apache License, Version 2.0 (the "License");
8 * * you may not use this file except in compliance with the License.
9 * * You may obtain a copy of the License at
11 * * http://www.apache.org/licenses/LICENSE-2.0
13 * * Unless required by applicable law or agreed to in writing, software
14 * * distributed under the License is distributed on an "AS IS" BASIS,
15 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * * See the License for the specific language governing permissions and
17 * * limitations under the License.
18 * * ============LICENSE_END====================================================
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 ******************************************************************************/
25 package org.onap.dmaap.datarouter.provisioning;
27 import java.io.ByteArrayOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.nio.file.FileStore;
32 import java.nio.file.FileSystem;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.nio.file.StandardCopyOption;
37 import java.util.Properties;
39 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse;
42 import org.json.JSONArray;
43 import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;
44 import org.onap.dmaap.datarouter.provisioning.beans.LogRecord;
45 import org.onap.dmaap.datarouter.provisioning.beans.Parameters;
46 import org.onap.dmaap.datarouter.provisioning.eelf.EelfMsgs;
47 import org.onap.dmaap.datarouter.provisioning.utils.DB;
48 import org.onap.dmaap.datarouter.provisioning.utils.LogfileLoader;
49 import org.onap.dmaap.datarouter.provisioning.utils.RLEBitSet;
51 import com.att.eelf.configuration.EELFLogger;
52 import com.att.eelf.configuration.EELFManager;
54 import static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;
58 * This servlet handles requests to URLs under /internal on the provisioning server. These include:
60 * <div class="contentContainer">
61 * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">
62 * <caption><span>URL Path Summary</span><span class="tabEnd"> </span></caption>
64 * <th class="colFirst" width="15%">URL Path</th>
65 * <th class="colOne">Method</th>
66 * <th class="colLast">Purpose</th>
68 * <tr class="altColor">
69 * <td class="colFirst">/internal/prov</td>
70 * <td class="colOne">GET</td>
71 * <td class="colLast">used to GET a full JSON copy of the provisioning data.</td>
73 * <tr class="rowColor">
74 * <td class="colFirst">/internal/fetchProv</td>
75 * <td class="colOne">GET</td>
76 * <td class="colLast">used to signal to a standby POD that the provisioning data should be fetched from the active
79 * <tr class="altColor">
80 * <td class="colFirst" rowspan="2">/internal/logs</td>
81 * <td class="colOne">GET</td>
82 * <td class="colLast">used to GET an index of log files and individual logs for this provisioning server.</td>
84 * <tr class="altColor">
85 * <td class="colOne">POST</td>
86 * <td class="colLast">used to POST log files from the individual nodes to this provisioning server.</td>
88 * <tr class="rowColor">
89 * <td class="colFirst" rowspan="4">/internal/api</td>
90 * <td class="colOne">GET</td>
91 * <td class="colLast">used to GET an individual parameter value. The parameter name is specified by the path after
94 * <tr class="rowColor">
95 * <td class="colOne">PUT</td>
96 * <td class="colLast">used to set an individual parameter value. The parameter name is specified by the path after
99 * <tr class="rowColor">
100 * <td class="colOne">DELETE</td>
101 * <td class="colLast">used to remove an individual parameter value. The parameter name is specified by the path after
104 * <tr class="rowColor">
105 * <td class="colOne">POST</td>
106 * <td class="colLast">used to create a new individual parameter value. The parameter name is specified by the path
109 * <tr class="altColor">
110 * <td class="colFirst">/internal/halt</td>
111 * <td class="colOne">GET</td>
112 * <td class="colLast">used to halt the server (must be accessed from 127.0.0.1).</td>
114 * <tr class="rowColor">
115 * <td class="colFirst" rowspan="2">/internal/drlogs</td>
116 * <td class="colOne">GET</td>
117 * <td class="colLast">used to get a list of DR log entries available for retrieval.
118 * Note: these are the actual data router log entries sent to the provisioning server by the nodes, not the provisioning
119 * server's internal logs (access via /internal/logs above). The range is returned as a list of record sequence
122 * <tr class="rowColor">
123 * <td class="colOne">POST</td>
124 * <td class="colLast">used to retrieve specific log entries.
125 * The sequence numbers of the records to fetch are POST-ed; the records matching the sequence numbers are
128 * <tr class="altColor">
129 * <td class="colFirst">/internal/route/*</td>
130 * <td class="colOne">*</td>
131 * <td class="colLast">URLs under this path are handled via the {@link org.onap.dmaap.datarouter.provisioning.RouteServlet}</td>
136 * Authorization to use these URLs is a little different than for other URLs on the provisioning server. For the most
137 * part, the IP address that the request comes from should be either:
140 * <li>an IP address of a provisioning server, or</li>
141 * <li>the IP address of a node (to allow access to /internal/prov), or</li>
142 * <li>an IP address from the "<i>special subnet</i>" which is configured with
143 * the PROV_SPECIAL_SUBNET parameter.
146 * In addition, requests to /internal/halt can ONLY come from localhost (127.0.0.1) on the HTTP port.
149 * All DELETE/GET/PUT/POST requests made to /internal/api on this servlet on the standby server are proxied to the
150 * active server (using the {@link ProxyServlet}) if it is up and reachable.
154 * @version $Id: InternalServlet.java,v 1.23 2014/03/24 18:47:10 eby Exp $
156 @SuppressWarnings("serial")
157 public class InternalServlet extends ProxyServlet {
160 private static final Object lock = new Object();
161 private static Integer logseq = 0; // another piece of info to make log spool file names unique
162 //Adding EELF Logger Rally:US664892
163 private static EELFLogger eelfLogger = EELFManager.getInstance()
164 .getLogger(InternalServlet.class);
167 * Delete a parameter at the address /internal/api/<parameter>. See the <b>Internal API</b> document for
168 * details on how this method should be invoked.
171 public void doDelete(HttpServletRequest req, HttpServletResponse resp) {
172 setIpFqdnRequestIDandInvocationIDForEelf("doDelete", req);
173 eelfLogger.info(EelfMsgs.ENTRY);
175 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");
176 EventLogRecord elr = new EventLogRecord(req);
177 if (!isAuthorizedForInternal(req)) {
178 elr.setMessage(UNAUTHORIZED);
179 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
180 eventlogger.error(elr.toString());
181 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
185 String path = req.getPathInfo();
186 if (path.startsWith(API)) {
187 if (isProxyOK(req) && isProxyServer()) {
188 super.doDelete(req, resp);
191 String key = path.substring(5);
192 if (key.length() > 0) {
193 Parameters param = Parameters.getParameter(key);
195 if (doDelete(param)) {
196 elr.setResult(HttpServletResponse.SC_OK);
197 eventlogger.info(elr.toString());
198 resp.setStatus(HttpServletResponse.SC_OK);
199 provisioningDataChanged();
200 provisioningParametersChanged();
202 // Something went wrong with the DELETE
203 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
204 eventlogger.error(elr.toString());
205 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, eventlogger);
211 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
213 eelfLogger.info(EelfMsgs.EXIT);
218 * Get some information (such as a parameter) underneath the /internal/ namespace. See the <b>Internal API</b>
219 * document for details on how this method should be invoked.
222 public void doGet(HttpServletRequest req, HttpServletResponse resp) {
223 setIpFqdnRequestIDandInvocationIDForEelf("doGet",req);
224 eelfLogger.info(EelfMsgs.ENTRY);
226 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");
227 String path = req.getPathInfo();
228 Properties props = (new DB()).getProperties();
229 if ("/halt".equals(path) && !req.isSecure()) {
230 // request to halt the server - can ONLY come from localhost
231 String remote = req.getRemoteAddr();
232 if (remote.equals(props.getProperty("org.onap.dmaap.datarouter.provserver.localhost"))) {
233 intlogger.info("PROV0009 Request to HALT received.");
234 resp.setStatus(HttpServletResponse.SC_OK);
237 intlogger.info("PROV0010 Disallowed request to HALT received from " + remote);
238 resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
243 EventLogRecord elr = new EventLogRecord(req);
244 if (!isAuthorizedForInternal(req)) {
245 elr.setMessage(UNAUTHORIZED);
246 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
247 eventlogger.error(elr.toString());
248 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
251 if ("/fetchProv".equals(path) && !req.isSecure()) {
252 // if request came from active_pod or standby_pod and it is not us, reload prov data
253 SynchronizerTask s = SynchronizerTask.getSynchronizer();
255 resp.setStatus(HttpServletResponse.SC_OK);
258 if ("/prov".equals(path)) {
259 if (isProxyOK(req) && isProxyServer()) {
260 if (super.doGetWithFallback(req, resp)) {
263 // fall back to returning the local data if the remote is unreachable
264 intlogger.info("Active server unavailable; falling back to local copy.");
266 Poker p = Poker.getPoker();
267 resp.setStatus(HttpServletResponse.SC_OK);
268 resp.setContentType(PROVFULL_CONTENT_TYPE2);
270 resp.getOutputStream().print(p.getProvisioningString());
271 } catch (IOException ioe) {
272 intlogger.error("PROV0131 InternalServlet.doGet: " + ioe.getMessage(), ioe);
276 if ("/logs".equals(path) || LOGS.equals(path)) {
277 resp.setStatus(HttpServletResponse.SC_OK);
278 resp.setContentType("application/json");
280 resp.getOutputStream().print(generateLogfileList().toString());
281 } catch (IOException ioe) {
282 intlogger.error("PROV0132 InternalServlet.doGet: " + ioe.getMessage(), ioe);
286 if (path.startsWith(LOGS)) {
287 String logdir = props.getProperty("org.onap.dmaap.datarouter.provserver.accesslog.dir");
288 String logfile = path.substring(6);
289 if (logdir != null && logfile != null && logfile.indexOf('/') < 0) {
290 File log = new File(logdir + "/" + logfile);
291 if (log.exists() && log.isFile()) {
292 resp.setStatus(HttpServletResponse.SC_OK);
293 resp.setContentType(TEXT_CT);
294 Path logpath = Paths.get(log.getAbsolutePath());
296 Files.copy(logpath, resp.getOutputStream());
297 } catch (IOException ioe) {
298 intlogger.error("PROV0133 InternalServlet.doGet: " + ioe.getMessage(), ioe);
303 sendResponseError(resp, HttpServletResponse.SC_NO_CONTENT, "No file.", eventlogger);
306 if (path.startsWith(API)) {
307 if (isProxyOK(req) && isProxyServer()) {
308 super.doGet(req, resp);
311 String key = path.substring(5);
312 if (key.length() > 0) {
313 Parameters param = Parameters.getParameter(key);
315 resp.setStatus(HttpServletResponse.SC_OK);
316 resp.setContentType(TEXT_CT);
318 resp.getOutputStream().print(param.getValue() + "\n");
319 } catch (IOException ioe) {
320 intlogger.error("PROV0134 InternalServlet.doGet: " + ioe.getMessage(), ioe);
326 if ("/drlogs".equals(path) || "/drlogs/".equals(path)) {
327 // Special POD <=> POD API to determine what log file records are loaded here
328 LogfileLoader lfl = LogfileLoader.getLoader();
329 resp.setStatus(HttpServletResponse.SC_OK);
330 resp.setContentType(TEXT_CT);
332 resp.getOutputStream().print(lfl.getBitSet().toString());
333 } catch (IOException ioe) {
334 intlogger.error("PROV0135 InternalServlet.doGet: " + ioe.getMessage(), ioe);
338 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
340 eelfLogger.info(EelfMsgs.EXIT);
345 * Modify a parameter at the address /internal/api/<parameter>. See the <b>Internal API</b> document for
346 * details on how this method should be invoked.
349 public void doPut(HttpServletRequest req, HttpServletResponse resp) {
350 setIpFqdnRequestIDandInvocationIDForEelf("doPut", req);
351 eelfLogger.info(EelfMsgs.ENTRY);
353 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");
354 EventLogRecord elr = new EventLogRecord(req);
355 if (!isAuthorizedForInternal(req)) {
356 elr.setMessage(UNAUTHORIZED);
357 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
358 eventlogger.error(elr.toString());
359 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
362 String path = req.getPathInfo();
363 if (path.startsWith(API)) {
364 if (isProxyOK(req) && isProxyServer()) {
365 super.doPut(req, resp);
368 String key = path.substring(5);
369 if (key.length() > 0) {
370 Parameters param = Parameters.getParameter(key);
372 String t = catValues(req.getParameterValues("val"));
374 if (doUpdate(param)) {
375 elr.setResult(HttpServletResponse.SC_OK);
376 eventlogger.info(elr.toString());
377 resp.setStatus(HttpServletResponse.SC_OK);
378 provisioningDataChanged();
379 provisioningParametersChanged();
381 // Something went wrong with the UPDATE
382 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
383 eventlogger.error(elr.toString());
384 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, eventlogger);
390 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
392 eelfLogger.info(EelfMsgs.EXIT);
397 * Create some new information (such as a parameter or log entries) underneath the /internal/ namespace. See the
398 * <b>Internal API</b> document for details on how this method should be invoked.
400 @SuppressWarnings("resource")
402 public void doPost(HttpServletRequest req, HttpServletResponse resp) {
403 setIpFqdnRequestIDandInvocationIDForEelf("doPost", req);
404 eelfLogger.info(EelfMsgs.ENTRY);
406 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
407 EventLogRecord elr = new EventLogRecord(req);
408 if (!isAuthorizedForInternal(req)) {
409 elr.setMessage(UNAUTHORIZED);
410 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
411 eventlogger.error(elr.toString());
412 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
416 String path = req.getPathInfo();
417 if (path.startsWith(API)) {
418 if (isProxyOK(req) && isProxyServer()) {
419 super.doPost(req, resp);
422 String key = path.substring(5);
423 if (key.length() > 0) {
424 Parameters param = Parameters.getParameter(key);
426 String t = catValues(req.getParameterValues("val"));
427 param = new Parameters(key, t);
428 if (doInsert(param)) {
429 elr.setResult(HttpServletResponse.SC_OK);
430 eventlogger.info(elr.toString());
431 resp.setStatus(HttpServletResponse.SC_OK);
432 provisioningDataChanged();
433 provisioningParametersChanged();
435 // Something went wrong with the INSERT
436 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
437 eventlogger.error(elr.toString());
438 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, eventlogger);
445 if ("/logs".equals(path) || LOGS.equals(path)) {
446 String ctype = req.getHeader("Content-Type");
447 if (ctype == null || !TEXT_CT.equals(ctype)) {
448 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
449 elr.setMessage("Bad media type: " + ctype);
450 resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
451 eventlogger.error(elr.toString());
454 String spooldir = (new DB()).getProperties().getProperty("org.onap.dmaap.datarouter.provserver.spooldir");
455 String spoolname = String.format("%d-%d-", System.currentTimeMillis(), Thread.currentThread().getId());
456 synchronized (lock) {
457 // perhaps unnecessary, but it helps make the name unique
458 spoolname += logseq.toString();
461 String encoding = req.getHeader("Content-Encoding");
462 if (encoding != null) {
463 if ("gzip".equals(encoding.trim())) {
466 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
467 resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
468 eventlogger.error(elr.toString());
472 // Determine space available -- available space must be at least 5%
473 FileSystem fs = (Paths.get(spooldir)).getFileSystem();
476 for (FileStore store : fs.getFileStores()) {
478 total += store.getTotalSpace();
479 avail += store.getUsableSpace();
480 } catch (IOException ioe) {
481 intlogger.error("PROV0136 InternalServlet.doPost: " + ioe.getMessage(), ioe);
486 } catch (Exception e) {
487 intlogger.error("PROV0137 InternalServlet.doPost: " + e.getMessage(), e);
489 if (((avail * 100) / total) < 5) {
490 elr.setResult(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
491 resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
492 eventlogger.error(elr.toString());
495 Path tmppath = Paths.get(spooldir, spoolname);
496 Path donepath = Paths.get(spooldir, "IN." + spoolname);
498 Files.copy(req.getInputStream(), Paths.get(spooldir, spoolname), StandardCopyOption.REPLACE_EXISTING);
499 Files.move(tmppath, donepath, StandardCopyOption.REPLACE_EXISTING);
500 elr.setResult(HttpServletResponse.SC_CREATED);
501 resp.setStatus(HttpServletResponse.SC_CREATED);
502 eventlogger.info(elr.toString());
503 LogfileLoader.getLoader(); // This starts the logfile loader "task"
504 } catch (IOException ioe) {
505 intlogger.error("PROV0138 InternalServlet.doPost: " + ioe.getMessage(), ioe);
510 if ("/drlogs".equals(path) || "/drlogs/".equals(path)) {
511 // Receive post request and generate log entries
512 String ctype = req.getHeader("Content-Type");
513 if (ctype == null || !TEXT_CT.equals(ctype)) {
514 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
515 elr.setMessage("Bad media type: " + ctype);
516 resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
517 eventlogger.error(elr.toString());
521 InputStream is = req.getInputStream();
522 ByteArrayOutputStream bos = new ByteArrayOutputStream();
524 while ((ch = is.read()) >= 0) {
527 RLEBitSet bs = new RLEBitSet(bos.toString()); // The set of records to retrieve
528 elr.setResult(HttpServletResponse.SC_OK);
529 resp.setStatus(HttpServletResponse.SC_OK);
530 resp.setContentType(TEXT_CT);
531 LogRecord.printLogRecords(resp.getOutputStream(), bs);
532 eventlogger.info(elr.toString());
533 } catch (IOException ioe) {
534 intlogger.error("PROV0139 InternalServlet.doPost: " + ioe.getMessage(), ioe);
539 elr.setResult(HttpServletResponse.SC_NOT_FOUND);
540 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
541 eventlogger.error(elr.toString());
543 eelfLogger.info(EelfMsgs.EXIT);
547 private String catValues(String[] v) {
548 StringBuilder sb = new StringBuilder();
557 return sb.toString();
560 private JSONArray generateLogfileList() {
561 JSONArray ja = new JSONArray();
562 Properties p = (new DB()).getProperties();
563 String s = p.getProperty("org.onap.dmaap.datarouter.provserver.accesslog.dir");
565 String[] dirs = s.split(",");
566 for (String dir : dirs) {
567 File f = new File(dir);
568 String[] list = f.list();
570 for (String s2 : list) {
571 if (!s2.startsWith(".")) {