/**
* (c) Copyright 2012 WibiData, Inc.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* Licensed 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.kiji.mapreduce.tools;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import com.google.common.base.Preconditions;
import org.kiji.annotations.ApiAudience;
import org.kiji.common.flags.Flag;
import org.kiji.mapreduce.avro.generated.JobHistoryEntry;
import org.kiji.mapreduce.framework.JobHistoryKijiTable;
import org.kiji.schema.KConstants;
import org.kiji.schema.Kiji;
import org.kiji.schema.KijiRowData;
import org.kiji.schema.KijiRowScanner;
import org.kiji.schema.KijiURI;
import org.kiji.schema.tools.BaseTool;
import org.kiji.schema.tools.KijiToolLauncher;
/** A tool that installs a job history table and lets you query individual jobs from it. */
@ApiAudience.Private
public final class KijiJobHistory extends BaseTool {
@Flag(name="kiji", usage="URI of the Kiji instance to query.")
private String mKijiURIFlag = KConstants.DEFAULT_URI;
@Flag(name="job-id", usage="ID of the job to query.")
private String mJobId = "";
@Flag(name="verbose", usage="Include counters and configuration for a given job-id.")
private boolean mVerbose = false;
@Flag(name="get-counter", usage="Get the named counter for this job-id. Name must"
+ "be of the form group:counter-name.")
private String mCounterName = "";
@Flag(name="counter-names", usage="Get the names of existing counters for the job-id.")
private boolean mGetCounterNames = false;
/** URI of the Kiji instance to query. */
private KijiURI mKijiURI = null;
/** {@inheritDoc} */
@Override
public String getName() {
return "job-history";
}
/** {@inheritDoc} */
@Override
public String getDescription() {
return "Inspect or manipulate the MapReduce job history table.";
}
/** {@inheritDoc} */
@Override
public String getCategory() {
return "Admin";
}
/** {@inheritDoc} */
@Override
protected void validateFlags() throws Exception {
super.validateFlags();
Preconditions.checkArgument((mKijiURIFlag != null) && !mKijiURIFlag.isEmpty(),
"Specify a Kiji instance with --kiji=kiji://hbase-address/kiji-instance");
mKijiURI = KijiURI.newBuilder(mKijiURIFlag).build();
}
/** {@inheritDoc} */
@Override
protected int run(List<String> nonFlagArgs) throws Exception {
final Kiji kiji = Kiji.Factory.open(mKijiURI, getConf());
try {
JobHistoryKijiTable jobHistoryTable = JobHistoryKijiTable.open(kiji);
try {
if (!mJobId.isEmpty()) {
JobHistoryEntry data = jobHistoryTable.getJobDetails(mJobId);
printEntry(data);
} else {
KijiRowScanner jobScanner = jobHistoryTable.getJobScanner();
for (KijiRowData data : jobScanner) {
String jobid = data.getMostRecentValue("info", "jobId").toString();
printEntry(jobHistoryTable.getJobDetails(jobid));
getPrintStream().printf("%n");
}
jobScanner.close();
}
} finally {
jobHistoryTable.close();
}
} finally {
kiji.release();
}
return SUCCESS;
}
/**
* Prints a job details.
*
* @param entry a JobHistoryEntry retrieved from the JobHistoryTable.
* @throws IOException If there is an error retrieving the counters.
*/
private void printEntry(JobHistoryEntry entry) throws IOException {
final PrintStream ps = getPrintStream();
ps.printf("Job:\t\t%s%n", entry.getJobId());
ps.printf("Name:\t\t%s%n", entry.getJobName());
ps.printf("Started:\t%s%n", new Date(entry.getJobStartTime()));
ps.printf("Ended:\t\t%s%n", new Date(entry.getJobEndTime()));
ps.printf("End Status:\t\t%s%n", entry.getJobEndStatus());
if (mVerbose) {
// we don't print individual counters, instead pretty print the counters string we stored.
printCounters(entry);
printConfiguration(entry);
}
if (mGetCounterNames) {
ps.println("Counters for this job:");
ps.println(Arrays.toString(entry.getCountersFamily().keySet().toArray()));
}
if (!mCounterName.isEmpty()) {
if (entry.getCountersFamily().containsKey(mCounterName)) {
ps.println("Value for counter " + mCounterName + ":");
ps.println(entry.getCountersFamily().get(mCounterName));
} else {
ps.println("Counter not found: " + mCounterName);
}
}
}
/**
* Prints a representation of the Counters for a Job.
*
* @param entry a JobHistoryEntry retrieved from the JobHistoryTable.
*/
private void printCounters(JobHistoryEntry entry) {
PrintStream ps = getPrintStream();
ps.println("Counters:");
ps.println(entry.getJobCounters());
}
/**
* Prints a representation of the Configuration for the Job.
* @param entry a JobHistoryEntry retrieved from the JobHistoryTable.
*/
private void printConfiguration(JobHistoryEntry entry) {
PrintStream ps = getPrintStream();
ps.println("Configuration:");
ps.println(entry.getJobConfiguration());
}
/**
* Program entry point.
*
* @param args The command-line arguments.
* @throws Exception If there is an error.
*/
public static void main(String[] args) throws Exception {
System.exit(new KijiToolLauncher().run(new KijiJobHistory(), args));
}
}