/**
* 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.tajo.cli.tools;
import com.google.protobuf.ServiceException;
import org.apache.commons.cli.*;
import org.apache.commons.lang.StringUtils;
import org.apache.tajo.QueryId;
import org.apache.tajo.TajoProtos;
import org.apache.tajo.client.QueryStatus;
import org.apache.tajo.client.TajoClient;
import org.apache.tajo.client.TajoClientImpl;
import org.apache.tajo.client.TajoClientUtil;
import org.apache.tajo.conf.TajoConf;
import org.apache.tajo.ipc.ClientProtos.BriefQueryInfo;
import org.apache.tajo.ipc.ClientProtos.WorkerResourceInfo;
import org.apache.tajo.service.ServiceTracker;
import org.apache.tajo.service.ServiceTrackerFactory;
import org.apache.tajo.util.NetUtils;
import org.apache.tajo.util.TajoIdUtils;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
public class TajoAdmin {
private static final org.apache.commons.cli.Options options;
private static DecimalFormat decimalF = new DecimalFormat("###.0");
private enum WorkerStatus {
RUNNING,
LOST,
DECOMMISSIONED
}
final static String DASHLINE_LEN5 = "-----";
final static String DASHLINE_LEN10 = "----------";
final static String DASHLINE_LEN12 = "------------";
final static String DASHLINE_LEN25 = "-------------------------";
final static String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
static {
options = new Options();
options.addOption("h", "host", true, "Tajo server host");
options.addOption("p", "port", true, "Tajo server port");
options.addOption("list", null, false, "Show Tajo query list");
options.addOption("cluster", null, false, "Show Cluster Info");
options.addOption("showmasters", null, false, "gets list of tajomasters in the cluster");
options.addOption("desc", null, false, "Show Query Description");
options.addOption("kill", null, true, "Kill a running query");
}
private TajoConf tajoConf;
private TajoClient tajoClient;
private Writer writer;
private ServiceTracker serviceTracker;
public TajoAdmin(TajoConf tajoConf, Writer writer) {
this(tajoConf, writer, null);
}
public TajoAdmin(TajoConf tajoConf, Writer writer, TajoClient tajoClient) {
this.tajoConf = tajoConf;
this.writer = writer;
this.tajoClient = tajoClient;
serviceTracker = ServiceTrackerFactory.get(this.tajoConf);
}
private void printUsage() {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "admin [options]", options );
}
public void runCommand(String[] args) throws Exception {
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse(options, args);
String param = "";
int cmdType = 0;
String hostName = null;
Integer port = null;
if (cmd.hasOption("h")) {
hostName = cmd.getOptionValue("h");
}
if (cmd.hasOption("p")) {
port = Integer.parseInt(cmd.getOptionValue("p"));
}
String queryId = null;
if (cmd.hasOption("list")) {
cmdType = 1;
} else if (cmd.hasOption("desc")) {
cmdType = 2;
} else if (cmd.hasOption("cluster")) {
cmdType = 3;
} else if (cmd.hasOption("kill")) {
cmdType = 4;
queryId = cmd.getOptionValue("kill");
} else if (cmd.hasOption("showmasters")) {
cmdType = 5;
}
// if there is no "-h" option,
InetSocketAddress address = tajoConf.getSocketAddrVar(TajoConf.ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS,
TajoConf.ConfVars.TAJO_MASTER_UMBILICAL_RPC_ADDRESS);
if(hostName == null) {
hostName = address.getHostName();
}
if (port == null) {
port = address.getPort();
}
if (cmdType == 0) {
printUsage();
return;
}
if ((hostName == null) ^ (port == null)) {
System.err.println("ERROR: cannot find valid Tajo server address");
return;
} else if (hostName != null && port != null) {
tajoConf.setVar(TajoConf.ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS, NetUtils.getHostPortString(hostName, port));
tajoClient = new TajoClientImpl(ServiceTrackerFactory.get(tajoConf));
} else if (hostName == null && port == null) {
tajoClient = new TajoClientImpl(ServiceTrackerFactory.get(tajoConf));
}
switch (cmdType) {
case 1:
processList(writer);
break;
case 2:
processDesc(writer);
break;
case 3:
processCluster(writer);
break;
case 4:
processKill(writer, queryId);
break;
case 5:
processMasters(writer);
break;
default:
printUsage();
break;
}
writer.flush();
}
private void processDesc(Writer writer) throws ParseException, IOException,
ServiceException, SQLException {
List<BriefQueryInfo> queryList = tajoClient.getRunningQueryList();
SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
int id = 1;
for (BriefQueryInfo queryInfo : queryList) {
String queryId = String.format("q_%s_%04d",
queryInfo.getQueryId().getId(),
queryInfo.getQueryId().getSeq());
writer.write("Id: " + id);
writer.write("\n");
id++;
writer.write("Query Id: " + queryId);
writer.write("\n");
writer.write("Started Time: " + df.format(queryInfo.getStartTime()));
writer.write("\n");
writer.write("Query State: " + queryInfo.getState().name());
writer.write("\n");
long end = queryInfo.getFinishTime();
long start = queryInfo.getStartTime();
String executionTime = decimalF.format((end-start) / 1000) + " sec";
if (TajoClientUtil.isQueryComplete(queryInfo.getState())) {
writer.write("Finished Time: " + df.format(queryInfo.getFinishTime()));
writer.write("\n");
}
writer.write("Execution Time: " + executionTime);
writer.write("\n");
writer.write("Query Progress: " + queryInfo.getProgress());
writer.write("\n");
writer.write("Query Statement:");
writer.write("\n");
writer.write(queryInfo.getQuery());
writer.write("\n");
writer.write("\n");
}
}
private void processCluster(Writer writer) throws ParseException, IOException,
ServiceException, SQLException {
List<WorkerResourceInfo> workerList = tajoClient.getClusterInfo();
int runningQueryMasterTasks = 0;
List<WorkerResourceInfo> liveWorkers = new ArrayList<>();
List<WorkerResourceInfo> deadWorkers = new ArrayList<>();
List<WorkerResourceInfo> decommissionWorkers = new ArrayList<>();
List<WorkerResourceInfo> liveQueryMasters = new ArrayList<>();
List<WorkerResourceInfo> deadQueryMasters = new ArrayList<>();
for (WorkerResourceInfo eachWorker : workerList) {
if(eachWorker.getWorkerStatus().equals(WorkerStatus.RUNNING.toString())) {
liveQueryMasters.add(eachWorker);
liveWorkers.add(eachWorker);
runningQueryMasterTasks += eachWorker.getNumQueryMasterTasks();
} else if(eachWorker.getWorkerStatus().equals(WorkerStatus.LOST.toString())) {
deadQueryMasters.add(eachWorker);
deadWorkers.add(eachWorker);
} else if(eachWorker.getWorkerStatus().equals(WorkerStatus.DECOMMISSIONED.toString())) {
decommissionWorkers.add(eachWorker);
}
}
String fmtInfo = "%1$-5s %2$-5s %3$-5s%n";
String infoLine = String.format(fmtInfo, "Live", "Dead", "Tasks");
writer.write("Query Master\n");
writer.write("============\n\n");
writer.write(infoLine);
String line = String.format(fmtInfo, DASHLINE_LEN5, DASHLINE_LEN5, DASHLINE_LEN5);
writer.write(line);
line = String.format(fmtInfo, liveQueryMasters.size(),
deadQueryMasters.size(), runningQueryMasterTasks);
writer.write(line);
writer.write("\n");
writer.write("Live QueryMasters\n");
writer.write("=================\n\n");
if (liveQueryMasters.isEmpty()) {
writer.write("No Live QueryMasters\n");
} else {
String fmtQueryMasterLine = "%1$-25s %2$-5s %3$-5s %4$-10s %5$-10s%n";
line = String.format(fmtQueryMasterLine, "QueryMaster", "Port", "Query",
"Mem", "Status");
writer.write(line);
line = String.format(fmtQueryMasterLine, DASHLINE_LEN25, DASHLINE_LEN5,
DASHLINE_LEN5, DASHLINE_LEN10, DASHLINE_LEN10);
writer.write(line);
for (WorkerResourceInfo queryMaster : liveQueryMasters) {
TajoProtos.WorkerConnectionInfoProto connInfo = queryMaster.getConnectionInfo();
String queryMasterHost = String.format("%s:%d", connInfo.getHost(), connInfo.getQueryMasterPort());
String memory = String.format("%d MB", queryMaster.getAvailableResource().getMemory());
line = String.format(fmtQueryMasterLine,
queryMasterHost,
connInfo.getClientPort(),
queryMaster.getNumQueryMasterTasks(),
memory,
queryMaster.getWorkerStatus());
writer.write(line);
}
writer.write("\n\n");
}
if (!deadQueryMasters.isEmpty()) {
writer.write("Dead QueryMasters\n");
writer.write("=================\n\n");
String fmtQueryMasterLine = "%1$-25s %2$-5s %3$-10s%n";
line = String.format(fmtQueryMasterLine, "QueryMaster", "Port", "Status");
writer.write(line);
line = String.format(fmtQueryMasterLine, DASHLINE_LEN25, DASHLINE_LEN5, DASHLINE_LEN10);
writer.write(line);
for (WorkerResourceInfo queryMaster : deadQueryMasters) {
TajoProtos.WorkerConnectionInfoProto connInfo = queryMaster.getConnectionInfo();
String queryMasterHost = String.format("%s:%d", connInfo.getHost(), connInfo.getQueryMasterPort());
line = String.format(fmtQueryMasterLine,
queryMasterHost,
connInfo.getClientPort(),
queryMaster.getWorkerStatus());
writer.write(line);
}
writer.write("\n\n");
}
writer.write("Worker\n");
writer.write("======\n\n");
String fmtWorkerInfo = "%1$-5s %2$-5s%n";
String workerInfoLine = String.format(fmtWorkerInfo, "Live", "Dead");
writer.write(workerInfoLine);
line = String.format(fmtWorkerInfo, DASHLINE_LEN5, DASHLINE_LEN5);
writer.write(line);
line = String.format(fmtWorkerInfo, liveWorkers.size(), deadWorkers.size());
writer.write(line);
writer.write("\n");
writer.write("Live Workers\n");
writer.write("============\n\n");
if(liveWorkers.isEmpty()) {
writer.write("No Live Workers\n\n");
} else {
writeWorkerInfo(writer, liveWorkers);
}
writer.write("Dead Workers\n");
writer.write("============\n\n");
if(deadWorkers.isEmpty()) {
writer.write("No Dead Workers\n\n");
} else {
writeWorkerInfo(writer, deadWorkers);
}
}
private void writeWorkerInfo(Writer writer, List<WorkerResourceInfo> workers) throws ParseException,
IOException, ServiceException, SQLException {
String fmtWorkerLine = "%1$-25s %2$-5s %3$-5s %4$-10s %5$-12s %6$-10s%n";
String line = String.format(fmtWorkerLine,
"Worker", "Port", "Tasks", "Mem", "Cpu", "Status");
writer.write(line);
line = String.format(fmtWorkerLine,
DASHLINE_LEN25, DASHLINE_LEN5, DASHLINE_LEN5,
DASHLINE_LEN10, DASHLINE_LEN12, DASHLINE_LEN10);
writer.write(line);
for (WorkerResourceInfo worker : workers) {
TajoProtos.WorkerConnectionInfoProto connInfo = worker.getConnectionInfo();
String workerHost = String.format("%s:%d", connInfo.getHost(), connInfo.getPeerRpcPort());
String mem = String.format("%d/%d", worker.getAvailableResource().getMemory(),
worker.getTotalResource().getMemory());
String cpu = String.format("%d/%d", worker.getAvailableResource().getVirtualCores(),
worker.getTotalResource().getVirtualCores());
line = String.format(fmtWorkerLine, workerHost,
connInfo.getPullServerPort(),
worker.getNumRunningTasks(),
mem, cpu, worker.getWorkerStatus());
writer.write(line);
}
writer.write("\n\n");
}
private void processList(Writer writer) throws ParseException, IOException,
ServiceException, SQLException {
List<BriefQueryInfo> queryList = tajoClient.getRunningQueryList();
SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
StringBuilder builder = new StringBuilder();
/* print title */
builder.append(StringUtils.rightPad("QueryId", 21));
builder.append(StringUtils.rightPad("State", 20));
builder.append(StringUtils.rightPad("StartTime", 20));
builder.append(StringUtils.rightPad("Query", 30)).append("\n");
builder.append(StringUtils.rightPad(StringUtils.repeat("-", 20), 21));
builder.append(StringUtils.rightPad(StringUtils.repeat("-", 19), 20));
builder.append(StringUtils.rightPad(StringUtils.repeat("-", 19), 20));
builder.append(StringUtils.rightPad(StringUtils.repeat("-", 29), 30)).append("\n");
writer.write(builder.toString());
builder = new StringBuilder();
for (BriefQueryInfo queryInfo : queryList) {
builder.append(StringUtils.rightPad(new QueryId(queryInfo.getQueryId()).toString(), 21));
builder.append(StringUtils.rightPad(queryInfo.getState().name(), 20));
builder.append(StringUtils.rightPad(df.format(queryInfo.getStartTime()), 20));
builder.append(StringUtils.abbreviate(queryInfo.getQuery(), 30)).append("\n");
}
writer.write(builder.toString());
}
public void processKill(Writer writer, String queryIdStr)
throws IOException, ServiceException {
try {
QueryStatus status = tajoClient.killQuery(TajoIdUtils.parseQueryId(queryIdStr));
if (status.getState() == TajoProtos.QueryState.QUERY_KILLED) {
writer.write(queryIdStr + " is killed successfully.\n");
} else if (status.getState() == TajoProtos.QueryState.QUERY_KILL_WAIT) {
writer.write(queryIdStr + " will be finished after a while.\n");
} else {
writer.write("ERROR:" + status.getErrorMessage());
}
} catch (Throwable t) {
writer.write("ERROR:" + t.getMessage());
}
}
private void processMasters(Writer writer) throws ParseException, IOException,
ServiceException, SQLException {
if (tajoConf.getBoolVar(TajoConf.ConfVars.TAJO_MASTER_HA_ENABLE)) {
List<String> list = serviceTracker.getMasters(tajoConf);
int i = 0;
for (String master : list) {
if (i > 0) {
writer.write(" ");
}
writer.write(master);
i++;
}
writer.write("\n");
} else {
InetSocketAddress masterAddress = tajoConf.getSocketAddrVar(TajoConf.ConfVars.TAJO_MASTER_UMBILICAL_RPC_ADDRESS);
writer.write(masterAddress.getHostName());
writer.write("\n");
}
}
public static void main(String [] args) throws Exception {
TajoConf conf = new TajoConf();
try (Writer writer = new PrintWriter(System.out)) {
TajoAdmin admin = new TajoAdmin(conf, writer);
admin.runCommand(args);
} finally {
System.exit(0);
}
}
}