Fix formatting
[dmaap/datarouter.git] / datarouter-node / src / main / java / org / onap / dmaap / datarouter / node / NodeConfig.java
1 /*******************************************************************************
2  * ============LICENSE_START==================================================
3  * * org.onap.dmaap
4  * * ===========================================================================
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * * ===========================================================================
7  * * Licensed under the Apache License, Version 2.0 (the "License");
8  * * you may not use this file except in compliance with the License.
9  * * You may obtain a copy of the License at
10  * *
11  *  *      http://www.apache.org/licenses/LICENSE-2.0
12  * *
13  *  * Unless required by applicable law or agreed to in writing, software
14  * * distributed under the License is distributed on an "AS IS" BASIS,
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * * See the License for the specific language governing permissions and
17  * * limitations under the License.
18  * * ============LICENSE_END====================================================
19  * *
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  * *
22  ******************************************************************************/
23
24
25 package org.onap.dmaap.datarouter.node;
26
27 import java.io.File;
28 import java.util.HashSet;
29 import java.util.Hashtable;
30 import java.util.Vector;
31
32 /**
33  * Processed configuration for this node.
34  * <p>
35  * The NodeConfig represents a processed configuration from the Data Router provisioning server.  Each time
36  * configuration data is received from the provisioning server, a new NodeConfig is created and the previous one
37  * discarded.
38  */
39 public class NodeConfig {
40
41     /**
42      * Raw configuration entry for a data router node
43      */
44     public static class ProvNode {
45
46         private String cname;
47
48         /**
49          * Construct a node configuration entry.
50          *
51          * @param cname The cname of the node.
52          */
53         public ProvNode(String cname) {
54             this.cname = cname;
55         }
56
57         /**
58          * Get the cname of the node
59          */
60         public String getCName() {
61             return (cname);
62         }
63     }
64
65     /**
66      * Raw configuration entry for a provisioning parameter
67      */
68     public static class ProvParam {
69
70         private String name;
71         private String value;
72
73         /**
74          * Construct a provisioning parameter configuration entry.
75          *
76          * @param name The name of the parameter.
77          * @param value The value of the parameter.
78          */
79         public ProvParam(String name, String value) {
80             this.name = name;
81             this.value = value;
82         }
83
84         /**
85          * Get the name of the parameter.
86          */
87         public String getName() {
88             return (name);
89         }
90
91         /**
92          * Get the value of the parameter.
93          */
94         public String getValue() {
95             return (value);
96         }
97     }
98
99     /**
100      * Raw configuration entry for a data feed.
101      */
102     public static class ProvFeed {
103
104         private String id;
105         private String logdata;
106         private String status;
107
108         /**
109          * Construct a feed configuration entry.
110          *
111          * @param id The feed ID of the entry.
112          * @param logdata String for log entries about the entry.
113          * @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or
114          * null if it is valid.
115          */
116         public ProvFeed(String id, String logdata, String status) {
117             this.id = id;
118             this.logdata = logdata;
119             this.status = status;
120         }
121
122         /**
123          * Get the feed id of the data feed.
124          */
125         public String getId() {
126             return (id);
127         }
128
129         /**
130          * Get the log data of the data feed.
131          */
132         public String getLogData() {
133             return (logdata);
134         }
135
136         /**
137          * Get the status of the data feed.
138          */
139         public String getStatus() {
140             return (status);
141         }
142     }
143
144     /**
145      * Raw configuration entry for a feed user.
146      */
147     public static class ProvFeedUser {
148
149         private String feedid;
150         private String user;
151         private String credentials;
152
153         /**
154          * Construct a feed user configuration entry
155          *
156          * @param feedid The feed id.
157          * @param user The user that will publish to the feed.
158          * @param credentials The Authorization header the user will use to publish.
159          */
160         public ProvFeedUser(String feedid, String user, String credentials) {
161             this.feedid = feedid;
162             this.user = user;
163             this.credentials = credentials;
164         }
165
166         /**
167          * Get the feed id of the feed user.
168          */
169         public String getFeedId() {
170             return (feedid);
171         }
172
173         /**
174          * Get the user for the feed user.
175          */
176         public String getUser() {
177             return (user);
178         }
179
180         /**
181          * Get the credentials for the feed user.
182          */
183         public String getCredentials() {
184             return (credentials);
185         }
186     }
187
188     /**
189      * Raw configuration entry for a feed subnet
190      */
191     public static class ProvFeedSubnet {
192
193         private String feedid;
194         private String cidr;
195
196         /**
197          * Construct a feed subnet configuration entry
198          *
199          * @param feedid The feed ID
200          * @param cidr The CIDR allowed to publish to the feed.
201          */
202         public ProvFeedSubnet(String feedid, String cidr) {
203             this.feedid = feedid;
204             this.cidr = cidr;
205         }
206
207         /**
208          * Get the feed id of the feed subnet.
209          */
210         public String getFeedId() {
211             return (feedid);
212         }
213
214         /**
215          * Get the CIDR of the feed subnet.
216          */
217         public String getCidr() {
218             return (cidr);
219         }
220     }
221
222     /**
223      * Raw configuration entry for a subscription
224      */
225     public static class ProvSubscription {
226
227         private String subid;
228         private String feedid;
229         private String url;
230         private String authuser;
231         private String credentials;
232         private boolean metaonly;
233         private boolean use100;
234
235         /**
236          * Construct a subscription configuration entry
237          *
238          * @param subid The subscription ID
239          * @param feedid The feed ID
240          * @param url The base delivery URL (not including the fileid)
241          * @param authuser The user in the credentials used to deliver
242          * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the
243          * Authorization header.
244          * @param metaonly Is this a meta data only subscription?
245          * @param use100 Should we send Expect: 100-continue?
246          */
247         public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials,
248                 boolean metaonly, boolean use100) {
249             this.subid = subid;
250             this.feedid = feedid;
251             this.url = url;
252             this.authuser = authuser;
253             this.credentials = credentials;
254             this.metaonly = metaonly;
255             this.use100 = use100;
256         }
257
258         /**
259          * Get the subscription ID
260          */
261         public String getSubId() {
262             return (subid);
263         }
264
265         /**
266          * Get the feed ID
267          */
268         public String getFeedId() {
269             return (feedid);
270         }
271
272         /**
273          * Get the delivery URL
274          */
275         public String getURL() {
276             return (url);
277         }
278
279         /**
280          * Get the user
281          */
282         public String getAuthUser() {
283             return (authuser);
284         }
285
286         /**
287          * Get the delivery credentials
288          */
289         public String getCredentials() {
290             return (credentials);
291         }
292
293         /**
294          * Is this a meta data only subscription?
295          */
296         public boolean isMetaDataOnly() {
297             return (metaonly);
298         }
299
300         /**
301          * Should we send Expect: 100-continue?
302          */
303         public boolean isUsing100() {
304             return (use100);
305         }
306     }
307
308     /**
309      * Raw configuration entry for controlled ingress to the data router node
310      */
311     public static class ProvForceIngress {
312
313         private String feedid;
314         private String subnet;
315         private String user;
316         private String[] nodes;
317
318         /**
319          * Construct a forced ingress configuration entry
320          *
321          * @param feedid The feed ID that this entry applies to
322          * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all
323          * publisher IP addresses
324          * @param user The publishing user this entry applies to or "" if it applies to all publishing users.
325          * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
326          */
327         public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
328             this.feedid = feedid;
329             this.subnet = subnet;
330             this.user = user;
331             this.nodes = nodes;
332         }
333
334         /**
335          * Get the feed ID
336          */
337         public String getFeedId() {
338             return (feedid);
339         }
340
341         /**
342          * Get the subnet
343          */
344         public String getSubnet() {
345             return (subnet);
346         }
347
348         /**
349          * Get the user
350          */
351         public String getUser() {
352             return (user);
353         }
354
355         /**
356          * Get the node
357          */
358         public String[] getNodes() {
359             return (nodes);
360         }
361     }
362
363     /**
364      * Raw configuration entry for controlled egress from the data router
365      */
366     public static class ProvForceEgress {
367
368         private String subid;
369         private String node;
370
371         /**
372          * Construct a forced egress configuration entry
373          *
374          * @param subid The subscription ID the subscription with forced egress
375          * @param node The node handling deliveries for this subscription
376          */
377         public ProvForceEgress(String subid, String node) {
378             this.subid = subid;
379             this.node = node;
380         }
381
382         /**
383          * Get the subscription ID
384          */
385         public String getSubId() {
386             return (subid);
387         }
388
389         /**
390          * Get the node
391          */
392         public String getNode() {
393             return (node);
394         }
395     }
396
397     /**
398      * Raw configuration entry for routing within the data router network
399      */
400     public static class ProvHop {
401
402         private String from;
403         private String to;
404         private String via;
405
406         /**
407          * A human readable description of this entry
408          */
409         public String toString() {
410             return ("Hop " + from + "->" + to + " via " + via);
411         }
412
413         /**
414          * Construct a hop entry
415          *
416          * @param from The FQDN of the node with the data to be delivered
417          * @param to The FQDN of the node that will deliver to the subscriber
418          * @param via The FQDN of the node where the from node should send the data
419          */
420         public ProvHop(String from, String to, String via) {
421             this.from = from;
422             this.to = to;
423             this.via = via;
424         }
425
426         /**
427          * Get the from node
428          */
429         public String getFrom() {
430             return (from);
431         }
432
433         /**
434          * Get the to node
435          */
436         public String getTo() {
437             return (to);
438         }
439
440         /**
441          * Get the next intermediate node
442          */
443         public String getVia() {
444             return (via);
445         }
446     }
447
448     private static class Redirection {
449
450         SubnetMatcher snm;
451         String user;
452         String[] nodes;
453     }
454
455     private static class Feed {
456
457         String loginfo;
458         String status;
459         SubnetMatcher[] subnets;
460         Hashtable<String, String> authusers = new Hashtable<String, String>();
461         Redirection[] redirections;
462         Target[] targets;
463     }
464
465     private Hashtable<String, String> params = new Hashtable<String, String>();
466     private Hashtable<String, Feed> feeds = new Hashtable<String, Feed>();
467     private Hashtable<String, DestInfo> nodeinfo = new Hashtable<String, DestInfo>();
468     private Hashtable<String, DestInfo> subinfo = new Hashtable<String, DestInfo>();
469     private Hashtable<String, IsFrom> nodes = new Hashtable<String, IsFrom>();
470     private String myname;
471     private String myauth;
472     private DestInfo[] alldests;
473     private int rrcntr;
474
475     /**
476      * Process the raw provisioning data to configure this node
477      *
478      * @param pd The parsed provisioning data
479      * @param myname My name as seen by external systems
480      * @param spooldir The directory where temporary files live
481      * @param port The port number for URLs
482      * @param nodeauthkey The keying string used to generate node authentication credentials
483      */
484     public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
485         this.myname = myname;
486         for (ProvParam p : pd.getParams()) {
487             params.put(p.getName(), p.getValue());
488         }
489         Vector<DestInfo> div = new Vector<DestInfo>();
490         myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
491         for (ProvNode pn : pd.getNodes()) {
492             String cn = pn.getCName();
493             if (nodeinfo.get(cn) != null) {
494                 continue;
495             }
496             String auth = NodeUtils.getNodeAuthHdr(cn, nodeauthkey);
497             DestInfo di = new DestInfo("n:" + cn, spooldir + "/n/" + cn, null, "n2n-" + cn,
498                     "https://" + cn + ":" + port + "/internal/publish", cn, myauth, false, true);
499             (new File(di.getSpool())).mkdirs();
500             div.add(di);
501             nodeinfo.put(cn, di);
502             nodes.put(auth, new IsFrom(cn));
503         }
504         PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[nodeinfo.size()]), pd.getHops());
505         Hashtable<String, Vector<Redirection>> rdtab = new Hashtable<String, Vector<Redirection>>();
506         for (ProvForceIngress pfi : pd.getForceIngress()) {
507             Vector<Redirection> v = rdtab.get(pfi.getFeedId());
508             if (v == null) {
509                 v = new Vector<Redirection>();
510                 rdtab.put(pfi.getFeedId(), v);
511             }
512             Redirection r = new Redirection();
513             if (pfi.getSubnet() != null) {
514                 r.snm = new SubnetMatcher(pfi.getSubnet());
515             }
516             r.user = pfi.getUser();
517             r.nodes = pfi.getNodes();
518             v.add(r);
519         }
520         Hashtable<String, Hashtable<String, String>> pfutab = new Hashtable<String, Hashtable<String, String>>();
521         for (ProvFeedUser pfu : pd.getFeedUsers()) {
522             Hashtable<String, String> t = pfutab.get(pfu.getFeedId());
523             if (t == null) {
524                 t = new Hashtable<String, String>();
525                 pfutab.put(pfu.getFeedId(), t);
526             }
527             t.put(pfu.getCredentials(), pfu.getUser());
528         }
529         Hashtable<String, String> egrtab = new Hashtable<String, String>();
530         for (ProvForceEgress pfe : pd.getForceEgress()) {
531             if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
532                 continue;
533             }
534             egrtab.put(pfe.getSubId(), pfe.getNode());
535         }
536         Hashtable<String, Vector<SubnetMatcher>> pfstab = new Hashtable<String, Vector<SubnetMatcher>>();
537         for (ProvFeedSubnet pfs : pd.getFeedSubnets()) {
538             Vector<SubnetMatcher> v = pfstab.get(pfs.getFeedId());
539             if (v == null) {
540                 v = new Vector<SubnetMatcher>();
541                 pfstab.put(pfs.getFeedId(), v);
542             }
543             v.add(new SubnetMatcher(pfs.getCidr()));
544         }
545         Hashtable<String, StringBuffer> ttab = new Hashtable<String, StringBuffer>();
546         HashSet<String> allfeeds = new HashSet<String>();
547         for (ProvFeed pfx : pd.getFeeds()) {
548             if (pfx.getStatus() == null) {
549                 allfeeds.add(pfx.getId());
550             }
551         }
552         for (ProvSubscription ps : pd.getSubscriptions()) {
553             String sid = ps.getSubId();
554             String fid = ps.getFeedId();
555             if (!allfeeds.contains(fid)) {
556                 continue;
557             }
558             if (subinfo.get(sid) != null) {
559                 continue;
560             }
561             int sididx = 999;
562             try {
563                 sididx = Integer.parseInt(sid);
564                 sididx -= sididx % 100;
565             } catch (Exception e) {
566             }
567             String siddir = sididx + "/" + sid;
568             DestInfo di = new DestInfo("s:" + sid, spooldir + "/s/" + siddir, sid, fid, ps.getURL(), ps.getAuthUser(),
569                     ps.getCredentials(), ps.isMetaDataOnly(), ps.isUsing100());
570             (new File(di.getSpool())).mkdirs();
571             div.add(di);
572             subinfo.put(sid, di);
573             String egr = egrtab.get(sid);
574             if (egr != null) {
575                 sid = pf.getPath(egr) + sid;
576             }
577             StringBuffer sb = ttab.get(fid);
578             if (sb == null) {
579                 sb = new StringBuffer();
580                 ttab.put(fid, sb);
581             }
582             sb.append(' ').append(sid);
583         }
584         alldests = div.toArray(new DestInfo[div.size()]);
585         for (ProvFeed pfx : pd.getFeeds()) {
586             String fid = pfx.getId();
587             Feed f = feeds.get(fid);
588             if (f != null) {
589                 continue;
590             }
591             f = new Feed();
592             feeds.put(fid, f);
593             f.loginfo = pfx.getLogData();
594             f.status = pfx.getStatus();
595             Vector<SubnetMatcher> v1 = pfstab.get(fid);
596             if (v1 == null) {
597                 f.subnets = new SubnetMatcher[0];
598             } else {
599                 f.subnets = v1.toArray(new SubnetMatcher[v1.size()]);
600             }
601             Hashtable<String, String> h1 = pfutab.get(fid);
602             if (h1 == null) {
603                 h1 = new Hashtable<String, String>();
604             }
605             f.authusers = h1;
606             Vector<Redirection> v2 = rdtab.get(fid);
607             if (v2 == null) {
608                 f.redirections = new Redirection[0];
609             } else {
610                 f.redirections = v2.toArray(new Redirection[v2.size()]);
611             }
612             StringBuffer sb = ttab.get(fid);
613             if (sb == null) {
614                 f.targets = new Target[0];
615             } else {
616                 f.targets = parseRouting(sb.toString());
617             }
618         }
619     }
620
621     /**
622      * Parse a target string into an array of targets
623      *
624      * @param routing Target string
625      * @return Array of targets.
626      */
627     public Target[] parseRouting(String routing) {
628         routing = routing.trim();
629         if ("".equals(routing)) {
630             return (new Target[0]);
631         }
632         String[] xx = routing.split("\\s+");
633         Hashtable<String, Target> tmap = new Hashtable<String, Target>();
634         HashSet<String> subset = new HashSet<String>();
635         Vector<Target> tv = new Vector<Target>();
636         Target[] ret = new Target[xx.length];
637         for (int i = 0; i < xx.length; i++) {
638             String t = xx[i];
639             int j = t.indexOf('/');
640             if (j == -1) {
641                 DestInfo di = subinfo.get(t);
642                 if (di == null) {
643                     tv.add(new Target(null, t));
644                 } else {
645                     if (!subset.contains(t)) {
646                         subset.add(t);
647                         tv.add(new Target(di, null));
648                     }
649                 }
650             } else {
651                 String node = t.substring(0, j);
652                 String rtg = t.substring(j + 1);
653                 DestInfo di = nodeinfo.get(node);
654                 if (di == null) {
655                     tv.add(new Target(null, t));
656                 } else {
657                     Target tt = tmap.get(node);
658                     if (tt == null) {
659                         tt = new Target(di, rtg);
660                         tmap.put(node, tt);
661                         tv.add(tt);
662                     } else {
663                         tt.addRouting(rtg);
664                     }
665                 }
666             }
667         }
668         return (tv.toArray(new Target[tv.size()]));
669     }
670
671     /**
672      * Check whether this is a valid node-to-node transfer
673      *
674      * @param credentials Credentials offered by the supposed node
675      * @param ip IP address the request came from
676      */
677     public boolean isAnotherNode(String credentials, String ip) {
678         IsFrom n = nodes.get(credentials);
679         return (n != null && n.isFrom(ip));
680     }
681
682     /**
683      * Check whether publication is allowed.
684      *
685      * @param feedid The ID of the feed being requested.
686      * @param credentials The offered credentials
687      * @param ip The requesting IP address
688      */
689     public String isPublishPermitted(String feedid, String credentials, String ip) {
690         Feed f = feeds.get(feedid);
691         String nf = "Feed does not exist";
692         if (f != null) {
693             nf = f.status;
694         }
695         if (nf != null) {
696             return (nf);
697         }
698         String user = f.authusers.get(credentials);
699         if (user == null) {
700             return ("Publisher not permitted for this feed");
701         }
702         if (f.subnets.length == 0) {
703             return (null);
704         }
705         byte[] addr = NodeUtils.getInetAddress(ip);
706         for (SubnetMatcher snm : f.subnets) {
707             if (snm.matches(addr)) {
708                 return (null);
709             }
710         }
711         return ("Publisher not permitted for this feed");
712     }
713
714     /**
715      * Get authenticated user
716      */
717     public String getAuthUser(String feedid, String credentials) {
718         return (feeds.get(feedid).authusers.get(credentials));
719     }
720
721     /**
722      * Check if the request should be redirected to a different ingress node
723      */
724     public String getIngressNode(String feedid, String user, String ip) {
725         Feed f = feeds.get(feedid);
726         if (f.redirections.length == 0) {
727             return (null);
728         }
729         byte[] addr = NodeUtils.getInetAddress(ip);
730         for (Redirection r : f.redirections) {
731             if (r.user != null && !user.equals(r.user)) {
732                 continue;
733             }
734             if (r.snm != null && !r.snm.matches(addr)) {
735                 continue;
736             }
737             for (String n : r.nodes) {
738                 if (myname.equals(n)) {
739                     return (null);
740                 }
741             }
742             if (r.nodes.length == 0) {
743                 return (null);
744             }
745             return (r.nodes[rrcntr++ % r.nodes.length]);
746         }
747         return (null);
748     }
749
750     /**
751      * Get a provisioned configuration parameter
752      */
753     public String getProvParam(String name) {
754         return (params.get(name));
755     }
756
757     /**
758      * Get all the DestInfos
759      */
760     public DestInfo[] getAllDests() {
761         return (alldests);
762     }
763
764     /**
765      * Get the targets for a feed
766      *
767      * @param feedid The feed ID
768      * @return The targets this feed should be delivered to
769      */
770     public Target[] getTargets(String feedid) {
771         if (feedid == null) {
772             return (new Target[0]);
773         }
774         Feed f = feeds.get(feedid);
775         if (f == null) {
776             return (new Target[0]);
777         }
778         return (f.targets);
779     }
780
781     /**
782      * Get the feed ID for a subscription
783      *
784      * @param subid The subscription ID
785      * @return The feed ID
786      */
787     public String getFeedId(String subid) {
788         DestInfo di = subinfo.get(subid);
789         if (di == null) {
790             return (null);
791         }
792         return (di.getLogData());
793     }
794
795     /**
796      * Get the spool directory for a subscription
797      *
798      * @param subid The subscription ID
799      * @return The spool directory
800      */
801     public String getSpoolDir(String subid) {
802         DestInfo di = subinfo.get(subid);
803         if (di == null) {
804             return (null);
805         }
806         return (di.getSpool());
807     }
808
809     /**
810      * Get the Authorization value this node uses
811      *
812      * @return The Authorization header value for this node
813      */
814     public String getMyAuth() {
815         return (myauth);
816     }
817
818 }