From 35049a05c3491acc0ea3ebfd84a5f221622b1674 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Sat, 6 Dec 2014 08:25:29 +0100 Subject: [PATCH] Allocation: add support for filtering by transport IP address Allocation filtering by IP only works today using the node host address. But in some cases, you might want to filter using the publish address which could be different. --- .../cluster/node/DiscoveryNodeFilters.java | 74 +++++++-- .../node/DiscoveryNodeFiltersTests.java | 156 +++++++++++++++++- .../allocation/filtering.asciidoc | 8 +- 3 files changed, 217 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java index 2ac98dcea13..ab7da4bfc56 100644 --- a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java +++ b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java @@ -19,9 +19,12 @@ package org.elasticsearch.cluster.node; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.InetSocketTransportAddress; import java.util.HashMap; import java.util.Map; @@ -64,21 +67,72 @@ public class DiscoveryNodeFilters { this.filters = filters; } + private boolean matchByIP(String[] values, @Nullable String hostIp, @Nullable String publishIp) { + for (String value : values) { + boolean matchIp = Regex.simpleMatch(value, hostIp) || Regex.simpleMatch(value, publishIp); + if (matchIp) { + return matchIp; + } + } + return false; + } + public boolean match(DiscoveryNode node) { for (Map.Entry entry : filters.entrySet()) { String attr = entry.getKey(); String[] values = entry.getValue(); if ("_ip".equals(attr)) { - for (String value : values) { - if (Regex.simpleMatch(value, node.getHostAddress())) { - if (opType == OpType.OR) { - return true; - } - } else { - if (opType == OpType.AND) { - return false; - } + // We check both the host_ip or the publish_ip + String publishAddress = null; + if (node.address() instanceof InetSocketTransportAddress) { + publishAddress = NetworkAddress.format(((InetSocketTransportAddress) node.address()).address().getAddress()); + } + + boolean match = matchByIP(values, node.getHostAddress(), publishAddress); + + if (opType == OpType.AND) { + if (match) { + // If we match, we can check to the next filter + continue; } + return false; + } + + if (match && opType == OpType.OR) { + return true; + } + } else if ("_host_ip".equals(attr)) { + // We check explicitly only the host_ip + boolean match = matchByIP(values, node.getHostAddress(), null); + if (opType == OpType.AND) { + if (match) { + // If we match, we can check to the next filter + continue; + } + return false; + } + + if (match && opType == OpType.OR) { + return true; + } + } else if ("_publish_ip".equals(attr)) { + // We check explicitly only the publish_ip + String address = null; + if (node.address() instanceof InetSocketTransportAddress) { + address = NetworkAddress.format(((InetSocketTransportAddress) node.address()).address().getAddress()); + } + + boolean match = matchByIP(values, address, null); + if (opType == OpType.AND) { + if (match) { + // If we match, we can check to the next filter + continue; + } + return false; + } + + if (match && opType == OpType.OR) { + return true; } } else if ("_host".equals(attr)) { for (String value : values) { @@ -171,7 +225,7 @@ public class DiscoveryNodeFilters { for (String value : values) { sb.append(value); if (valueCount > 1) { - sb.append(" " + opType.toString() + " "); + sb.append(" ").append(opType.toString()).append(" "); } valueCount--; } diff --git a/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeFiltersTests.java b/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeFiltersTests.java index 141d983ab92..b37495e3285 100644 --- a/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeFiltersTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeFiltersTests.java @@ -23,18 +23,38 @@ import com.google.common.collect.ImmutableMap; import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.DummyTransportAddress; +import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.test.ESTestCase; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.OpType.AND; import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.OpType.OR; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; /** */ public class DiscoveryNodeFiltersTests extends ESTestCase { + private static InetSocketTransportAddress localAddress; + + @BeforeClass + public static void createLocalAddress() throws UnknownHostException { + localAddress = new InetSocketTransportAddress(InetAddress.getByName("192.1.1.54"), 9999); + } + + @AfterClass + public static void releaseLocalAddress() { + localAddress = null; + } + @Test public void nameMatch() { Settings settings = Settings.settingsBuilder() @@ -65,10 +85,10 @@ public class DiscoveryNodeFiltersTests extends ESTestCase { @Test public void idOrNameMatch() { - Settings settings = Settings.settingsBuilder() + Settings settings = shuffleSettings(Settings.settingsBuilder() .put("xxx._id", "id1,blah") .put("xxx.name", "blah,name2") - .build(); + .build()); DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(OR, "xxx.", settings); DiscoveryNode node = new DiscoveryNode("name1", "id1", DummyTransportAddress.INSTANCE, ImmutableMap.of(), Version.CURRENT); @@ -83,22 +103,22 @@ public class DiscoveryNodeFiltersTests extends ESTestCase { @Test public void tagAndGroupMatch() { - Settings settings = Settings.settingsBuilder() + Settings settings = shuffleSettings(Settings.settingsBuilder() .put("xxx.tag", "A") .put("xxx.group", "B") - .build(); + .build()); DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(AND, "xxx.", settings); DiscoveryNode node = new DiscoveryNode("name1", "id1", DummyTransportAddress.INSTANCE, - ImmutableMap.of("tag", "A", "group", "B"), Version.CURRENT); + ImmutableMap.of("tag", "A", "group", "B"), Version.CURRENT); assertThat(filters.match(node), equalTo(true)); node = new DiscoveryNode("name2", "id2", DummyTransportAddress.INSTANCE, - ImmutableMap.of("tag", "A", "group", "B", "name", "X"), Version.CURRENT); + ImmutableMap.of("tag", "A", "group", "B", "name", "X"), Version.CURRENT); assertThat(filters.match(node), equalTo(true)); node = new DiscoveryNode("name3", "id3", DummyTransportAddress.INSTANCE, - ImmutableMap.of("tag", "A", "group", "F", "name", "X"), Version.CURRENT); + ImmutableMap.of("tag", "A", "group", "F", "name", "X"), Version.CURRENT); assertThat(filters.match(node), equalTo(false)); node = new DiscoveryNode("name4", "id4", DummyTransportAddress.INSTANCE, ImmutableMap.of(), Version.CURRENT); @@ -115,4 +135,124 @@ public class DiscoveryNodeFiltersTests extends ESTestCase { DiscoveryNode node = new DiscoveryNode("name1", "id1", DummyTransportAddress.INSTANCE, ImmutableMap.of(), Version.CURRENT); assertThat(filters.match(node), equalTo(true)); } + + @Test + public void ipBindFilteringMatchingAnd() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx.tag", "A") + .put("xxx." + randomFrom("_ip", "_host_ip", "_publish_ip"), "192.1.1.54") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(AND, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(true)); + } + + @Test + public void ipBindFilteringNotMatching() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx.tag", "B") + .put("xxx." + randomFrom("_ip", "_host_ip", "_publish_ip"), "192.1.1.54") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(AND, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(false)); + } + + @Test + public void ipBindFilteringNotMatchingAnd() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx.tag", "A") + .put("xxx." + randomFrom("_ip", "_host_ip", "_publish_ip"), "8.8.8.8") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(AND, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(false)); + } + + @Test + public void ipBindFilteringMatchingOr() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx." + randomFrom("_ip", "_host_ip", "_publish_ip"), "192.1.1.54") + .put("xxx.tag", "A") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(OR, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(true)); + } + + @Test + public void ipBindFilteringNotMatchingOr() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx.tag", "A") + .put("xxx." + randomFrom("_ip", "_host_ip", "_publish_ip"), "8.8.8.8") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(OR, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(true)); + } + + @Test + public void ipPublishFilteringMatchingAnd() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx.tag", "A") + .put("xxx._publish_ip", "192.1.1.54") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(AND, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(true)); + } + + @Test + public void ipPublishFilteringNotMatchingAnd() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx.tag", "A") + .put("xxx._publish_ip", "8.8.8.8") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(AND, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(false)); + } + + @Test + public void ipPublishFilteringMatchingOr() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx._publish_ip", "192.1.1.54") + .put("xxx.tag", "A") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(OR, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(true)); + } + + @Test + public void ipPublishFilteringNotMatchingOr() { + Settings settings = shuffleSettings(Settings.settingsBuilder() + .put("xxx.tag", "A") + .put("xxx._publish_ip", "8.8.8.8") + .build()); + DiscoveryNodeFilters filters = DiscoveryNodeFilters.buildFromSettings(OR, "xxx.", settings); + + DiscoveryNode node = new DiscoveryNode("", "", "", "192.1.1.54", localAddress, ImmutableMap.of("tag", "A"), null); + assertThat(filters.match(node), equalTo(true)); + } + + private Settings shuffleSettings(Settings source) { + Settings.Builder settings = Settings.settingsBuilder(); + List keys = new ArrayList(source.getAsMap().keySet()); + Collections.shuffle(keys, getRandom()); + for (String o : keys) { + settings.put(o, source.getAsMap().get(o)); + } + return settings.build(); + } + + } diff --git a/docs/reference/index-modules/allocation/filtering.asciidoc b/docs/reference/index-modules/allocation/filtering.asciidoc index 99fd1dc7e2e..4c2b7f41a87 100644 --- a/docs/reference/index-modules/allocation/filtering.asciidoc +++ b/docs/reference/index-modules/allocation/filtering.asciidoc @@ -81,9 +81,11 @@ one set of nodes to another: These special attributes are also supported: [horizontal] -`_name`:: Match nodes by node name -`_ip`:: Match nodes by IP address (the IP address associated with the hostname) -`_host`:: Match nodes by hostname +`_name`:: Match nodes by node name +`_host_ip`:: Match nodes by host IP address (IP associated with hostname) +`_publish_ip`:: Match nodes by publish IP address +`_ip`:: Match either `_host_ip` or `_publish_ip` +`_host`:: Match nodes by hostname All attribute values can be specified with wildcards, eg: