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 com.att.research.datarouter.node;
\r
31 * Processed configuration for this node.
\r
33 * The NodeConfig represents a processed configuration from the Data Router
\r
34 * provisioning server. Each time configuration data is received from the
\r
35 * provisioning server, a new NodeConfig is created and the previous one
\r
38 public class NodeConfig {
\r
40 * Raw configuration entry for a data router node
\r
42 public static class ProvNode {
\r
43 private String cname;
\r
45 * Construct a node configuration entry.
\r
46 * @param cname The cname of the node.
\r
48 public ProvNode(String cname) {
\r
52 * Get the cname of the node
\r
54 public String getCName() {
\r
59 * Raw configuration entry for a provisioning parameter
\r
61 public static class ProvParam {
\r
62 private String name;
\r
63 private String value;
\r
65 * Construct a provisioning parameter configuration entry.
\r
66 * @param name The name of the parameter.
\r
67 * @param value The value of the parameter.
\r
69 public ProvParam(String name, String value) {
\r
74 * Get the name of the parameter.
\r
76 public String getName() {
\r
80 * Get the value of the parameter.
\r
82 public String getValue() {
\r
87 * Raw configuration entry for a data feed.
\r
89 public static class ProvFeed {
\r
91 private String logdata;
\r
92 private String status;
\r
94 * Construct a feed configuration entry.
\r
95 * @param id The feed ID of the entry.
\r
96 * @param logdata String for log entries about the entry.
\r
97 * @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or null if it is valid.
\r
99 public ProvFeed(String id, String logdata, String status) {
\r
101 this.logdata = logdata;
\r
102 this.status = status;
\r
105 * Get the feed id of the data feed.
\r
107 public String getId() {
\r
111 * Get the log data of the data feed.
\r
113 public String getLogData() {
\r
117 * Get the status of the data feed.
\r
119 public String getStatus() {
\r
124 * Raw configuration entry for a feed user.
\r
126 public static class ProvFeedUser {
\r
127 private String feedid;
\r
128 private String user;
\r
129 private String credentials;
\r
131 * Construct a feed user configuration entry
\r
132 * @param feedid The feed id.
\r
133 * @param user The user that will publish to the feed.
\r
134 * @param credentials The Authorization header the user will use to publish.
\r
136 public ProvFeedUser(String feedid, String user, String credentials) {
\r
137 this.feedid = feedid;
\r
139 this.credentials = credentials;
\r
142 * Get the feed id of the feed user.
\r
144 public String getFeedId() {
\r
148 * Get the user for the feed user.
\r
150 public String getUser() {
\r
154 * Get the credentials for the feed user.
\r
156 public String getCredentials() {
\r
157 return(credentials);
\r
161 * Raw configuration entry for a feed subnet
\r
163 public static class ProvFeedSubnet {
\r
164 private String feedid;
\r
165 private String cidr;
\r
167 * Construct a feed subnet configuration entry
\r
168 * @param feedid The feed ID
\r
169 * @param cidr The CIDR allowed to publish to the feed.
\r
171 public ProvFeedSubnet(String feedid, String cidr) {
\r
172 this.feedid = feedid;
\r
176 * Get the feed id of the feed subnet.
\r
178 public String getFeedId() {
\r
182 * Get the CIDR of the feed subnet.
\r
184 public String getCidr() {
\r
189 * Raw configuration entry for a subscription
\r
191 public static class ProvSubscription {
\r
192 private String subid;
\r
193 private String feedid;
\r
194 private String url;
\r
195 private String authuser;
\r
196 private String credentials;
\r
197 private boolean metaonly;
\r
198 private boolean use100;
\r
200 * Construct a subscription configuration entry
\r
201 * @param subid The subscription ID
\r
202 * @param feedid The feed ID
\r
203 * @param url The base delivery URL (not including the fileid)
\r
204 * @param authuser The user in the credentials used to deliver
\r
205 * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the Authorization header.
\r
206 * @param metaonly Is this a meta data only subscription?
\r
207 * @param use100 Should we send Expect: 100-continue?
\r
209 public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials, boolean metaonly, boolean use100) {
\r
210 this.subid = subid;
\r
211 this.feedid = feedid;
\r
213 this.authuser = authuser;
\r
214 this.credentials = credentials;
\r
215 this.metaonly = metaonly;
\r
216 this.use100 = use100;
\r
219 * Get the subscription ID
\r
221 public String getSubId() {
\r
227 public String getFeedId() {
\r
231 * Get the delivery URL
\r
233 public String getURL() {
\r
239 public String getAuthUser() {
\r
243 * Get the delivery credentials
\r
245 public String getCredentials() {
\r
246 return(credentials);
\r
249 * Is this a meta data only subscription?
\r
251 public boolean isMetaDataOnly() {
\r
255 * Should we send Expect: 100-continue?
\r
257 public boolean isUsing100() {
\r
262 * Raw configuration entry for controlled ingress to the data router node
\r
264 public static class ProvForceIngress {
\r
265 private String feedid;
\r
266 private String subnet;
\r
267 private String user;
\r
268 private String[] nodes;
\r
270 * Construct a forced ingress configuration entry
\r
271 * @param feedid The feed ID that this entry applies to
\r
272 * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all publisher IP addresses
\r
273 * @param user The publishing user this entry applies to or "" if it applies to all publishing users.
\r
274 * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
\r
276 public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
\r
277 this.feedid = feedid;
\r
278 this.subnet = subnet;
\r
280 this.nodes = nodes;
\r
285 public String getFeedId() {
\r
291 public String getSubnet() {
\r
297 public String getUser() {
\r
303 public String[] getNodes() {
\r
308 * Raw configuration entry for controlled egress from the data router
\r
310 public static class ProvForceEgress {
\r
311 private String subid;
\r
312 private String node;
\r
314 * Construct a forced egress configuration entry
\r
315 * @param subid The subscription ID the subscription with forced egress
\r
316 * @param node The node handling deliveries for this subscription
\r
318 public ProvForceEgress(String subid, String node) {
\r
319 this.subid = subid;
\r
323 * Get the subscription ID
\r
325 public String getSubId() {
\r
331 public String getNode() {
\r
336 * Raw configuration entry for routing within the data router network
\r
338 public static class ProvHop {
\r
339 private String from;
\r
341 private String via;
\r
343 * A human readable description of this entry
\r
345 public String toString() {
\r
346 return("Hop " + from + "->" + to + " via " + via);
\r
349 * Construct a hop entry
\r
350 * @param from The FQDN of the node with the data to be delivered
\r
351 * @param to The FQDN of the node that will deliver to the subscriber
\r
352 * @param via The FQDN of the node where the from node should send the data
\r
354 public ProvHop(String from, String to, String via) {
\r
360 * Get the from node
\r
362 public String getFrom() {
\r
368 public String getTo() {
\r
372 * Get the next intermediate node
\r
374 public String getVia() {
\r
378 private static class Redirection {
\r
379 public SubnetMatcher snm;
\r
380 public String user;
\r
381 public String[] nodes;
\r
383 private static class Feed {
\r
384 public String loginfo;
\r
385 public String status;
\r
386 public SubnetMatcher[] subnets;
\r
387 public Hashtable<String, String> authusers = new Hashtable<String, String>();
\r
388 public Redirection[] redirections;
\r
389 public Target[] targets;
\r
391 private Hashtable<String, String> params = new Hashtable<String, String>();
\r
392 private Hashtable<String, Feed> feeds = new Hashtable<String, Feed>();
\r
393 private Hashtable<String, DestInfo> nodeinfo = new Hashtable<String, DestInfo>();
\r
394 private Hashtable<String, DestInfo> subinfo = new Hashtable<String, DestInfo>();
\r
395 private Hashtable<String, IsFrom> nodes = new Hashtable<String, IsFrom>();
\r
396 private String myname;
\r
397 private String myauth;
\r
398 private DestInfo[] alldests;
\r
399 private int rrcntr;
\r
401 * Process the raw provisioning data to configure this node
\r
402 * @param pd The parsed provisioning data
\r
403 * @param myname My name as seen by external systems
\r
404 * @param spooldir The directory where temporary files live
\r
405 * @param port The port number for URLs
\r
406 * @param nodeauthkey The keying string used to generate node authentication credentials
\r
408 public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
\r
409 this.myname = myname;
\r
410 for (ProvParam p: pd.getParams()) {
\r
411 params.put(p.getName(), p.getValue());
\r
413 Vector<DestInfo> div = new Vector<DestInfo>();
\r
414 myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
\r
415 for (ProvNode pn: pd.getNodes()) {
\r
416 String cn = pn.getCName();
\r
417 if (nodeinfo.get(cn) != null) {
\r
420 String auth = NodeUtils.getNodeAuthHdr(cn, nodeauthkey);
\r
421 DestInfo di = new DestInfo("n:" + cn, spooldir + "/n/" + cn, null, "n2n-" + cn, "https://" + cn + ":" + port + "/internal/publish", cn, myauth, false, true);
\r
422 (new File(di.getSpool())).mkdirs();
\r
424 nodeinfo.put(cn, di);
\r
425 nodes.put(auth, new IsFrom(cn));
\r
427 PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[nodeinfo.size()]), pd.getHops());
\r
428 Hashtable<String, Vector<Redirection>> rdtab = new Hashtable<String, Vector<Redirection>>();
\r
429 for (ProvForceIngress pfi: pd.getForceIngress()) {
\r
430 Vector<Redirection> v = rdtab.get(pfi.getFeedId());
\r
432 v = new Vector<Redirection>();
\r
433 rdtab.put(pfi.getFeedId(), v);
\r
435 Redirection r = new Redirection();
\r
436 if (pfi.getSubnet() != null) {
\r
437 r.snm = new SubnetMatcher(pfi.getSubnet());
\r
439 r.user = pfi.getUser();
\r
440 r.nodes = pfi.getNodes();
\r
443 Hashtable<String, Hashtable<String, String>> pfutab = new Hashtable<String, Hashtable<String, String>>();
\r
444 for (ProvFeedUser pfu: pd.getFeedUsers()) {
\r
445 Hashtable<String, String> t = pfutab.get(pfu.getFeedId());
\r
447 t = new Hashtable<String, String>();
\r
448 pfutab.put(pfu.getFeedId(), t);
\r
450 t.put(pfu.getCredentials(), pfu.getUser());
\r
452 Hashtable<String, String> egrtab = new Hashtable<String, String>();
\r
453 for (ProvForceEgress pfe: pd.getForceEgress()) {
\r
454 if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
\r
457 egrtab.put(pfe.getSubId(), pfe.getNode());
\r
459 Hashtable<String, Vector<SubnetMatcher>> pfstab = new Hashtable<String, Vector<SubnetMatcher>>();
\r
460 for (ProvFeedSubnet pfs: pd.getFeedSubnets()) {
\r
461 Vector<SubnetMatcher> v = pfstab.get(pfs.getFeedId());
\r
463 v = new Vector<SubnetMatcher>();
\r
464 pfstab.put(pfs.getFeedId(), v);
\r
466 v.add(new SubnetMatcher(pfs.getCidr()));
\r
468 Hashtable<String, StringBuffer> ttab = new Hashtable<String, StringBuffer>();
\r
469 HashSet<String> allfeeds = new HashSet<String>();
\r
470 for (ProvFeed pfx: pd.getFeeds()) {
\r
471 if (pfx.getStatus() == null) {
\r
472 allfeeds.add(pfx.getId());
\r
475 for (ProvSubscription ps: pd.getSubscriptions()) {
\r
476 String sid = ps.getSubId();
\r
477 String fid = ps.getFeedId();
\r
478 if (!allfeeds.contains(fid)) {
\r
481 if (subinfo.get(sid) != null) {
\r
486 sididx = Integer.parseInt(sid);
\r
487 sididx -= sididx % 100;
\r
488 } catch (Exception e) {
\r
490 String siddir = sididx + "/" + sid;
\r
491 DestInfo di = new DestInfo("s:" + sid, spooldir + "/s/" + siddir, sid, fid, ps.getURL(), ps.getAuthUser(), ps.getCredentials(), ps.isMetaDataOnly(), ps.isUsing100());
\r
492 (new File(di.getSpool())).mkdirs();
\r
494 subinfo.put(sid, di);
\r
495 String egr = egrtab.get(sid);
\r
497 sid = pf.getPath(egr) + sid;
\r
499 StringBuffer sb = ttab.get(fid);
\r
501 sb = new StringBuffer();
\r
504 sb.append(' ').append(sid);
\r
506 alldests = div.toArray(new DestInfo[div.size()]);
\r
507 for (ProvFeed pfx: pd.getFeeds()) {
\r
508 String fid = pfx.getId();
\r
509 Feed f = feeds.get(fid);
\r
515 f.loginfo = pfx.getLogData();
\r
516 f.status = pfx.getStatus();
\r
517 Vector<SubnetMatcher> v1 = pfstab.get(fid);
\r
519 f.subnets = new SubnetMatcher[0];
\r
521 f.subnets = v1.toArray(new SubnetMatcher[v1.size()]);
\r
523 Hashtable<String, String> h1 = pfutab.get(fid);
\r
525 h1 = new Hashtable<String, String>();
\r
528 Vector<Redirection> v2 = rdtab.get(fid);
\r
530 f.redirections = new Redirection[0];
\r
532 f.redirections = v2.toArray(new Redirection[v2.size()]);
\r
534 StringBuffer sb = ttab.get(fid);
\r
536 f.targets = new Target[0];
\r
538 f.targets = parseRouting(sb.toString());
\r
543 * Parse a target string into an array of targets
\r
544 * @param routing Target string
\r
545 * @return Array of targets.
\r
547 public Target[] parseRouting(String routing) {
\r
548 routing = routing.trim();
\r
549 if ("".equals(routing)) {
\r
550 return(new Target[0]);
\r
552 String[] xx = routing.split("\\s+");
\r
553 Hashtable<String, Target> tmap = new Hashtable<String, Target>();
\r
554 HashSet<String> subset = new HashSet<String>();
\r
555 Vector<Target> tv = new Vector<Target>();
\r
556 Target[] ret = new Target[xx.length];
\r
557 for (int i = 0; i < xx.length; i++) {
\r
559 int j = t.indexOf('/');
\r
561 DestInfo di = subinfo.get(t);
\r
563 tv.add(new Target(null, t));
\r
565 if (!subset.contains(t)) {
\r
567 tv.add(new Target(di, null));
\r
571 String node = t.substring(0, j);
\r
572 String rtg = t.substring(j + 1);
\r
573 DestInfo di = nodeinfo.get(node);
\r
575 tv.add(new Target(null, t));
\r
577 Target tt = tmap.get(node);
\r
579 tt = new Target(di, rtg);
\r
580 tmap.put(node, tt);
\r
583 tt.addRouting(rtg);
\r
588 return(tv.toArray(new Target[tv.size()]));
\r
591 * Check whether this is a valid node-to-node transfer
\r
592 * @param credentials Credentials offered by the supposed node
\r
593 * @param ip IP address the request came from
\r
595 public boolean isAnotherNode(String credentials, String ip) {
\r
596 IsFrom n = nodes.get(credentials);
\r
597 return (n != null && n.isFrom(ip));
\r
600 * Check whether publication is allowed.
\r
601 * @param feedid The ID of the feed being requested.
\r
602 * @param credentials The offered credentials
\r
603 * @param ip The requesting IP address
\r
605 public String isPublishPermitted(String feedid, String credentials, String ip) {
\r
606 Feed f = feeds.get(feedid);
\r
607 String nf = "Feed does not exist";
\r
614 String user = f.authusers.get(credentials);
\r
615 if (user == null) {
\r
616 return("Publisher not permitted for this feed");
\r
618 if (f.subnets.length == 0) {
\r
621 byte[] addr = NodeUtils.getInetAddress(ip);
\r
622 for (SubnetMatcher snm: f.subnets) {
\r
623 if (snm.matches(addr)) {
\r
627 return("Publisher not permitted for this feed");
\r
630 * Get authenticated user
\r
632 public String getAuthUser(String feedid, String credentials) {
\r
633 return(feeds.get(feedid).authusers.get(credentials));
\r
636 * Check if the request should be redirected to a different ingress node
\r
638 public String getIngressNode(String feedid, String user, String ip) {
\r
639 Feed f = feeds.get(feedid);
\r
640 if (f.redirections.length == 0) {
\r
643 byte[] addr = NodeUtils.getInetAddress(ip);
\r
644 for (Redirection r: f.redirections) {
\r
645 if (r.user != null && !user.equals(r.user)) {
\r
648 if (r.snm != null && !r.snm.matches(addr)) {
\r
651 for (String n: r.nodes) {
\r
652 if (myname.equals(n)) {
\r
656 if (r.nodes.length == 0) {
\r
659 return(r.nodes[rrcntr++ % r.nodes.length]);
\r
664 * Get a provisioned configuration parameter
\r
666 public String getProvParam(String name) {
\r
667 return(params.get(name));
\r
670 * Get all the DestInfos
\r
672 public DestInfo[] getAllDests() {
\r
676 * Get the targets for a feed
\r
677 * @param feedid The feed ID
\r
678 * @return The targets this feed should be delivered to
\r
680 public Target[] getTargets(String feedid) {
\r
681 if (feedid == null) {
\r
682 return(new Target[0]);
\r
684 Feed f = feeds.get(feedid);
\r
686 return(new Target[0]);
\r
691 * Get the feed ID for a subscription
\r
692 * @param subid The subscription ID
\r
693 * @return The feed ID
\r
695 public String getFeedId(String subid) {
\r
696 DestInfo di = subinfo.get(subid);
\r
700 return(di.getLogData());
\r
703 * Get the spool directory for a subscription
\r
704 * @param subid The subscription ID
\r
705 * @return The spool directory
\r
707 public String getSpoolDir(String subid) {
\r
708 DestInfo di = subinfo.get(subid);
\r
712 return(di.getSpool());
\r
715 * Get the Authorization value this node uses
\r
716 * @return The Authorization header value for this node
\r
718 public String getMyAuth() {
\r