X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=datarouter-prov%2Fsrc%2Fmain%2Fjava%2Fcom%2Fatt%2Fresearch%2Fdatarouter%2Fprovisioning%2FInternalServlet.java;fp=datarouter-prov%2Fsrc%2Fmain%2Fjava%2Fcom%2Fatt%2Fresearch%2Fdatarouter%2Fprovisioning%2FInternalServlet.java;h=e50a478ebc301387421b6c5fcd6648f3841b167d;hb=aaf2df8b908fcb48043d2cd51803d8fd99f18b43;hp=0000000000000000000000000000000000000000;hpb=6ec9a9ce6c1062efa99da501fe8c6ea116aebf6f;p=dmaap%2Fdatarouter.git diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/InternalServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/InternalServlet.java new file mode 100644 index 00000000..e50a478e --- /dev/null +++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/InternalServlet.java @@ -0,0 +1,506 @@ +/******************************************************************************* + * ============LICENSE_START================================================== + * * org.onap.dmaap + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ + + +package com.att.research.datarouter.provisioning; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Properties; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONArray; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.att.research.datarouter.provisioning.beans.EventLogRecord; +import com.att.research.datarouter.provisioning.beans.LogRecord; +import com.att.research.datarouter.provisioning.beans.Parameters; +import com.att.research.datarouter.provisioning.eelf.EelfMsgs; +import com.att.research.datarouter.provisioning.utils.DB; +import com.att.research.datarouter.provisioning.utils.RLEBitSet; +import com.att.research.datarouter.provisioning.utils.LogfileLoader; + +/** + *

+ * This servlet handles requests to URLs under /internal on the provisioning server. + * These include: + *

+ *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
URL Path Summary 
URL PathMethodPurpose
/internal/provGETused to GET a full JSON copy of the provisioning data.
/internal/fetchProvGETused to signal to a standby POD that the provisioning data should be fetched from the active POD.
/internal/logsGETused to GET an index of log files and individual logs for this provisioning server.
POSTused to POST log files from the individual nodes to this provisioning server.
/internal/apiGETused to GET an individual parameter value. The parameter name is specified by the path after /api/.
PUTused to set an individual parameter value. The parameter name is specified by the path after /api/.
DELETEused to remove an individual parameter value. The parameter name is specified by the path after /api/.
POSTused to create a new individual parameter value. The parameter name is specified by the path after /api/.
/internal/haltGETused to halt the server (must be accessed from 127.0.0.1).
/internal/drlogsGETused to get a list of DR log entries available for retrieval. + * Note: these are the actual data router log entries sent to the provisioning server + * by the nodes, not the provisioning server's internal logs (access via /internal/logs above). + * The range is returned as a list of record sequence numbers.
POSTused to retrieve specific log entries. + * The sequence numbers of the records to fetch are POST-ed; the records matching the sequence numbers are returned.
/internal/route/**URLs under this path are handled via the {@link com.att.research.datarouter.provisioning.RouteServlet}
+ *
+ *

+ * Authorization to use these URLs is a little different than for other URLs on the provisioning server. + * For the most part, the IP address that the request comes from should be either: + *

+ *
    + *
  1. an IP address of a provisioning server, or
  2. + *
  3. the IP address of a node (to allow access to /internal/prov), or
  4. + *
  5. an IP address from the "special subnet" which is configured with + * the PROV_SPECIAL_SUBNET parameter. + *
+ *

+ * In addition, requests to /internal/halt can ONLY come from localhost (127.0.0.1) on the HTTP port. + *

+ *

+ * All DELETE/GET/PUT/POST requests made to /internal/api on this servlet on the standby server are + * proxied to the active server (using the {@link ProxyServlet}) if it is up and reachable. + *

+ * + * @author Robert Eby + * @version $Id: InternalServlet.java,v 1.23 2014/03/24 18:47:10 eby Exp $ + */ +@SuppressWarnings("serial") +public class InternalServlet extends ProxyServlet { + private static Integer logseq = new Integer(0); // another piece of info to make log spool file names unique + //Adding EELF Logger Rally:US664892 + private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.InternalServlet"); + + /** + * Delete a parameter at the address /internal/api/<parameter>. + * See the Internal API document for details on how this method should be invoked. + */ + @Override + public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException { + setIpAndFqdnForEelf("doDelete"); + eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+""); + EventLogRecord elr = new EventLogRecord(req); + if (!isAuthorizedForInternal(req)) { + elr.setMessage("Unauthorized."); + elr.setResult(HttpServletResponse.SC_FORBIDDEN); + eventlogger.info(elr); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized."); + return; + } + + String path = req.getPathInfo(); + if (path.startsWith("/api/")) { + if (isProxyOK(req) && isProxyServer()) { + super.doDelete(req, resp); + return; + } + String key = path.substring(5); + if (key.length() > 0) { + Parameters param = Parameters.getParameter(key); + if (param != null) { + if (doDelete(param)) { + elr.setResult(HttpServletResponse.SC_OK); + eventlogger.info(elr); + resp.setStatus(HttpServletResponse.SC_OK); + provisioningDataChanged(); + provisioningParametersChanged(); + } else { + // Something went wrong with the DELETE + elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + eventlogger.info(elr); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG); + } + return; + } + } + } + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL."); + } + /** + * Get some information (such as a parameter) underneath the /internal/ namespace. + * See the Internal API document for details on how this method should be invoked. + */ + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + setIpAndFqdnForEelf("doGet"); + eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+""); + String path = req.getPathInfo(); + if (path.equals("/halt") && !req.isSecure()) { + // request to halt the server - can ONLY come from localhost + String remote = req.getRemoteAddr(); + if (remote.equals("127.0.0.1")) { + intlogger.info("PROV0009 Request to HALT received."); + resp.setStatus(HttpServletResponse.SC_OK); + Main.shutdown(); + } else { + intlogger.info("PROV0010 Disallowed request to HALT received from "+remote); + resp.setStatus(HttpServletResponse.SC_FORBIDDEN); + } + return; + } + + EventLogRecord elr = new EventLogRecord(req); + if (!isAuthorizedForInternal(req)) { + elr.setMessage("Unauthorized."); + elr.setResult(HttpServletResponse.SC_FORBIDDEN); + eventlogger.info(elr); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized."); + return; + } + if (path.equals("/fetchProv") && !req.isSecure()) { + // if request came from active_pod or standby_pod and it is not us, reload prov data + SynchronizerTask s = SynchronizerTask.getSynchronizer(); + s.doFetch(); + resp.setStatus(HttpServletResponse.SC_OK); + return; + } + if (path.equals("/prov")) { + if (isProxyOK(req) && isProxyServer()) { + if (super.doGetWithFallback(req, resp)) + return; + // fall back to returning the local data if the remote is unreachable + intlogger.info("Active server unavailable; falling back to local copy."); + } + Poker p = Poker.getPoker(); + resp.setStatus(HttpServletResponse.SC_OK); + resp.setContentType(PROVFULL_CONTENT_TYPE2); + resp.getOutputStream().print(p.getProvisioningString()); + return; + } + if (path.equals("/logs") || path.equals("/logs/")) { + resp.setStatus(HttpServletResponse.SC_OK); + resp.setContentType("application/json"); + resp.getOutputStream().print(generateLogfileList().toString()); + return; + } + if (path.startsWith("/logs/")) { + Properties p = (new DB()).getProperties(); + String logdir = p.getProperty("com.att.research.datarouter.provserver.accesslog.dir"); + String logfile = path.substring(6); + if (logdir != null && logfile != null && logfile.indexOf('/') < 0) { + File log = new File(logdir + "/" + logfile); + if (log.exists() && log.isFile()) { + resp.setStatus(HttpServletResponse.SC_OK); + resp.setContentType("text/plain"); + Path logpath = Paths.get(log.getAbsolutePath()); + Files.copy(logpath, resp.getOutputStream()); + return; + } + } + resp.sendError(HttpServletResponse.SC_NO_CONTENT, "No file."); + return; + } + if (path.startsWith("/api/")) { + if (isProxyOK(req) && isProxyServer()) { + super.doGet(req, resp); + return; + } + String key = path.substring(5); + if (key.length() > 0) { + Parameters param = Parameters.getParameter(key); + if (param != null) { + resp.setStatus(HttpServletResponse.SC_OK); + resp.setContentType("text/plain"); + resp.getOutputStream().print(param.getValue() + "\n"); + return; + } + } + } + if (path.equals("/drlogs") || path.equals("/drlogs/")) { + // Special POD <=> POD API to determine what log file records are loaded here + LogfileLoader lfl = LogfileLoader.getLoader(); + resp.setStatus(HttpServletResponse.SC_OK); + resp.setContentType("text/plain"); + resp.getOutputStream().print(lfl.getBitSet().toString()); + return; + } + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL."); + } + /** + * Modify a parameter at the address /internal/api/<parameter>. + * See the Internal API document for details on how this method should be invoked. + */ + @Override + public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException { + setIpAndFqdnForEelf("doPut"); + eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+""); + EventLogRecord elr = new EventLogRecord(req); + if (!isAuthorizedForInternal(req)) { + elr.setMessage("Unauthorized."); + elr.setResult(HttpServletResponse.SC_FORBIDDEN); + eventlogger.info(elr); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized."); + return; + } + String path = req.getPathInfo(); + if (path.startsWith("/api/")) { + if (isProxyOK(req) && isProxyServer()) { + super.doPut(req, resp); + return; + } + String key = path.substring(5); + if (key.length() > 0) { + Parameters param = Parameters.getParameter(key); + if (param != null) { + String t = catValues(req.getParameterValues("val")); + param.setValue(t); + if (doUpdate(param)) { + elr.setResult(HttpServletResponse.SC_OK); + eventlogger.info(elr); + resp.setStatus(HttpServletResponse.SC_OK); + provisioningDataChanged(); + provisioningParametersChanged(); + } else { + // Something went wrong with the UPDATE + elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + eventlogger.info(elr); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG); + } + return; + } + } + } + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL."); + } + /** + * Create some new information (such as a parameter or log entries) underneath the /internal/ namespace. + * See the Internal API document for details on how this method should be invoked. + */ + @SuppressWarnings("resource") + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + setIpAndFqdnForEelf("doPost"); + eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER)); + EventLogRecord elr = new EventLogRecord(req); + if (!isAuthorizedForInternal(req)) { + elr.setMessage("Unauthorized."); + elr.setResult(HttpServletResponse.SC_FORBIDDEN); + eventlogger.info(elr); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized."); + return; + } + + String path = req.getPathInfo(); + if (path.startsWith("/api/")) { + if (isProxyOK(req) && isProxyServer()) { + super.doPost(req, resp); + return; + } + String key = path.substring(5); + if (key.length() > 0) { + Parameters param = Parameters.getParameter(key); + if (param == null) { + String t = catValues(req.getParameterValues("val")); + param = new Parameters(key, t); + if (doInsert(param)) { + elr.setResult(HttpServletResponse.SC_OK); + eventlogger.info(elr); + resp.setStatus(HttpServletResponse.SC_OK); + provisioningDataChanged(); + provisioningParametersChanged(); + } else { + // Something went wrong with the INSERT + elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + eventlogger.info(elr); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG); + } + return; + } + } + } + + if (path.equals("/logs") || path.equals("/logs/")) { + String ctype = req.getHeader("Content-Type"); + if (ctype == null || !ctype.equals("text/plain")) { + elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + elr.setMessage("Bad media type: "+ctype); + resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + eventlogger.info(elr); + return; + } + String spooldir = (new DB()).getProperties().getProperty("com.att.research.datarouter.provserver.spooldir"); + String spoolname = String.format("%d-%d-", System.currentTimeMillis(), Thread.currentThread().getId()); + synchronized (logseq) { + // perhaps unnecessary, but it helps make the name unique + spoolname += logseq.toString(); + logseq++; + } + String encoding = req.getHeader("Content-Encoding"); + if (encoding != null) { + if (encoding.trim().equals("gzip")) { + spoolname += ".gz"; + } else { + elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + eventlogger.info(elr); + return; + } + } + // Determine space available -- available space must be at least 5% + FileSystem fs = (Paths.get(spooldir)).getFileSystem(); + long total = 0; + long avail = 0; + for (FileStore store: fs.getFileStores()) { + total += store.getTotalSpace(); + avail += store.getUsableSpace(); + } + try { fs.close(); } catch (Exception e) { } + if (((avail * 100) / total) < 5) { + elr.setResult(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + eventlogger.info(elr); + return; + } + Path tmppath = Paths.get(spooldir, spoolname); + Path donepath = Paths.get(spooldir, "IN."+spoolname); + Files.copy(req.getInputStream(), Paths.get(spooldir, spoolname), StandardCopyOption.REPLACE_EXISTING); + Files.move(tmppath, donepath, StandardCopyOption.REPLACE_EXISTING); + elr.setResult(HttpServletResponse.SC_CREATED); + resp.setStatus(HttpServletResponse.SC_CREATED); + eventlogger.info(elr); + LogfileLoader.getLoader(); // This starts the logfile loader "task" + return; + } + + if (path.equals("/drlogs") || path.equals("/drlogs/")) { + // Receive post request and generate log entries + String ctype = req.getHeader("Content-Type"); + if (ctype == null || !ctype.equals("text/plain")) { + elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + elr.setMessage("Bad media type: "+ctype); + resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + eventlogger.info(elr); + return; + } + InputStream is = req.getInputStream(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int ch = 0; + while ((ch = is.read()) >= 0) + bos.write(ch); + RLEBitSet bs = new RLEBitSet(bos.toString()); // The set of records to retrieve + elr.setResult(HttpServletResponse.SC_OK); + resp.setStatus(HttpServletResponse.SC_OK); + resp.setContentType("text/plain"); + LogRecord.printLogRecords(resp.getOutputStream(), bs); + eventlogger.info(elr); + return; + } + + elr.setResult(HttpServletResponse.SC_NOT_FOUND); + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL."); + eventlogger.info(elr); + } + + private String catValues(String[] v) { + StringBuilder sb = new StringBuilder(); + if (v != null) { + String pfx = ""; + for (String s : v) { + sb.append(pfx); + sb.append(s); + pfx = "|"; + } + } + return sb.toString(); + } + private JSONArray generateLogfileList() { + JSONArray ja = new JSONArray(); + Properties p = (new DB()).getProperties(); + String s = p.getProperty("com.att.research.datarouter.provserver.accesslog.dir"); + if (s != null) { + String[] dirs = s.split(","); + for (String dir : dirs) { + File f = new File(dir); + String[] list = f.list(); + if (list != null) { + for (String s2 : list) { + if (!s2.startsWith(".")) + ja.put(s2); + } + } + } + } + return ja; + } +}