blob: 6bb3372d251396eafae87084bf13898be5dea638 [file]
/*
* Copyright 2017 HugeGraph Authors
*
* 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 com.baidu.hugegraph.cmd;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import com.baidu.hugegraph.api.API;
import com.baidu.hugegraph.manager.TasksManager;
import com.baidu.hugegraph.structure.constant.GraphMode;
import com.baidu.hugegraph.structure.constant.HugeType;
import com.baidu.hugegraph.util.E;
import com.beust.jcommander.DynamicParameter;
import com.beust.jcommander.IParameterValidator;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import com.beust.jcommander.ParametersDelegate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public class SubCommands {
private Map<String, Object> commands;
public SubCommands() {
this.commands = new HashMap<>();
this.initSubCommands();
}
private void initSubCommands() {
this.commands.put("backup", new Backup());
this.commands.put("restore", new Restore());
this.commands.put("schedule-backup", new ScheduleBackup());
this.commands.put("dump", new DumpGraph());
this.commands.put("graph-list", new GraphList());
this.commands.put("graph-get", new GraphGet());
this.commands.put("graph-clear", new GraphClear());
this.commands.put("graph-mode-set", new GraphModeSet());
this.commands.put("graph-mode-get", new GraphModeGet());
this.commands.put("gremlin-execute", new Gremlin());
this.commands.put("gremlin-schedule", new GremlinJob());
this.commands.put("task-list", new TaskList());
this.commands.put("task-get", new TaskGet());
this.commands.put("task-delete", new TaskDelete());
this.commands.put("task-cancel", new TaskCancel());
this.commands.put("task-clear", new TaskClear());
this.commands.put("deploy", new Deploy());
this.commands.put("start-all", new StartAll());
this.commands.put("clear", new Clear());
this.commands.put("stop-all", new StopAll());
this.commands.put("help", new Help());
}
public Map<String, Object> commands() {
return this.commands;
}
@Parameters(commandDescription = "Schedule backup task")
public class ScheduleBackup {
@Parameter(names = {"--interval"}, arity = 1,
description =
"The interval of backup, format is: \"a b c d e\"." +
" 'a' means minute (0 - 59)," +
" 'b' means hour (0 - 23)," +
" 'c' means day of month (1 - 31)," +
" 'd' means month (1 - 12)," +
" 'e' means day of week (0 - 6) (Sunday=0)," +
" \"*\" means all")
public String interval = "0,0,*,*,*";
@Parameter(names = {"--backup-num"}, arity = 1,
description = "The number of latest backups to keep")
public int num = 3;
@Parameter(names = {"--directory", "-d"}, arity = 1, required = true,
description = "The directory of backups stored")
public String directory;
}
@Parameters(commandDescription = "Backup graph schema/data. If directory " +
"is on HDFS, use -D to set HDFS params " +
"if needed. For exmaple:" +
"-Dfs.default.name=hdfs://localhost:9000")
public class Backup extends BackupRestore {
@Parameter(names = {"--split-size", "-s"}, arity = 1,
description = "Split size of shard")
public long splitSize = 1024 * 1024L;
public long splitSize() {
return this.splitSize;
}
}
@Parameters(commandDescription = "Restore graph schema/data. If directory" +
" is on HDFS, use -D to set HDFS params " +
"if needed. For exmaple:" +
"-Dfs.default.name=hdfs://localhost:9000")
public class Restore extends BackupRestore {}
@Parameters(commandDescription = "Dump graph to files")
public class DumpGraph {
@ParametersDelegate
private Retry retry = new Retry();
@Parameter(names = {"--directory", "-d"}, arity = 1,
description = "Directory to store graph data")
public String directory = "./";
@Parameter(names = {"--formatter", "-f"}, arity = 1,
description = "Formatter to customize format of vertex/edge")
public String formatter = "JsonFormatter";
public int retry() {
return this.retry.retry;
}
public String directory() {
return this.directory;
}
public String formatter() {
return this.formatter;
}
}
@Parameters(commandDescription = "List all graphs")
public class GraphList {
}
@Parameters(commandDescription = "Get graph info")
public class GraphGet {
@ParametersDelegate
private GraphName graph = new GraphName();
public String graph() {
return this.graph.graphName;
}
}
@Parameters(commandDescription = "Clear graph schema and data")
public class GraphClear {
@ParametersDelegate
private GraphName graph = new GraphName();
@ParametersDelegate
private ConfirmMessage message = new ConfirmMessage();
public String graph() {
return this.graph.graphName;
}
public String confirmMessage() {
return this.message.confirmMessage;
}
}
@Parameters(commandDescription = "Set graph mode")
public class GraphModeSet {
@ParametersDelegate
private GraphName graph = new GraphName();
@ParametersDelegate
private Mode mode = new Mode();
public String graph() {
return this.graph.graphName;
}
public GraphMode mode() {
return this.mode.mode;
}
}
@Parameters(commandDescription = "Get graph mode")
public class GraphModeGet {
@ParametersDelegate
private GraphName graph = new GraphName();
public String graph() {
return this.graph.graphName;
}
}
@Parameters(commandDescription = "Execute Gremlin statements")
public class Gremlin extends GremlinJob {
@ParametersDelegate
private Aliases aliases = new Aliases();
public Map<String, String> aliases() {
return this.aliases.aliases;
}
}
@Parameters(commandDescription = "Execute Gremlin statements as " +
"asynchronous job")
public class GremlinJob {
@ParametersDelegate
private FileScript fileScript = new FileScript();
@ParametersDelegate
private GremlinScript cmdScript = new GremlinScript();
@ParametersDelegate
private Language language = new Language();
@ParametersDelegate
private Bindings bindings = new Bindings();
public String fileScript() {
return this.fileScript.script;
}
public String cmdScript() {
return this.cmdScript.script;
}
public String language() {
return this.language.language;
}
public Map<String, String> bindings() {
return this.bindings.bindings;
}
public String script() {
String script;
if (this.cmdScript() == null) {
E.checkArgument(this.fileScript() != null,
"Either --script or --file must " +
"be provided, but both are null");
script = this.fileScript();
} else {
E.checkArgument(this.fileScript() == null,
"Either --script or --file must " +
"be provided, but both are provided: " +
"'%s' and '%s'",
this.cmdScript(), this.fileScript());
script = this.cmdScript();
}
return script;
}
}
@Parameters(commandDescription = "List tasks")
public class TaskList {
@ParametersDelegate
private TaskStatus status = new TaskStatus();
@ParametersDelegate
private Limit limit = new Limit();
public String status() {
return this.status.status.toUpperCase();
}
public long limit() {
return this.limit.limit;
}
}
@Parameters(commandDescription = "Get task info")
public class TaskGet {
@ParametersDelegate
private TaskId taskId = new TaskId();
public long taskId() {
return this.taskId.taskId;
}
}
@Parameters(commandDescription = "Delete task")
public class TaskDelete {
@ParametersDelegate
private TaskId taskId = new TaskId();
public long taskId() {
return this.taskId.taskId;
}
}
@Parameters(commandDescription = "Cancel task")
public class TaskCancel {
@ParametersDelegate
private TaskId taskId = new TaskId();
public long taskId() {
return this.taskId.taskId;
}
}
@Parameters(commandDescription = "Clear completed tasks")
public class TaskClear {
@Parameter(names = "--force",
description = "Force to clear all tasks, " +
"cancel all uncompleted tasks firstly, " +
"and delete all completed task")
private boolean force = false;
public boolean force() {
return this.force;
}
}
@Parameters(commandDescription = "Install HugeGraph-Server and " +
"HugeGraph-Studio")
public class Deploy {
@ParametersDelegate
private Version version = new Version();
@ParametersDelegate
public InstallPath path = new InstallPath();
@ParametersDelegate
public DownloadURL url = new DownloadURL();
}
@Parameters(commandDescription = "Start HugeGraph-Server and " +
"HugeGraph-Studio")
public class StartAll {
@ParametersDelegate
private Version version = new Version();
@ParametersDelegate
public InstallPath path = new InstallPath();
}
@Parameters(commandDescription = "Clear HugeGraph-Server and " +
"HugeGraph-Studio")
public class Clear {
@ParametersDelegate
public InstallPath path = new InstallPath();
}
@Parameters(commandDescription = "Stop HugeGraph-Server and " +
"HugeGraph-Studio")
public class StopAll {
}
@Parameters(commandDescription = "Print usage")
public class Help {
}
public class BackupRestore {
@Parameter(names = {"--directory", "-d"}, arity = 1,
description = "Directory of graph schema/data")
public String directory = "./";
@Parameter(names = {"--log", "-l"}, arity = 1,
description = "Directory of log")
public String logDir = "./";
@ParametersDelegate
private HugeTypes types = new HugeTypes();
@ParametersDelegate
private Retry retry = new Retry();
@DynamicParameter(names = "-D",
description = "HDFS config parameters")
private Map<String, String> hdfsConf = new HashMap<>();
public String directory() {
return this.directory;
}
public String logDir() {
return this.logDir;
}
public List<HugeType> types() {
return this.types.types;
}
public int retry() {
return this.retry.retry;
}
public Map<String, String> hdfsConf() {
return this.hdfsConf;
}
}
public static class Url {
@Parameter(names = {"--url"}, arity = 1,
validateWith = {UrlValidator.class},
description = "The URL of HugeGraph-Server url")
public String url = "http://127.0.0.1:8080";
}
public static class Graph {
@Parameter(names = {"--graph"}, arity = 1,
description = "Name of graph")
public String graph = "hugegraph";
}
public static class Username {
@Parameter(names = {"--user"}, arity = 1,
description = "User Name")
public String username;
}
public static class Password {
@Parameter(names = {"--password"}, arity = 1,
description = "Password of user")
public String password;
}
public static class Timeout {
@Parameter(names = {"--timeout"}, arity = 1,
description = "Connection timeout")
public int timeout = 30;
}
public static class GraphName {
@Parameter(names = {"--graph-name"}, arity = 1,
description = "Name of graph")
public String graphName = "hugegraph";
}
public class HugeTypes {
@Parameter(names = {"--huge-types", "-t"},
listConverter = HugeTypeListConverter.class,
required = true,
description = "Type of schema/data. " +
"Concat with ',' if more than one. " +
"'all' means 'vertex,edge,vertex_label," +
"edge_label,property_key,index_label'")
public List<HugeType> types;
}
public class ExistDirectory {
@Parameter(names = {"--directory", "-d"}, arity = 1,
validateWith = {DirectoryValidator.class},
description = "Directory of graph schema/data")
public String directory = "./";
}
public class InstallPath {
@Parameter(names = {"-p"}, arity = 1, required = true,
description = "Install path of HugeGraph-Server and " +
"HugeGraph-Studio")
public String directory = null;
}
public class DownloadURL {
@Parameter(names = {"-u"}, arity = 1,
description = "Download url prefix path of " +
"HugeGraph-Server and HugeGraph-Studio")
public String url = null;
}
public class ConfirmMessage {
@Parameter(names = {"--confirm-message", "-c"}, arity = 1,
description = "Confirm message of graph clear is " +
"\"I'm sure to delete all data\". " +
"(Note: include \"\")",
required = true)
public String confirmMessage;
}
public class Mode {
@Parameter(names = {"--graph-mode", "-m"}, arity = 1,
converter = GraphModeConverter.class,
description = "Graph mode, " +
"include: [NONE, RESTORING, MERGING]",
required = true)
public GraphMode mode;
}
public class FileScript {
@Parameter(names = {"--file", "-f"}, arity = 1,
converter = FileNameToContentConverter.class,
description = "Gremlin Script file to be executed, UTF-8" +
"encoded, exclusive to --script")
public String script;
}
public class GremlinScript {
@Parameter(names = {"--script", "-s"}, arity = 1,
description = "Gremlin script to be executed, " +
"exclusive to --file")
public String script;
}
public class Language {
@Parameter(names = {"--language", "-l"}, arity = 1,
description = "Gremlin script language")
public String language = "gremlin-groovy";
}
public class Bindings {
@Parameter(names = {"--bindings", "-b"}, arity = 1,
converter = MapConverter.class,
description = "Gremlin bindings, valid format is: " +
"'key1=value1,key2=value2...'")
public Map<String, String> bindings = ImmutableMap.of();
}
public class Aliases {
@Parameter(names = {"--aliases", "-a"}, arity = 1,
converter = MapConverter.class,
description = "Gremlin aliases, valid format is: " +
"'key1=value1,key2=value2...'")
public Map<String, String> aliases = ImmutableMap.of();
}
public class Version {
@Parameter(names = {"-v"}, arity = 1, required = true,
description = "Version of HugeGraph-Server and " +
"HugeGraph-Studio")
public String version;
}
public class Retry {
@Parameter(names = {"--retry"}, arity = 1,
validateWith = {PositiveValidator.class},
description = "Retry times, default is 3")
public int retry = 3;
}
public class Limit {
@Parameter(names = {"--limit"}, arity = 1,
validateWith = {PositiveValidator.class},
description = "Limit number, no limit if not provided")
public long limit = -1;
}
public class TaskStatus {
@Parameter(names = {"--status"}, arity = 1,
validateWith = TaskStatusValidator.class,
description = "Status of task")
public String status = null;
}
public class TaskId {
@Parameter(names = {"--task-id"}, arity = 1, required = true,
validateWith = {PositiveValidator.class},
description = "Task id")
private long taskId;
}
public static class GraphModeConverter
implements IStringConverter<GraphMode> {
@Override
public GraphMode convert(String value) {
E.checkArgument(value != null && !value.isEmpty(),
"GraphMode can't be null or empty");
return GraphMode.valueOf(value);
}
}
public static class HugeTypeListConverter
implements IStringConverter<List<HugeType>> {
@Override
public List<HugeType> convert(String value) {
E.checkArgument(value != null && !value.isEmpty(),
"HugeType can't be null or empty");
String[] types = value.split(",");
if (types.length == 1 && types[0].equalsIgnoreCase("all")) {
return ImmutableList.of(HugeType.PROPERTY_KEY,
HugeType.VERTEX_LABEL,
HugeType.EDGE_LABEL,
HugeType.INDEX_LABEL,
HugeType.VERTEX,
HugeType.EDGE);
}
List<HugeType> hugeTypes = new ArrayList<>();
for (String type : types) {
try {
hugeTypes.add(HugeType.valueOf(type.toUpperCase()));
} catch (IllegalArgumentException e) {
throw new ParameterException(String.format(
"Invalid --type '%s', valid value is 'all' or " +
"combination of 'vertex,edge,vertex_label," +
"edge_label,property_key,index_label'", type));
}
}
return hugeTypes;
}
}
public static class MapConverter
implements IStringConverter<Map<String, String>> {
@Override
public Map<String, String> convert(String value) {
E.checkArgument(value != null && !value.isEmpty(),
"HugeType can't be null or empty");
String[] equals = value.split(",");
Map<String, String> result = new HashMap<>();
for (String equal : equals) {
String[] kv = equal.split("=");
E.checkArgument(kv.length == 2,
"Map arguments format should be key=value, " +
"but got '%s'", equal);
result.put(kv[0], kv[1]);
}
return result;
}
}
public static class FileNameToContentConverter
implements IStringConverter<String> {
@Override
public String convert(String value) {
File file = FileUtils.getFile(value);
if (!file.exists() || !file.isFile() || !file.canRead()) {
throw new ParameterException(String.format(
"'%s' must be existed readable file", value));
}
String content;
try {
content = FileUtils.readFileToString(file, API.CHARSET);
} catch (IOException e) {
throw new ParameterException(String.format(
"Read file '%s' error", value), e);
}
return content;
}
}
public static class TaskStatusValidator implements IParameterValidator {
@Override
public void validate(String name, String value) {
if (!TasksManager.TASK_STATUSES.contains(value.toUpperCase())) {
throw new ParameterException(String.format(
"Invalid --status '%s', valid value is %s",
value, TasksManager.TASK_STATUSES));
}
}
}
public static class UrlValidator implements IParameterValidator {
@Override
public void validate(String name, String value) {
String regex = "^(http://)?"
// IP URL, like: 10.0.0.1
+ "(((25[0-5])|(2[0-4]\\d)|(1\\d\\d)|([1-9]\\d)|\\d)"
+ "(\\.((25[0-5])|(2[0-4]\\d)|(1\\d\\d)|([1-9]\\d)|\\d)){3}"
+ "|"
// Or domain name
+ "([0-9a-z_!~*'()-]+\\.)*[0-9a-z_!~*'()-]+)"
// Port
+ ":([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-5]{2}[0-3][0-5])$";
if (!value.matches(regex)) {
throw new ParameterException(String.format(
"Invalid url value of args '%s': '%s'", name, value));
}
}
}
public static class DirectoryValidator implements IParameterValidator {
@Override
public void validate(String name, String value) {
File file = new File(value);
if (!file.exists() || !file.isDirectory()) {
throw new ParameterException(String.format(
"Invalid value of argument '%s': '%s'", name, value));
}
}
}
public static class PositiveValidator implements IParameterValidator {
@Override
public void validate(String name, String value) {
int retry = Integer.parseInt(value);
if (retry <= 0) {
throw new ParameterException(
"Parameter " + name + " should be positive, " +
"but got " + value);
}
}
}
}