/*******************************************************************************
* ============LICENSE_START==================================================
* * org.onap.dmaap
* * ===========================================================================
* * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
* * ===========================================================================
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* * ============LICENSE_END====================================================
* *
* * ECOMP is a trademark and service mark of AT&T Intellectual Property.
* *
******************************************************************************/
package org.onap.dmaap.datarouter.node;
import com.att.eelf.configuration.EELFLogger;
import com.att.eelf.configuration.EELFManager;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import org.jetbrains.annotations.NotNull;
/**
* Processed configuration for this node.
*
*
The NodeConfig represents a processed configuration from the Data Router provisioning server. Each time
* configuration data is received from the provisioning server, a new NodeConfig is created and the previous one
* discarded.
*/
public class NodeConfig {
private static final String PUBLISHER_NOT_PERMITTED = "Publisher not permitted for this feed";
private static EELFLogger logger = EELFManager.getInstance().getLogger(NodeConfig.class);
private HashMap params = new HashMap<>();
private HashMap feeds = new HashMap<>();
private HashMap nodeinfo = new HashMap<>();
private HashMap subinfo = new HashMap<>();
private HashMap nodes = new HashMap<>();
private HashMap provSubscriptions = new HashMap<>();
private String myname;
private String myauth;
private DestInfo[] alldests;
private int rrcntr;
/**
* Process the raw provisioning data to configure this node.
*
* @param pd The parsed provisioning data
* @param myname My name as seen by external systems
* @param spooldir The directory where temporary files live
* @param port The port number for URLs
* @param nodeauthkey The keying string used to generate node authentication credentials
*/
public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
this.myname = myname;
for (ProvParam p : pd.getParams()) {
params.put(p.getName(), p.getValue());
}
ArrayList destInfos = addDestInfoToNodeConfig(pd, myname, spooldir, port, nodeauthkey);
PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[0]), pd.getHops());
HashMap> rdtab = addSubRedirInfoToNodeConfig(pd);
HashMap> pfutab = addFeedUsersToNodeConfig(pd);
HashMap egrtab = addEgressRoutesToNodeConfig(pd, myname);
HashMap> pfstab = addFeedSubnetToNodeConfig(pd);
HashSet allfeeds = addFeedsToNodeConfig(pd);
HashMap feedTargets = addSubsToNodeConfig(pd, spooldir, destInfos, pf, egrtab, allfeeds);
alldests = destInfos.toArray(new DestInfo[0]);
addFeedTargetsToNodeConfig(pd, rdtab, pfutab, pfstab, feedTargets);
}
@NotNull
private ArrayList addDestInfoToNodeConfig(ProvData pd, String myname, String spooldir, int port,
String nodeauthkey) {
ArrayList destInfos = new ArrayList<>();
myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
for (ProvNode pn : pd.getNodes()) {
String commonName = pn.getCName();
if (nodeinfo.get(commonName) != null) {
continue;
}
DestInfo di = new DestInfoBuilder().setName("n:" + commonName).setSpool(spooldir + "/n/" + commonName)
.setSubid(null)
.setLogdata("n2n-" + commonName).setUrl("https://" + commonName + ":" + port + "/internal/publish")
.setAuthuser(commonName).setAuthentication(myauth).setMetaonly(false).setUse100(true)
.setPrivilegedSubscriber(false).setFollowRedirects(false).setDecompress(false).createDestInfo();
(new File(di.getSpool())).mkdirs();
String auth = NodeUtils.getNodeAuthHdr(commonName, nodeauthkey);
destInfos.add(di);
nodeinfo.put(commonName, di);
nodes.put(auth, new IsFrom(commonName));
}
return destInfos;
}
@NotNull
private HashMap> addSubRedirInfoToNodeConfig(ProvData pd) {
HashMap> rdtab = new HashMap<>();
for (ProvForceIngress pfi : pd.getForceIngress()) {
ArrayList redirections = rdtab.get(pfi.getFeedId());
if (redirections == null) {
redirections = new ArrayList<>();
rdtab.put(pfi.getFeedId(), redirections);
}
Redirection redirection = new Redirection();
if (pfi.getSubnet() != null) {
redirection.snm = new SubnetMatcher(pfi.getSubnet());
}
redirection.user = pfi.getUser();
redirection.nodes = pfi.getNodes();
redirections.add(redirection);
}
return rdtab;
}
@NotNull
private HashMap> addFeedUsersToNodeConfig(ProvData pd) {
HashMap> pfutab = new HashMap<>();
for (ProvFeedUser pfu : pd.getFeedUsers()) {
HashMap userInfo = pfutab.get(pfu.getFeedId());
if (userInfo == null) {
userInfo = new HashMap<>();
pfutab.put(pfu.getFeedId(), userInfo);
}
userInfo.put(pfu.getCredentials(), pfu.getUser());
}
return pfutab;
}
@NotNull
private HashMap addEgressRoutesToNodeConfig(ProvData pd, String myname) {
HashMap egrtab = new HashMap<>();
for (ProvForceEgress pfe : pd.getForceEgress()) {
if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
continue;
}
egrtab.put(pfe.getSubId(), pfe.getNode());
}
return egrtab;
}
@NotNull
private HashMap> addFeedSubnetToNodeConfig(ProvData pd) {
HashMap> pfstab = new HashMap<>();
for (ProvFeedSubnet pfs : pd.getFeedSubnets()) {
ArrayList subnetMatchers = pfstab.get(pfs.getFeedId());
if (subnetMatchers == null) {
subnetMatchers = new ArrayList<>();
pfstab.put(pfs.getFeedId(), subnetMatchers);
}
subnetMatchers.add(new SubnetMatcher(pfs.getCidr()));
}
return pfstab;
}
@NotNull
private HashSet addFeedsToNodeConfig(ProvData pd) {
HashSet allfeeds = new HashSet<>();
for (ProvFeed pfx : pd.getFeeds()) {
if (pfx.getStatus() == null) {
allfeeds.add(pfx.getId());
}
}
return allfeeds;
}
@NotNull
private HashMap addSubsToNodeConfig(ProvData pd, String spooldir,
ArrayList destInfos, PathFinder pf, HashMap egrtab, HashSet allfeeds) {
HashMap feedTargets = new HashMap<>();
for (ProvSubscription provSubscription : pd.getSubscriptions()) {
String subId = provSubscription.getSubId();
String feedId = provSubscription.getFeedId();
if (isFeedOrSubKnown(allfeeds, subId, feedId)) {
continue;
}
int sididx = 999;
try {
sididx = Integer.parseInt(subId);
sididx -= sididx % 100;
} catch (Exception e) {
logger.error("NODE0517 Exception NodeConfig: " + e);
}
String subscriptionDirectory = sididx + "/" + subId;
DestInfo destinationInfo = new DestInfo("s:" + subId,
spooldir + "/s/" + subscriptionDirectory, provSubscription);
(new File(destinationInfo.getSpool())).mkdirs();
destInfos.add(destinationInfo);
provSubscriptions.put(subId, provSubscription);
subinfo.put(subId, destinationInfo);
String egr = egrtab.get(subId);
if (egr != null) {
subId = pf.getPath(egr) + subId;
}
StringBuilder sb = feedTargets.get(feedId);
if (sb == null) {
sb = new StringBuilder();
feedTargets.put(feedId, sb);
}
sb.append(' ').append(subId);
}
return feedTargets;
}
private void addFeedTargetsToNodeConfig(ProvData pd, HashMap> rdtab,
HashMap> pfutab, HashMap> pfstab,
HashMap feedTargets) {
for (ProvFeed pfx : pd.getFeeds()) {
String fid = pfx.getId();
Feed feed = feeds.get(fid);
if (feed != null) {
continue;
}
feed = new Feed();
feeds.put(fid, feed);
feed.createdDate = pfx.getCreatedDate();
feed.loginfo = pfx.getLogData();
feed.status = pfx.getStatus();
/*
* AAF changes: TDP EPIC US# 307413
* Passing aafInstance from ProvFeed to identify legacy/AAF feeds
*/
feed.aafInstance = pfx.getAafInstance();
ArrayList v1 = pfstab.get(fid);
if (v1 == null) {
feed.subnets = new SubnetMatcher[0];
} else {
feed.subnets = v1.toArray(new SubnetMatcher[0]);
}
HashMap h1 = pfutab.get(fid);
if (h1 == null) {
h1 = new HashMap();
}
feed.authusers = h1;
ArrayList v2 = rdtab.get(fid);
if (v2 == null) {
feed.redirections = new Redirection[0];
} else {
feed.redirections = v2.toArray(new Redirection[0]);
}
StringBuilder sb = feedTargets.get(fid);
if (sb == null) {
feed.targets = new Target[0];
} else {
feed.targets = parseRouting(sb.toString());
}
}
}
/**
* Parse a target string into an array of targets.
*
* @param routing Target string
* @return Array of targets.
*/
public Target[] parseRouting(String routing) {
routing = routing.trim();
if ("".equals(routing)) {
return (new Target[0]);
}
String[] routingTable = routing.split("\\s+");
HashMap tmap = new HashMap<>();
HashSet subset = new HashSet<>();
ArrayList targets = new ArrayList<>();
for (int i = 0; i < routingTable.length; i++) {
String target = routingTable[i];
int index = target.indexOf('/');
if (index == -1) {
addTarget(subset, targets, target);
} else {
addTargetWithRouting(tmap, targets, target, index);
}
}
return (targets.toArray(new Target[0]));
}
/**
* Check whether this is a valid node-to-node transfer.
*
* @param credentials Credentials offered by the supposed node
* @param ip IP address the request came from
*/
public boolean isAnotherNode(String credentials, String ip) {
IsFrom node = nodes.get(credentials);
return (node != null && node.isFrom(ip));
}
/**
* Check whether publication is allowed.
*
* @param feedid The ID of the feed being requested.
* @param credentials The offered credentials
* @param ip The requesting IP address
*/
public String isPublishPermitted(String feedid, String credentials, String ip) {
Feed feed = feeds.get(feedid);
String nf = "Feed does not exist";
if (feed != null) {
nf = feed.status;
}
if (nf != null) {
return (nf);
}
String user = feed.authusers.get(credentials);
if (user == null) {
return (PUBLISHER_NOT_PERMITTED);
}
if (feed.subnets.length == 0) {
return (null);
}
byte[] addr = NodeUtils.getInetAddress(ip);
for (SubnetMatcher snm : feed.subnets) {
if (snm.matches(addr)) {
return (null);
}
}
return (PUBLISHER_NOT_PERMITTED);
}
/**
* Check whether publication is allowed for AAF Feed.
*
* @param feedid The ID of the feed being requested.
* @param ip The requesting IP address
*/
public String isPublishPermitted(String feedid, String ip) {
Feed feed = feeds.get(feedid);
String nf = "Feed does not exist";
if (feed != null) {
nf = feed.status;
}
if (nf != null) {
return nf;
}
if (feed.subnets.length == 0) {
return null;
}
byte[] addr = NodeUtils.getInetAddress(ip);
for (SubnetMatcher snm : feed.subnets) {
if (snm.matches(addr)) {
return null;
}
}
return PUBLISHER_NOT_PERMITTED;
}
/**
* Check whether delete file is allowed.
*
* @param subId The ID of the subscription being requested.
*/
public boolean isDeletePermitted(String subId) {
ProvSubscription provSubscription = provSubscriptions.get(subId);
return provSubscription.isPrivilegedSubscriber();
}
/**
* Get authenticated user.
*/
public String getAuthUser(String feedid, String credentials) {
return (feeds.get(feedid).authusers.get(credentials));
}
/**
* AAF changes: TDP EPIC US# 307413 Check AAF_instance for feed ID.
*
* @param feedid The ID of the feed specified
*/
public String getAafInstance(String feedid) {
Feed feed = feeds.get(feedid);
return feed.aafInstance;
}
/**
* Check if the request should be redirected to a different ingress node.
*/
public String getIngressNode(String feedid, String user, String ip) {
Feed feed = feeds.get(feedid);
if (feed.redirections.length == 0) {
return (null);
}
byte[] addr = NodeUtils.getInetAddress(ip);
for (Redirection r : feed.redirections) {
if ((r.user != null && !user.equals(r.user)) || (r.snm != null && !r.snm.matches(addr))) {
continue;
}
for (String n : r.nodes) {
if (myname.equals(n)) {
return (null);
}
}
if (r.nodes.length == 0) {
return (null);
}
return (r.nodes[rrcntr++ % r.nodes.length]);
}
return (null);
}
/**
* Get a provisioned configuration parameter.
*/
public String getProvParam(String name) {
return (params.get(name));
}
/**
* Get all the DestInfos.
*/
public DestInfo[] getAllDests() {
return (alldests);
}
/**
* Get the targets for a feed.
*
* @param feedid The feed ID
* @return The targets this feed should be delivered to
*/
public Target[] getTargets(String feedid) {
if (feedid == null) {
return (new Target[0]);
}
Feed feed = feeds.get(feedid);
if (feed == null) {
return (new Target[0]);
}
return (feed.targets);
}
/**
* Get the creation date for a feed.
*
* @param feedid The feed ID
* @return the timestamp of creation date of feed id passed
*/
public String getCreatedDate(String feedid) {
Feed feed = feeds.get(feedid);
return (feed.createdDate);
}
/**
* Get the feed ID for a subscription.
*
* @param subid The subscription ID
* @return The feed ID
*/
public String getFeedId(String subid) {
DestInfo di = subinfo.get(subid);
if (di == null) {
return (null);
}
return (di.getLogData());
}
/**
* Get the spool directory for a subscription.
*
* @param subid The subscription ID
* @return The spool directory
*/
public String getSpoolDir(String subid) {
DestInfo di = subinfo.get(subid);
if (di == null) {
return (null);
}
return (di.getSpool());
}
/**
* Get the Authorization value this node uses.
*
* @return The Authorization header value for this node
*/
public String getMyAuth() {
return (myauth);
}
private boolean isFeedOrSubKnown(HashSet allfeeds, String subId, String feedId) {
return !allfeeds.contains(feedId) || subinfo.get(subId) != null;
}
private void addTargetWithRouting(HashMap tmap, ArrayList targets, String target,
int index) {
String node = target.substring(0, index);
String rtg = target.substring(index + 1);
DestInfo di = nodeinfo.get(node);
if (di == null) {
targets.add(new Target(null, target));
} else {
Target tt = tmap.get(node);
if (tt == null) {
tt = new Target(di, rtg);
tmap.put(node, tt);
targets.add(tt);
} else {
tt.addRouting(rtg);
}
}
}
private void addTarget(HashSet subset, ArrayList targets, String target) {
DestInfo destInfo = subinfo.get(target);
if (destInfo == null) {
targets.add(new Target(null, target));
} else {
if (!subset.contains(target)) {
subset.add(target);
targets.add(new Target(destInfo, null));
}
}
}
/**
* Raw configuration entry for a data router node.
*/
public static class ProvNode {
private String cname;
/**
* Construct a node configuration entry.
*
* @param cname The cname of the node.
*/
public ProvNode(String cname) {
this.cname = cname;
}
/**
* Get the cname of the node.
*/
public String getCName() {
return (cname);
}
}
/**
* Raw configuration entry for a provisioning parameter.
*/
public static class ProvParam {
private String name;
private String value;
/**
* Construct a provisioning parameter configuration entry.
*
* @param name The name of the parameter.
* @param value The value of the parameter.
*/
public ProvParam(String name, String value) {
this.name = name;
this.value = value;
}
/**
* Get the name of the parameter.
*/
public String getName() {
return (name);
}
/**
* Get the value of the parameter.
*/
public String getValue() {
return (value);
}
}
/**
* Raw configuration entry for a data feed.
*/
public static class ProvFeed {
private String id;
private String logdata;
private String status;
private String createdDate;
/*
* AAF changes: TDP EPIC US# 307413
* Passing aafInstance from to identify legacy/AAF feeds
*/
private String aafInstance;
/**
* Construct a feed configuration entry.
*
* @param id The feed ID of the entry.
* @param logdata String for log entries about the entry.
* @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or
* null if it is valid.
*/
public ProvFeed(String id, String logdata, String status, String createdDate, String aafInstance) {
this.id = id;
this.logdata = logdata;
this.status = status;
this.createdDate = createdDate;
this.aafInstance = aafInstance;
}
/**
* Get the created date of the data feed.
*/
public String getCreatedDate() {
return (createdDate);
}
/**
* Get the aafInstance of the data feed.
*/
public String getAafInstance() {
return aafInstance;
}
/**
* Get the feed id of the data feed.
*/
public String getId() {
return (id);
}
/**
* Get the log data of the data feed.
*/
public String getLogData() {
return (logdata);
}
/**
* Get the status of the data feed.
*/
public String getStatus() {
return (status);
}
}
/**
* Raw configuration entry for a feed user.
*/
public static class ProvFeedUser {
private String feedid;
private String user;
private String credentials;
/**
* Construct a feed user configuration entry.
*
* @param feedid The feed id.
* @param user The user that will publish to the feed.
* @param credentials The Authorization header the user will use to publish.
*/
public ProvFeedUser(String feedid, String user, String credentials) {
this.feedid = feedid;
this.user = user;
this.credentials = credentials;
}
/**
* Get the feed id of the feed user.
*/
public String getFeedId() {
return (feedid);
}
/**
* Get the user for the feed user.
*/
public String getUser() {
return (user);
}
/**
* Get the credentials for the feed user.
*/
public String getCredentials() {
return (credentials);
}
}
/**
* Raw configuration entry for a feed subnet.
*/
public static class ProvFeedSubnet {
private String feedid;
private String cidr;
/**
* Construct a feed subnet configuration entry.
*
* @param feedid The feed ID
* @param cidr The CIDR allowed to publish to the feed.
*/
public ProvFeedSubnet(String feedid, String cidr) {
this.feedid = feedid;
this.cidr = cidr;
}
/**
* Get the feed id of the feed subnet.
*/
public String getFeedId() {
return (feedid);
}
/**
* Get the CIDR of the feed subnet.
*/
public String getCidr() {
return (cidr);
}
}
/**
* Raw configuration entry for a subscription.
*/
public static class ProvSubscription {
private String subid;
private String feedid;
private String url;
private String authuser;
private String credentials;
private boolean metaonly;
private boolean use100;
private boolean privilegedSubscriber;
private boolean followRedirect;
private boolean decompress;
/**
* Construct a subscription configuration entry.
*
* @param subid The subscription ID
* @param feedid The feed ID
* @param url The base delivery URL (not including the fileid)
* @param authuser The user in the credentials used to deliver
* @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the
* Authorization header.
* @param metaonly Is this a meta data only subscription?
* @param use100 Should we send Expect: 100-continue?
* @param privilegedSubscriber Can we wait to receive a delete file call before deleting file
* @param followRedirect Is follow redirect of destination enabled?
* @param decompress To see if they want their information compressed or decompressed
*/
public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials,
boolean metaonly, boolean use100, boolean privilegedSubscriber, boolean followRedirect,
boolean decompress) {
this.subid = subid;
this.feedid = feedid;
this.url = url;
this.authuser = authuser;
this.credentials = credentials;
this.metaonly = metaonly;
this.use100 = use100;
this.privilegedSubscriber = privilegedSubscriber;
this.followRedirect = followRedirect;
this.decompress = decompress;
}
/**
* Get the subscription ID.
*/
public String getSubId() {
return (subid);
}
/**
* Get the feed ID.
*/
public String getFeedId() {
return (feedid);
}
/**
* Get the delivery URL.
*/
public String getURL() {
return (url);
}
/**
* Get the user.
*/
public String getAuthUser() {
return (authuser);
}
/**
* Get the delivery credentials.
*/
public String getCredentials() {
return (credentials);
}
/**
* Is this a meta data only subscription.
*/
public boolean isMetaDataOnly() {
return (metaonly);
}
/**
* Should we send Expect: 100-continue.
*/
public boolean isUsing100() {
return (use100);
}
/**
* Can we wait to receive a delete file call before deleting file.
*/
public boolean isPrivilegedSubscriber() {
return (privilegedSubscriber);
}
/**
* Should I decompress the file before sending it on.
*/
public boolean isDecompress() {
return (decompress);
}
/**
* New field is added - FOLLOW_REDIRECTS feature iTrack:DATARTR-17 - 1706 Get the followRedirect of this
* destination.
*/
boolean getFollowRedirect() {
return (followRedirect);
}
}
/**
* Raw configuration entry for controlled ingress to the data router node.
*/
public static class ProvForceIngress {
private String feedid;
private String subnet;
private String user;
private String[] nodes;
/**
* Construct a forced ingress configuration entry.
*
* @param feedid The feed ID that this entry applies to
* @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all
* publisher IP addresses
* @param user The publishing user this entry applies to or "" if it applies to all publishing users.
* @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
*/
public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
this.feedid = feedid;
this.subnet = subnet;
this.user = user;
//Sonar fix
if (nodes == null) {
this.nodes = new String[0];
} else {
this.nodes = Arrays.copyOf(nodes, nodes.length);
}
}
/**
* Get the feed ID.
*/
public String getFeedId() {
return (feedid);
}
/**
* Get the subnet.
*/
public String getSubnet() {
return (subnet);
}
/**
* Get the user.
*/
public String getUser() {
return (user);
}
/**
* Get the node.
*/
public String[] getNodes() {
return (nodes);
}
}
/**
* Raw configuration entry for controlled egress from the data router.
*/
public static class ProvForceEgress {
private String subid;
private String node;
/**
* Construct a forced egress configuration entry.
*
* @param subid The subscription ID the subscription with forced egress
* @param node The node handling deliveries for this subscription
*/
public ProvForceEgress(String subid, String node) {
this.subid = subid;
this.node = node;
}
/**
* Get the subscription ID.
*/
public String getSubId() {
return (subid);
}
/**
* Get the node.
*/
public String getNode() {
return (node);
}
}
/**
* Raw configuration entry for routing within the data router network.
*/
public static class ProvHop {
private String from;
private String to;
private String via;
/**
* Construct a hop entry.
*
* @param from The FQDN of the node with the data to be delivered
* @param to The FQDN of the node that will deliver to the subscriber
* @param via The FQDN of the node where the from node should send the data
*/
public ProvHop(String from, String to, String via) {
this.from = from;
this.to = to;
this.via = via;
}
/**
* A human readable description of this entry.
*/
public String toString() {
return ("Hop " + from + "->" + to + " via " + via);
}
/**
* Get the from node.
*/
public String getFrom() {
return (from);
}
/**
* Get the to node.
*/
public String getTo() {
return (to);
}
/**
* Get the next intermediate node.
*/
public String getVia() {
return (via);
}
}
private static class Redirection {
SubnetMatcher snm;
String user;
String[] nodes;
}
private static class Feed {
String loginfo;
String status;
SubnetMatcher[] subnets;
HashMap authusers = new HashMap<>();
Redirection[] redirections;
Target[] targets;
String createdDate;
String aafInstance;
}
}