/**
* Copyright The Apache Software Foundation
*
* 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 com.alibaba.wasp.master;
import com.alibaba.wasp.FConstants;
import com.alibaba.wasp.LocalWaspCluster;
import com.alibaba.wasp.MasterNotRunningException;
import com.alibaba.wasp.ZNodeClearer;
import com.alibaba.wasp.ZooKeeperConnectionException;
import com.alibaba.wasp.client.WaspAdmin;
import com.alibaba.wasp.fserver.FServer;
import com.alibaba.wasp.util.JVMClusterUtil;
import com.alibaba.wasp.util.ServerCommandLine;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
import org.apache.zookeeper.KeeperException;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class FMasterCommandLine extends ServerCommandLine {
private static final Log LOG = LogFactory.getLog(FMasterCommandLine.class);
private static final String USAGE = "Usage: Master [opts] start|stop|clear\n"
+ " start Start Master. If local mode, start Master and FServer in same JVM\n"
+ " stop Start cluster shutdown; Master signals FServer shutdown\n"
+ " clear Delete the master znode in ZooKeeper after a master crashes\n "
+ " where [opts] are:\n"
+ " --minServers=<servers> Minimum FServers needed to host user tables.\n"
+ " --backup Master should start in backup mode";
private final Class<? extends FMaster> masterClass;
public FMasterCommandLine(Class<? extends FMaster> masterClass) {
this.masterClass = masterClass;
}
protected String getUsage() {
return USAGE;
}
public int run(String args[]) throws Exception {
Options opt = new Options();
opt.addOption("minServers", true,
"Minimum FServers needed to host user tables");
opt.addOption("backup", false,
"Do not try to become FMaster until the primary fails");
CommandLine cmd;
try {
cmd = new GnuParser().parse(opt, args);
} catch (ParseException e) {
LOG.error("Could not parse: ", e);
usage(null);
return -1;
}
if (cmd.hasOption("minServers")) {
String val = cmd.getOptionValue("minServers");
getConf().setInt("wasp.fserver.count.min", Integer.valueOf(val));
LOG.debug("minServers set to " + val);
}
// check if we are the backup master - override the conf if so
if (cmd.hasOption("backup")) {
getConf().setBoolean(FConstants.MASTER_TYPE_BACKUP, true);
}
List<String> remainingArgs = cmd.getArgList();
if (remainingArgs.size() != 1) {
usage(null);
return -1;
}
String command = remainingArgs.get(0);
if ("start".equals(command)) {
return startMaster();
} else if ("stop".equals(command)) {
return stopMaster();
} else if ("clear".equals(command)) {
return (ZNodeClearer.clear(getConf()) ? 0 : -1);
} else {
usage("Invalid command: " + command);
return -1;
}
}
private int startMaster() {
Configuration conf = getConf();
try {
// If 'local', defer to LocalWaspCluster instance. Starts master
// and fserver both in the one JVM.
if (LocalWaspCluster.isLocal(conf)) {
final MiniZooKeeperCluster zooKeeperCluster = new MiniZooKeeperCluster();
File zkDataPath = new File(conf.get(FConstants.ZOOKEEPER_DATA_DIR));
int zkClientPort = conf.getInt(FConstants.ZOOKEEPER_CLIENT_PORT, 0);
if (zkClientPort == 0) {
throw new IOException("No config value for "
+ FConstants.ZOOKEEPER_CLIENT_PORT);
}
zooKeeperCluster.setDefaultClientPort(zkClientPort);
int clientPort = zooKeeperCluster.startup(zkDataPath);
if (clientPort != zkClientPort) {
String errorMsg = "Could not start ZK at requested port of "
+ zkClientPort + ". ZK was started at port: " + clientPort
+ ". Aborting as clients (e.g. shell) will not be able to find "
+ "this ZK quorum.";
System.err.println(errorMsg);
throw new IOException(errorMsg);
}
conf.set(FConstants.ZOOKEEPER_CLIENT_PORT, Integer.toString(clientPort));
// Need to have the zk cluster shutdown when master is shutdown.
// Run a subclass that does the zk cluster shutdown on its way out.
LocalWaspCluster cluster = new LocalWaspCluster(conf, 1, 1,
LocalFMaster.class, FServer.class);
((LocalFMaster) cluster.getMaster(0)).setZKCluster(zooKeeperCluster);
cluster.startup();
waitOnMasterThreads(cluster);
} else {
FMaster master = FMaster.constructMaster(masterClass, conf);
if (master.isStopped()) {
LOG.info("Won't bring the Master up as a shutdown is requested");
return -1;
}
master.start();
master.join();
if (master.isAborted())
throw new RuntimeException("FMaster Aborted");
}
} catch (Throwable t) {
LOG.error("Failed to start master", t);
return -1;
}
return 0;
}
private int stopMaster() {
WaspAdmin adm = null;
try {
Configuration conf = getConf();
// Don't try more than once
conf.setInt("wasp.client.retries.number", 1);
adm = new WaspAdmin(getConf());
} catch (MasterNotRunningException e) {
LOG.error("Master not running");
return -1;
} catch (ZooKeeperConnectionException e) {
LOG.error("ZooKeeper not available");
return -1;
}
try {
adm.shutdown();
} catch (Throwable t) {
LOG.error("Failed to stop master", t);
return -1;
} finally {
if (adm != null) {
try {
adm.close();
} catch (IOException e) {
LOG.error("Failed to close admin.");
return -1;
}
}
}
return 0;
}
private void waitOnMasterThreads(LocalWaspCluster cluster)
throws InterruptedException {
List<JVMClusterUtil.MasterThread> masters = cluster.getMasters();
List<JVMClusterUtil.FServerThread> fservers = cluster.getFServers();
if (masters != null) {
for (JVMClusterUtil.MasterThread t : masters) {
t.join();
if (t.getMaster().isAborted()) {
closeAllFServerThreads(fservers);
throw new RuntimeException("FMaster Aborted");
}
}
}
}
private static void closeAllFServerThreads(
List<JVMClusterUtil.FServerThread> fservers) {
for (JVMClusterUtil.FServerThread t : fservers) {
t.getFServer().stop("FMaster Aborted; Bringing down fservers");
}
}
/*
* Version of master that will shutdown the passed zk cluster on its way out.
*/
public static class LocalFMaster extends FMaster {
private MiniZooKeeperCluster zkcluster = null;
public LocalFMaster(Configuration conf) throws IOException,
KeeperException, InterruptedException {
super(conf);
}
@Override
public void run() {
super.run();
if (this.zkcluster != null) {
try {
this.zkcluster.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
}
void setZKCluster(final MiniZooKeeperCluster zkcluster) {
this.zkcluster = zkcluster;
}
}
}