Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions hadoop-ozone/dist/src/shell/ozone/ozone
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function ozone_usage
ozone_add_option "--validate (continue)" "validates if all jars as indicated in the corresponding OZONE_RUN_ARTIFACT_NAME classpath file are present, command execution shall continue post validation failure if 'continue' is passed"

ozone_add_subcommand "classpath" client "prints the class path needed for running ozone commands"
ozone_add_subcommand "completion" client "generate autocompletion script for bash/zsh"
ozone_add_subcommand "datanode" daemon "run a HDDS datanode"
ozone_add_subcommand "envvars" client "display computed Hadoop environment variables"
ozone_add_subcommand "daemonlog" admin "get/set the log level for each daemon"
Expand Down Expand Up @@ -97,6 +98,10 @@ function ozonecmd_case
exit -1
fi
;;
completion)
OZONE_CLASSNAME=org.apache.hadoop.ozone.utils.AutoCompletion;
OZONE_RUN_ARTIFACT_NAME="ozone-tools"
;;
Comment on lines +101 to +104
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer your original idea about adding this as ozone util completion (let's wait for more feedback on this before changing anything). Candidates for moving under ozone util in the future:

  • checknative
  • classpath
  • envvars

(Or maybe these belong to ozone debug?)

Maybe also the following, but need to examine what they are exactly, and if we still need them.

  • daemonlog
  • dtutil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Will wait for more input from others on this.

cc. @errose28 @dombizita @Tejaskriya @sarvekshayr

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

daemonlog looks like a wrapper to set the log level on a service over HTTP, similar to doing this through the web UI, which looks useful. I can't figure out the use case for dtutil though.

What would the distinction be between util and debug? I think checknative, classpath, and envvars make sense under debug, but I'm ok with ozone completion being a top level command since its result applies to the whole CLI.

datanode)
OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
ozone_deprecate_envvar HDDS_DN_OPTS OZONE_DATANODE_OPTS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
* Ozone Admin Command line tool.
*/
@CommandLine.Command(name = "ozone admin",
hidden = true,
description = "Developer tools for Ozone Admin operations",
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* 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.utils;

import java.util.Objects;
import org.apache.hadoop.hdds.cli.GenericCli;
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
import org.apache.ratis.util.ReflectionUtils;
import org.reflections.Reflections;
import picocli.AutoComplete;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

/** Tool to generate auto-completion scripts for Ozone CLI. */
@Command(name = "ozone completion",
header = "Generate autocompletion script for the specified shell.",
synopsisHeading = "%nUsage: ",
description = AutoCompletion.DESCRIPTION,
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true)
public final class AutoCompletion extends GenericCli {

public static final String DESCRIPTION = "%nThe generated shell code must " +
"be evaluated to provide interactive completion of ozone commands. " +
"See each sub-command's help for details on how to use the generated script.%n";

private static final String BASH_DESCRIPTION = "%nTo load completions in your current shell session: %n" +
"%n\t source <(ozone completion bash) %n" +
"%nTo load completions for every new session automatically, %n" +
"add this to your `~/.bash_profile`: %n" +
"%n\t eval \"$(ozone completion bash)\" %n";

private static final String ZSH_DESCRIPTION = "%nTo load completions in your current shell session: %n" +
"%n\t source <(ozone completion zsh)%n" +
"%nTo load completions for every new session automatically, %n" +
"generate a `_ozone` completion script and put it in your `$fpath`: %n" +
"%n\t ozone completion zsh > \"$${fpath[1]}/_ozone\" %n";

private static final String OZONE_COMMAND = "ozone ";
private static final int PREFIX_LENGTH = OZONE_COMMAND.length();
private static final String[] PACKAGES_TO_SCAN = {
"org.apache.hadoop.hdds",
"org.apache.hadoop.ozone",
"org.apache.ozone",
};

/** Generates bash auto-completion script for Ozone CLI. */
@Command(name = "bash",
header = "Generate the shell completion code for bash.",
synopsisHeading = "%nUsage: ",
description = AutoCompletion.BASH_DESCRIPTION,
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true)
public void bashCompletion() {
out().println(getBashCompletion());
}

/** Generates zsh auto-completion script for Ozone CLI. */
@Command(name = "zsh",
header = "Generate the shell completion code for zsh.",
synopsisHeading = "%nUsage: ",
description = AutoCompletion.ZSH_DESCRIPTION,
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true)
public void zshCompletion() {
bashCompletion();
}

private String getBashCompletion() {
final CommandLine ozone = new CommandLine(new Ozone());
for (String pkg : PACKAGES_TO_SCAN) {
new Reflections(pkg).getSubTypesOf(GenericCli.class).stream()
.map(AutoCompletion::getCommand)
.filter(Objects::nonNull)
.filter(AutoCompletion::isPublicCommand)
.forEach(command -> ozone.addSubcommand(getCommandName(command), command));
}
return AutoComplete.bash("ozone", ozone);
}

private static CommandLine getCommand(Class<? extends GenericCli> clazz) {
CommandLine command = null;
try {
command = ReflectionUtils.newInstance(clazz).getCmd();
} catch (UnsupportedOperationException ignored) {
// Skip the GenericCli subclasses that do not have no-args constructor.
}
return command;
}

private static boolean isPublicCommand(CommandLine command) {
final CommandLine.Model.CommandSpec spec = command.getCommandSpec();
return !spec.usageMessage().hidden() &&
!CommandLine.Model.CommandSpec.DEFAULT_COMMAND_NAME.equals(spec.name());
}

private static String getCommandName(CommandLine command) {
final CommandLine.Model.CommandSpec spec = command.getCommandSpec();
final String qualifiedName = spec.qualifiedName();
return qualifiedName.startsWith(OZONE_COMMAND) ?
qualifiedName.substring(PREFIX_LENGTH) : qualifiedName;
}

public static void main(String[] args) {
new AutoCompletion().run(args);
}

/** Ozone top level command, used only to generate auto-complete. */
@Command(name = "ozone",
description = "Ozone top level command")
private static final class Ozone {

@Option(names = {"--buildpaths"},
description = "attempt to add class files from build tree")
private String buildpaths;

@Option(names = {"--config"},
description = "Ozone config directory")
private String config;

@Option(names = {"--debug"},
description = "turn on shell script debug mode")
private String debug;

@Option(names = {"--daemon"},
description = "attempt to add class files from build tree")
private String daemon;

@Option(names = {"--help"},
description = "usage information")
private String help;

@Option(names = {"--hostnames"},
description = "hosts to use in worker mode")
private String hostnames;

@Option(names = {"--hosts"},
description = "list of hosts to use in worker mode")
private String hosts;

@Option(names = {"--loglevel"},
description = "set the log4j level for this command")
private String loglevel;

@Option(names = {"--workers"},
description = "turn on worker mode")
private String workers;

@Option(names = {"--jvmargs"},
description = "append JVM options to any existing options defined in the OZONE_OPTS environment variable. " +
"Any defined in OZONE_CLIENT_OPTS will be appended after these jvmargs")
private String jvmargs;

@Option(names = {"--validate"},
description = "validates if all jars as indicated in the corresponding OZONE_RUN_ARTIFACT_NAME classpath " +
"file are present, command execution shall continue post validation failure if 'continue' is passed")
private String validate;
}
}