/** * 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. */ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.Runtime; import java.net.URL; import java.net.URLConnection; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * This class repeatedly queries a namenode looking for corrupt replicas. If * any are found a provided hadoop job is launched and the output printed * to stdout. * * The syntax is: * * java BlockForensics http://[namenode]:[port]/corrupt_replicas_xml.jsp * [sleep time between namenode query for corrupt blocks * (in seconds)] [mapred jar location] [hdfs input path] * * All arguments are required. */ public class BlockForensics { public static String join(List<?> l, String sep) { StringBuilder sb = new StringBuilder(); Iterator it = l.iterator(); while(it.hasNext()){ sb.append(it.next()); if (it.hasNext()) { sb.append(sep); } } return sb.toString(); } // runs hadoop command and prints output to stdout public static void runHadoopCmd(String ... args) throws IOException { String hadoop_home = System.getenv("HADOOP_PREFIX"); List<String> l = new LinkedList<String>(); l.add("bin/hadoop"); l.addAll(Arrays.asList(args)); ProcessBuilder pb = new ProcessBuilder(l); if (hadoop_home != null) { pb.directory(new File(hadoop_home)); } pb.redirectErrorStream(true); Process p = pb.start(); BufferedReader br = new BufferedReader( new InputStreamReader(p.getInputStream())); String line; while ((line = br.readLine()) != null) { System.out.println(line); } } public static void main(String[] args) throws SAXException, ParserConfigurationException, InterruptedException, IOException { if (System.getenv("HADOOP_PREFIX") == null) { System.err.println("The environmental variable HADOOP_PREFIX is undefined"); System.exit(1); } if (args.length < 4) { System.out.println("Usage: java BlockForensics [http://namenode:port/" + "corrupt_replicas_xml.jsp] [sleep time between " + "requests (in milliseconds)] [mapred jar location] " + "[hdfs input path]"); return; } int sleepTime = 30000; try { sleepTime = Integer.parseInt(args[1]); } catch (NumberFormatException e) { System.out.println("The sleep time entered is invalid, " + "using default value: "+sleepTime+"ms"); } Set<Long> blockIds = new TreeSet<Long>(); while (true) { InputStream xml = new URL(args[0]).openConnection().getInputStream(); DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = fact.newDocumentBuilder(); Document doc = builder.parse(xml); NodeList corruptReplicaNodes = doc.getElementsByTagName("block_id"); List<Long> searchBlockIds = new LinkedList<Long>(); for(int i=0; i<corruptReplicaNodes.getLength(); i++) { Long blockId = new Long(corruptReplicaNodes.item(i) .getFirstChild() .getNodeValue()); if (!blockIds.contains(blockId)) { blockIds.add(blockId); searchBlockIds.add(blockId); } } if (searchBlockIds.size() > 0) { String blockIdsStr = BlockForensics.join(searchBlockIds, ","); System.out.println("\nSearching for: " + blockIdsStr); String tmpDir = new String("/tmp-block-forensics-" + Integer.toString(new Random().nextInt(Integer.MAX_VALUE))); System.out.println("Using temporary dir: "+tmpDir); // delete tmp dir BlockForensics.runHadoopCmd("fs", "-rmr", tmpDir); // launch mapred job BlockForensics.runHadoopCmd("jar", args[2], // jar location args[3], // input dir tmpDir, // output dir blockIdsStr// comma delimited list of blocks ); // cat output BlockForensics.runHadoopCmd("fs", "-cat", tmpDir+"/part*"); // delete temp dir BlockForensics.runHadoopCmd("fs", "-rmr", tmpDir); int sleepSecs = (int)(sleepTime/1000.); System.out.print("Sleeping for "+sleepSecs + " second"+(sleepSecs == 1?"":"s")+"."); } System.out.print("."); Thread.sleep(sleepTime); } } }