/** * 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.yarn.server.resourcemanager.recovery; import java.io.IOException; import java.io.InterruptedIOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationMaster; import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.NodeId; import org.apache.hadoop.yarn.api.records.NodeReport; import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationMasterPBImpl; import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationSubmissionContextPBImpl; import org.apache.hadoop.yarn.api.records.impl.pb.ContainerPBImpl; import org.apache.hadoop.yarn.api.records.impl.pb.NodeIdPBImpl; import org.apache.hadoop.yarn.api.records.impl.pb.NodeReportPBImpl; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.factories.RecordFactory; import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider; import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationMasterProto; import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationSubmissionContextProto; import org.apache.hadoop.yarn.proto.YarnProtos.ContainerProto; import org.apache.hadoop.yarn.proto.YarnProtos.NodeIdProto; import org.apache.hadoop.yarn.proto.YarnProtos.NodeReportProto; import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNode; import org.apache.hadoop.yarn.util.ConverterUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; public class ZKStore implements Store { private final Configuration conf; private final ZooKeeper zkClient; private static final Log LOG = LogFactory.getLog(ZKStore.class); private final RecordFactory recordFactory = RecordFactoryProvider.getRecordFactory(null); private static final String NODES = "nodes/"; private static final String APPS = "apps/"; private static final String ZK_PATH_SEPARATOR = "/"; private static final String NODE_ID = "nodeid"; private static final String APP_MASTER = "master"; private static final String APP_MASTER_CONTAINER = "mastercontainer"; private final String ZK_ADDRESS; private final int ZK_TIMEOUT; private boolean doneWithRecovery = false; /** TODO make this generic **/ private NodeIdPBImpl nodeId = new NodeIdPBImpl(); /** * TODO fix this for later to handle all kinds of events * of connection and session events. * */ private static class ZKWatcher implements Watcher { @Override public void process(WatchedEvent arg0) { } } public ZKStore(Configuration conf) throws IOException { this.conf = conf; this.ZK_ADDRESS = conf.get(YarnConfiguration.RM_ZK_STORE_ADDRESS); this.ZK_TIMEOUT = conf.getInt(YarnConfiguration.RM_ZK_STORE_TIMEOUT_MS, YarnConfiguration.DEFAULT_RM_ZK_STORE_TIMEOUT_MS); zkClient = new ZooKeeper(this.ZK_ADDRESS, this.ZK_TIMEOUT, createZKWatcher() ); // TODO: FIXMEVinodkv // this.nodeId.setId(0); } protected Watcher createZKWatcher() { return new ZKWatcher(); } private NodeReportPBImpl createNodeManagerInfo(RMNode rmNode) { NodeReport node = recordFactory.newRecordInstance(NodeReport.class); node.setNodeId(rmNode.getNodeID()); node.setRackName(rmNode.getRackName()); node.setCapability(rmNode.getTotalCapability()); // TODO: FIXME // node.setUsed(nodeInfo.getUsedResource()); // TODO: acm: refactor2 FIXME // node.setNumContainers(rmNode.getNumContainers()); return (NodeReportPBImpl)node; } @Override public synchronized void storeNode(RMNode node) throws IOException { /** create a storage node and store it in zk **/ if (!doneWithRecovery) return; // TODO: FIXMEVinodkv // NodeReportPBImpl nodeManagerInfo = createNodeManagerInfo(node); // byte[] bytes = nodeManagerInfo.getProto().toByteArray(); // try { // zkClient.create(NODES + Integer.toString(node.getNodeID().getId()), bytes, null, // CreateMode.PERSISTENT); // } catch(InterruptedException ie) { // LOG.info("Interrupted", ie); // throw new InterruptedIOException("Interrupted"); // } catch(KeeperException ke) { // LOG.info("Keeper exception", ke); // throw convertToIOException(ke); // } } @Override public synchronized void removeNode(RMNode node) throws IOException { if (!doneWithRecovery) return; // TODO: FIXME VINODKV // /** remove a storage node **/ // try { // zkClient.delete(NODES + Integer.toString(node.getNodeID().getId()), -1); // } catch(InterruptedException ie) { // LOG.info("Interrupted", ie); // throw new InterruptedIOException("Interrupted"); // } catch(KeeperException ke) { // LOG.info("Keeper exception", ke); // throw convertToIOException(ke); // } } private static IOException convertToIOException(KeeperException ke) { IOException io = new IOException(); io.setStackTrace(ke.getStackTrace()); return io; } @Override public synchronized NodeId getNextNodeId() throws IOException { // TODO: FIXME VINODKV // int num = nodeId.getId(); // num++; // nodeId.setId(num); // try { // zkClient.setData(NODES + NODE_ID, nodeId.getProto().toByteArray() , -1); // } catch(InterruptedException ie) { // LOG.info("Interrupted", ie); // throw new InterruptedIOException(ie.getMessage()); // } catch(KeeperException ke) { // throw convertToIOException(ke); // } return nodeId; } private String containerPathFromContainerId(ContainerId containerId) { String appString = ConverterUtils.toString( containerId.getApplicationAttemptId().getApplicationId()); return appString + "/" + containerId.getId(); } private class ZKApplicationStore implements ApplicationStore { private final ApplicationId applicationId; public ZKApplicationStore(ApplicationId applicationId) { this.applicationId = applicationId; } @Override public void storeMasterContainer(Container container) throws IOException { if (!doneWithRecovery) return; ContainerPBImpl containerPBImpl = (ContainerPBImpl) container; try { zkClient.setData(APPS + ConverterUtils.toString( container.getId().getApplicationAttemptId().getApplicationId()) + ZK_PATH_SEPARATOR + APP_MASTER_CONTAINER , containerPBImpl.getProto().toByteArray(), -1); } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { LOG.info("Keeper exception", ke); throw convertToIOException(ke); } } @Override public synchronized void storeContainer(Container container) throws IOException { if (!doneWithRecovery) return; ContainerPBImpl containerPBImpl = (ContainerPBImpl) container; try { zkClient.create(APPS + containerPathFromContainerId(container.getId()) , containerPBImpl.getProto().toByteArray(), null, CreateMode.PERSISTENT); } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { LOG.info("Keeper exception", ke); throw convertToIOException(ke); } } @Override public synchronized void removeContainer(Container container) throws IOException { if (!doneWithRecovery) return; try { zkClient.delete(APPS + containerPathFromContainerId(container.getId()), -1); } catch(InterruptedException ie) { throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { LOG.info("Keeper exception", ke); throw convertToIOException(ke); } } @Override public void updateApplicationState( ApplicationMaster master) throws IOException { if (!doneWithRecovery) return; String appString = APPS + ConverterUtils.toString(applicationId); ApplicationMasterPBImpl masterPBImpl = (ApplicationMasterPBImpl) master; try { zkClient.setData(appString, masterPBImpl.getProto().toByteArray(), -1); } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { LOG.info("Keeper exception", ke); throw convertToIOException(ke); } } @Override public boolean isLoggable() { return doneWithRecovery; } } @Override public synchronized ApplicationStore createApplicationStore(ApplicationId application, ApplicationSubmissionContext context) throws IOException { if (!doneWithRecovery) return new ZKApplicationStore(application); ApplicationSubmissionContextPBImpl contextPBImpl = (ApplicationSubmissionContextPBImpl) context; String appString = APPS + ConverterUtils.toString(application); ApplicationMasterPBImpl masterPBImpl = new ApplicationMasterPBImpl(); ContainerPBImpl container = new ContainerPBImpl(); try { zkClient.create(appString, contextPBImpl.getProto() .toByteArray(), null, CreateMode.PERSISTENT); zkClient.create(appString + ZK_PATH_SEPARATOR + APP_MASTER, masterPBImpl.getProto().toByteArray(), null, CreateMode.PERSISTENT); zkClient.create(appString + ZK_PATH_SEPARATOR + APP_MASTER_CONTAINER, container.getProto().toByteArray(), null, CreateMode.PERSISTENT); } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { LOG.info("Keeper exception", ke); throw convertToIOException(ke); } return new ZKApplicationStore(application); } @Override public synchronized void removeApplication(ApplicationId application) throws IOException { if (!doneWithRecovery) return; try { zkClient.delete(APPS + ConverterUtils.toString(application), -1); } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { LOG.info("Keeper Exception", ke); throw convertToIOException(ke); } } @Override public boolean isLoggable() { return doneWithRecovery; } @Override public void doneWithRecovery() { this.doneWithRecovery = true; } @Override public synchronized RMState restore() throws IOException { ZKRMState rmState = new ZKRMState(); rmState.load(); return rmState; } private static class ApplicationInfoImpl implements ApplicationInfo { private ApplicationMaster master; private Container masterContainer; private final ApplicationSubmissionContext context; private final List<Container> containers = new ArrayList<Container>(); public ApplicationInfoImpl(ApplicationSubmissionContext context) { this.context = context; } public void setApplicationMaster(ApplicationMaster master) { this.master = master; } public void setMasterContainer(Container container) { this.masterContainer = container; } @Override public ApplicationMaster getApplicationMaster() { return this.master; } @Override public ApplicationSubmissionContext getApplicationSubmissionContext() { return this.context; } @Override public Container getMasterContainer() { return this.masterContainer; } @Override public List<Container> getContainers() { return this.containers; } public void addContainer(Container container) { containers.add(container); } } private class ZKRMState implements RMState { private List<RMNode> nodeManagers = new ArrayList<RMNode>(); private Map<ApplicationId, ApplicationInfo> applications = new HashMap<ApplicationId, ApplicationInfo>(); public ZKRMState() { LOG.info("Restoring RM state from ZK"); } private synchronized List<NodeReport> listStoredNodes() throws IOException { /** get the list of nodes stored in zk **/ //TODO PB List<NodeReport> nodes = new ArrayList<NodeReport>(); Stat stat = new Stat(); try { List<String> children = zkClient.getChildren(NODES, false); for (String child: children) { byte[] data = zkClient.getData(NODES + child, false, stat); NodeReportPBImpl nmImpl = new NodeReportPBImpl( NodeReportProto.parseFrom(data)); nodes.add(nmImpl); } } catch (InterruptedException ie) { LOG.info("Interrupted" , ie); throw new InterruptedIOException("Interrupted"); } catch(KeeperException ke) { LOG.error("Failed to list nodes", ke); throw convertToIOException(ke); } return nodes; } @Override public List<RMNode> getStoredNodeManagers() { return nodeManagers; } @Override public NodeId getLastLoggedNodeId() { return nodeId; } private void readLastNodeId() throws IOException { Stat stat = new Stat(); try { byte[] data = zkClient.getData(NODES + NODE_ID, false, stat); nodeId = new NodeIdPBImpl(NodeIdProto.parseFrom(data)); } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { LOG.info("Keeper Exception", ke); throw convertToIOException(ke); } } private ApplicationInfo getAppInfo(String app) throws IOException { ApplicationInfoImpl info = null; Stat stat = new Stat(); try { ApplicationSubmissionContext context = null; byte[] data = zkClient.getData(APPS + app, false, stat); context = new ApplicationSubmissionContextPBImpl( ApplicationSubmissionContextProto.parseFrom(data)); info = new ApplicationInfoImpl(context); List<String> children = zkClient.getChildren(APPS + app, false, stat); ApplicationMaster master = null; for (String child: children) { byte[] childdata = zkClient.getData(APPS + app + ZK_PATH_SEPARATOR + child, false, stat); if (APP_MASTER.equals(child)) { master = new ApplicationMasterPBImpl(ApplicationMasterProto.parseFrom(childdata)); info.setApplicationMaster(master); } else if (APP_MASTER_CONTAINER.equals(child)) { Container masterContainer = new ContainerPBImpl(ContainerProto.parseFrom(data)); info.setMasterContainer(masterContainer); } else { Container container = new ContainerPBImpl(ContainerProto.parseFrom(data)); info.addContainer(container); } } } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { throw convertToIOException(ke); } return info; } private void load() throws IOException { List<NodeReport> nodeInfos = listStoredNodes(); final Pattern trackerPattern = Pattern.compile(".*:.*"); final Matcher m = trackerPattern.matcher(""); for (NodeReport node: nodeInfos) { m.reset(node.getNodeId().getHost()); if (!m.find()) { LOG.info("Skipping node, bad node-address " + node.getNodeId().getHost()); continue; } String hostName = m.group(0); int cmPort = Integer.valueOf(m.group(1)); m.reset(node.getHttpAddress()); if (!m.find()) { LOG.info("Skipping node, bad http-address " + node.getHttpAddress()); continue; } int httpPort = Integer.valueOf(m.group(1)); // TODO: FindBugs warns passing null below. Commenting this for later. // RMNode nm = new RMNodeImpl(node.getNodeId(), null, // hostName, cmPort, httpPort, // ResourceTrackerService.resolve(node.getNodeId().getHost()), // node.getCapability()); // nodeManagers.add(nm); } readLastNodeId(); /* make sure we get all the applications */ List<String> apps = null; try { apps = zkClient.getChildren(APPS, false); } catch(InterruptedException ie) { LOG.info("Interrupted", ie); throw new InterruptedIOException(ie.getMessage()); } catch(KeeperException ke) { throw convertToIOException(ke); } for (String app: apps) { ApplicationInfo info = getAppInfo(app); applications.put(info.getApplicationMaster().getApplicationId(), info); } } @Override public Map<ApplicationId, ApplicationInfo> getStoredApplications() { return applications; } } }