update DR logging to log under one system
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / beans / IngressRoute.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.beans;\r
26 \r
27 import java.net.InetAddress;\r
28 import java.net.UnknownHostException;\r
29 import java.sql.Connection;\r
30 import java.sql.PreparedStatement;\r
31 import java.sql.ResultSet;\r
32 import java.sql.SQLException;\r
33 import java.sql.Statement;\r
34 import java.util.ArrayList;\r
35 import java.util.Collection;\r
36 import java.util.Set;\r
37 import java.util.SortedSet;\r
38 import java.util.TreeSet;\r
39 \r
40 import javax.servlet.http.HttpServletRequest;\r
41 \r
42 import com.att.eelf.configuration.EELFLogger;\r
43 import com.att.eelf.configuration.EELFManager;\r
44 import org.apache.commons.codec.binary.Base64;\r
45 import org.json.JSONArray;\r
46 import org.json.JSONObject;\r
47 import org.onap.dmaap.datarouter.provisioning.utils.DB;\r
48 \r
49 /**\r
50  * The representation of one route in the Ingress Route Table.\r
51  *\r
52  * @author Robert P. Eby\r
53  * @version $Id: IngressRoute.java,v 1.3 2013/12/16 20:30:23 eby Exp $\r
54  */\r
55 public class IngressRoute extends NodeClass implements Comparable<IngressRoute> {\r
56 \r
57     private static EELFLogger intlogger = EELFManager.getInstance().getLogger("InternalLog");\r
58     private final int seq;\r
59     private final int feedid;\r
60     private final String userid;\r
61     private final String subnet;\r
62     private int nodelist;\r
63     private SortedSet<String> nodes;\r
64 \r
65     /**\r
66      * Get all IngressRoutes in the database, sorted in order according to their sequence field.\r
67      *\r
68      * @return a sorted set of IngressRoutes\r
69      */\r
70     public static SortedSet<IngressRoute> getAllIngressRoutes() {\r
71         return getAllIngressRoutesForSQL("select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES");\r
72     }\r
73 \r
74     /**\r
75      * Get all IngressRoutes in the database with a particular sequence number.\r
76      *\r
77      * @param seq the sequence number\r
78      * @return a set of IngressRoutes\r
79      */\r
80     public static Set<IngressRoute> getIngressRoutesForSeq(int seq) {\r
81         return getAllIngressRoutesForSQL(\r
82                 "select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = " + seq);\r
83     }\r
84 \r
85     private static SortedSet<IngressRoute> getAllIngressRoutesForSQL(String sql) {\r
86         SortedSet<IngressRoute> set = new TreeSet<IngressRoute>();\r
87         try {\r
88             DB db = new DB();\r
89             @SuppressWarnings("resource")\r
90             Connection conn = db.getConnection();\r
91             try (Statement stmt = conn.createStatement()) {\r
92                 try (ResultSet rs = stmt.executeQuery(sql)) {\r
93                     while (rs.next()) {\r
94                         int seq = rs.getInt("SEQUENCE");\r
95                         int feedid = rs.getInt("FEEDID");\r
96                         String user = rs.getString("USERID");\r
97                         String subnet = rs.getString("SUBNET");\r
98                         int nodeset = rs.getInt("NODESET");\r
99                         set.add(new IngressRoute(seq, feedid, user, subnet, nodeset));\r
100                     }\r
101                 }\r
102             }\r
103             db.release(conn);\r
104         } catch (SQLException e) {\r
105             intlogger.error("SQLException " + e.getMessage());\r
106         }\r
107         return set;\r
108     }\r
109 \r
110     /**\r
111      * Get the maximum node set ID in use in the DB.\r
112      *\r
113      * @return the integer value of the maximum\r
114      */\r
115     public static int getMaxNodeSetID() {\r
116         return getMax("select max(SETID) as MAX from NODESETS");\r
117     }\r
118 \r
119     /**\r
120      * Get the maximum node sequence number in use in the DB.\r
121      *\r
122      * @return the integer value of the maximum\r
123      */\r
124     public static int getMaxSequence() {\r
125         return getMax("select max(SEQUENCE) as MAX from INGRESS_ROUTES");\r
126     }\r
127 \r
128     private static int getMax(String sql) {\r
129         int rv = 0;\r
130         try {\r
131             DB db = new DB();\r
132             @SuppressWarnings("resource")\r
133             Connection conn = db.getConnection();\r
134             try (Statement stmt = conn.createStatement()) {\r
135                 try (ResultSet rs = stmt.executeQuery(sql)) {\r
136                     if (rs.next()) {\r
137                         rv = rs.getInt("MAX");\r
138                     }\r
139                 }\r
140             }\r
141             db.release(conn);\r
142         } catch (SQLException e) {\r
143             intlogger.error("SQLException " + e.getMessage());\r
144         }\r
145         return rv;\r
146     }\r
147 \r
148     /**\r
149      * Get an Ingress Route for a particular feed ID, user, and subnet\r
150      *\r
151      * @param feedid the Feed ID to look for\r
152      * @param user the user name to look for\r
153      * @param subnet the subnet to look for\r
154      * @return the Ingress Route, or null of there is none\r
155      */\r
156     public static IngressRoute getIngressRoute(int feedid, String user, String subnet) {\r
157         IngressRoute v = null;\r
158         PreparedStatement ps = null;\r
159         try {\r
160             DB db = new DB();\r
161             @SuppressWarnings("resource")\r
162             Connection conn = db.getConnection();\r
163             String sql = "select SEQUENCE, NODESET from INGRESS_ROUTES where FEEDID = ? AND USERID = ? and SUBNET = ?";\r
164             ps = conn.prepareStatement(sql);\r
165             ps.setInt(1, feedid);\r
166             ps.setString(2, user);\r
167             ps.setString(3, subnet);\r
168             try (ResultSet rs = ps.executeQuery()) {\r
169                 if (rs.next()) {\r
170                     int seq = rs.getInt("SEQUENCE");\r
171                     int nodeset = rs.getInt("NODESET");\r
172                     v = new IngressRoute(seq, feedid, user, subnet, nodeset);\r
173                 }\r
174             }\r
175             ps.close();\r
176             db.release(conn);\r
177         } catch (SQLException e) {\r
178             intlogger.error("SQLException " + e.getMessage());\r
179         } finally {\r
180             try {\r
181                 if (ps != null) {\r
182                     ps.close();\r
183                 }\r
184             } catch (SQLException e) {\r
185                 intlogger.error("SQLException " + e.getMessage());\r
186             }\r
187         }\r
188         return v;\r
189     }\r
190 \r
191     /**\r
192      * Get a collection of all Ingress Routes with a particular sequence number.\r
193      *\r
194      * @param seq the sequence number to look for\r
195      * @return the collection (may be empty).\r
196      */\r
197     public static Collection<IngressRoute> getIngressRoute(int seq) {\r
198         Collection<IngressRoute> rv = new ArrayList<IngressRoute>();\r
199         try {\r
200             DB db = new DB();\r
201             @SuppressWarnings("resource")\r
202             Connection conn = db.getConnection();\r
203             String sql = "select FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = ?";\r
204             try (PreparedStatement ps = conn.prepareStatement(sql)) {\r
205                 ps.setInt(1, seq);\r
206                 try (ResultSet rs = ps.executeQuery()) {\r
207                     while (rs.next()) {\r
208                         int feedid = rs.getInt("FEEDID");\r
209                         String user = rs.getString("USERID");\r
210                         String subnet = rs.getString("SUBNET");\r
211                         int nodeset = rs.getInt("NODESET");\r
212                         rv.add(new IngressRoute(seq, feedid, user, subnet, nodeset));\r
213                     }\r
214                 }\r
215             }\r
216             db.release(conn);\r
217         } catch (SQLException e) {\r
218             intlogger.error("SQLException " + e.getMessage());\r
219         }\r
220         return rv;\r
221     }\r
222 \r
223     public IngressRoute(int seq, int feedid, String user, String subnet, Collection<String> nodes)\r
224             throws IllegalArgumentException {\r
225         this(seq, feedid, user, subnet);\r
226         this.nodelist = -1;\r
227         this.nodes = new TreeSet<String>(nodes);\r
228     }\r
229 \r
230     public IngressRoute(int seq, int feedid, String user, String subnet, int nodeset)\r
231             throws IllegalArgumentException {\r
232         this(seq, feedid, user, subnet);\r
233         this.nodelist = nodeset;\r
234         this.nodes = new TreeSet<String>(readNodes());\r
235     }\r
236 \r
237     private IngressRoute(int seq, int feedid, String user, String subnet)\r
238             throws IllegalArgumentException {\r
239         this.seq = seq;\r
240         this.feedid = feedid;\r
241         this.userid = (user == null) ? "-" : user;\r
242         this.subnet = (subnet == null) ? "-" : subnet;\r
243         this.nodelist = -1;\r
244         this.nodes = null;\r
245         if (Feed.getFeedById(feedid) == null) {\r
246             throw new IllegalArgumentException("No such feed: " + feedid);\r
247         }\r
248         if (!this.subnet.equals("-")) {\r
249             SubnetMatcher sm = new SubnetMatcher(subnet);\r
250             if (!sm.isValid()) {\r
251                 throw new IllegalArgumentException("Invalid subnet: " + subnet);\r
252             }\r
253         }\r
254     }\r
255 \r
256     public IngressRoute(JSONObject jo) {\r
257         this.seq = jo.optInt("seq");\r
258         this.feedid = jo.optInt("feedid");\r
259         String t = jo.optString("user");\r
260         this.userid = t.equals("") ? "-" : t;\r
261         t = jo.optString("subnet");\r
262         this.subnet = t.equals("") ? "-" : t;\r
263         this.nodelist = -1;\r
264         this.nodes = new TreeSet<String>();\r
265         JSONArray ja = jo.getJSONArray("node");\r
266         for (int i = 0; i < ja.length(); i++) {\r
267             this.nodes.add(ja.getString(i));\r
268         }\r
269     }\r
270 \r
271     /**\r
272      * Does this particular IngressRoute match a request, represented by feedid and req? To match, <i>feedid</i> must\r
273      * match the feed ID in the route, the user in the route (if specified) must match the user in the request, and the\r
274      * subnet in the route (if specified) must match the subnet from the request.\r
275      *\r
276      * @param feedid the feedid for this request\r
277      * @param req the remainder of the request\r
278      * @return true if a match, false otherwise\r
279      */\r
280     public boolean matches(int feedid, HttpServletRequest req) {\r
281         // Check feedid\r
282         if (this.feedid != feedid) {\r
283             return false;\r
284         }\r
285 \r
286         // Get user from request and compare\r
287         // Note: we don't check the password; the node will do that\r
288         if (userid.length() > 0 && !userid.equals("-")) {\r
289             String credentials = req.getHeader("Authorization");\r
290             if (credentials == null || !credentials.startsWith("Basic ")) {\r
291                 return false;\r
292             }\r
293             String t = new String(Base64.decodeBase64(credentials.substring(6)));\r
294             int ix = t.indexOf(':');\r
295             if (ix >= 0) {\r
296                 t = t.substring(0, ix);\r
297             }\r
298             if (!t.equals(this.userid)) {\r
299                 return false;\r
300             }\r
301         }\r
302 \r
303         // If this route has a subnet, match it against the requester's IP addr\r
304         if (subnet.length() > 0 && !subnet.equals("-")) {\r
305             try {\r
306                 InetAddress inet = InetAddress.getByName(req.getRemoteAddr());\r
307                 SubnetMatcher sm = new SubnetMatcher(subnet);\r
308                 return sm.matches(inet.getAddress());\r
309             } catch (UnknownHostException e) {\r
310                 return false;\r
311             }\r
312         }\r
313         return true;\r
314     }\r
315 \r
316     /**\r
317      * Compare IP addresses as byte arrays to a subnet specified as a CIDR. Taken from\r
318      * org.onap.dmaap.datarouter.node.SubnetMatcher and modified somewhat.\r
319      */\r
320     public class SubnetMatcher {\r
321 \r
322         private byte[] sn;\r
323         private int len;\r
324         private int mask;\r
325         private boolean valid;\r
326 \r
327         /**\r
328          * Construct a subnet matcher given a CIDR\r
329          *\r
330          * @param subnet The CIDR to match\r
331          */\r
332         public SubnetMatcher(String subnet) {\r
333             int i = subnet.lastIndexOf('/');\r
334             if (i == -1) {\r
335                 try {\r
336                     sn = InetAddress.getByName(subnet).getAddress();\r
337                     len = sn.length;\r
338                     valid = true;\r
339                 } catch (UnknownHostException e) {\r
340                     len = 0;\r
341                     valid = false;\r
342                 }\r
343                 mask = 0;\r
344             } else {\r
345                 int n = Integer.parseInt(subnet.substring(i + 1));\r
346                 try {\r
347                     sn = InetAddress.getByName(subnet.substring(0, i)).getAddress();\r
348                     valid = true;\r
349                 } catch (UnknownHostException e) {\r
350                     valid = false;\r
351                 }\r
352                 len = n / 8;\r
353                 mask = ((0xff00) >> (n % 8)) & 0xff;\r
354             }\r
355         }\r
356 \r
357         public boolean isValid() {\r
358             return valid;\r
359         }\r
360 \r
361         /**\r
362          * Is the IP address in the CIDR?\r
363          *\r
364          * @param addr the IP address as bytes in network byte order\r
365          * @return true if the IP address matches.\r
366          */\r
367         public boolean matches(byte[] addr) {\r
368             if (!valid || addr.length != sn.length) {\r
369                 return false;\r
370             }\r
371             for (int i = 0; i < len; i++) {\r
372                 if (addr[i] != sn[i]) {\r
373                     return false;\r
374                 }\r
375             }\r
376             if (mask != 0 && ((addr[len] ^ sn[len]) & mask) != 0) {\r
377                 return false;\r
378             }\r
379             return true;\r
380         }\r
381     }\r
382 \r
383     /**\r
384      * Get the list of node names for this route.\r
385      *\r
386      * @return the list\r
387      */\r
388     public SortedSet<String> getNodes() {\r
389         return this.nodes;\r
390     }\r
391 \r
392     private Collection<String> readNodes() {\r
393         Collection<String> set = new TreeSet<String>();\r
394         try {\r
395             DB db = new DB();\r
396             @SuppressWarnings("resource")\r
397             Connection conn = db.getConnection();\r
398             String sql = "select NODEID from NODESETS where SETID = ?";\r
399             try (PreparedStatement ps = conn.prepareStatement(sql)) {\r
400                 ps.setInt(1, nodelist);\r
401                 try (ResultSet rs = ps.executeQuery()) {\r
402                     while (rs.next()) {\r
403                         int id = rs.getInt("NODEID");\r
404                         set.add(lookupNodeID(id));\r
405                     }\r
406                 }\r
407             }\r
408             db.release(conn);\r
409         } catch (SQLException e) {\r
410             intlogger.error("SQLException " + e.getMessage());\r
411         }\r
412         return set;\r
413     }\r
414 \r
415     /**\r
416      * Delete the IRT route having this IngressRoutes feed ID, user ID, and subnet from the database.\r
417      *\r
418      * @return true if the delete succeeded\r
419      */\r
420     @Override\r
421     public boolean doDelete(Connection c) {\r
422         boolean rv = true;\r
423         PreparedStatement ps = null;\r
424         try {\r
425             ps = c.prepareStatement("delete from INGRESS_ROUTES where FEEDID = ? and USERID = ? and SUBNET = ?");\r
426             ps.setInt(1, feedid);\r
427             ps.setString(2, userid);\r
428             ps.setString(3, subnet);\r
429             ps.execute();\r
430             ps.close();\r
431 \r
432             ps = c.prepareStatement("delete from NODESETS where SETID = ?");\r
433             ps.setInt(1, nodelist);\r
434             ps.execute();\r
435         } catch (SQLException e) {\r
436             rv = false;\r
437             intlogger.warn("PROV0007 doDelete: " + e.getMessage());\r
438         } finally {\r
439             try {\r
440                 if (ps != null) {\r
441                     ps.close();\r
442                 }\r
443             } catch (SQLException e) {\r
444                 intlogger.error("SQLException " + e.getMessage());\r
445             }\r
446         }\r
447         return rv;\r
448     }\r
449 \r
450     @SuppressWarnings("resource")\r
451     @Override\r
452     public boolean doInsert(Connection c) {\r
453         boolean rv = false;\r
454         PreparedStatement ps = null;\r
455         try {\r
456             // Create the NODESETS rows & set nodelist\r
457             int set = getMaxNodeSetID() + 1;\r
458             this.nodelist = set;\r
459             for (String node : nodes) {\r
460                 int id = lookupNodeName(node);\r
461                 ps = c.prepareStatement("insert into NODESETS (SETID, NODEID) values (?,?)");\r
462                 ps.setInt(1, this.nodelist);\r
463                 ps.setInt(2, id);\r
464                 ps.execute();\r
465                 ps.close();\r
466             }\r
467 \r
468             // Create the INGRESS_ROUTES row\r
469             ps = c.prepareStatement(\r
470                     "insert into INGRESS_ROUTES (SEQUENCE, FEEDID, USERID, SUBNET, NODESET) values (?, ?, ?, ?, ?)");\r
471             ps.setInt(1, this.seq);\r
472             ps.setInt(2, this.feedid);\r
473             ps.setString(3, this.userid);\r
474             ps.setString(4, this.subnet);\r
475             ps.setInt(5, this.nodelist);\r
476             ps.execute();\r
477             ps.close();\r
478             rv = true;\r
479         } catch (SQLException e) {\r
480             intlogger.warn("PROV0005 doInsert: " + e.getMessage());\r
481         } finally {\r
482             try {\r
483                 if (ps != null) {\r
484                     ps.close();\r
485                 }\r
486             } catch (SQLException e) {\r
487                 intlogger.error("SQLException " + e.getMessage());\r
488             }\r
489         }\r
490         return rv;\r
491     }\r
492 \r
493     @Override\r
494     public boolean doUpdate(Connection c) {\r
495         return doDelete(c) && doInsert(c);\r
496     }\r
497 \r
498     @Override\r
499     public JSONObject asJSONObject() {\r
500         JSONObject jo = new JSONObject();\r
501         jo.put("feedid", feedid);\r
502         // Note: for user and subnet, null, "", and "-" are equivalent\r
503         if (userid != null && !userid.equals("-") && !userid.equals("")) {\r
504             jo.put("user", userid);\r
505         }\r
506         if (subnet != null && !subnet.equals("-") && !subnet.equals("")) {\r
507             jo.put("subnet", subnet);\r
508         }\r
509         jo.put("seq", seq);\r
510         jo.put("node", nodes);\r
511         return jo;\r
512     }\r
513 \r
514     @Override\r
515     public String getKey() {\r
516         return String\r
517                 .format("%d/%s/%s/%d", feedid, (userid == null) ? "" : userid, (subnet == null) ? "" : subnet, seq);\r
518     }\r
519 \r
520     @Override\r
521     public int hashCode() {\r
522         return toString().hashCode();\r
523     }\r
524 \r
525     @Override\r
526     public boolean equals(Object obj) {\r
527         try {\r
528             if (!(obj instanceof IngressRoute)) {\r
529                 return false;\r
530             }\r
531             return this.compareTo((IngressRoute) obj) == 0;\r
532         } catch (NullPointerException e) {\r
533             return false;\r
534         }\r
535     }\r
536 \r
537     @Override\r
538     public int compareTo(IngressRoute in) {\r
539         if (in == null) {\r
540             throw new NullPointerException();\r
541         }\r
542         int n = this.feedid - in.feedid;\r
543         if (n != 0) {\r
544             return n;\r
545         }\r
546         n = this.seq - in.seq;\r
547         if (n != 0) {\r
548             return n;\r
549         }\r
550         n = this.userid.compareTo(in.userid);\r
551         if (n != 0) {\r
552             return n;\r
553         }\r
554         n = this.subnet.compareTo(in.subnet);\r
555         if (n != 0) {\r
556             return n;\r
557         }\r
558         return this.nodes.equals(in.nodes) ? 0 : 1;\r
559     }\r
560 \r
561     @Override\r
562     public String toString() {\r
563         return String.format("INGRESS: feed=%d, userid=%s, subnet=%s, seq=%d", feedid, (userid == null) ? "" : userid,\r
564                 (subnet == null) ? "" : subnet, seq);\r
565     }\r
566 }\r