blob: 6b9fdd202cbaa71d4d83e4fc0e621fc991e2cc1a [file] [log] [blame]
/**
* 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.tools.rumen;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
/**
* {@link ZombieCluster} rebuilds the cluster topology using the information
* obtained from job history logs.
*/
public class ZombieCluster extends AbstractClusterStory {
private Node root;
/**
* Construct a homogeneous cluster. We assume that the leaves on the topology
* are {@link MachineNode}s, and the parents of {@link MachineNode}s are
* {@link RackNode}s. We also expect all leaf nodes are on the same level.
*
* @param topology
* The network topology.
* @param defaultNode
* The default node setting.
*/
public ZombieCluster(LoggedNetworkTopology topology, MachineNode defaultNode) {
buildCluster(topology, defaultNode);
}
/**
* Construct a homogeneous cluster. We assume that the leaves on the topology
* are {@link MachineNode}s, and the parents of {@link MachineNode}s are
* {@link RackNode}s. We also expect all leaf nodes are on the same level.
*
* @param path Path to the JSON-encoded topology file.
* @param conf
* @param defaultNode
* The default node setting.
* @throws IOException
*/
public ZombieCluster(Path path, MachineNode defaultNode, Configuration conf) throws IOException {
this(new ClusterTopologyReader(path, conf).get(), defaultNode);
}
/**
* Construct a homogeneous cluster. We assume that the leaves on the topology
* are {@link MachineNode}s, and the parents of {@link MachineNode}s are
* {@link RackNode}s. We also expect all leaf nodes are on the same level.
*
* @param input The input stream for the JSON-encoded topology file.
* @param defaultNode
* The default node setting.
* @throws IOException
*/
public ZombieCluster(InputStream input, MachineNode defaultNode) throws IOException {
this(new ClusterTopologyReader(input).get(), defaultNode);
}
@Override
public Node getClusterTopology() {
return root;
}
private final void buildCluster(LoggedNetworkTopology topology,
MachineNode defaultNode) {
Map<LoggedNetworkTopology, Integer> levelMapping =
new IdentityHashMap<LoggedNetworkTopology, Integer>();
Deque<LoggedNetworkTopology> unvisited =
new ArrayDeque<LoggedNetworkTopology>();
unvisited.add(topology);
levelMapping.put(topology, 0);
// building levelMapping and determine leafLevel
int leafLevel = -1; // -1 means leafLevel unknown.
for (LoggedNetworkTopology n = unvisited.poll(); n != null;
n = unvisited.poll()) {
int level = levelMapping.get(n);
List<LoggedNetworkTopology> children = n.getChildren();
if (children == null || children.isEmpty()) {
if (leafLevel == -1) {
leafLevel = level;
} else if (leafLevel != level) {
throw new IllegalArgumentException(
"Leaf nodes are not on the same level");
}
} else {
for (LoggedNetworkTopology child : children) {
levelMapping.put(child, level + 1);
unvisited.addFirst(child);
}
}
}
/**
* A second-pass dfs traverse of topology tree. path[i] contains the parent
* of the node at level i+1.
*/
Node[] path = new Node[leafLevel];
unvisited.add(topology);
for (LoggedNetworkTopology n = unvisited.poll(); n != null;
n = unvisited.poll()) {
int level = levelMapping.get(n);
Node current;
if (level == leafLevel) { // a machine node
MachineNode.Builder builder =
new MachineNode.Builder(n.getName().getValue(), level);
if (defaultNode != null) {
builder.cloneFrom(defaultNode);
}
current = builder.build();
} else {
current = (level == leafLevel - 1)
? new RackNode(n.getName().getValue(), level) :
new Node(n.getName().getValue(), level);
path[level] = current;
// Add all children to the front of the queue.
for (LoggedNetworkTopology child : n.getChildren()) {
unvisited.addFirst(child);
}
}
if (level != 0) {
path[level - 1].addChild(current);
}
}
root = path[0];
}
}