diff --git a/hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/OzoneInteractiveShell.java b/hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/OzoneInteractiveShell.java new file mode 100644 index 000000000000..1f73df85a28b --- /dev/null +++ b/hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/OzoneInteractiveShell.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.hadoop.ozone.shell; + +import org.apache.hadoop.hdds.cli.GenericCli; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory; + +/** + * Interactive Shell for all Ozone commands. + */ +public final class OzoneInteractiveShell { + + private static final Logger LOG = LoggerFactory.getLogger(OzoneInteractiveShell.class); + + private OzoneInteractiveShell() { + } + + public static void main(String[] argv) throws Exception { + PicocliCommandsFactory factory = new PicocliCommandsFactory(); + CommandLine topCmd = new CommandLine(new TopCommand(), factory); + + // Add known subcommands statically if they are in the same module. + topCmd.addSubcommand("sh", new OzoneShell().getCmd()); + topCmd.addSubcommand("tenant", new org.apache.hadoop.ozone.shell.tenant.TenantShell().getCmd()); + topCmd.addSubcommand("s3", new org.apache.hadoop.ozone.shell.s3.S3Shell().getCmd()); + + // Dynamically add subcommands from other modules to avoid circular dependencies. + addDynamicSubcommand(topCmd, "admin", "org.apache.hadoop.ozone.admin.OzoneAdmin"); + addDynamicSubcommand(topCmd, "debug", "org.apache.hadoop.ozone.debug.OzoneDebug"); + addDynamicSubcommand(topCmd, "repair", "org.apache.hadoop.ozone.repair.OzoneRepair"); + + Shell dummyShell = new Shell() { + @Override + public String name() { + return "ozone"; + } + + @Override + public String prompt() { + return "ozone"; + } + }; + + new REPL(dummyShell, topCmd, factory, null); + } + + private static void addDynamicSubcommand(CommandLine topCmd, String name, String className) { + try { + Class clazz = Class.forName(className); + GenericCli instance = (GenericCli) clazz.getDeclaredConstructor().newInstance(); + topCmd.addSubcommand(name, instance.getCmd()); + } catch (Exception e) { + LOG.debug("Subcommand {} not loaded: class {} not found or could not be instantiated", + name, className, e); + } + } + + @Command(name = "ozone", description = "Interactive Shell for all Ozone commands", mixinStandardHelpOptions = true) + private static class TopCommand implements Runnable { + @Override + public void run() { + // The top-level command is only used to group subcommands and has no execution logic itself. + } + } +} diff --git a/hadoop-ozone/dist/src/shell/ozone/ozone b/hadoop-ozone/dist/src/shell/ozone/ozone index 2e29126762c2..e33e4648c9c5 100755 --- a/hadoop-ozone/dist/src/shell/ozone/ozone +++ b/hadoop-ozone/dist/src/shell/ozone/ozone @@ -59,6 +59,7 @@ function ozone_usage ozone_add_subcommand "insight" client "tool to get runtime operation information" ozone_add_subcommand "version" client "print the version" ozone_add_subcommand "dtutil" client "operations related to delegation tokens" + ozone_add_subcommand "interactive" client "interactive shell for ozone commands" ozone_add_subcommand "admin" client "Ozone admin tool" ozone_add_subcommand "debug" client "Ozone debug tool" ozone_add_subcommand "repair" client "Ozone repair tool" @@ -221,6 +222,17 @@ function ozonecmd_case OZONE_CLASSNAME=org.apache.hadoop.security.token.DtUtilShell OZONE_RUN_ARTIFACT_NAME="ozone-tools" ;; + interactive) + OZONE_CLASSNAME=org.apache.hadoop.ozone.shell.OzoneInteractiveShell + OZONE_RUN_ARTIFACT_NAME="ozone-cli-shell" + OZONE_OPTS="${OZONE_OPTS} ${RATIS_OPTS} ${OZONE_MODULE_ACCESS_ARGS}" + # Add all CLI classpaths to support all subcommands dynamically + for cp_file in "ozone-cli-admin" "ozone-cli-debug" "ozone-cli-repair" "ozone-tools"; do + if [[ -f "${OZONE_HOME}/share/ozone/classpath/${cp_file}.classpath" ]]; then + ozone_add_classpath_from_file "${OZONE_HOME}/share/ozone/classpath/${cp_file}.classpath" + fi + done + ;; admin) OZONE_CLASSNAME=org.apache.hadoop.ozone.admin.OzoneAdmin OZONE_ADMIN_OPTS="${OZONE_ADMIN_OPTS} ${RATIS_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"