Update project structure to org.onap
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / RouteServlet.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 \r
24 \r
25 package org.onap.dmaap.datarouter.provisioning;\r
26 \r
27 import java.io.IOException;\r
28 import java.util.Set;\r
29 \r
30 import javax.servlet.http.HttpServletRequest;\r
31 import javax.servlet.http.HttpServletResponse;\r
32 \r
33 import org.json.JSONObject;\r
34 import org.onap.dmaap.datarouter.provisioning.beans.Deleteable;\r
35 import org.onap.dmaap.datarouter.provisioning.beans.EgressRoute;\r
36 import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;\r
37 import org.onap.dmaap.datarouter.provisioning.beans.IngressRoute;\r
38 import org.onap.dmaap.datarouter.provisioning.beans.Insertable;\r
39 import org.onap.dmaap.datarouter.provisioning.beans.NetworkRoute;\r
40 import org.onap.dmaap.datarouter.provisioning.beans.NodeClass;\r
41 \r
42 /**\r
43  * <p>\r
44  * This servlet handles requests to URLs under /internal/route/ on the provisioning server.\r
45  * This part of the URL tree is used to manipulate the Data Router routing tables.\r
46  * These include:\r
47  * </p>\r
48  * <div class="contentContainer">\r
49  * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">\r
50  * <caption><span>URL Path Summary</span><span class="tabEnd">&nbsp;</span></caption>\r
51  * <tr>\r
52  *   <th class="colFirst" width="35%">URL Path</th>\r
53  *   <th class="colOne">Method</th>\r
54  *   <th class="colLast">Purpose</th>\r
55  * </tr>\r
56  * <tr class="altColor">\r
57  *   <td class="colFirst">/internal/route/</td>\r
58  *   <td class="colOne">GET</td>\r
59  *   <td class="colLast">used to GET a full JSON copy of all three routing tables.</td>\r
60  * </tr>\r
61  * <tr class="rowColor">\r
62  *   <td class="colFirst" rowspan="2">/internal/route/ingress/</td>\r
63  *   <td class="colOne">GET</td>\r
64  *   <td class="colLast">used to GET a full JSON copy of the ingress routing table (IRT).</td>\r
65  * </tr>\r
66  * <tr class="rowColor">\r
67  *   <td class="colOne">POST</td>\r
68  *   <td class="colLast">used to create a new entry in the ingress routing table (IRT).</td></tr>\r
69  * <tr class="altColor">\r
70  *   <td class="colFirst" rowspan="2">/internal/route/egress/</td>\r
71  *   <td class="colOne">GET</td>\r
72  *   <td class="colLast">used to GET a full JSON copy of the egress routing table (ERT).</td>\r
73  * </tr>\r
74  * <tr class="altColor">\r
75  *   <td class="colOne">POST</td>\r
76  *   <td class="colLast">used to create a new entry in the egress routing table (ERT).</td></tr>\r
77  * <tr class="rowColor">\r
78  *   <td class="colFirst" rowspan="2">/internal/route/network/</td>\r
79  *   <td class="colOne">GET</td>\r
80  *   <td class="colLast">used to GET a full JSON copy of the network routing table (NRT).</td>\r
81  * </tr>\r
82  * <tr class="rowColor">\r
83  *   <td class="colOne">POST</td>\r
84  *   <td class="colLast">used to create a new entry in the network routing table (NRT).</td>\r
85  * </tr>\r
86  * <tr class="altColor">\r
87  *   <td class="colFirst">/internal/route/ingress/&lt;feed&gt;/&lt;user&gt;/&lt;subnet&gt;</td>\r
88  *   <td class="colOne">DELETE</td>\r
89  *   <td class="colLast">used to DELETE the ingress route corresponding to <i>feed</i>, <i>user</i> and <i>subnet</i>.\r
90  *   The / in the subnet specified should be replaced with a !, since / cannot be used in a URL.</td>\r
91  * </tr>\r
92  * <tr class="rowColor">\r
93  *   <td class="colFirst">/internal/route/ingress/&lt;seq&gt;</td>\r
94  *   <td class="colOne">DELETE</td>\r
95  *   <td class="colLast">used to DELETE all ingress routes with the matching <i>seq</i> sequence number.</td>\r
96  * </tr>\r
97  * <tr class="altColor">\r
98  *   <td class="colFirst">/internal/route/egress/&lt;sub&gt;</td>\r
99  *   <td class="colOne">DELETE</td>\r
100  *   <td class="colLast">used to DELETE the egress route the matching <i>sub</i> subscriber number.</td>\r
101  * </tr>\r
102  * <tr class="rowColor">\r
103  *   <td class="colFirst">/internal/route/network/&lt;fromnode&gt;/&lt;tonode&gt;</td>\r
104  *   <td class="colOne">DELETE</td>\r
105  *   <td class="colLast">used to DELETE the network route corresponding to <i>fromnode</i>\r
106  *   and <i>tonode</i>.</td>\r
107  * </tr>\r
108  * </table>\r
109  * <p>\r
110  * Authorization to use these URLs is a little different than for other URLs on the provisioning server.\r
111  * For the most part, the IP address that the request comes from should be either:\r
112  * </p>\r
113  * <ol>\r
114  * <li>an IP address of a provisioning server, or</li>\r
115  * <li>the IP address of a node, or</li>\r
116  * <li>an IP address from the "<i>special subnet</i>" which is configured with\r
117  * the PROV_SPECIAL_SUBNET parameter.\r
118  * </ol>\r
119  * <p>\r
120  * All DELETE/GET/POST requests made to this servlet on the standby server are proxied to the\r
121  * active server (using the {@link ProxyServlet}) if it is up and reachable.\r
122  * </p>\r
123  *\r
124  * @author Robert Eby\r
125  * @version $Id$\r
126  */\r
127 @SuppressWarnings("serial")\r
128 public class RouteServlet extends ProxyServlet {\r
129         /**\r
130          * DELETE route table entries by deleting part of the route table tree.\r
131          */\r
132         @Override\r
133         public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
134                 EventLogRecord elr = new EventLogRecord(req);\r
135                 if (!isAuthorizedForInternal(req)) {\r
136                         elr.setMessage("Unauthorized.");\r
137                         elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
138                         eventlogger.info(elr);\r
139                         resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
140                         return;\r
141                 }\r
142                 if (isProxyOK(req) && isProxyServer()) {\r
143                         super.doDelete(req, resp);\r
144                         return;\r
145                 }\r
146 \r
147                 String path = req.getPathInfo();\r
148                 String[] parts = path.substring(1).split("/");\r
149                 Deleteable[] d = null;\r
150                 if (parts[0].equals("ingress")) {\r
151                         if (parts.length == 4) {\r
152                                 // /internal/route/ingress/<feed>/<user>/<subnet>\r
153                                 try {\r
154                                         int feedid = Integer.parseInt(parts[1]);\r
155                                         IngressRoute er = IngressRoute.getIngressRoute(feedid, parts[2], parts[3].replaceAll("!", "/"));\r
156                                         if (er == null) {\r
157                                                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified ingress route does not exist.");\r
158                                                 return;\r
159                                         }\r
160                                         d = new Deleteable[] { er };\r
161                                 } catch (NumberFormatException e) {\r
162                                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid feed ID in 'delete ingress' command.");\r
163                                         return;\r
164                                 }\r
165                         } else if (parts.length == 2) {\r
166                                 // /internal/route/ingress/<seq>\r
167                                 try {\r
168                                         int seq = Integer.parseInt(parts[1]);\r
169                                         Set<IngressRoute> set = IngressRoute.getIngressRoutesForSeq(seq);\r
170                                         d = set.toArray(new Deleteable[0]);\r
171                                 } catch (NumberFormatException e) {\r
172                                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid sequence number in 'delete ingress' command.");\r
173                                         return;\r
174                                 }\r
175                         } else {\r
176                                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete ingress' command.");\r
177                                 return;\r
178                         }\r
179                 } else if (parts[0].equals("egress")) {\r
180                         if (parts.length == 2) {\r
181                                 // /internal/route/egress/<sub>\r
182                                 try {\r
183                                         int subid = Integer.parseInt(parts[1]);\r
184                                         EgressRoute er = EgressRoute.getEgressRoute(subid);\r
185                                         if (er == null) {\r
186                                                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified egress route does not exist.");\r
187                                                 return;\r
188                                         }\r
189                                         d = new Deleteable[] { er };\r
190                                 } catch (NumberFormatException e) {\r
191                                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid sub ID in 'delete egress' command.");\r
192                                         return;\r
193                                 }\r
194                         } else {\r
195                                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete egress' command.");\r
196                                 return;\r
197                         }\r
198                 } else if (parts[0].equals("network")) {\r
199                         if (parts.length == 3) {\r
200                                 // /internal/route/network/<from>/<to>\r
201                                 try {//\r
202                                         NetworkRoute nr = new NetworkRoute(\r
203                                                 NodeClass.normalizeNodename(parts[1]),\r
204                                                 NodeClass.normalizeNodename(parts[2])\r
205                                         );\r
206                                         d = new Deleteable[] { nr };\r
207                                 } catch (IllegalArgumentException e) {\r
208                                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified network route does not exist.");\r
209                                         return;\r
210                                 }\r
211                         } else {\r
212                                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete network' command.");\r
213                                 return;\r
214                         }\r
215                 }\r
216                 if (d == null) {\r
217                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
218                         return;\r
219                 }\r
220                 boolean rv = true;\r
221                 for (Deleteable dd : d) {\r
222                         rv &= doDelete(dd);\r
223                 }\r
224                 if (rv) {\r
225                         elr.setResult(HttpServletResponse.SC_OK);\r
226                         eventlogger.info(elr);\r
227                         resp.setStatus(HttpServletResponse.SC_OK);\r
228                         provisioningDataChanged();\r
229                         provisioningParametersChanged();\r
230                 } else {\r
231                         // Something went wrong with the DELETE\r
232                         elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
233                         eventlogger.info(elr);\r
234                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);\r
235                 }\r
236         }\r
237         /**\r
238          * GET route table entries from the route table tree specified by the URL path.\r
239          */\r
240         @Override\r
241         public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
242                 EventLogRecord elr = new EventLogRecord(req);\r
243                 if (!isAuthorizedForInternal(req)) {\r
244                         elr.setMessage("Unauthorized.");\r
245                         elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
246                         eventlogger.info(elr);\r
247                         resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
248                         return;\r
249                 }\r
250                 if (isProxyOK(req) && isProxyServer()) {\r
251                         super.doGet(req, resp);\r
252                         return;\r
253                 }\r
254 \r
255                 String path = req.getPathInfo();\r
256                 if (!path.endsWith("/"))\r
257                         path += "/";\r
258                 if (!path.equals("/") && !path.equals("/ingress/") && !path.equals("/egress/") && !path.equals("/network/")) {\r
259                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
260                         return;\r
261                 }\r
262 \r
263                 StringBuilder sb = new StringBuilder("{\n");\r
264                 String px2 = "";\r
265                 if (path.equals("/") || path.equals("/ingress/")) {\r
266                         String pfx = "\n";\r
267                         sb.append("\"ingress\": [");\r
268                         for (IngressRoute in : IngressRoute.getAllIngressRoutes()) {\r
269                                 sb.append(pfx);\r
270                                 sb.append(in.asJSONObject().toString());\r
271                                 pfx = ",\n";\r
272                         }\r
273                         sb.append("\n]");\r
274                         px2 = ",\n";\r
275                 }\r
276 \r
277                 if (path.equals("/") || path.equals("/egress/")) {\r
278                         String pfx = "\n";\r
279                         sb.append(px2);\r
280                         sb.append("\"egress\": {");\r
281                         for (EgressRoute eg : EgressRoute.getAllEgressRoutes()) {\r
282                                 JSONObject jx = eg.asJSONObject();\r
283                                 for (String key : jx.keySet()) {\r
284                                         sb.append(pfx);\r
285                                         sb.append("  \"").append(key).append("\": ");\r
286                                         sb.append("\"").append(jx.getString(key)).append("\"");\r
287                                         pfx = ",\n";\r
288                                 }\r
289                         }\r
290                         sb.append("\n}");\r
291                         px2 = ",\n";\r
292                 }\r
293 \r
294                 if (path.equals("/") || path.equals("/network/")) {\r
295                         String pfx = "\n";\r
296                         sb.append(px2);\r
297                         sb.append("\"routing\": [");\r
298                         for (NetworkRoute ne : NetworkRoute.getAllNetworkRoutes()) {\r
299                                 sb.append(pfx);\r
300                                 sb.append(ne.asJSONObject().toString());\r
301                                 pfx = ",\n";\r
302                         }\r
303                         sb.append("\n]");\r
304                 }\r
305                 sb.append("}\n");\r
306                 resp.setStatus(HttpServletResponse.SC_OK);\r
307                 resp.setContentType("application/json");\r
308                 resp.getOutputStream().print(sb.toString());\r
309         }\r
310         /**\r
311          * PUT on &lt;/internal/route/*&gt; -- not supported.\r
312          */\r
313         @Override\r
314         public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
315                 EventLogRecord elr = new EventLogRecord(req);\r
316                 if (!isAuthorizedForInternal(req)) {\r
317                         elr.setMessage("Unauthorized.");\r
318                         elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
319                         eventlogger.info(elr);\r
320                         resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
321                         return;\r
322                 }\r
323                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
324         }\r
325         /**\r
326          * POST - modify existing route table entries in the route table tree specified by the URL path.\r
327          */\r
328         @Override\r
329         public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
330                 EventLogRecord elr = new EventLogRecord(req);\r
331                 if (!isAuthorizedForInternal(req)) {\r
332                         elr.setMessage("Unauthorized.");\r
333                         elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
334                         eventlogger.info(elr);\r
335                         resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");\r
336                         return;\r
337                 }\r
338                 if (isProxyOK(req) && isProxyServer()) {\r
339                         super.doPost(req, resp);\r
340                         return;\r
341                 }\r
342                 String path = req.getPathInfo();\r
343                 Insertable[] ins = null;\r
344                 if (path.startsWith("/ingress/")) {\r
345                         // /internal/route/ingress/?feed=%s&amp;user=%s&amp;subnet=%s&amp;nodepatt=%s\r
346                         try {\r
347                                 // Although it probably doesn't make sense, you can install two identical routes in the IRT\r
348                                 int feedid = Integer.parseInt(req.getParameter("feed"));\r
349                                 String user = req.getParameter("user");\r
350                                 if (user == null)\r
351                                         user = "-";\r
352                                 String subnet = req.getParameter("subnet");\r
353                                 if (subnet == null)\r
354                                         subnet = "-";\r
355                                 String nodepatt = req.getParameter("nodepatt");\r
356                                 String t = req.getParameter("seq");\r
357                                 int seq = (t != null) ? Integer.parseInt(t) : (IngressRoute.getMaxSequence() + 100);\r
358                                 ins = new Insertable[] { new IngressRoute(seq, feedid, user, subnet, NodeClass.lookupNodeNames(nodepatt)) };\r
359                         } catch (Exception e) {\r
360                                 intlogger.info(e);\r
361                                 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add ingress' command.");\r
362                                 return;\r
363                         }\r
364                 } else if (path.startsWith("/egress/")) {\r
365                         // /internal/route/egress/?sub=%s&amp;node=%s\r
366                         try {\r
367                                 int subid = Integer.parseInt(req.getParameter("sub"));\r
368                                 EgressRoute er = EgressRoute.getEgressRoute(subid);\r
369                                 if (er != null) {\r
370                                         resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "An egress route already exists for that subscriber.");\r
371                                         return;\r
372                                 }\r
373                                 String node = NodeClass.normalizeNodename(req.getParameter("node"));\r
374                                 ins = new Insertable[] { new EgressRoute(subid, node) };\r
375                         } catch (Exception e) {\r
376                                 intlogger.info(e);\r
377                                 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add egress' command.");\r
378                                 return;\r
379                         }\r
380                 } else if (path.startsWith("/network/")) {\r
381                         // /internal/route/network/?from=%s&amp;to=%s&amp;via=%s\r
382                         try {\r
383                                 String nfrom = req.getParameter("from");\r
384                                 String nto   = req.getParameter("to");\r
385                                 String nvia  = req.getParameter("via");\r
386                                 if (nfrom == null || nto == null || nvia == null) {\r
387                                         resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing arguments in 'add network' command.");\r
388                                         return;\r
389                                 }\r
390                                 nfrom = NodeClass.normalizeNodename(nfrom);\r
391                                 nto   = NodeClass.normalizeNodename(nto);\r
392                                 nvia  = NodeClass.normalizeNodename(nvia);\r
393                                 NetworkRoute nr = new NetworkRoute(nfrom, nto, nvia);\r
394                                 for (NetworkRoute route : NetworkRoute.getAllNetworkRoutes()) {\r
395                                         if (route.getFromnode() == nr.getFromnode() && route.getTonode() == nr.getTonode()) {\r
396                                                 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Network route table already contains a route for "+nfrom+" and "+nto);\r
397                                                 return;\r
398                                         }\r
399                                 }\r
400                                 ins = new Insertable[] { nr };\r
401                         } catch (IllegalArgumentException e) {\r
402                                 intlogger.info(e);\r
403                                 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add network' command.");\r
404                                 return;\r
405                         }\r
406                 }\r
407                 if (ins == null) {\r
408                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");\r
409                         return;\r
410                 }\r
411                 boolean rv = true;\r
412                 for (Insertable dd : ins) {\r
413                         rv &= doInsert(dd);\r
414                 }\r
415                 if (rv) {\r
416                         elr.setResult(HttpServletResponse.SC_OK);\r
417                         eventlogger.info(elr);\r
418                         resp.setStatus(HttpServletResponse.SC_OK);\r
419                         provisioningDataChanged();\r
420                         provisioningParametersChanged();\r
421                 } else {\r
422                         // Something went wrong with the INSERT\r
423                         elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
424                         eventlogger.info(elr);\r
425                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);\r
426                 }\r
427         }\r
428 }\r