1 /*******************************************************************************
\r
2 * ============LICENSE_START==================================================
\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
11 * * http://www.apache.org/licenses/LICENSE-2.0
\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
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
25 package org.onap.dmaap.datarouter.provisioning;
\r
27 import static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;
\r
29 import java.io.IOException;
\r
30 import java.util.Set;
\r
31 import javax.servlet.http.HttpServletRequest;
\r
32 import javax.servlet.http.HttpServletResponse;
\r
33 import org.json.JSONException;
\r
34 import org.json.JSONObject;
\r
36 import org.onap.dmaap.datarouter.provisioning.beans.Deleteable;
\r
37 import org.onap.dmaap.datarouter.provisioning.beans.EgressRoute;
\r
38 import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;
\r
39 import org.onap.dmaap.datarouter.provisioning.beans.IngressRoute;
\r
40 import org.onap.dmaap.datarouter.provisioning.beans.Insertable;
\r
41 import org.onap.dmaap.datarouter.provisioning.beans.NetworkRoute;
\r
42 import org.onap.dmaap.datarouter.provisioning.beans.NodeClass;
\r
48 * This servlet handles requests to URLs under /internal/route/ on the provisioning server.
\r
49 * This part of the URL tree is used to manipulate the Data Router routing tables.
\r
52 * <div class="contentContainer">
\r
53 * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">
\r
55 * <span>URL Path Summary</span>
\r
56 * <span class="tabEnd"> </span>
\r
59 * <th class="colFirst" width="35%">URL Path</th>
\r
60 * <th class="colOne">Method</th>
\r
61 * <th class="colLast">Purpose</th>
\r
63 * <tr class="altColor">
\r
64 * <td class="colFirst">/internal/route/</td>
\r
65 * <td class="colOne">GET</td>
\r
66 * <td class="colLast">used to GET a full JSON copy of all three routing tables.</td>
\r
68 * <tr class="rowColor">
\r
69 * <td class="colFirst" rowspan="2">/internal/route/ingress/</td>
\r
70 * <td class="colOne">GET</td>
\r
71 * <td class="colLast">used to GET a full JSON copy of the ingress routing table (IRT).</td>
\r
73 * <tr class="rowColor">
\r
74 * <td class="colOne">POST</td>
\r
75 * <td class="colLast">used to create a new entry in the ingress routing table (IRT).</td></tr>
\r
76 * <tr class="altColor">
\r
77 * <td class="colFirst" rowspan="2">/internal/route/egress/</td>
\r
78 * <td class="colOne">GET</td>
\r
79 * <td class="colLast">used to GET a full JSON copy of the egress routing table (ERT).</td>
\r
81 * <tr class="altColor">
\r
82 * <td class="colOne">POST</td>
\r
83 * <td class="colLast">used to create a new entry in the egress routing table (ERT).</td></tr>
\r
84 * <tr class="rowColor">
\r
85 * <td class="colFirst" rowspan="2">/internal/route/network/</td>
\r
86 * <td class="colOne">GET</td>
\r
87 * <td class="colLast">used to GET a full JSON copy of the network routing table (NRT).</td>
\r
89 * <tr class="rowColor">
\r
90 * <td class="colOne">POST</td>
\r
91 * <td class="colLast">used to create a new entry in the network routing table (NRT).</td>
\r
93 * <tr class="altColor">
\r
94 * <td class="colFirst">/internal/route/ingress/<feed>/<user>/<subnet></td>
\r
95 * <td class="colOne">DELETE</td>
\r
96 * <td class="colLast">used to DELETE the ingress route corresponding to <i>feed</i>, <i>user</i> and <i>subnet</i>.
\r
97 * The / in the subnet specified should be replaced with a !, since / cannot be used in a URL.</td>
\r
99 * <tr class="rowColor">
\r
100 * <td class="colFirst">/internal/route/ingress/<seq></td>
\r
101 * <td class="colOne">DELETE</td>
\r
102 * <td class="colLast">used to DELETE all ingress routes with the matching <i>seq</i> sequence number.</td>
\r
104 * <tr class="altColor">
\r
105 * <td class="colFirst">/internal/route/egress/<sub></td>
\r
106 * <td class="colOne">DELETE</td>
\r
107 * <td class="colLast">used to DELETE the egress route the matching <i>sub</i> subscriber number.</td>
\r
109 * <tr class="rowColor">
\r
110 * <td class="colFirst">/internal/route/network/<fromnode>/<tonode></td>
\r
111 * <td class="colOne">DELETE</td>
\r
112 * <td class="colLast">used to DELETE the network route corresponding to <i>fromnode</i>
\r
113 * and <i>tonode</i>.</td>
\r
118 * Authorization to use these URLs is a little different than for other URLs on the provisioning server.
\r
119 * For the most part, the IP address that the request comes from should be either:
\r
122 * <li>an IP address of a provisioning server, or</li>
\r
123 * <li>the IP address of a node, or</li>
\r
124 * <li>an IP address from the "<i>special subnet</i>" which is configured with
\r
125 * the PROV_SPECIAL_SUBNET parameter.
\r
128 * All DELETE/GET/POST requests made to this servlet on the standby server are proxied to the
\r
129 * active server (using the {@link ProxyServlet}) if it is up and reachable.
\r
132 * @author Robert Eby
\r
135 @SuppressWarnings("serial")
\r
137 public class RouteServlet extends ProxyServlet {
\r
140 * DELETE route table entries by deleting part of the route table tree.
\r
143 public void doDelete(HttpServletRequest req, HttpServletResponse resp) {
\r
144 EventLogRecord elr = new EventLogRecord(req);
\r
145 if (!isAuthorizedForInternal(req)) {
\r
146 elr.setMessage(UNAUTHORIZED);
\r
147 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
\r
148 eventlogger.error(elr.toString());
\r
149 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
\r
152 if (isProxyOK(req) && isProxyServer()) {
\r
153 super.doDelete(req, resp);
\r
157 String path = req.getPathInfo();
\r
158 String[] parts = path.substring(1).split("/");
\r
159 Deleteable[] deleteables = null;
\r
160 if ("ingress".equals(parts[0])) {
\r
161 if (parts.length == 4) {
\r
162 // /internal/route/ingress/<feed>/<user>/<subnet>
\r
164 int feedid = Integer.parseInt(parts[1]);
\r
165 IngressRoute er = IngressRoute.getIngressRoute(feedid, parts[2], parts[3].replaceAll("!", "/"));
\r
167 sendResponseError(resp,
\r
168 HttpServletResponse.SC_NOT_FOUND, "The specified ingress route does not exist.",
\r
172 deleteables = new Deleteable[] { er };
\r
173 } catch (NumberFormatException e) {
\r
174 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,
\r
175 "Invalid feed ID in 'delete ingress' command.", eventlogger);
\r
178 } else if (parts.length == 2) {
\r
179 // /internal/route/ingress/<seq>
\r
181 int seq = Integer.parseInt(parts[1]);
\r
182 Set<IngressRoute> set = IngressRoute.getIngressRoutesForSeq(seq);
\r
183 deleteables = set.toArray(new Deleteable[0]);
\r
184 } catch (NumberFormatException e) {
\r
185 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,
\r
186 "Invalid sequence number in 'delete ingress' command.", eventlogger);
\r
190 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,
\r
191 "Invalid number of arguments in 'delete ingress' command.", eventlogger);
\r
194 } else if ("egress".equals(parts[0])) {
\r
195 if (parts.length == 2) {
\r
196 // /internal/route/egress/<sub>
\r
198 int subid = Integer.parseInt(parts[1]);
\r
199 EgressRoute er = EgressRoute.getEgressRoute(subid);
\r
201 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,
\r
202 "The specified egress route does not exist.", eventlogger);
\r
205 deleteables = new Deleteable[] { er };
\r
206 } catch (NumberFormatException e) {
\r
207 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,
\r
208 "Invalid sub ID in 'delete egress' command.", eventlogger);
\r
212 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,
\r
213 "Invalid number of arguments in 'delete egress' command.", eventlogger);
\r
216 } else if ("network".equals(parts[0])) {
\r
217 if (parts.length == 3) {
\r
218 // /internal/route/network/<from>/<to>
\r
220 NetworkRoute nr = new NetworkRoute(
\r
221 NodeClass.normalizeNodename(parts[1]),
\r
222 NodeClass.normalizeNodename(parts[2])
\r
224 deleteables = new Deleteable[] { nr };
\r
225 } catch (IllegalArgumentException e) {
\r
226 String message = "The specified network route does not exist.";
\r
227 eventlogger.error(message, e);
\r
228 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, message, eventlogger);
\r
232 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,
\r
233 "Invalid number of arguments in 'delete network' command.", eventlogger);
\r
237 if (deleteables == null) {
\r
238 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
\r
242 for (Deleteable dd : deleteables) {
\r
243 rv &= doDelete(dd);
\r
246 elr.setResult(HttpServletResponse.SC_OK);
\r
247 eventlogger.info(elr.toString());
\r
248 resp.setStatus(HttpServletResponse.SC_OK);
\r
249 provisioningDataChanged();
\r
250 provisioningParametersChanged();
\r
252 // Something went wrong with the DELETE
\r
253 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
\r
254 eventlogger.error(elr.toString());
\r
255 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, eventlogger);
\r
260 * GET route table entries from the route table tree specified by the URL path.
\r
263 public void doGet(HttpServletRequest req, HttpServletResponse resp) {
\r
264 EventLogRecord elr = new EventLogRecord(req);
\r
265 if (!isAuthorizedForInternal(req)) {
\r
266 elr.setMessage(UNAUTHORIZED);
\r
267 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
\r
268 eventlogger.error(elr.toString());
\r
269 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
\r
272 if (isProxyOK(req) && isProxyServer()) {
\r
273 super.doGet(req, resp);
\r
277 String path = req.getPathInfo();
\r
278 if (!path.endsWith("/")) {
\r
281 if (!"/".equals(path) && !INGRESS.equals(path) && !EGRESS.equals(path) && !NETWORK.equals(path)) {
\r
282 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
\r
286 StringBuilder sb = new StringBuilder("{\n");
\r
288 if ("/".equals(path) || INGRESS.equals(path)) {
\r
290 sb.append("\"ingress\": [");
\r
291 for (IngressRoute in : IngressRoute.getAllIngressRoutes()) {
\r
293 sb.append(in.asJSONObject().toString());
\r
300 if ("/".equals(path) || EGRESS.equals(path)) {
\r
303 sb.append("\"egress\": {");
\r
304 for (EgressRoute eg : EgressRoute.getAllEgressRoutes()) {
\r
305 JSONObject jx = eg.asJSONObject();
\r
306 for (String key : jx.keySet()) {
\r
308 sb.append(" \"").append(key).append("\": ");
\r
310 sb.append("\"").append(jx.getString(key)).append("\"");
\r
311 } catch (JSONException je) {
\r
312 eventlogger.error("PROV0161 RouteServlet.doGet: " + je.getMessage(), je);
\r
321 if ("/".equals(path) || NETWORK.equals(path)) {
\r
324 sb.append("\"routing\": [");
\r
325 for (NetworkRoute ne : NetworkRoute.getAllNetworkRoutes()) {
\r
327 sb.append(ne.asJSONObject().toString());
\r
333 resp.setStatus(HttpServletResponse.SC_OK);
\r
334 resp.setContentType("application/json");
\r
336 resp.getOutputStream().print(sb.toString());
\r
337 } catch (IOException ioe) {
\r
338 eventlogger.error("PROV0162 RouteServlet.doGet: " + ioe.getMessage(), ioe);
\r
343 * PUT on </internal/route/*> -- not supported.
\r
346 public void doPut(HttpServletRequest req, HttpServletResponse resp) {
\r
347 EventLogRecord elr = new EventLogRecord(req);
\r
348 if (!isAuthorizedForInternal(req)) {
\r
349 elr.setMessage(UNAUTHORIZED);
\r
350 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
\r
351 eventlogger.error(elr.toString());
\r
352 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
\r
355 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);
\r
359 * POST - modify existing route table entries in the route table tree specified by the URL path.
\r
362 public void doPost(HttpServletRequest req, HttpServletResponse resp) {
\r
363 EventLogRecord elr = new EventLogRecord(req);
\r
364 if (!isAuthorizedForInternal(req)) {
\r
365 elr.setMessage(UNAUTHORIZED);
\r
366 elr.setResult(HttpServletResponse.SC_FORBIDDEN);
\r
367 eventlogger.error(elr.toString());
\r
368 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);
\r
371 if (isProxyOK(req) && isProxyServer()) {
\r
372 super.doPost(req, resp);
\r
375 String path = req.getPathInfo();
\r
376 Insertable[] ins = null;
\r
377 if (path.startsWith(INGRESS)) {
\r
378 // /internal/route/ingress/?feed=%s&user=%s&subnet=%s&nodepatt=%s
\r
380 // Although it probably doesn't make sense, you can install two identical routes in the IRT
\r
381 int feedid = Integer.parseInt(req.getParameter("feed"));
\r
382 String user = req.getParameter("user");
\r
383 if (user == null) {
\r
386 String subnet = req.getParameter("subnet");
\r
387 if (subnet == null) {
\r
390 String nodepatt = req.getParameter("nodepatt");
\r
391 String str = req.getParameter("seq");
\r
392 int seq = (str != null) ? Integer.parseInt(str) : (IngressRoute.getMaxSequence() + 100);
\r
393 ins = new Insertable[] { new IngressRoute(seq, feedid,
\r
394 user, subnet, NodeClass.lookupNodeNames(nodepatt)) };
\r
395 } catch (Exception e) {
\r
396 intlogger.info(e.toString(), e);
\r
397 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,
\r
398 "Invalid arguments in 'add ingress' command.", intlogger);
\r
401 } else if (path.startsWith(EGRESS)) {
\r
402 // /internal/route/egress/?sub=%s&node=%s
\r
404 int subid = Integer.parseInt(req.getParameter("sub"));
\r
405 EgressRoute er = EgressRoute.getEgressRoute(subid);
\r
407 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,
\r
408 "An egress route already exists for that subscriber.", intlogger);
\r
411 String node = NodeClass.normalizeNodename(req.getParameter("node"));
\r
412 ins = new Insertable[] { new EgressRoute(subid, node) };
\r
413 } catch (Exception e) {
\r
414 intlogger.info(e.toString(), e);
\r
415 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,
\r
416 "Invalid arguments in 'add egress' command.", intlogger);
\r
419 } else if (path.startsWith(NETWORK)) {
\r
420 // /internal/route/network/?from=%s&to=%s&via=%s
\r
422 String nfrom = req.getParameter("from");
\r
423 String nto = req.getParameter("to");
\r
424 String nvia = req.getParameter("via");
\r
425 if (nfrom == null || nto == null || nvia == null) {
\r
426 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,
\r
427 "Missing arguments in 'add network' command.", intlogger);
\r
430 nfrom = NodeClass.normalizeNodename(nfrom);
\r
431 nto = NodeClass.normalizeNodename(nto);
\r
432 nvia = NodeClass.normalizeNodename(nvia);
\r
433 NetworkRoute nr = new NetworkRoute(nfrom, nto, nvia);
\r
434 for (NetworkRoute route : NetworkRoute.getAllNetworkRoutes()) {
\r
435 if (route.getFromnode() == nr.getFromnode() && route.getTonode() == nr.getTonode()) {
\r
436 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,
\r
437 "Network route table already contains a route for " + nfrom
\r
438 + " and " + nto, intlogger);
\r
442 ins = new Insertable[] { nr };
\r
443 } catch (IllegalArgumentException e) {
\r
444 intlogger.info(e.toString(), e);
\r
445 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,
\r
446 "Invalid arguments in 'add network' command.", intlogger);
\r
451 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, intlogger);
\r
455 for (Insertable dd : ins) {
\r
456 rv &= doInsert(dd);
\r
459 elr.setResult(HttpServletResponse.SC_OK);
\r
460 eventlogger.info(elr.toString());
\r
461 resp.setStatus(HttpServletResponse.SC_OK);
\r
462 provisioningDataChanged();
\r
463 provisioningParametersChanged();
\r
465 // Something went wrong with the INSERT
\r
466 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
\r
467 eventlogger.error(elr.toString());
\r
468 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, intlogger);
\r