// This file is part of OpenTSDB. // Copyright (C) 2016 The OpenTSDB Authors. // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 2.1 of the License, or (at your // option) any later version. This program is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser // General Public License for more details. You should have received a copy // of the GNU Lesser General Public License along with this program. If not, // see <http://www.gnu.org/licenses/>. package net.opentsdb.uid; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import com.google.common.annotations.VisibleForTesting; import com.stumbleupon.async.Deferred; import net.opentsdb.core.TSDB; import net.opentsdb.stats.StatsCollector; import net.opentsdb.uid.UniqueId.UniqueIdType; import net.opentsdb.utils.Config; /** * A UID filter implementation using regular expression based whitelists. * Multiple regular expressions can be provided in the configuration file with * a configurable delimiter. Each expression is compiled into a list per UID * type and when a new UID passes through the filter, each expression in the * list is compared to make sure the name satisfies all expressions. */ public class UniqueIdWhitelistFilter extends UniqueIdFilterPlugin { /** Default delimiter */ public static final String DEFAULT_REGEX_DELIMITER = ","; /** Lists of patterns for each type. */ private List<Pattern> metric_patterns; private List<Pattern> tagk_patterns; private List<Pattern> tagv_patterns; /** Counters for tracking stats */ private final AtomicLong metrics_rejected = new AtomicLong(); private final AtomicLong metrics_allowed = new AtomicLong(); private final AtomicLong tagks_rejected = new AtomicLong(); private final AtomicLong tagks_allowed = new AtomicLong(); private final AtomicLong tagvs_rejected = new AtomicLong(); private final AtomicLong tagvs_allowed = new AtomicLong(); @Override public void initialize(final TSDB tsdb) { final Config config = tsdb.getConfig(); String delimiter = config.getString("tsd.uidfilter.whitelist.delimiter"); if (delimiter == null) { delimiter = DEFAULT_REGEX_DELIMITER; } String raw = config.getString("tsd.uidfilter.whitelist.metric_patterns"); if (raw != null) { final String[] splits = raw.split(delimiter); metric_patterns = new ArrayList<Pattern>(splits.length); for (final String pattern : splits) { try { metric_patterns.add(Pattern.compile(pattern)); } catch (PatternSyntaxException e) { throw new IllegalArgumentException("The metric whitelist pattern [" + pattern + "] does not compile.", e); } } } raw = config.getString("tsd.uidfilter.whitelist.tagk_patterns"); if (raw != null) { final String[] splits = raw.split(delimiter); tagk_patterns = new ArrayList<Pattern>(splits.length); for (final String pattern : splits) { try { tagk_patterns.add(Pattern.compile(pattern)); } catch (PatternSyntaxException e) { throw new IllegalArgumentException("The tagk whitelist pattern [" + pattern + "] does not compile.", e); } } } raw = config.getString("tsd.uidfilter.whitelist.tagv_patterns"); if (raw != null) { final String[] splits = raw.split(delimiter); tagv_patterns = new ArrayList<Pattern>(splits.length); for (final String pattern : splits) { try { tagv_patterns.add(Pattern.compile(pattern)); } catch (PatternSyntaxException e) { throw new IllegalArgumentException("The tagv whitelist pattern [" + pattern + "] does not compile.", e); } } } } @Override public Deferred<Object> shutdown() { return Deferred.fromResult(null); } @Override public String version() { return "2.3.0"; } @Override public void collectStats(final StatsCollector collector) { collector.record("uid.filter.whitelist.accepted", metrics_allowed.get(), "type=metrics"); collector.record("uid.filter.whitelist.accepted", tagks_allowed.get(), "type=tagk"); collector.record("uid.filter.whitelist.accepted", tagvs_allowed.get(), "type=tagv"); collector.record("uid.filter.whitelist.rejected", metrics_rejected.get(), "type=metrics"); collector.record("uid.filter.whitelist.rejected", tagks_rejected.get(), "type=tagk"); collector.record("uid.filter.whitelist.rejected", tagvs_rejected.get(), "type=tagv"); } @Override public Deferred<Boolean> allowUIDAssignment( final UniqueIdType type, final String value, final String metric, final Map<String, String> tags) { switch (type) { case METRIC: if (metric_patterns != null) { for (final Pattern pattern : metric_patterns) { if (!pattern.matcher(value).find()) { metrics_rejected.incrementAndGet(); return Deferred.fromResult(false); } } } metrics_allowed.incrementAndGet(); break; case TAGK: if (tagk_patterns != null) { for (final Pattern pattern : tagk_patterns) { if (!pattern.matcher(value).find()) { tagks_rejected.incrementAndGet(); return Deferred.fromResult(false); } } } tagks_allowed.incrementAndGet(); break; case TAGV: if (tagv_patterns != null) { for (final Pattern pattern : tagv_patterns) { if (!pattern.matcher(value).find()) { tagvs_rejected.incrementAndGet(); return Deferred.fromResult(false); } } } tagvs_allowed.incrementAndGet(); break; } // all patterns passed, yay! return Deferred.fromResult(true); } @Override public boolean fillterUIDAssignments() { return true; } @VisibleForTesting List<Pattern> metricPatterns() { return metric_patterns; } @VisibleForTesting List<Pattern> tagkPatterns() { return tagk_patterns; } @VisibleForTesting List<Pattern> tagvPatterns() { return tagv_patterns; } }