/* * 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.accumulo.minicluster.impl; import static java.util.Objects.requireNonNull; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.accumulo.cluster.ClusterControl; import org.apache.accumulo.gc.SimpleGarbageCollector; import org.apache.accumulo.master.Master; import org.apache.accumulo.minicluster.ServerType; import org.apache.accumulo.minicluster.impl.MiniAccumuloClusterImpl.LogWriter; import org.apache.accumulo.monitor.Monitor; import org.apache.accumulo.server.util.Admin; import org.apache.accumulo.tracer.TraceServer; import org.apache.accumulo.tserver.TabletServer; import org.apache.zookeeper.server.ZooKeeperServerMain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Maps; import java.util.Collections; import java.util.Map; /** * */ public class MiniAccumuloClusterControl implements ClusterControl { private static final Logger log = LoggerFactory.getLogger(MiniAccumuloClusterControl.class); protected MiniAccumuloClusterImpl cluster; Process zooKeeperProcess = null; Process masterProcess = null; Process gcProcess = null; Process monitor = null; Process tracer = null; final List<Process> tabletServerProcesses = new ArrayList<>(); public MiniAccumuloClusterControl(MiniAccumuloClusterImpl cluster) { requireNonNull(cluster); this.cluster = cluster; } public void start(ServerType server) throws IOException { start(server, null); } @Override public int exec(Class<?> clz, String[] args) throws IOException { Process p = cluster.exec(clz, args); int exitCode; try { exitCode = p.waitFor(); } catch (InterruptedException e) { log.warn("Interrupted waiting for process to exit", e); Thread.currentThread().interrupt(); throw new IOException(e); } return exitCode; } @Override public Entry<Integer,String> execWithStdout(Class<?> clz, String[] args) throws IOException { Process p = cluster.exec(clz, args); int exitCode; try { exitCode = p.waitFor(); } catch (InterruptedException e) { log.warn("Interrupted waiting for process to exit", e); Thread.currentThread().interrupt(); throw new IOException(e); } for (LogWriter writer : cluster.getLogWriters()) { writer.flush(); } return Maps.immutableEntry(exitCode, readAll(new FileInputStream(cluster.getConfig().getLogDir() + "/" + clz.getSimpleName() + "_" + p.hashCode() + ".out"))); } private String readAll(InputStream is) throws IOException { byte[] buffer = new byte[4096]; StringBuilder result = new StringBuilder(); while (true) { int n = is.read(buffer); if (n <= 0) break; result.append(new String(buffer, 0, n)); } return result.toString(); } @Override public void adminStopAll() throws IOException { Process p = cluster.exec(Admin.class, "stopAll"); try { p.waitFor(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IOException(e); } if (0 != p.exitValue()) { throw new IOException("Failed to run `accumulo admin stopAll`"); } } @Override public synchronized void startAllServers(ServerType server) throws IOException { start(server, null); } @Override public synchronized void start(ServerType server, String hostname) throws IOException { start(server, hostname, Collections.<String,String> emptyMap(), Integer.MAX_VALUE); } public synchronized void start(ServerType server, String hostname, Map<String,String> configOverrides, int limit) throws IOException { if (limit <= 0) { return; } switch (server) { case TABLET_SERVER: synchronized (tabletServerProcesses) { int count = 0; for (int i = tabletServerProcesses.size(); count < limit && i < cluster.getConfig().getNumTservers(); i++, ++count) { tabletServerProcesses.add(cluster._exec(TabletServer.class, server, configOverrides)); } } break; case MASTER: if (null == masterProcess) { masterProcess = cluster._exec(Master.class, server, configOverrides); } break; case ZOOKEEPER: if (null == zooKeeperProcess) { zooKeeperProcess = cluster._exec(ZooKeeperServerMain.class, server, configOverrides, cluster.getZooCfgFile().getAbsolutePath()); } break; case GARBAGE_COLLECTOR: if (null == gcProcess) { gcProcess = cluster._exec(SimpleGarbageCollector.class, server, configOverrides); } break; case MONITOR: if (null == monitor) { monitor = cluster._exec(Monitor.class, server, configOverrides); } break; case TRACER: if (null == tracer) { tracer = cluster._exec(TraceServer.class, server, configOverrides); } break; default: throw new UnsupportedOperationException("Cannot start process for " + server); } } @Override public synchronized void stopAllServers(ServerType server) throws IOException { stop(server); } public void stop(ServerType server) throws IOException { stop(server, null); } @Override public synchronized void stop(ServerType server, String hostname) throws IOException { switch (server) { case MASTER: if (null != masterProcess) { try { cluster.stopProcessWithTimeout(masterProcess, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("Master did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("Master did not fully stop after 30 seconds", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { masterProcess = null; } } break; case GARBAGE_COLLECTOR: if (null != gcProcess) { try { cluster.stopProcessWithTimeout(gcProcess, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("Garbage collector did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("Garbage collector did not fully stop after 30 seconds", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { gcProcess = null; } } break; case ZOOKEEPER: if (null != zooKeeperProcess) { try { cluster.stopProcessWithTimeout(zooKeeperProcess, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("ZooKeeper did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("ZooKeeper did not fully stop after 30 seconds", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { zooKeeperProcess = null; } } break; case TABLET_SERVER: synchronized (tabletServerProcesses) { try { for (Process tserver : tabletServerProcesses) { try { cluster.stopProcessWithTimeout(tserver, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("TabletServer did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("TabletServer did not fully stop after 30 seconds", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } finally { tabletServerProcesses.clear(); } } break; case MONITOR: if (monitor != null) { try { cluster.stopProcessWithTimeout(monitor, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("Monitor did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("Monitor did not fully stop after 30 seconds", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { monitor = null; } } break; case TRACER: if (tracer != null) { try { cluster.stopProcessWithTimeout(tracer, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("Tracer did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("Tracer did not fully stop after 30 seconds", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { tracer = null; } } break; default: throw new UnsupportedOperationException("ServerType is not yet supported " + server); } } @Override public void signal(ServerType server, String hostname, String signal) throws IOException { throw new UnsupportedOperationException(); } @Override public void suspend(ServerType server, String hostname) throws IOException { throw new UnsupportedOperationException(); } @Override public void resume(ServerType server, String hostname) throws IOException { throw new UnsupportedOperationException(); } public void killProcess(ServerType type, ProcessReference proc) throws ProcessNotFoundException, InterruptedException { boolean found = false; switch (type) { case MASTER: if (proc.equals(masterProcess)) { try { cluster.stopProcessWithTimeout(masterProcess, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("Master did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("Master did not fully stop after 30 seconds", e); } masterProcess = null; found = true; } break; case TABLET_SERVER: synchronized (tabletServerProcesses) { for (Process tserver : tabletServerProcesses) { if (proc.equals(tserver)) { tabletServerProcesses.remove(tserver); try { cluster.stopProcessWithTimeout(tserver, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("TabletServer did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("TabletServer did not fully stop after 30 seconds", e); } found = true; break; } } } break; case ZOOKEEPER: if (proc.equals(zooKeeperProcess)) { try { cluster.stopProcessWithTimeout(zooKeeperProcess, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("ZooKeeper did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("ZooKeeper did not fully stop after 30 seconds", e); } zooKeeperProcess = null; found = true; } break; case GARBAGE_COLLECTOR: if (proc.equals(gcProcess)) { try { cluster.stopProcessWithTimeout(gcProcess, 30, TimeUnit.SECONDS); } catch (ExecutionException e) { log.warn("GarbageCollector did not fully stop after 30 seconds", e); } catch (TimeoutException e) { log.warn("GarbageCollector did not fully stop after 30 seconds", e); } gcProcess = null; found = true; } break; default: // Ignore the other types MAC currently doesn't automateE found = true; break; } if (!found) throw new ProcessNotFoundException(); } @Override public void kill(ServerType server, String hostname) throws IOException { stop(server, hostname); } }