diff --git a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DecommissionSubCommand.java b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DecommissionSubCommand.java index 23ff9176df9..e7d3a444383 100644 --- a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DecommissionSubCommand.java +++ b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DecommissionSubCommand.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Scanner; /** * Decommission one or more datanodes. @@ -41,12 +42,26 @@ public class DecommissionSubCommand extends ScmSubcommand { @CommandLine.Spec private CommandLine.Model.CommandSpec spec; - @CommandLine.Parameters(description = "List of fully qualified host names") - private List hosts = new ArrayList<>(); + @CommandLine.Parameters(description = "One or more host names separated by spaces. " + + "To read from stdin, specify '-' and supply the host names " + + "separated by newlines.", + paramLabel = "") + private List parameters = new ArrayList<>(); @Override public void execute(ScmClient scmClient) throws IOException { - if (hosts.size() > 0) { + if (parameters.size() > 0) { + List hosts; + // Whether to read from stdin + if (parameters.get(0).equals("-")) { + hosts = new ArrayList<>(); + Scanner scanner = new Scanner(System.in, "UTF-8"); + while (scanner.hasNextLine()) { + hosts.add(scanner.nextLine().trim()); + } + } else { + hosts = parameters; + } List errors = scmClient.decommissionNodes(hosts); System.out.println("Started decommissioning datanode(s):\n" + String.join("\n", hosts)); diff --git a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/MaintenanceSubCommand.java b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/MaintenanceSubCommand.java index a64c400f66f..82d263b416f 100644 --- a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/MaintenanceSubCommand.java +++ b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/MaintenanceSubCommand.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Scanner; /** * Place one or more datanodes into Maintenance Mode. @@ -41,8 +42,11 @@ public class MaintenanceSubCommand extends ScmSubcommand { @CommandLine.Spec private CommandLine.Model.CommandSpec spec; - @CommandLine.Parameters(description = "List of fully qualified host names") - private List hosts = new ArrayList<>(); + @CommandLine.Parameters(description = "One or more host names separated by spaces. " + + "To read from stdin, specify '-' and supply the host names " + + "separated by newlines.", + paramLabel = "") + private List parameters = new ArrayList<>(); @CommandLine.Option(names = {"--end"}, description = "Automatically end maintenance after the given hours. " + @@ -51,7 +55,18 @@ public class MaintenanceSubCommand extends ScmSubcommand { @Override public void execute(ScmClient scmClient) throws IOException { - if (hosts.size() > 0) { + if (parameters.size() > 0) { + List hosts; + // Whether to read from stdin + if (parameters.get(0).equals("-")) { + hosts = new ArrayList<>(); + Scanner scanner = new Scanner(System.in, "UTF-8"); + while (scanner.hasNextLine()) { + hosts.add(scanner.nextLine().trim()); + } + } else { + hosts = parameters; + } List errors = scmClient.startMaintenanceNodes(hosts, endInHours); System.out.println("Entering maintenance mode on datanode(s):\n" + diff --git a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/RecommissionSubCommand.java b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/RecommissionSubCommand.java index 61f7826cf64..e21d61ed3d7 100644 --- a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/RecommissionSubCommand.java +++ b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/RecommissionSubCommand.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Scanner; /** * Recommission one or more datanodes. @@ -42,12 +43,26 @@ public class RecommissionSubCommand extends ScmSubcommand { @CommandLine.Spec private CommandLine.Model.CommandSpec spec; - @CommandLine.Parameters(description = "List of fully qualified host names") - private List hosts = new ArrayList<>(); + @CommandLine.Parameters(description = "One or more host names separated by spaces. " + + "To read from stdin, specify '-' and supply the host names " + + "separated by newlines.", + paramLabel = "") + private List parameters = new ArrayList<>(); @Override public void execute(ScmClient scmClient) throws IOException { - if (hosts.size() > 0) { + if (parameters.size() > 0) { + List hosts; + // Whether to read from stdin + if (parameters.get(0).equals("-")) { + hosts = new ArrayList<>(); + Scanner scanner = new Scanner(System.in, "UTF-8"); + while (scanner.hasNextLine()) { + hosts.add(scanner.nextLine().trim()); + } + } else { + hosts = parameters; + } List errors = scmClient.recommissionNodes(hosts); System.out.println("Started recommissioning datanode(s):\n" + String.join("\n", hosts)); diff --git a/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDecommissionSubCommand.java b/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDecommissionSubCommand.java index 7e5b857d179..afce23b5fd5 100644 --- a/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDecommissionSubCommand.java +++ b/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDecommissionSubCommand.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; @@ -47,6 +48,7 @@ public class TestDecommissionSubCommand { private DecommissionSubCommand cmd; + private ScmClient scmClient; private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; @@ -56,6 +58,7 @@ public class TestDecommissionSubCommand { @BeforeEach public void setup() throws UnsupportedEncodingException { cmd = new DecommissionSubCommand(); + scmClient = mock(ScmClient.class); System.setOut(new PrintStream(outContent, false, DEFAULT_ENCODING)); System.setErr(new PrintStream(errContent, false, DEFAULT_ENCODING)); } @@ -66,9 +69,37 @@ public void tearDown() { System.setErr(originalErr); } + @Test + public void testMultipleHostnamesCanBeReadFromStdin() throws Exception { + when(scmClient.decommissionNodes(anyList())) + .thenAnswer(invocation -> new ArrayList()); + + String input = "host1\nhost2\nhost3\n"; + System.setIn(new ByteArrayInputStream(input.getBytes(DEFAULT_ENCODING))); + CommandLine c = new CommandLine(cmd); + c.parseArgs("-"); + cmd.execute(scmClient); + + Pattern p = Pattern.compile( + "^Started\\sdecommissioning\\sdatanode\\(s\\)", Pattern.MULTILINE); + Matcher m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host1$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host2$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host3$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + } + @Test public void testNoErrorsWhenDecommissioning() throws IOException { - ScmClient scmClient = mock(ScmClient.class); when(scmClient.decommissionNodes(anyList())) .thenAnswer(invocation -> new ArrayList()); @@ -92,7 +123,6 @@ public void testNoErrorsWhenDecommissioning() throws IOException { @Test public void testErrorsReportedWhenDecommissioning() throws IOException { - ScmClient scmClient = mock(ScmClient.class); when(scmClient.decommissionNodes(anyList())) .thenAnswer(invocation -> { ArrayList e = new ArrayList<>(); diff --git a/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestMaintenanceSubCommand.java b/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestMaintenanceSubCommand.java index d3f7f026ddb..694ba0e282c 100644 --- a/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestMaintenanceSubCommand.java +++ b/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestMaintenanceSubCommand.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; @@ -48,6 +49,7 @@ public class TestMaintenanceSubCommand { private MaintenanceSubCommand cmd; + private ScmClient scmClient; private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; @@ -57,6 +59,7 @@ public class TestMaintenanceSubCommand { @BeforeEach public void setup() throws UnsupportedEncodingException { cmd = new MaintenanceSubCommand(); + scmClient = mock(ScmClient.class); System.setOut(new PrintStream(outContent, false, DEFAULT_ENCODING)); System.setErr(new PrintStream(errContent, false, DEFAULT_ENCODING)); } @@ -67,9 +70,37 @@ public void tearDown() { System.setErr(originalErr); } + @Test + public void testMultipleHostnamesCanBeReadFromStdin() throws Exception { + when(scmClient.decommissionNodes(anyList())) + .thenAnswer(invocation -> new ArrayList()); + + String input = "host1\nhost2\nhost3\n"; + System.setIn(new ByteArrayInputStream(input.getBytes(DEFAULT_ENCODING))); + CommandLine c = new CommandLine(cmd); + c.parseArgs("-"); + cmd.execute(scmClient); + + Pattern p = Pattern.compile( + "^Entering\\smaintenance\\smode\\son\\sdatanode\\(s\\)", Pattern.MULTILINE); + Matcher m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host1$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host2$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host3$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + } + @Test public void testNoErrorsWhenEnteringMaintenance() throws IOException { - ScmClient scmClient = mock(ScmClient.class); when(scmClient.startMaintenanceNodes(anyList(), anyInt())) .thenAnswer(invocation -> new ArrayList()); @@ -94,7 +125,6 @@ public void testNoErrorsWhenEnteringMaintenance() throws IOException { @Test public void testErrorsReportedWhenEnteringMaintenance() throws IOException { - ScmClient scmClient = mock(ScmClient.class); when(scmClient.startMaintenanceNodes(anyList(), anyInt())) .thenAnswer(invocation -> { ArrayList e = new ArrayList<>(); diff --git a/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestRecommissionSubCommand.java b/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestRecommissionSubCommand.java index 41ce0d90cb7..7f4dbec7734 100644 --- a/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestRecommissionSubCommand.java +++ b/hadoop-hdds/tools/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestRecommissionSubCommand.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; @@ -47,6 +48,7 @@ public class TestRecommissionSubCommand { private RecommissionSubCommand cmd; + private ScmClient scmClient; private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; @@ -56,6 +58,7 @@ public class TestRecommissionSubCommand { @BeforeEach public void setup() throws UnsupportedEncodingException { cmd = new RecommissionSubCommand(); + scmClient = mock(ScmClient.class); System.setOut(new PrintStream(outContent, false, DEFAULT_ENCODING)); System.setErr(new PrintStream(errContent, false, DEFAULT_ENCODING)); } @@ -66,9 +69,37 @@ public void tearDown() { System.setErr(originalErr); } + @Test + public void testMultipleHostnamesCanBeReadFromStdin() throws Exception { + when(scmClient.decommissionNodes(anyList())) + .thenAnswer(invocation -> new ArrayList()); + + String input = "host1\nhost2\nhost3\n"; + System.setIn(new ByteArrayInputStream(input.getBytes(DEFAULT_ENCODING))); + CommandLine c = new CommandLine(cmd); + c.parseArgs("-"); + cmd.execute(scmClient); + + Pattern p = Pattern.compile( + "^Started\\srecommissioning\\sdatanode\\(s\\)", Pattern.MULTILINE); + Matcher m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host1$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host2$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + + p = Pattern.compile("^host3$", Pattern.MULTILINE); + m = p.matcher(outContent.toString(DEFAULT_ENCODING)); + assertTrue(m.find()); + } + @Test public void testNoErrorsWhenRecommissioning() throws IOException { - ScmClient scmClient = mock(ScmClient.class); when(scmClient.recommissionNodes(anyList())) .thenAnswer(invocation -> new ArrayList()); @@ -92,7 +123,6 @@ public void testNoErrorsWhenRecommissioning() throws IOException { @Test public void testErrorsReportedWhenRecommissioning() throws IOException { - ScmClient scmClient = mock(ScmClient.class); when(scmClient.recommissionNodes(anyList())) .thenAnswer(invocation -> { ArrayList e = new ArrayList<>();