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 static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;
29 import com.att.eelf.configuration.EELFLogger;
30 import com.att.eelf.configuration.EELFManager;
31 import java.io.ByteArrayOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.nio.file.FileStore;
36 import java.nio.file.FileSystem;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.Paths;
40 import java.nio.file.StandardCopyOption;
41 import java.util.Properties;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletResponse;
46 import org.json.JSONArray;
47 import org.onap.dmaap.datarouter.provisioning.utils.Poker;
48 import org.onap.dmaap.datarouter.provisioning.utils.SynchronizerTask;
49 import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;
50 import org.onap.dmaap.datarouter.provisioning.beans.LogRecord;
51 import org.onap.dmaap.datarouter.provisioning.beans.Parameters;
52 import org.onap.dmaap.datarouter.provisioning.eelf.EelfMsgs;
53 import org.onap.dmaap.datarouter.provisioning.utils.LogfileLoader;
54 import org.onap.dmaap.datarouter.provisioning.utils.RLEBitSet;
60 * This servlet handles requests to URLs under /internal on the provisioning server. These include:
62 * <div class="contentContainer">
63 * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">
64 * <caption><span>URL Path Summary</span><span class="tabEnd"> </span></caption>
66 * <th class="colFirst" width="15%">URL Path</th>
67 * <th class="colOne">Method</th>
68 * <th class="colLast">Purpose</th>
70 * <tr class="altColor">
71 * <td class="colFirst">/internal/prov</td>
72 * <td class="colOne">GET</td>
73 * <td class="colLast">used to GET a full JSON copy of the provisioning data.</td>
75 * <tr class="rowColor">
76 * <td class="colFirst">/internal/fetchProv</td>
77 * <td class="colOne">GET</td>
78 * <td class="colLast">used to signal to a standby POD that the provisioning data should be fetched from the active
81 * <tr class="altColor">
82 * <td class="colFirst" rowspan="2">/internal/logs</td>
83 * <td class="colOne">GET</td>
84 * <td class="colLast">used to GET an index of log files and individual logs for this provisioning server.</td>
86 * <tr class="altColor">
87 * <td class="colOne">POST</td>
88 * <td class="colLast">used to POST log files from the individual nodes to this provisioning server.</td>
90 * <tr class="rowColor">
91 * <td class="colFirst" rowspan="4">/internal/api</td>
92 * <td class="colOne">GET</td>
93 * <td class="colLast">used to GET an individual parameter value. The parameter name is specified by the path after
96 * <tr class="rowColor">
97 * <td class="colOne">PUT</td>
98 * <td class="colLast">used to set an individual parameter value. The parameter name is specified by the path after
101 * <tr class="rowColor">
102 * <td class="colOne">DELETE</td>
103 * <td class="colLast">used to remove an individual parameter value. The parameter name is specified by the path after
106 * <tr class="rowColor">
107 * <td class="colOne">POST</td>
108 * <td class="colLast">used to create a new individual parameter value. The parameter name is specified by the path
111 * <tr class="altColor">
112 * <td class="colFirst">/internal/halt</td>
113 * <td class="colOne">GET</td>
114 * <td class="colLast">used to halt the server (must be accessed from 127.0.0.1).</td>
116 * <tr class="rowColor">
117 * <td class="colFirst" rowspan="2">/internal/drlogs</td>
118 * <td class="colOne">GET</td>
119 * <td class="colLast">used to get a list of DR log entries available for retrieval.
120 * Note: these are the actual data router log entries sent to the provisioning server by the nodes, not the provisioning
121 * server's internal logs (access via /internal/logs above). The range is returned as a list of record sequence
124 * <tr class="rowColor">
125 * <td class="colOne">POST</td>
126 * <td class="colLast">used to retrieve specific log entries.
127 * The sequence numbers of the records to fetch are POST-ed; the records matching the sequence numbers are
130 * <tr class="altColor">
131 * <td class="colFirst">/internal/route/*</td>
132 * <td class="colOne">*</td>
133 * <td class="colLast">URLs under this path are handled via the
134 * {@link RouteServlet}</td>
139 * Authorization to use these URLs is a little different than for other URLs on the provisioning server. For the most
140 * part, the IP address that the request comes from should be either:
143 * <li>an IP address of a provisioning server, or</li>
144 * <li>the IP address of a node (to allow access to /internal/prov), or</li>
145 * <li>an IP address from the "<i>special subnet</i>" which is configured with
146 * the PROV_SPECIAL_SUBNET parameter.
149 * In addition, requests to /internal/halt can ONLY come from localhost (127.0.0.1) on the HTTP port.
152 * All DELETE/GET/PUT/POST requests made to /internal/api on this servlet on the standby server are proxied to the
153 * active server (using the {@link ProxyServlet}) if it is up and reachable.
157 * @version $Id: InternalServlet.java,v 1.23 2014/03/24 18:47:10 eby Exp $
160 @SuppressWarnings("serial")
161 public class InternalServlet extends ProxyServlet {
164 private static final Object lock = new Object();
165 private static Integer logseq = 0; // another piece of info to make log spool file names unique
166 //Adding EELF Logger Rally:US664892
167 private static EELFLogger eelfLogger = EELFManager.getInstance()
168 .getLogger(InternalServlet.class);
171 * Delete a parameter at the address /internal/api/<parameter>. See the <b>Internal API</b> document for
172 * details on how this method should be invoked.
175 public void doDelete(HttpServletRequest req, HttpServletResponse resp) {
176 setIpFqdnRequestIDandInvocationIDForEelf("doDelete", req);
177 eelfLogger.info(EelfMsgs.ENTRY);
179 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID,
180 req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");
181 EventLogRecord elr = new EventLogRecord(req);
182 if (!isAuthorizedForInternal(req)) {
183 elr.setMessage(UNAUTHORIZED);
184 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
185 eventlogger.error(elr.toString());
186 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
190 String path = req.getPathInfo();
191 if (path.startsWith(API)) {
192 if (isProxyOK(req) && isProxyServer()) {
193 super.doDelete(req, resp);
196 String key = path.substring(5);
197 if (key.length() > 0) {
198 Parameters param = Parameters.getParameter(key);
200 if (doDelete(param)) {
201 elr.setResult(HttpServletResponse.SC_OK);
202 eventlogger.info(elr.toString());
203 resp.setStatus(HttpServletResponse.SC_OK);
204 provisioningDataChanged();
205 provisioningParametersChanged();
207 // Something went wrong with the DELETE
208 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
209 eventlogger.error(elr.toString());
210 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
211 DB_PROBLEM_MSG, eventlogger);
217 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
219 eelfLogger.info(EelfMsgs.EXIT);
224 * Get some information (such as a parameter) underneath the /internal/ namespace. See the <b>Internal API</b>
225 * document for details on how this method should be invoked.
228 public void doGet(HttpServletRequest req, HttpServletResponse resp) {
229 setIpFqdnRequestIDandInvocationIDForEelf("doGet",req);
230 eelfLogger.info(EelfMsgs.ENTRY);
232 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID,
233 req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");
234 String path = req.getPathInfo();
235 Properties props = ProvRunner.getProvProperties();
236 if ("/halt".equals(path) && !req.isSecure()) {
237 // request to halt the server - can ONLY come from localhost
238 String remote = req.getRemoteAddr();
239 if (remote.equals(props.getProperty("org.onap.dmaap.datarouter.provserver.localhost"))) {
240 intlogger.info("PROV0009 Request to HALT received.");
241 resp.setStatus(HttpServletResponse.SC_OK);
242 ProvRunner.shutdown();
244 intlogger.info("PROV0010 Disallowed request to HALT received from " + remote);
245 resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
250 EventLogRecord elr = new EventLogRecord(req);
251 if (!isAuthorizedForInternal(req)) {
252 elr.setMessage(UNAUTHORIZED);
253 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
254 eventlogger.error(elr.toString());
255 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
258 if ("/fetchProv".equals(path) && !req.isSecure()) {
259 // if request came from active_pod or standby_pod and it is not us, reload prov data
260 SynchronizerTask sync = SynchronizerTask.getSynchronizer();
262 resp.setStatus(HttpServletResponse.SC_OK);
265 if ("/prov".equals(path)) {
266 if (isProxyOK(req) && isProxyServer()) {
267 if (super.doGetWithFallback(req, resp)) {
270 // fall back to returning the local data if the remote is unreachable
271 intlogger.info("Active server unavailable; falling back to local copy.");
273 Poker pkr = Poker.getPoker();
274 resp.setStatus(HttpServletResponse.SC_OK);
275 resp.setContentType(PROVFULL_CONTENT_TYPE2);
277 resp.getOutputStream().print(pkr.getProvisioningString());
278 } catch (IOException ioe) {
279 intlogger.error("PROV0131 InternalServlet.doGet: " + ioe.getMessage(), ioe);
283 if ("/logs".equals(path) || LOGS.equals(path)) {
284 resp.setStatus(HttpServletResponse.SC_OK);
285 resp.setContentType("application/json");
287 resp.getOutputStream().print(generateLogfileList().toString());
288 } catch (IOException ioe) {
289 intlogger.error("PROV0132 InternalServlet.doGet: " + ioe.getMessage(), ioe);
293 if (path.startsWith(LOGS)) {
294 String logdir = props.getProperty("org.onap.dmaap.datarouter.provserver.accesslog.dir");
295 String logfile = path.substring(6);
296 if (logdir != null && logfile != null && logfile.indexOf('/') < 0) {
297 File log = new File(logdir + File.separator + logfile);
298 if (log.exists() && log.isFile()) {
299 resp.setStatus(HttpServletResponse.SC_OK);
300 resp.setContentType(TEXT_CT);
301 Path logpath = Paths.get(log.getAbsolutePath());
303 Files.copy(logpath, resp.getOutputStream());
304 } catch (IOException ioe) {
305 intlogger.error("PROV0133 InternalServlet.doGet: " + ioe.getMessage(), ioe);
310 sendResponseError(resp, HttpServletResponse.SC_NO_CONTENT, "No file.", eventlogger);
313 if (path.startsWith(API)) {
314 if (isProxyOK(req) && isProxyServer()) {
315 super.doGet(req, resp);
318 String key = path.substring(5);
319 if (key.length() > 0) {
320 Parameters param = Parameters.getParameter(key);
322 resp.setStatus(HttpServletResponse.SC_OK);
323 resp.setContentType(TEXT_CT);
325 resp.getOutputStream().print(param.getValue() + "\n");
326 } catch (IOException ioe) {
327 intlogger.error("PROV0134 InternalServlet.doGet: " + ioe.getMessage(), ioe);
333 if ("/drlogs".equals(path) || "/drlogs/".equals(path)) {
334 // Special POD <=> POD API to determine what log file records are loaded here
335 LogfileLoader lfl = LogfileLoader.getLoader();
336 resp.setStatus(HttpServletResponse.SC_OK);
337 resp.setContentType(TEXT_CT);
339 resp.getOutputStream().print(lfl.getBitSet().toString());
340 } catch (IOException ioe) {
341 intlogger.error("PROV0135 InternalServlet.doGet: " + ioe.getMessage(), ioe);
345 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
347 eelfLogger.info(EelfMsgs.EXIT);
352 * Modify a parameter at the address /internal/api/<parameter>. See the <b>Internal API</b> document for
353 * details on how this method should be invoked.
356 public void doPut(HttpServletRequest req, HttpServletResponse resp) {
357 setIpFqdnRequestIDandInvocationIDForEelf("doPut", req);
358 eelfLogger.info(EelfMsgs.ENTRY);
360 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID,
361 req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");
362 EventLogRecord elr = new EventLogRecord(req);
363 if (!isAuthorizedForInternal(req)) {
364 elr.setMessage(UNAUTHORIZED);
365 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
366 eventlogger.error(elr.toString());
367 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
370 String path = req.getPathInfo();
371 if (path.startsWith(API)) {
372 if (isProxyOK(req) && isProxyServer()) {
373 super.doPut(req, resp);
376 String key = path.substring(5);
377 if (key.length() > 0) {
378 Parameters param = Parameters.getParameter(key);
380 String str = catValues(req.getParameterValues("val"));
382 if (doUpdate(param)) {
383 elr.setResult(HttpServletResponse.SC_OK);
384 eventlogger.info(elr.toString());
385 resp.setStatus(HttpServletResponse.SC_OK);
386 provisioningDataChanged();
387 provisioningParametersChanged();
389 // Something went wrong with the UPDATE
390 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
391 eventlogger.error(elr.toString());
392 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
393 DB_PROBLEM_MSG, eventlogger);
399 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
401 eelfLogger.info(EelfMsgs.EXIT);
406 * Create some new information (such as a parameter or log entries) underneath the /internal/ namespace. See the
407 * <b>Internal API</b> document for details on how this method should be invoked.
409 @SuppressWarnings("resource")
411 public void doPost(HttpServletRequest req, HttpServletResponse resp) {
412 setIpFqdnRequestIDandInvocationIDForEelf("doPost", req);
413 eelfLogger.info(EelfMsgs.ENTRY);
415 eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
416 EventLogRecord elr = new EventLogRecord(req);
417 if (!isAuthorizedForInternal(req)) {
418 elr.setMessage(UNAUTHORIZED);
419 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
420 eventlogger.error(elr.toString());
421 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
425 String path = req.getPathInfo();
426 if (path.startsWith(API)) {
427 if (isProxyOK(req) && isProxyServer()) {
428 super.doPost(req, resp);
431 String key = path.substring(5);
432 if (key.length() > 0) {
433 Parameters param = Parameters.getParameter(key);
435 String str = catValues(req.getParameterValues("val"));
436 param = new Parameters(key, str);
437 if (doInsert(param)) {
438 elr.setResult(HttpServletResponse.SC_OK);
439 eventlogger.info(elr.toString());
440 resp.setStatus(HttpServletResponse.SC_OK);
441 provisioningDataChanged();
442 provisioningParametersChanged();
444 // Something went wrong with the INSERT
445 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
446 eventlogger.error(elr.toString());
447 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
448 DB_PROBLEM_MSG, eventlogger);
455 if ("/logs".equals(path) || LOGS.equals(path)) {
456 String ctype = req.getHeader("Content-Type");
457 if (ctype == null || !TEXT_CT.equals(ctype)) {
458 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
459 elr.setMessage("Bad media type: " + ctype);
460 resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
461 eventlogger.error(elr.toString());
465 ProvRunner.getProvProperties().getProperty("org.onap.dmaap.datarouter.provserver.spooldir");
466 String spoolname = String.format("%d-%d-", System.currentTimeMillis(), Thread.currentThread().getId());
467 synchronized (lock) {
468 // perhaps unnecessary, but it helps make the name unique
469 spoolname += logseq.toString();
472 String encoding = req.getHeader("Content-Encoding");
473 if (encoding != null) {
474 if ("gzip".equals(encoding.trim())) {
477 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
478 resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
479 eventlogger.error(elr.toString());
483 // Determine space available -- available space must be at least 5%
484 FileSystem fs = (Paths.get(spooldir)).getFileSystem();
487 for (FileStore store : fs.getFileStores()) {
489 total += store.getTotalSpace();
490 avail += store.getUsableSpace();
491 } catch (IOException ioe) {
492 intlogger.error("PROV0136 InternalServlet.doPost: " + ioe.getMessage(), ioe);
497 } catch (Exception e) {
498 intlogger.error("PROV0137 InternalServlet.doPost: " + e.getMessage(), e);
500 if (total != 0 && ((avail * 100) / total) < 5) {
501 elr.setResult(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
502 resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
503 eventlogger.error(elr.toString());
506 Path tmppath = Paths.get(spooldir, spoolname);
507 Path donepath = Paths.get(spooldir, "IN." + spoolname);
509 Files.copy(req.getInputStream(), Paths.get(spooldir, spoolname),
510 StandardCopyOption.REPLACE_EXISTING);
511 Files.move(tmppath, donepath, StandardCopyOption.REPLACE_EXISTING);
512 elr.setResult(HttpServletResponse.SC_CREATED);
513 resp.setStatus(HttpServletResponse.SC_CREATED);
514 eventlogger.info(elr.toString());
515 LogfileLoader.getLoader(); // This starts the logfile loader "task"
516 } catch (IOException ioe) {
517 intlogger.error("PROV0138 InternalServlet.doPost: " + ioe.getMessage(), ioe);
522 if ("/drlogs".equals(path) || "/drlogs/".equals(path)) {
523 // Receive post request and generate log entries
524 String ctype = req.getHeader("Content-Type");
525 if (ctype == null || !TEXT_CT.equals(ctype)) {
526 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
527 elr.setMessage("Bad media type: " + ctype);
528 resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
529 eventlogger.error(elr.toString());
533 InputStream is = req.getInputStream();
534 ByteArrayOutputStream bos = new ByteArrayOutputStream();
536 while ((ch = is.read()) >= 0) {
539 elr.setResult(HttpServletResponse.SC_OK);
540 resp.setStatus(HttpServletResponse.SC_OK);
541 resp.setContentType(TEXT_CT);
542 RLEBitSet bs = new RLEBitSet(bos.toString()); // The set of records to retrieve
543 LogRecord.printLogRecords(resp.getOutputStream(), bs);
544 eventlogger.info(elr.toString());
545 } catch (IOException ioe) {
546 intlogger.error("PROV0139 InternalServlet.doPost: " + ioe.getMessage(), ioe);
551 elr.setResult(HttpServletResponse.SC_NOT_FOUND);
552 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
553 eventlogger.error(elr.toString());
555 eelfLogger.info(EelfMsgs.EXIT);
559 private String catValues(String[] val) {
560 StringBuilder sb = new StringBuilder();
563 for (String s : val) {
569 return sb.toString();
572 private JSONArray generateLogfileList() {
573 JSONArray ja = new JSONArray();
574 Properties prop = ProvRunner.getProvProperties();
575 String str = prop.getProperty("org.onap.dmaap.datarouter.provserver.accesslog.dir");
577 String[] dirs = str.split(",");
578 for (String dir : dirs) {
579 File file = new File(dir);
580 String[] list = file.list();
582 for (String s2 : list) {
583 if (!s2.startsWith(".")) {