Update project structure to org.onap
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / RouteServlet.java
diff --git a/datarouter-prov/src/main/java/org/onap/dmaap/datarouter/provisioning/RouteServlet.java b/datarouter-prov/src/main/java/org/onap/dmaap/datarouter/provisioning/RouteServlet.java
new file mode 100644 (file)
index 0000000..ab7aff8
--- /dev/null
@@ -0,0 +1,428 @@
+/*******************************************************************************\r
+ * ============LICENSE_START==================================================\r
+ * * org.onap.dmaap\r
+ * * ===========================================================================\r
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
+ * * ===========================================================================\r
+ * * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * * you may not use this file except in compliance with the License.\r
+ * * You may obtain a copy of the License at\r
+ * * \r
+ *  *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * * \r
+ *  * Unless required by applicable law or agreed to in writing, software\r
+ * * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * * See the License for the specific language governing permissions and\r
+ * * limitations under the License.\r
+ * * ============LICENSE_END====================================================\r
+ * *\r
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
+ * *\r
+ ******************************************************************************/\r
+\r
+\r
+package org.onap.dmaap.datarouter.provisioning;\r
+\r
+import java.io.IOException;\r
+import java.util.Set;\r
+\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.json.JSONObject;\r
+import org.onap.dmaap.datarouter.provisioning.beans.Deleteable;\r
+import org.onap.dmaap.datarouter.provisioning.beans.EgressRoute;\r
+import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;\r
+import org.onap.dmaap.datarouter.provisioning.beans.IngressRoute;\r
+import org.onap.dmaap.datarouter.provisioning.beans.Insertable;\r
+import org.onap.dmaap.datarouter.provisioning.beans.NetworkRoute;\r
+import org.onap.dmaap.datarouter.provisioning.beans.NodeClass;\r
+\r
+/**\r
+ * <p>\r
+ * This servlet handles requests to URLs under /internal/route/ on the provisioning server.\r
+ * This part of the URL tree is used to manipulate the Data Router routing tables.\r
+ * These include:\r
+ * </p>\r
+ * <div class="contentContainer">\r
+ * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">\r
+ * <caption><span>URL Path Summary</span><span class="tabEnd">&nbsp;</span></caption>\r
+ * <tr>\r
+ *   <th class="colFirst" width="35%">URL Path</th>\r
+ *   <th class="colOne">Method</th>\r
+ *   <th class="colLast">Purpose</th>\r
+ * </tr>\r
+ * <tr class="altColor">\r
+ *   <td class="colFirst">/internal/route/</td>\r
+ *   <td class="colOne">GET</td>\r
+ *   <td class="colLast">used to GET a full JSON copy of all three routing tables.</td>\r
+ * </tr>\r
+ * <tr class="rowColor">\r
+ *   <td class="colFirst" rowspan="2">/internal/route/ingress/</td>\r
+ *   <td class="colOne">GET</td>\r
+ *   <td class="colLast">used to GET a full JSON copy of the ingress routing table (IRT).</td>\r
+ * </tr>\r
+ * <tr class="rowColor">\r
+ *   <td class="colOne">POST</td>\r
+ *   <td class="colLast">used to create a new entry in the ingress routing table (IRT).</td></tr>\r
+ * <tr class="altColor">\r
+ *   <td class="colFirst" rowspan="2">/internal/route/egress/</td>\r
+ *   <td class="colOne">GET</td>\r
+ *   <td class="colLast">used to GET a full JSON copy of the egress routing table (ERT).</td>\r
+ * </tr>\r
+ * <tr class="altColor">\r
+ *   <td class="colOne">POST</td>\r
+ *   <td class="colLast">used to create a new entry in the egress routing table (ERT).</td></tr>\r
+ * <tr class="rowColor">\r
+ *   <td class="colFirst" rowspan="2">/internal/route/network/</td>\r
+ *   <td class="colOne">GET</td>\r
+ *   <td class="colLast">used to GET a full JSON copy of the network routing table (NRT).</td>\r
+ * </tr>\r
+ * <tr class="rowColor">\r
+ *   <td class="colOne">POST</td>\r
+ *   <td class="colLast">used to create a new entry in the network routing table (NRT).</td>\r
+ * </tr>\r
+ * <tr class="altColor">\r
+ *   <td class="colFirst">/internal/route/ingress/&lt;feed&gt;/&lt;user&gt;/&lt;subnet&gt;</td>\r
+ *   <td class="colOne">DELETE</td>\r
+ *   <td class="colLast">used to DELETE the ingress route corresponding to <i>feed</i>, <i>user</i> and <i>subnet</i>.\r
+ *   The / in the subnet specified should be replaced with a !, since / cannot be used in a URL.</td>\r
+ * </tr>\r
+ * <tr class="rowColor">\r
+ *   <td class="colFirst">/internal/route/ingress/&lt;seq&gt;</td>\r
+ *   <td class="colOne">DELETE</td>\r
+ *   <td class="colLast">used to DELETE all ingress routes with the matching <i>seq</i> sequence number.</td>\r
+ * </tr>\r
+ * <tr class="altColor">\r
+ *   <td class="colFirst">/internal/route/egress/&lt;sub&gt;</td>\r
+ *   <td class="colOne">DELETE</td>\r
+ *   <td class="colLast">used to DELETE the egress route the matching <i>sub</i> subscriber number.</td>\r
+ * </tr>\r
+ * <tr class="rowColor">\r
+ *   <td class="colFirst">/internal/route/network/&lt;fromnode&gt;/&lt;tonode&gt;</td>\r
+ *   <td class="colOne">DELETE</td>\r
+ *   <td class="colLast">used to DELETE the network route corresponding to <i>fromnode</i>\r
+ *   and <i>tonode</i>.</td>\r
+ * </tr>\r
+ * </table>\r
+ * <p>\r
+ * Authorization to use these URLs is a little different than for other URLs on the provisioning server.\r
+ * For the most part, the IP address that the request comes from should be either:\r
+ * </p>\r
+ * <ol>\r
+ * <li>an IP address of a provisioning server, or</li>\r
+ * <li>the IP address of a node, or</li>\r
+ * <li>an IP address from the "<i>special subnet</i>" which is configured with\r
+ * the PROV_SPECIAL_SUBNET parameter.\r
+ * </ol>\r
+ * <p>\r
+ * All DELETE/GET/POST requests made to this servlet on the standby server are proxied to the\r
+ * active server (using the {@link ProxyServlet}) if it is up and reachable.\r
+ * </p>\r
+ *\r
+ * @author Robert Eby\r
+ * @version $Id$\r
+ */\r
+@SuppressWarnings("serial")\r
+public class RouteServlet extends ProxyServlet {\r
+       /**\r
+        * DELETE route table entries by deleting part of the route table tree.\r
+        */\r
+       @Override\r
+       public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               EventLogRecord elr = new EventLogRecord(req);\r
+               if (!isAuthorizedForInternal(req)) {\r
+                       elr.setMessage("Unauthorized.");\r
+                       elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
+                       eventlogger.info(elr);\r
+                       resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
+                       return;\r
+               }\r
+               if (isProxyOK(req) && isProxyServer()) {\r
+                       super.doDelete(req, resp);\r
+                       return;\r
+               }\r
+\r
+               String path = req.getPathInfo();\r
+               String[] parts = path.substring(1).split("/");\r
+               Deleteable[] d = null;\r
+               if (parts[0].equals("ingress")) {\r
+                       if (parts.length == 4) {\r
+                               // /internal/route/ingress/<feed>/<user>/<subnet>\r
+                               try {\r
+                                       int feedid = Integer.parseInt(parts[1]);\r
+                                       IngressRoute er = IngressRoute.getIngressRoute(feedid, parts[2], parts[3].replaceAll("!", "/"));\r
+                                       if (er == null) {\r
+                                               resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified ingress route does not exist.");\r
+                                               return;\r
+                                       }\r
+                                       d = new Deleteable[] { er };\r
+                               } catch (NumberFormatException e) {\r
+                                       resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid feed ID in 'delete ingress' command.");\r
+                                       return;\r
+                               }\r
+                       } else if (parts.length == 2) {\r
+                               // /internal/route/ingress/<seq>\r
+                               try {\r
+                                       int seq = Integer.parseInt(parts[1]);\r
+                                       Set<IngressRoute> set = IngressRoute.getIngressRoutesForSeq(seq);\r
+                                       d = set.toArray(new Deleteable[0]);\r
+                               } catch (NumberFormatException e) {\r
+                                       resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid sequence number in 'delete ingress' command.");\r
+                                       return;\r
+                               }\r
+                       } else {\r
+                               resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete ingress' command.");\r
+                               return;\r
+                       }\r
+               } else if (parts[0].equals("egress")) {\r
+                       if (parts.length == 2) {\r
+                               // /internal/route/egress/<sub>\r
+                               try {\r
+                                       int subid = Integer.parseInt(parts[1]);\r
+                                       EgressRoute er = EgressRoute.getEgressRoute(subid);\r
+                                       if (er == null) {\r
+                                               resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified egress route does not exist.");\r
+                                               return;\r
+                                       }\r
+                                       d = new Deleteable[] { er };\r
+                               } catch (NumberFormatException e) {\r
+                                       resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid sub ID in 'delete egress' command.");\r
+                                       return;\r
+                               }\r
+                       } else {\r
+                               resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete egress' command.");\r
+                               return;\r
+                       }\r
+               } else if (parts[0].equals("network")) {\r
+                       if (parts.length == 3) {\r
+                               // /internal/route/network/<from>/<to>\r
+                               try {//\r
+                                       NetworkRoute nr = new NetworkRoute(\r
+                                               NodeClass.normalizeNodename(parts[1]),\r
+                                               NodeClass.normalizeNodename(parts[2])\r
+                                       );\r
+                                       d = new Deleteable[] { nr };\r
+                               } catch (IllegalArgumentException e) {\r
+                                       resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified network route does not exist.");\r
+                                       return;\r
+                               }\r
+                       } else {\r
+                               resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete network' command.");\r
+                               return;\r
+                       }\r
+               }\r
+               if (d == null) {\r
+                       resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
+                       return;\r
+               }\r
+               boolean rv = true;\r
+               for (Deleteable dd : d) {\r
+                       rv &= doDelete(dd);\r
+               }\r
+               if (rv) {\r
+                       elr.setResult(HttpServletResponse.SC_OK);\r
+                       eventlogger.info(elr);\r
+                       resp.setStatus(HttpServletResponse.SC_OK);\r
+                       provisioningDataChanged();\r
+                       provisioningParametersChanged();\r
+               } else {\r
+                       // Something went wrong with the DELETE\r
+                       elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
+                       eventlogger.info(elr);\r
+                       resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);\r
+               }\r
+       }\r
+       /**\r
+        * GET route table entries from the route table tree specified by the URL path.\r
+        */\r
+       @Override\r
+       public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               EventLogRecord elr = new EventLogRecord(req);\r
+               if (!isAuthorizedForInternal(req)) {\r
+                       elr.setMessage("Unauthorized.");\r
+                       elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
+                       eventlogger.info(elr);\r
+                       resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
+                       return;\r
+               }\r
+               if (isProxyOK(req) && isProxyServer()) {\r
+                       super.doGet(req, resp);\r
+                       return;\r
+               }\r
+\r
+               String path = req.getPathInfo();\r
+               if (!path.endsWith("/"))\r
+                       path += "/";\r
+               if (!path.equals("/") && !path.equals("/ingress/") && !path.equals("/egress/") && !path.equals("/network/")) {\r
+                       resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
+                       return;\r
+               }\r
+\r
+               StringBuilder sb = new StringBuilder("{\n");\r
+               String px2 = "";\r
+               if (path.equals("/") || path.equals("/ingress/")) {\r
+                       String pfx = "\n";\r
+                       sb.append("\"ingress\": [");\r
+                       for (IngressRoute in : IngressRoute.getAllIngressRoutes()) {\r
+                               sb.append(pfx);\r
+                               sb.append(in.asJSONObject().toString());\r
+                               pfx = ",\n";\r
+                       }\r
+                       sb.append("\n]");\r
+                       px2 = ",\n";\r
+               }\r
+\r
+               if (path.equals("/") || path.equals("/egress/")) {\r
+                       String pfx = "\n";\r
+                       sb.append(px2);\r
+                       sb.append("\"egress\": {");\r
+                       for (EgressRoute eg : EgressRoute.getAllEgressRoutes()) {\r
+                               JSONObject jx = eg.asJSONObject();\r
+                               for (String key : jx.keySet()) {\r
+                                       sb.append(pfx);\r
+                                       sb.append("  \"").append(key).append("\": ");\r
+                                       sb.append("\"").append(jx.getString(key)).append("\"");\r
+                                       pfx = ",\n";\r
+                               }\r
+                       }\r
+                       sb.append("\n}");\r
+                       px2 = ",\n";\r
+               }\r
+\r
+               if (path.equals("/") || path.equals("/network/")) {\r
+                       String pfx = "\n";\r
+                       sb.append(px2);\r
+                       sb.append("\"routing\": [");\r
+                       for (NetworkRoute ne : NetworkRoute.getAllNetworkRoutes()) {\r
+                               sb.append(pfx);\r
+                               sb.append(ne.asJSONObject().toString());\r
+                               pfx = ",\n";\r
+                       }\r
+                       sb.append("\n]");\r
+               }\r
+               sb.append("}\n");\r
+               resp.setStatus(HttpServletResponse.SC_OK);\r
+               resp.setContentType("application/json");\r
+               resp.getOutputStream().print(sb.toString());\r
+       }\r
+       /**\r
+        * PUT on &lt;/internal/route/*&gt; -- not supported.\r
+        */\r
+       @Override\r
+       public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               EventLogRecord elr = new EventLogRecord(req);\r
+               if (!isAuthorizedForInternal(req)) {\r
+                       elr.setMessage("Unauthorized.");\r
+                       elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
+                       eventlogger.info(elr);\r
+                       resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
+                       return;\r
+               }\r
+               resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
+       }\r
+       /**\r
+        * POST - modify existing route table entries in the route table tree specified by the URL path.\r
+        */\r
+       @Override\r
+       public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               EventLogRecord elr = new EventLogRecord(req);\r
+               if (!isAuthorizedForInternal(req)) {\r
+                       elr.setMessage("Unauthorized.");\r
+                       elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
+                       eventlogger.info(elr);\r
+                       resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
+                       return;\r
+               }\r
+               if (isProxyOK(req) && isProxyServer()) {\r
+                       super.doPost(req, resp);\r
+                       return;\r
+               }\r
+               String path = req.getPathInfo();\r
+               Insertable[] ins = null;\r
+               if (path.startsWith("/ingress/")) {\r
+                       // /internal/route/ingress/?feed=%s&amp;user=%s&amp;subnet=%s&amp;nodepatt=%s\r
+                       try {\r
+                               // Although it probably doesn't make sense, you can install two identical routes in the IRT\r
+                               int feedid = Integer.parseInt(req.getParameter("feed"));\r
+                               String user = req.getParameter("user");\r
+                               if (user == null)\r
+                                       user = "-";\r
+                               String subnet = req.getParameter("subnet");\r
+                               if (subnet == null)\r
+                                       subnet = "-";\r
+                               String nodepatt = req.getParameter("nodepatt");\r
+                               String t = req.getParameter("seq");\r
+                               int seq = (t != null) ? Integer.parseInt(t) : (IngressRoute.getMaxSequence() + 100);\r
+                               ins = new Insertable[] { new IngressRoute(seq, feedid, user, subnet, NodeClass.lookupNodeNames(nodepatt)) };\r
+                       } catch (Exception e) {\r
+                               intlogger.info(e);\r
+                               resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add ingress' command.");\r
+                               return;\r
+                       }\r
+               } else if (path.startsWith("/egress/")) {\r
+                       // /internal/route/egress/?sub=%s&amp;node=%s\r
+                       try {\r
+                               int subid = Integer.parseInt(req.getParameter("sub"));\r
+                               EgressRoute er = EgressRoute.getEgressRoute(subid);\r
+                               if (er != null) {\r
+                                       resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "An egress route already exists for that subscriber.");\r
+                                       return;\r
+                               }\r
+                               String node = NodeClass.normalizeNodename(req.getParameter("node"));\r
+                               ins = new Insertable[] { new EgressRoute(subid, node) };\r
+                       } catch (Exception e) {\r
+                               intlogger.info(e);\r
+                               resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add egress' command.");\r
+                               return;\r
+                       }\r
+               } else if (path.startsWith("/network/")) {\r
+                       // /internal/route/network/?from=%s&amp;to=%s&amp;via=%s\r
+                       try {\r
+                               String nfrom = req.getParameter("from");\r
+                               String nto   = req.getParameter("to");\r
+                               String nvia  = req.getParameter("via");\r
+                               if (nfrom == null || nto == null || nvia == null) {\r
+                                       resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing arguments in 'add network' command.");\r
+                                       return;\r
+                               }\r
+                               nfrom = NodeClass.normalizeNodename(nfrom);\r
+                               nto   = NodeClass.normalizeNodename(nto);\r
+                               nvia  = NodeClass.normalizeNodename(nvia);\r
+                               NetworkRoute nr = new NetworkRoute(nfrom, nto, nvia);\r
+                               for (NetworkRoute route : NetworkRoute.getAllNetworkRoutes()) {\r
+                                       if (route.getFromnode() == nr.getFromnode() && route.getTonode() == nr.getTonode()) {\r
+                                               resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Network route table already contains a route for "+nfrom+" and "+nto);\r
+                                               return;\r
+                                       }\r
+                               }\r
+                               ins = new Insertable[] { nr };\r
+                       } catch (IllegalArgumentException e) {\r
+                               intlogger.info(e);\r
+                               resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add network' command.");\r
+                               return;\r
+                       }\r
+               }\r
+               if (ins == null) {\r
+                       resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
+                       return;\r
+               }\r
+               boolean rv = true;\r
+               for (Insertable dd : ins) {\r
+                       rv &= doInsert(dd);\r
+               }\r
+               if (rv) {\r
+                       elr.setResult(HttpServletResponse.SC_OK);\r
+                       eventlogger.info(elr);\r
+                       resp.setStatus(HttpServletResponse.SC_OK);\r
+                       provisioningDataChanged();\r
+                       provisioningParametersChanged();\r
+               } else {\r
+                       // Something went wrong with the INSERT\r
+                       elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
+                       eventlogger.info(elr);\r
+                       resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);\r
+               }\r
+       }\r
+}\r