001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.zookeeper;
020
021import java.io.BufferedReader;
022import java.io.BufferedWriter;
023import java.io.IOException;
024import java.io.InputStreamReader;
025import java.io.OutputStreamWriter;
026import java.io.PrintWriter;
027import java.net.InetSocketAddress;
028import java.net.Socket;
029import java.nio.charset.StandardCharsets;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.Collections;
033import java.util.Deque;
034import java.util.HashMap;
035import java.util.Iterator;
036import java.util.LinkedList;
037import java.util.List;
038import java.util.Map;
039import java.util.stream.Collectors;
040import javax.security.auth.login.AppConfigurationEntry;
041import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
042import org.apache.commons.lang3.StringUtils;
043import org.apache.hadoop.conf.Configuration;
044import org.apache.hadoop.hbase.AuthUtil;
045import org.apache.hadoop.hbase.HConstants;
046import org.apache.hadoop.hbase.exceptions.DeserializationException;
047import org.apache.hadoop.hbase.security.Superusers;
048import org.apache.hadoop.hbase.util.Bytes;
049import org.apache.hadoop.hbase.util.Threads;
050import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.CreateAndFailSilent;
051import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.DeleteNodeFailSilent;
052import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp.SetData;
053import org.apache.hadoop.security.SecurityUtil;
054import org.apache.hadoop.security.UserGroupInformation;
055import org.apache.hadoop.security.authentication.util.KerberosUtil;
056import org.apache.yetus.audience.InterfaceAudience;
057import org.apache.zookeeper.AsyncCallback;
058import org.apache.zookeeper.CreateMode;
059import org.apache.zookeeper.KeeperException;
060import org.apache.zookeeper.KeeperException.NoNodeException;
061import org.apache.zookeeper.Op;
062import org.apache.zookeeper.Watcher;
063import org.apache.zookeeper.ZooDefs.Ids;
064import org.apache.zookeeper.ZooDefs.Perms;
065import org.apache.zookeeper.ZooKeeper;
066import org.apache.zookeeper.client.ZooKeeperSaslClient;
067import org.apache.zookeeper.data.ACL;
068import org.apache.zookeeper.data.Id;
069import org.apache.zookeeper.data.Stat;
070import org.apache.zookeeper.proto.CreateRequest;
071import org.apache.zookeeper.proto.DeleteRequest;
072import org.apache.zookeeper.proto.SetDataRequest;
073import org.apache.zookeeper.server.ZooKeeperSaslServer;
074import org.slf4j.Logger;
075import org.slf4j.LoggerFactory;
076
077import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
078
079import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
080import org.apache.hadoop.hbase.shaded.protobuf.generated.ReplicationProtos;
081
082/**
083 * Internal HBase utility class for ZooKeeper.
084 *
085 * <p>Contains only static methods and constants.
086 *
087 * <p>Methods all throw {@link KeeperException} if there is an unexpected
088 * zookeeper exception, so callers of these methods must handle appropriately.
089 * If ZK is required for the operation, the server will need to be aborted.
090 */
091@InterfaceAudience.Private
092public final class ZKUtil {
093  private static final Logger LOG = LoggerFactory.getLogger(ZKUtil.class);
094
095  private static int zkDumpConnectionTimeOut;
096
097  private ZKUtil() {
098  }
099
100  /**
101   * Creates a new connection to ZooKeeper, pulling settings and ensemble config
102   * from the specified configuration object using methods from {@link ZKConfig}.
103   *
104   * Sets the connection status monitoring watcher to the specified watcher.
105   *
106   * @param conf configuration to pull ensemble and other settings from
107   * @param watcher watcher to monitor connection changes
108   * @return connection to zookeeper
109   * @throws IOException if unable to connect to zk or config problem
110   */
111  public static RecoverableZooKeeper connect(Configuration conf, Watcher watcher)
112    throws IOException {
113    String ensemble = ZKConfig.getZKQuorumServersString(conf);
114    return connect(conf, ensemble, watcher);
115  }
116
117  public static RecoverableZooKeeper connect(Configuration conf, String ensemble,
118      Watcher watcher)
119    throws IOException {
120    return connect(conf, ensemble, watcher, null);
121  }
122
123  public static RecoverableZooKeeper connect(Configuration conf, String ensemble,
124      Watcher watcher, final String identifier)
125    throws IOException {
126    if(ensemble == null) {
127      throw new IOException("Unable to determine ZooKeeper ensemble");
128    }
129    int timeout = conf.getInt(HConstants.ZK_SESSION_TIMEOUT,
130        HConstants.DEFAULT_ZK_SESSION_TIMEOUT);
131    if (LOG.isTraceEnabled()) {
132      LOG.trace("{} opening connection to ZooKeeper ensemble={}", identifier, ensemble);
133    }
134    int retry = conf.getInt("zookeeper.recovery.retry", 3);
135    int retryIntervalMillis =
136      conf.getInt("zookeeper.recovery.retry.intervalmill", 1000);
137    int maxSleepTime = conf.getInt("zookeeper.recovery.retry.maxsleeptime", 60000);
138    zkDumpConnectionTimeOut = conf.getInt("zookeeper.dump.connection.timeout",
139        1000);
140    int multiMaxSize = conf.getInt("zookeeper.multi.max.size", 1024*1024);
141    return new RecoverableZooKeeper(ensemble, timeout, watcher,
142        retry, retryIntervalMillis, maxSleepTime, identifier, multiMaxSize);
143  }
144
145  /**
146   * Log in the current zookeeper server process using the given configuration
147   * keys for the credential file and login principal.
148   *
149   * <p><strong>This is only applicable when running on secure hbase</strong>
150   * On regular HBase (without security features), this will safely be ignored.
151   * </p>
152   *
153   * @param conf The configuration data to use
154   * @param keytabFileKey Property key used to configure the path to the credential file
155   * @param userNameKey Property key used to configure the login principal
156   * @param hostname Current hostname to use in any credentials
157   * @throws IOException underlying exception from SecurityUtil.login() call
158   */
159  public static void loginServer(Configuration conf, String keytabFileKey,
160      String userNameKey, String hostname) throws IOException {
161    login(conf, keytabFileKey, userNameKey, hostname,
162          ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY,
163          JaasConfiguration.SERVER_KEYTAB_KERBEROS_CONFIG_NAME);
164  }
165
166  /**
167   * Log in the current zookeeper client using the given configuration
168   * keys for the credential file and login principal.
169   *
170   * <p><strong>This is only applicable when running on secure hbase</strong>
171   * On regular HBase (without security features), this will safely be ignored.
172   * </p>
173   *
174   * @param conf The configuration data to use
175   * @param keytabFileKey Property key used to configure the path to the credential file
176   * @param userNameKey Property key used to configure the login principal
177   * @param hostname Current hostname to use in any credentials
178   * @throws IOException underlying exception from SecurityUtil.login() call
179   */
180  public static void loginClient(Configuration conf, String keytabFileKey,
181      String userNameKey, String hostname) throws IOException {
182    login(conf, keytabFileKey, userNameKey, hostname,
183          ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY,
184          JaasConfiguration.CLIENT_KEYTAB_KERBEROS_CONFIG_NAME);
185  }
186
187  /**
188   * Log in the current process using the given configuration keys for the
189   * credential file and login principal.
190   *
191   * <p><strong>This is only applicable when running on secure hbase</strong>
192   * On regular HBase (without security features), this will safely be ignored.
193   * </p>
194   *
195   * @param conf The configuration data to use
196   * @param keytabFileKey Property key used to configure the path to the credential file
197   * @param userNameKey Property key used to configure the login principal
198   * @param hostname Current hostname to use in any credentials
199   * @param loginContextProperty property name to expose the entry name
200   * @param loginContextName jaas entry name
201   * @throws IOException underlying exception from SecurityUtil.login() call
202   */
203  private static void login(Configuration conf, String keytabFileKey,
204      String userNameKey, String hostname,
205      String loginContextProperty, String loginContextName)
206      throws IOException {
207    if (!isSecureZooKeeper(conf)) {
208      return;
209    }
210
211    // User has specified a jaas.conf, keep this one as the good one.
212    // HBASE_OPTS="-Djava.security.auth.login.config=jaas.conf"
213    if (System.getProperty("java.security.auth.login.config") != null) {
214      return;
215    }
216
217    // No keytab specified, no auth
218    String keytabFilename = conf.get(keytabFileKey);
219    if (keytabFilename == null) {
220      LOG.warn("no keytab specified for: {}", keytabFileKey);
221      return;
222    }
223
224    String principalConfig = conf.get(userNameKey, System.getProperty("user.name"));
225    String principalName = SecurityUtil.getServerPrincipal(principalConfig, hostname);
226
227    // Initialize the "jaas.conf" for keyTab/principal,
228    // If keyTab is not specified use the Ticket Cache.
229    // and set the zookeeper login context name.
230    JaasConfiguration jaasConf = new JaasConfiguration(loginContextName,
231        principalName, keytabFilename);
232    javax.security.auth.login.Configuration.setConfiguration(jaasConf);
233    System.setProperty(loginContextProperty, loginContextName);
234  }
235
236  /**
237   * A JAAS configuration that defines the login modules that we want to use for login.
238   */
239  private static class JaasConfiguration extends javax.security.auth.login.Configuration {
240    private static final String SERVER_KEYTAB_KERBEROS_CONFIG_NAME =
241      "zookeeper-server-keytab-kerberos";
242    private static final String CLIENT_KEYTAB_KERBEROS_CONFIG_NAME =
243      "zookeeper-client-keytab-kerberos";
244
245    private static final Map<String, String> BASIC_JAAS_OPTIONS = new HashMap<>();
246    static {
247      String jaasEnvVar = System.getenv("HBASE_JAAS_DEBUG");
248      if ("true".equalsIgnoreCase(jaasEnvVar)) {
249        BASIC_JAAS_OPTIONS.put("debug", "true");
250      }
251    }
252
253    private static final Map<String,String> KEYTAB_KERBEROS_OPTIONS = new HashMap<>();
254    static {
255      KEYTAB_KERBEROS_OPTIONS.put("doNotPrompt", "true");
256      KEYTAB_KERBEROS_OPTIONS.put("storeKey", "true");
257      KEYTAB_KERBEROS_OPTIONS.put("refreshKrb5Config", "true");
258      KEYTAB_KERBEROS_OPTIONS.putAll(BASIC_JAAS_OPTIONS);
259    }
260
261    private static final AppConfigurationEntry KEYTAB_KERBEROS_LOGIN =
262      new AppConfigurationEntry(KerberosUtil.getKrb5LoginModuleName(),
263                                LoginModuleControlFlag.REQUIRED,
264                                KEYTAB_KERBEROS_OPTIONS);
265
266    private static final AppConfigurationEntry[] KEYTAB_KERBEROS_CONF =
267      new AppConfigurationEntry[]{KEYTAB_KERBEROS_LOGIN};
268
269    private javax.security.auth.login.Configuration baseConfig;
270    private final String loginContextName;
271    private final boolean useTicketCache;
272    private final String keytabFile;
273    private final String principal;
274
275    public JaasConfiguration(String loginContextName, String principal, String keytabFile) {
276      this(loginContextName, principal, keytabFile, keytabFile == null || keytabFile.length() == 0);
277    }
278
279    private JaasConfiguration(String loginContextName, String principal,
280                             String keytabFile, boolean useTicketCache) {
281      try {
282        this.baseConfig = javax.security.auth.login.Configuration.getConfiguration();
283      } catch (SecurityException e) {
284        this.baseConfig = null;
285      }
286      this.loginContextName = loginContextName;
287      this.useTicketCache = useTicketCache;
288      this.keytabFile = keytabFile;
289      this.principal = principal;
290      LOG.info("JaasConfiguration loginContextName={} principal={} useTicketCache={} keytabFile={}",
291        loginContextName, principal, useTicketCache, keytabFile);
292    }
293
294    @Override
295    public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
296      if (loginContextName.equals(appName)) {
297        if (!useTicketCache) {
298          KEYTAB_KERBEROS_OPTIONS.put("keyTab", keytabFile);
299          KEYTAB_KERBEROS_OPTIONS.put("useKeyTab", "true");
300        }
301        KEYTAB_KERBEROS_OPTIONS.put("principal", principal);
302        KEYTAB_KERBEROS_OPTIONS.put("useTicketCache", useTicketCache ? "true" : "false");
303        return KEYTAB_KERBEROS_CONF;
304      }
305
306      if (baseConfig != null) {
307        return baseConfig.getAppConfigurationEntry(appName);
308      }
309
310      return(null);
311    }
312  }
313
314  //
315  // Helper methods
316  //
317  /**
318   * Returns the full path of the immediate parent of the specified node.
319   * @param node path to get parent of
320   * @return parent of path, null if passed the root node or an invalid node
321   */
322  public static String getParent(String node) {
323    int idx = node.lastIndexOf(ZNodePaths.ZNODE_PATH_SEPARATOR);
324    return idx <= 0 ? null : node.substring(0, idx);
325  }
326
327  /**
328   * Get the name of the current node from the specified fully-qualified path.
329   * @param path fully-qualified path
330   * @return name of the current node
331   */
332  public static String getNodeName(String path) {
333    return path.substring(path.lastIndexOf("/")+1);
334  }
335
336  //
337  // Existence checks and watches
338  //
339
340  /**
341   * Watch the specified znode for delete/create/change events.  The watcher is
342   * set whether or not the node exists.  If the node already exists, the method
343   * returns true.  If the node does not exist, the method returns false.
344   *
345   * @param zkw zk reference
346   * @param znode path of node to watch
347   * @return true if znode exists, false if does not exist or error
348   * @throws KeeperException if unexpected zookeeper exception
349   */
350  public static boolean watchAndCheckExists(ZKWatcher zkw, String znode)
351    throws KeeperException {
352    try {
353      Stat s = zkw.getRecoverableZooKeeper().exists(znode, zkw);
354      boolean exists = s != null;
355      if (exists) {
356        LOG.debug(zkw.prefix("Set watcher on existing znode=" + znode));
357      } else {
358        LOG.debug(zkw.prefix("Set watcher on znode that does not yet exist, " + znode));
359      }
360      return exists;
361    } catch (KeeperException e) {
362      LOG.warn(zkw.prefix("Unable to set watcher on znode " + znode), e);
363      zkw.keeperException(e);
364      return false;
365    } catch (InterruptedException e) {
366      LOG.warn(zkw.prefix("Unable to set watcher on znode " + znode), e);
367      zkw.interruptedException(e);
368      return false;
369    }
370  }
371
372  /**
373   * Watch the specified znode, but only if exists. Useful when watching
374   * for deletions. Uses .getData() (and handles NoNodeException) instead
375   * of .exists() to accomplish this, as .getData() will only set a watch if
376   * the znode exists.
377   * @param zkw zk reference
378   * @param znode path of node to watch
379   * @return true if the watch is set, false if node does not exists
380   * @throws KeeperException if unexpected zookeeper exception
381   */
382  public static boolean setWatchIfNodeExists(ZKWatcher zkw, String znode)
383      throws KeeperException {
384    try {
385      zkw.getRecoverableZooKeeper().getData(znode, true, null);
386      return true;
387    } catch (NoNodeException e) {
388      return false;
389    } catch (InterruptedException e) {
390      LOG.warn(zkw.prefix("Unable to set watcher on znode " + znode), e);
391      zkw.interruptedException(e);
392      return false;
393    }
394  }
395
396  /**
397   * Check if the specified node exists.  Sets no watches.
398   *
399   * @param zkw zk reference
400   * @param znode path of node to watch
401   * @return version of the node if it exists, -1 if does not exist
402   * @throws KeeperException if unexpected zookeeper exception
403   */
404  public static int checkExists(ZKWatcher zkw, String znode)
405    throws KeeperException {
406    try {
407      Stat s = zkw.getRecoverableZooKeeper().exists(znode, null);
408      return s != null ? s.getVersion() : -1;
409    } catch (KeeperException e) {
410      LOG.warn(zkw.prefix("Unable to set watcher on znode (" + znode + ")"), e);
411      zkw.keeperException(e);
412      return -1;
413    } catch (InterruptedException e) {
414      LOG.warn(zkw.prefix("Unable to set watcher on znode (" + znode + ")"), e);
415      zkw.interruptedException(e);
416      return -1;
417    }
418  }
419
420  //
421  // Znode listings
422  //
423
424  /**
425   * Lists the children znodes of the specified znode.  Also sets a watch on
426   * the specified znode which will capture a NodeDeleted event on the specified
427   * znode as well as NodeChildrenChanged if any children of the specified znode
428   * are created or deleted.
429   *
430   * Returns null if the specified node does not exist.  Otherwise returns a
431   * list of children of the specified node.  If the node exists but it has no
432   * children, an empty list will be returned.
433   *
434   * @param zkw zk reference
435   * @param znode path of node to list and watch children of
436   * @return list of children of the specified node, an empty list if the node
437   *          exists but has no children, and null if the node does not exist
438   * @throws KeeperException if unexpected zookeeper exception
439   */
440  public static List<String> listChildrenAndWatchForNewChildren(
441          ZKWatcher zkw, String znode)
442    throws KeeperException {
443    try {
444      return zkw.getRecoverableZooKeeper().getChildren(znode, zkw);
445    } catch(KeeperException.NoNodeException ke) {
446      LOG.debug(zkw.prefix("Unable to list children of znode " + znode + " " +
447          "because node does not exist (not an error)"));
448    } catch (KeeperException e) {
449      LOG.warn(zkw.prefix("Unable to list children of znode " + znode + " "), e);
450      zkw.keeperException(e);
451    } catch (InterruptedException e) {
452      LOG.warn(zkw.prefix("Unable to list children of znode " + znode + " "), e);
453      zkw.interruptedException(e);
454    }
455
456    return null;
457  }
458
459  /**
460   * List all the children of the specified znode, setting a watch for children
461   * changes and also setting a watch on every individual child in order to get
462   * the NodeCreated and NodeDeleted events.
463   * @param zkw zookeeper reference
464   * @param znode node to get children of and watch
465   * @return list of znode names, null if the node doesn't exist
466   * @throws KeeperException if a ZooKeeper operation fails
467   */
468  public static List<String> listChildrenAndWatchThem(ZKWatcher zkw,
469      String znode) throws KeeperException {
470    List<String> children = listChildrenAndWatchForNewChildren(zkw, znode);
471    if (children == null) {
472      return null;
473    }
474    for (String child : children) {
475      watchAndCheckExists(zkw, ZNodePaths.joinZNode(znode, child));
476    }
477    return children;
478  }
479
480  /**
481   * Lists the children of the specified znode without setting any watches.
482   *
483   * Sets no watches at all, this method is best effort.
484   *
485   * Returns an empty list if the node has no children.  Returns null if the
486   * parent node itself does not exist.
487   *
488   * @param zkw zookeeper reference
489   * @param znode node to get children
490   * @return list of data of children of specified znode, empty if no children,
491   *         null if parent does not exist
492   * @throws KeeperException if unexpected zookeeper exception
493   */
494  public static List<String> listChildrenNoWatch(ZKWatcher zkw, String znode)
495    throws KeeperException {
496    List<String> children = null;
497    try {
498      // List the children without watching
499      children = zkw.getRecoverableZooKeeper().getChildren(znode, null);
500    } catch(KeeperException.NoNodeException nne) {
501      return null;
502    } catch(InterruptedException ie) {
503      zkw.interruptedException(ie);
504    }
505    return children;
506  }
507
508  /**
509   * Simple class to hold a node path and node data.
510   * @deprecated Unused
511   */
512  @Deprecated
513  public static class NodeAndData {
514    private String node;
515    private byte [] data;
516    public NodeAndData(String node, byte [] data) {
517      this.node = node;
518      this.data = data;
519    }
520    public String getNode() {
521      return node;
522    }
523    public byte [] getData() {
524      return data;
525    }
526    @Override
527    public String toString() {
528      return node;
529    }
530    public boolean isEmpty() {
531      return (data == null || data.length == 0);
532    }
533  }
534
535  /**
536   * Checks if the specified znode has any children.  Sets no watches.
537   *
538   * Returns true if the node exists and has children.  Returns false if the
539   * node does not exist or if the node does not have any children.
540   *
541   * Used during master initialization to determine if the master is a
542   * failed-over-to master or the first master during initial cluster startup.
543   * If the directory for regionserver ephemeral nodes is empty then this is
544   * a cluster startup, if not then it is not cluster startup.
545   *
546   * @param zkw zk reference
547   * @param znode path of node to check for children of
548   * @return true if node has children, false if not or node does not exist
549   * @throws KeeperException if unexpected zookeeper exception
550   */
551  public static boolean nodeHasChildren(ZKWatcher zkw, String znode)
552    throws KeeperException {
553    try {
554      return !zkw.getRecoverableZooKeeper().getChildren(znode, null).isEmpty();
555    } catch(KeeperException.NoNodeException ke) {
556      LOG.debug(zkw.prefix("Unable to list children of znode " + znode +
557              " because node does not exist (not an error)"));
558      return false;
559    } catch (KeeperException e) {
560      LOG.warn(zkw.prefix("Unable to list children of znode " + znode), e);
561      zkw.keeperException(e);
562      return false;
563    } catch (InterruptedException e) {
564      LOG.warn(zkw.prefix("Unable to list children of znode " + znode), e);
565      zkw.interruptedException(e);
566      return false;
567    }
568  }
569
570  /**
571   * Get the number of children of the specified node.
572   *
573   * If the node does not exist or has no children, returns 0.
574   *
575   * Sets no watches at all.
576   *
577   * @param zkw zk reference
578   * @param znode path of node to count children of
579   * @return number of children of specified node, 0 if none or parent does not
580   *         exist
581   * @throws KeeperException if unexpected zookeeper exception
582   */
583  public static int getNumberOfChildren(ZKWatcher zkw, String znode)
584    throws KeeperException {
585    try {
586      Stat stat = zkw.getRecoverableZooKeeper().exists(znode, null);
587      return stat == null ? 0 : stat.getNumChildren();
588    } catch(KeeperException e) {
589      LOG.warn(zkw.prefix("Unable to get children of node " + znode));
590      zkw.keeperException(e);
591    } catch(InterruptedException e) {
592      zkw.interruptedException(e);
593    }
594    return 0;
595  }
596
597  //
598  // Data retrieval
599  //
600
601  /**
602   * Get znode data. Does not set a watcher.
603   *
604   * @return ZNode data, null if the node does not exist or if there is an error.
605   */
606  public static byte [] getData(ZKWatcher zkw, String znode)
607      throws KeeperException, InterruptedException {
608    try {
609      byte [] data = zkw.getRecoverableZooKeeper().getData(znode, null, null);
610      logRetrievedMsg(zkw, znode, data, false);
611      return data;
612    } catch (KeeperException.NoNodeException e) {
613      LOG.debug(zkw.prefix("Unable to get data of znode " + znode + " " +
614          "because node does not exist (not an error)"));
615      return null;
616    } catch (KeeperException e) {
617      LOG.warn(zkw.prefix("Unable to get data of znode " + znode), e);
618      zkw.keeperException(e);
619      return null;
620    }
621  }
622
623  /**
624   * Get the data at the specified znode and set a watch.
625   *
626   * Returns the data and sets a watch if the node exists.  Returns null and no
627   * watch is set if the node does not exist or there is an exception.
628   *
629   * @param zkw zk reference
630   * @param znode path of node
631   * @return data of the specified znode, or null
632   * @throws KeeperException if unexpected zookeeper exception
633   */
634  public static byte[] getDataAndWatch(ZKWatcher zkw, String znode) throws KeeperException {
635    return getDataInternal(zkw, znode, null, true, true);
636  }
637
638  /**
639   * Get the data at the specified znode and set a watch.
640   * Returns the data and sets a watch if the node exists.  Returns null and no
641   * watch is set if the node does not exist or there is an exception.
642   *
643   * @param zkw              zk reference
644   * @param znode            path of node
645   * @param throwOnInterrupt if false then just interrupt the thread, do not throw exception
646   * @return data of the specified znode, or null
647   * @throws KeeperException if unexpected zookeeper exception
648   */
649  public static byte[] getDataAndWatch(ZKWatcher zkw, String znode, boolean throwOnInterrupt)
650    throws KeeperException {
651    return getDataInternal(zkw, znode, null, true, throwOnInterrupt);
652  }
653
654  /**
655   * Get the data at the specified znode and set a watch.
656   *
657   * Returns the data and sets a watch if the node exists.  Returns null and no
658   * watch is set if the node does not exist or there is an exception.
659   *
660   * @param zkw zk reference
661   * @param znode path of node
662   * @param stat object to populate the version of the znode
663   * @return data of the specified znode, or null
664   * @throws KeeperException if unexpected zookeeper exception
665   */
666  public static byte[] getDataAndWatch(ZKWatcher zkw, String znode,
667                                       Stat stat) throws KeeperException {
668    return getDataInternal(zkw, znode, stat, true, true);
669  }
670
671  private static byte[] getDataInternal(ZKWatcher zkw, String znode, Stat stat, boolean watcherSet,
672    boolean throwOnInterrupt)
673      throws KeeperException {
674    try {
675      byte [] data = zkw.getRecoverableZooKeeper().getData(znode, zkw, stat);
676      logRetrievedMsg(zkw, znode, data, watcherSet);
677      return data;
678    } catch (KeeperException.NoNodeException e) {
679      // This log can get pretty annoying when we cycle on 100ms waits.
680      // Enable trace if you really want to see it.
681      LOG.trace(zkw.prefix("Unable to get data of znode " + znode + " " +
682        "because node does not exist (not an error)"));
683      return null;
684    } catch (KeeperException e) {
685      LOG.warn(zkw.prefix("Unable to get data of znode " + znode), e);
686      zkw.keeperException(e);
687      return null;
688    } catch (InterruptedException e) {
689      LOG.warn(zkw.prefix("Unable to get data of znode " + znode), e);
690      if (throwOnInterrupt) {
691        zkw.interruptedException(e);
692      } else {
693        zkw.interruptedExceptionNoThrow(e, true);
694      }
695      return null;
696    }
697  }
698
699  /**
700   * Get the data at the specified znode without setting a watch.
701   *
702   * Returns the data if the node exists.  Returns null if the node does not
703   * exist.
704   *
705   * Sets the stats of the node in the passed Stat object.  Pass a null stat if
706   * not interested.
707   *
708   * @param zkw zk reference
709   * @param znode path of node
710   * @param stat node status to get if node exists
711   * @return data of the specified znode, or null if node does not exist
712   * @throws KeeperException if unexpected zookeeper exception
713   */
714  public static byte [] getDataNoWatch(ZKWatcher zkw, String znode,
715                                       Stat stat)
716    throws KeeperException {
717    try {
718      byte [] data = zkw.getRecoverableZooKeeper().getData(znode, null, stat);
719      logRetrievedMsg(zkw, znode, data, false);
720      return data;
721    } catch (KeeperException.NoNodeException e) {
722      LOG.debug(zkw.prefix("Unable to get data of znode " + znode + " " +
723          "because node does not exist (not necessarily an error)"));
724      return null;
725    } catch (KeeperException e) {
726      LOG.warn(zkw.prefix("Unable to get data of znode " + znode), e);
727      zkw.keeperException(e);
728      return null;
729    } catch (InterruptedException e) {
730      LOG.warn(zkw.prefix("Unable to get data of znode " + znode), e);
731      zkw.interruptedException(e);
732      return null;
733    }
734  }
735
736  /**
737   * Returns the date of child znodes of the specified znode.  Also sets a watch on
738   * the specified znode which will capture a NodeDeleted event on the specified
739   * znode as well as NodeChildrenChanged if any children of the specified znode
740   * are created or deleted.
741   *
742   * Returns null if the specified node does not exist.  Otherwise returns a
743   * list of children of the specified node.  If the node exists but it has no
744   * children, an empty list will be returned.
745   *
746   * @param zkw zk reference
747   * @param baseNode path of node to list and watch children of
748   * @return list of data of children of the specified node, an empty list if the node
749   *          exists but has no children, and null if the node does not exist
750   * @throws KeeperException if unexpected zookeeper exception
751   * @deprecated Unused
752   */
753  @Deprecated
754  public static List<NodeAndData> getChildDataAndWatchForNewChildren(ZKWatcher zkw, String baseNode)
755    throws KeeperException {
756    return getChildDataAndWatchForNewChildren(zkw, baseNode, true);
757  }
758
759  /**
760   * Returns the date of child znodes of the specified znode.  Also sets a watch on
761   * the specified znode which will capture a NodeDeleted event on the specified
762   * znode as well as NodeChildrenChanged if any children of the specified znode
763   * are created or deleted.
764   *
765   * Returns null if the specified node does not exist.  Otherwise returns a
766   * list of children of the specified node.  If the node exists but it has no
767   * children, an empty list will be returned.
768   *
769   * @param zkw zk reference
770   * @param baseNode path of node to list and watch children of
771   * @param throwOnInterrupt if true then just interrupt the thread, do not throw exception
772   * @return list of data of children of the specified node, an empty list if the node
773   *          exists but has no children, and null if the node does not exist
774   * @throws KeeperException if unexpected zookeeper exception
775   * @deprecated Unused
776   */
777  @Deprecated
778  public static List<NodeAndData> getChildDataAndWatchForNewChildren(
779          ZKWatcher zkw, String baseNode, boolean throwOnInterrupt) throws KeeperException {
780    List<String> nodes =
781      ZKUtil.listChildrenAndWatchForNewChildren(zkw, baseNode);
782    if (nodes != null) {
783      List<NodeAndData> newNodes = new ArrayList<>();
784      for (String node : nodes) {
785        if (Thread.interrupted()) {
786          // Partial data should not be processed. Cancel processing by sending empty list.
787          return Collections.emptyList();
788        }
789        String nodePath = ZNodePaths.joinZNode(baseNode, node);
790        byte[] data = ZKUtil.getDataAndWatch(zkw, nodePath, throwOnInterrupt);
791        newNodes.add(new NodeAndData(nodePath, data));
792      }
793      return newNodes;
794    }
795    return null;
796  }
797
798  /**
799   * Update the data of an existing node with the expected version to have the
800   * specified data.
801   *
802   * Throws an exception if there is a version mismatch or some other problem.
803   *
804   * Sets no watches under any conditions.
805   *
806   * @param zkw zk reference
807   * @param znode the path to the ZNode
808   * @param data the data to store in ZooKeeper
809   * @param expectedVersion the expected version
810   * @throws KeeperException if unexpected zookeeper exception
811   * @throws KeeperException.BadVersionException if version mismatch
812   * @deprecated Unused
813   */
814  @Deprecated
815  public static void updateExistingNodeData(ZKWatcher zkw, String znode, byte[] data,
816      int expectedVersion) throws KeeperException {
817    try {
818      zkw.getRecoverableZooKeeper().setData(znode, data, expectedVersion);
819    } catch(InterruptedException ie) {
820      zkw.interruptedException(ie);
821    }
822  }
823
824  //
825  // Data setting
826  //
827
828  /**
829   * Sets the data of the existing znode to be the specified data.  Ensures that
830   * the current data has the specified expected version.
831   *
832   * <p>If the node does not exist, a {@link NoNodeException} will be thrown.
833   *
834   * <p>If their is a version mismatch, method returns null.
835   *
836   * <p>No watches are set but setting data will trigger other watchers of this
837   * node.
838   *
839   * <p>If there is another problem, a KeeperException will be thrown.
840   *
841   * @param zkw zk reference
842   * @param znode path of node
843   * @param data data to set for node
844   * @param expectedVersion version expected when setting data
845   * @return true if data set, false if version mismatch
846   * @throws KeeperException if unexpected zookeeper exception
847   */
848  public static boolean setData(ZKWatcher zkw, String znode,
849                                byte [] data, int expectedVersion)
850    throws KeeperException, KeeperException.NoNodeException {
851    try {
852      return zkw.getRecoverableZooKeeper().setData(znode, data, expectedVersion) != null;
853    } catch (InterruptedException e) {
854      zkw.interruptedException(e);
855      return false;
856    }
857  }
858
859  /**
860   * Set data into node creating node if it doesn't yet exist.
861   * Does not set watch.
862   *
863   * @param zkw zk reference
864   * @param znode path of node
865   * @param data data to set for node
866   * @throws KeeperException if a ZooKeeper operation fails
867   */
868  public static void createSetData(final ZKWatcher zkw, final String znode, final byte [] data)
869          throws KeeperException {
870    if (checkExists(zkw, znode) == -1) {
871      ZKUtil.createWithParents(zkw, znode, data);
872    } else {
873      ZKUtil.setData(zkw, znode, data);
874    }
875  }
876
877  /**
878   * Sets the data of the existing znode to be the specified data.  The node
879   * must exist but no checks are done on the existing data or version.
880   *
881   * <p>If the node does not exist, a {@link NoNodeException} will be thrown.
882   *
883   * <p>No watches are set but setting data will trigger other watchers of this
884   * node.
885   *
886   * <p>If there is another problem, a KeeperException will be thrown.
887   *
888   * @param zkw zk reference
889   * @param znode path of node
890   * @param data data to set for node
891   * @throws KeeperException if unexpected zookeeper exception
892   */
893  public static void setData(ZKWatcher zkw, String znode, byte [] data)
894    throws KeeperException, KeeperException.NoNodeException {
895    setData(zkw, (SetData)ZKUtilOp.setData(znode, data));
896  }
897
898  private static void setData(ZKWatcher zkw, SetData setData)
899    throws KeeperException, KeeperException.NoNodeException {
900    SetDataRequest sd = (SetDataRequest)toZooKeeperOp(zkw, setData).toRequestRecord();
901    setData(zkw, sd.getPath(), sd.getData(), sd.getVersion());
902  }
903
904  /**
905   * Returns whether or not secure authentication is enabled
906   * (whether <code>hbase.security.authentication</code> is set to
907   * <code>kerberos</code>.
908   */
909  public static boolean isSecureZooKeeper(Configuration conf) {
910    // Detection for embedded HBase client with jaas configuration
911    // defined for third party programs.
912    try {
913      javax.security.auth.login.Configuration testConfig =
914          javax.security.auth.login.Configuration.getConfiguration();
915      if (testConfig.getAppConfigurationEntry("Client") == null
916          && testConfig.getAppConfigurationEntry(
917            JaasConfiguration.CLIENT_KEYTAB_KERBEROS_CONFIG_NAME) == null
918          && testConfig.getAppConfigurationEntry(
919              JaasConfiguration.SERVER_KEYTAB_KERBEROS_CONFIG_NAME) == null
920          && conf.get(HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL) == null
921          && conf.get(HConstants.ZK_SERVER_KERBEROS_PRINCIPAL) == null) {
922
923        return false;
924      }
925    } catch(Exception e) {
926      // No Jaas configuration defined.
927      return false;
928    }
929
930    // Master & RSs uses hbase.zookeeper.client.*
931    return "kerberos".equalsIgnoreCase(conf.get("hbase.security.authentication"));
932  }
933
934  private static ArrayList<ACL> createACL(ZKWatcher zkw, String node) {
935    return createACL(zkw, node, isSecureZooKeeper(zkw.getConfiguration()));
936  }
937
938  public static ArrayList<ACL> createACL(ZKWatcher zkw, String node,
939                                         boolean isSecureZooKeeper) {
940    if (!node.startsWith(zkw.getZNodePaths().baseZNode)) {
941      return Ids.OPEN_ACL_UNSAFE;
942    }
943    if (isSecureZooKeeper) {
944      ArrayList<ACL> acls = new ArrayList<>();
945      // add permission to hbase supper user
946      String[] superUsers = zkw.getConfiguration().getStrings(Superusers.SUPERUSER_CONF_KEY);
947      String hbaseUser = null;
948      try {
949        hbaseUser = UserGroupInformation.getCurrentUser().getShortUserName();
950      } catch (IOException e) {
951        LOG.debug("Could not acquire current User.", e);
952      }
953      if (superUsers != null) {
954        List<String> groups = new ArrayList<>();
955        for (String user : superUsers) {
956          if (AuthUtil.isGroupPrincipal(user)) {
957            // TODO: Set node ACL for groups when ZK supports this feature
958            groups.add(user);
959          } else {
960            if(!user.equals(hbaseUser)) {
961              acls.add(new ACL(Perms.ALL, new Id("sasl", user)));
962            }
963          }
964        }
965        if (!groups.isEmpty()) {
966          LOG.warn("Znode ACL setting for group {} is skipped, ZooKeeper doesn't support this " +
967            "feature presently.", groups);
968        }
969      }
970      // Certain znodes are accessed directly by the client,
971      // so they must be readable by non-authenticated clients
972      if (zkw.getZNodePaths().isClientReadable(node)) {
973        acls.addAll(Ids.CREATOR_ALL_ACL);
974        acls.addAll(Ids.READ_ACL_UNSAFE);
975      } else {
976        acls.addAll(Ids.CREATOR_ALL_ACL);
977      }
978      return acls;
979    } else {
980      return Ids.OPEN_ACL_UNSAFE;
981    }
982  }
983
984  //
985  // Node creation
986  //
987
988  /**
989   *
990   * Set the specified znode to be an ephemeral node carrying the specified
991   * data.
992   *
993   * If the node is created successfully, a watcher is also set on the node.
994   *
995   * If the node is not created successfully because it already exists, this
996   * method will also set a watcher on the node.
997   *
998   * If there is another problem, a KeeperException will be thrown.
999   *
1000   * @param zkw zk reference
1001   * @param znode path of node
1002   * @param data data of node
1003   * @return true if node created, false if not, watch set in both cases
1004   * @throws KeeperException if unexpected zookeeper exception
1005   */
1006  public static boolean createEphemeralNodeAndWatch(ZKWatcher zkw,
1007      String znode, byte [] data)
1008    throws KeeperException {
1009    boolean ret = true;
1010    try {
1011      zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
1012          CreateMode.EPHEMERAL);
1013    } catch (KeeperException.NodeExistsException nee) {
1014      ret = false;
1015    } catch (InterruptedException e) {
1016      LOG.info("Interrupted", e);
1017      Thread.currentThread().interrupt();
1018    }
1019    if(!watchAndCheckExists(zkw, znode)) {
1020      // It did exist but now it doesn't, try again
1021      return createEphemeralNodeAndWatch(zkw, znode, data);
1022    }
1023    return ret;
1024  }
1025
1026  /**
1027   * Creates the specified znode to be a persistent node carrying the specified
1028   * data.
1029   *
1030   * Returns true if the node was successfully created, false if the node
1031   * already existed.
1032   *
1033   * If the node is created successfully, a watcher is also set on the node.
1034   *
1035   * If the node is not created successfully because it already exists, this
1036   * method will also set a watcher on the node but return false.
1037   *
1038   * If there is another problem, a KeeperException will be thrown.
1039   *
1040   * @param zkw zk reference
1041   * @param znode path of node
1042   * @param data data of node
1043   * @return true if node created, false if not, watch set in both cases
1044   * @throws KeeperException if unexpected zookeeper exception
1045   */
1046  public static boolean createNodeIfNotExistsAndWatch(
1047          ZKWatcher zkw, String znode, byte [] data)
1048    throws KeeperException {
1049    boolean ret = true;
1050    try {
1051      zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
1052          CreateMode.PERSISTENT);
1053    } catch (KeeperException.NodeExistsException nee) {
1054      ret = false;
1055    } catch (InterruptedException e) {
1056      zkw.interruptedException(e);
1057      return false;
1058    }
1059    try {
1060      zkw.getRecoverableZooKeeper().exists(znode, zkw);
1061    } catch (InterruptedException e) {
1062      zkw.interruptedException(e);
1063      return false;
1064    }
1065    return ret;
1066  }
1067
1068  /**
1069   * Creates the specified znode with the specified data but does not watch it.
1070   *
1071   * Returns the znode of the newly created node
1072   *
1073   * If there is another problem, a KeeperException will be thrown.
1074   *
1075   * @param zkw zk reference
1076   * @param znode path of node
1077   * @param data data of node
1078   * @param createMode specifying whether the node to be created is ephemeral and/or sequential
1079   * @return true name of the newly created znode or null
1080   * @throws KeeperException if unexpected zookeeper exception
1081   */
1082  public static String createNodeIfNotExistsNoWatch(ZKWatcher zkw, String znode, byte[] data,
1083      CreateMode createMode) throws KeeperException {
1084    String createdZNode = null;
1085    try {
1086      createdZNode = zkw.getRecoverableZooKeeper().create(znode, data,
1087          createACL(zkw, znode), createMode);
1088    } catch (KeeperException.NodeExistsException nee) {
1089      return znode;
1090    } catch (InterruptedException e) {
1091      zkw.interruptedException(e);
1092      return null;
1093    }
1094    return createdZNode;
1095  }
1096
1097  /**
1098   * Creates the specified node with the specified data and watches it.
1099   *
1100   * <p>Throws an exception if the node already exists.
1101   *
1102   * <p>The node created is persistent and open access.
1103   *
1104   * <p>Returns the version number of the created node if successful.
1105   *
1106   * @param zkw zk reference
1107   * @param znode path of node to create
1108   * @param data data of node to create
1109   * @return version of node created
1110   * @throws KeeperException if unexpected zookeeper exception
1111   * @throws KeeperException.NodeExistsException if node already exists
1112   */
1113  public static int createAndWatch(ZKWatcher zkw,
1114      String znode, byte [] data)
1115    throws KeeperException, KeeperException.NodeExistsException {
1116    try {
1117      zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
1118          CreateMode.PERSISTENT);
1119      Stat stat = zkw.getRecoverableZooKeeper().exists(znode, zkw);
1120      if (stat == null){
1121        // Likely a race condition. Someone deleted the znode.
1122        throw KeeperException.create(KeeperException.Code.SYSTEMERROR,
1123            "ZK.exists returned null (i.e.: znode does not exist) for znode=" + znode);
1124      }
1125
1126      return stat.getVersion();
1127    } catch (InterruptedException e) {
1128      zkw.interruptedException(e);
1129      return -1;
1130    }
1131  }
1132
1133  /**
1134   * Async creates the specified node with the specified data.
1135   *
1136   * <p>Throws an exception if the node already exists.
1137   *
1138   * <p>The node created is persistent and open access.
1139   *
1140   * @param zkw zk reference
1141   * @param znode path of node to create
1142   * @param data data of node to create
1143   * @param cb the callback to use for the creation
1144   * @param ctx the context to use for the creation
1145   */
1146  public static void asyncCreate(ZKWatcher zkw,
1147      String znode, byte [] data, final AsyncCallback.StringCallback cb,
1148      final Object ctx) {
1149    zkw.getRecoverableZooKeeper().getZooKeeper().create(znode, data,
1150        createACL(zkw, znode), CreateMode.PERSISTENT, cb, ctx);
1151  }
1152
1153  /**
1154   * Creates the specified node, iff the node does not exist.  Does not set a
1155   * watch and fails silently if the node already exists.
1156   *
1157   * The node created is persistent and open access.
1158   *
1159   * @param zkw zk reference
1160   * @param znode path of node
1161   * @throws KeeperException if unexpected zookeeper exception
1162   */
1163  public static void createAndFailSilent(ZKWatcher zkw,
1164      String znode) throws KeeperException {
1165    createAndFailSilent(zkw, znode, new byte[0]);
1166  }
1167
1168  /**
1169   * Creates the specified node containing specified data, iff the node does not exist.  Does
1170   * not set a watch and fails silently if the node already exists.
1171   *
1172   * The node created is persistent and open access.
1173   *
1174   * @param zkw zk reference
1175   * @param znode path of node
1176   * @param data a byte array data to store in the znode
1177   * @throws KeeperException if unexpected zookeeper exception
1178   */
1179  public static void createAndFailSilent(ZKWatcher zkw,
1180      String znode, byte[] data)
1181    throws KeeperException {
1182    createAndFailSilent(zkw,
1183        (CreateAndFailSilent)ZKUtilOp.createAndFailSilent(znode, data));
1184  }
1185
1186  private static void createAndFailSilent(ZKWatcher zkw, CreateAndFailSilent cafs)
1187    throws KeeperException {
1188    CreateRequest create = (CreateRequest)toZooKeeperOp(zkw, cafs).toRequestRecord();
1189    String znode = create.getPath();
1190    try {
1191      RecoverableZooKeeper zk = zkw.getRecoverableZooKeeper();
1192      if (zk.exists(znode, false) == null) {
1193        zk.create(znode, create.getData(), create.getAcl(), CreateMode.fromFlag(create.getFlags()));
1194      }
1195    } catch(KeeperException.NodeExistsException nee) {
1196    } catch(KeeperException.NoAuthException nee){
1197      try {
1198        if (null == zkw.getRecoverableZooKeeper().exists(znode, false)) {
1199          // If we failed to create the file and it does not already exist.
1200          throw(nee);
1201        }
1202      } catch (InterruptedException ie) {
1203        zkw.interruptedException(ie);
1204      }
1205    } catch(InterruptedException ie) {
1206      zkw.interruptedException(ie);
1207    }
1208  }
1209
1210  /**
1211   * Creates the specified node and all parent nodes required for it to exist.
1212   *
1213   * No watches are set and no errors are thrown if the node already exists.
1214   *
1215   * The nodes created are persistent and open access.
1216   *
1217   * @param zkw zk reference
1218   * @param znode path of node
1219   * @throws KeeperException if unexpected zookeeper exception
1220   */
1221  public static void createWithParents(ZKWatcher zkw, String znode)
1222    throws KeeperException {
1223    createWithParents(zkw, znode, new byte[0]);
1224  }
1225
1226  /**
1227   * Creates the specified node and all parent nodes required for it to exist.  The creation of
1228   * parent znodes is not atomic with the leafe znode creation but the data is written atomically
1229   * when the leaf node is created.
1230   *
1231   * No watches are set and no errors are thrown if the node already exists.
1232   *
1233   * The nodes created are persistent and open access.
1234   *
1235   * @param zkw zk reference
1236   * @param znode path of node
1237   * @throws KeeperException if unexpected zookeeper exception
1238   */
1239  public static void createWithParents(ZKWatcher zkw, String znode, byte[] data)
1240    throws KeeperException {
1241    try {
1242      if(znode == null) {
1243        return;
1244      }
1245      zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
1246          CreateMode.PERSISTENT);
1247    } catch(KeeperException.NodeExistsException nee) {
1248      return;
1249    } catch(KeeperException.NoNodeException nne) {
1250      createWithParents(zkw, getParent(znode));
1251      createWithParents(zkw, znode, data);
1252    } catch(InterruptedException ie) {
1253      zkw.interruptedException(ie);
1254    }
1255  }
1256
1257  //
1258  // Deletes
1259  //
1260
1261  /**
1262   * Delete the specified node.  Sets no watches.  Throws all exceptions.
1263   */
1264  public static void deleteNode(ZKWatcher zkw, String node)
1265    throws KeeperException {
1266    deleteNode(zkw, node, -1);
1267  }
1268
1269  /**
1270   * Delete the specified node with the specified version.  Sets no watches.
1271   * Throws all exceptions.
1272   */
1273  public static boolean deleteNode(ZKWatcher zkw, String node,
1274                                   int version)
1275    throws KeeperException {
1276    try {
1277      zkw.getRecoverableZooKeeper().delete(node, version);
1278      return true;
1279    } catch(KeeperException.BadVersionException bve) {
1280      return false;
1281    } catch(InterruptedException ie) {
1282      zkw.interruptedException(ie);
1283      return false;
1284    }
1285  }
1286
1287  /**
1288   * Deletes the specified node.  Fails silent if the node does not exist.
1289   *
1290   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
1291   * @param node the node to delete
1292   * @throws KeeperException if a ZooKeeper operation fails
1293   */
1294  public static void deleteNodeFailSilent(ZKWatcher zkw, String node)
1295    throws KeeperException {
1296    deleteNodeFailSilent(zkw,
1297      (DeleteNodeFailSilent)ZKUtilOp.deleteNodeFailSilent(node));
1298  }
1299
1300  private static void deleteNodeFailSilent(ZKWatcher zkw,
1301      DeleteNodeFailSilent dnfs) throws KeeperException {
1302    DeleteRequest delete = (DeleteRequest)toZooKeeperOp(zkw, dnfs).toRequestRecord();
1303    try {
1304      zkw.getRecoverableZooKeeper().delete(delete.getPath(), delete.getVersion());
1305    } catch(KeeperException.NoNodeException nne) {
1306    } catch(InterruptedException ie) {
1307      zkw.interruptedException(ie);
1308    }
1309  }
1310
1311
1312  /**
1313   * Delete the specified node and all of it's children.
1314   * <p>
1315   * If the node does not exist, just returns.
1316   * <p>
1317   * Sets no watches. Throws all exceptions besides dealing with deletion of
1318   * children.
1319   */
1320  public static void deleteNodeRecursively(ZKWatcher zkw, String node)
1321    throws KeeperException {
1322    deleteNodeRecursivelyMultiOrSequential(zkw, true, node);
1323  }
1324
1325  /**
1326   * Delete all the children of the specified node but not the node itself.
1327   *
1328   * Sets no watches.  Throws all exceptions besides dealing with deletion of
1329   * children.
1330   *
1331   * @throws KeeperException if a ZooKeeper operation fails
1332   */
1333  public static void deleteChildrenRecursively(ZKWatcher zkw, String node)
1334      throws KeeperException {
1335    deleteChildrenRecursivelyMultiOrSequential(zkw, true, node);
1336  }
1337
1338  /**
1339   * Delete all the children of the specified node but not the node itself. This
1340   * will first traverse the znode tree for listing the children and then delete
1341   * these znodes using multi-update api or sequential based on the specified
1342   * configurations.
1343   * <p>
1344   * Sets no watches. Throws all exceptions besides dealing with deletion of
1345   * children.
1346   * <p>
1347   * If the following is true:
1348   * <ul>
1349   * <li>runSequentialOnMultiFailure is true
1350   * </ul>
1351   * on calling multi, we get a ZooKeeper exception that can be handled by a
1352   * sequential call(*), we retry the operations one-by-one (sequentially).
1353   *
1354   * @param zkw
1355   *          - zk reference
1356   * @param runSequentialOnMultiFailure
1357   *          - if true when we get a ZooKeeper exception that could retry the
1358   *          operations one-by-one (sequentially)
1359   * @param pathRoots
1360   *          - path of the parent node(s)
1361   * @throws KeeperException.NotEmptyException
1362   *           if node has children while deleting
1363   * @throws KeeperException
1364   *           if unexpected ZooKeeper exception
1365   * @throws IllegalArgumentException
1366   *           if an invalid path is specified
1367   */
1368  public static void deleteChildrenRecursivelyMultiOrSequential(
1369          ZKWatcher zkw, boolean runSequentialOnMultiFailure,
1370          String... pathRoots) throws KeeperException {
1371    if (pathRoots == null || pathRoots.length <= 0) {
1372      LOG.warn("Given path is not valid!");
1373      return;
1374    }
1375    List<ZKUtilOp> ops = new ArrayList<>();
1376    for (String eachRoot : pathRoots) {
1377      List<String> children = listChildrenBFSNoWatch(zkw, eachRoot);
1378      // Delete the leaves first and eventually get rid of the root
1379      for (int i = children.size() - 1; i >= 0; --i) {
1380        ops.add(ZKUtilOp.deleteNodeFailSilent(children.get(i)));
1381      }
1382    }
1383    submitBatchedMultiOrSequential(zkw, runSequentialOnMultiFailure, ops);
1384  }
1385
1386  /**
1387   * Delete the specified node and its children. This traverse the
1388   * znode tree for listing the children and then delete
1389   * these znodes including the parent using multi-update api or
1390   * sequential based on the specified configurations.
1391   * <p>
1392   * Sets no watches. Throws all exceptions besides dealing with deletion of
1393   * children.
1394   * <p>
1395   * If the following is true:
1396   * <ul>
1397   * <li>runSequentialOnMultiFailure is true
1398   * </ul>
1399   * on calling multi, we get a ZooKeeper exception that can be handled by a
1400   * sequential call(*), we retry the operations one-by-one (sequentially).
1401   *
1402   * @param zkw
1403   *          - zk reference
1404   * @param runSequentialOnMultiFailure
1405   *          - if true when we get a ZooKeeper exception that could retry the
1406   *          operations one-by-one (sequentially)
1407   * @param pathRoots
1408   *          - path of the parent node(s)
1409   * @throws KeeperException.NotEmptyException
1410   *           if node has children while deleting
1411   * @throws KeeperException
1412   *           if unexpected ZooKeeper exception
1413   * @throws IllegalArgumentException
1414   *           if an invalid path is specified
1415   */
1416  public static void deleteNodeRecursivelyMultiOrSequential(ZKWatcher zkw,
1417      boolean runSequentialOnMultiFailure, String... pathRoots) throws KeeperException {
1418    if (pathRoots == null || pathRoots.length <= 0) {
1419      LOG.warn("Given path is not valid!");
1420      return;
1421    }
1422    List<ZKUtilOp> ops = new ArrayList<>();
1423    for (String eachRoot : pathRoots) {
1424      // ZooKeeper Watches are one time triggers; When children of parent nodes are deleted
1425      // recursively, must set another watch, get notified of delete node
1426      List<String> children = listChildrenBFSAndWatchThem(zkw, eachRoot);
1427      // Delete the leaves first and eventually get rid of the root
1428      for (int i = children.size() - 1; i >= 0; --i) {
1429        ops.add(ZKUtilOp.deleteNodeFailSilent(children.get(i)));
1430      }
1431      try {
1432        if (zkw.getRecoverableZooKeeper().exists(eachRoot, zkw) != null) {
1433          ops.add(ZKUtilOp.deleteNodeFailSilent(eachRoot));
1434        }
1435      } catch (InterruptedException e) {
1436        zkw.interruptedException(e);
1437      }
1438    }
1439    submitBatchedMultiOrSequential(zkw, runSequentialOnMultiFailure, ops);
1440  }
1441
1442  /**
1443   * Chunks the provided {@code ops} when their approximate size exceeds the the configured limit.
1444   * Take caution that this can ONLY be used for operations where atomicity is not important,
1445   * e.g. deletions. It must not be used when atomicity of the operations is critical.
1446   *
1447   * @param zkw reference to the {@link ZKWatcher} which contains configuration and constants
1448   * @param runSequentialOnMultiFailure if true when we get a ZooKeeper exception that could
1449   *        retry the operations one-by-one (sequentially)
1450   * @param ops list of ZKUtilOp {@link ZKUtilOp} to partition while submitting batched multi
1451   *        or sequential
1452   * @throws KeeperException unexpected ZooKeeper Exception / Zookeeper unreachable
1453   */
1454  private static void submitBatchedMultiOrSequential(ZKWatcher zkw,
1455      boolean runSequentialOnMultiFailure, List<ZKUtilOp> ops) throws KeeperException {
1456    // at least one element should exist
1457    if (ops.isEmpty()) {
1458      return;
1459    }
1460    final int maxMultiSize = zkw.getRecoverableZooKeeper().getMaxMultiSizeLimit();
1461    // Batch up the items to over smashing through jute.maxbuffer with too many Ops.
1462    final List<List<ZKUtilOp>> batchedOps = partitionOps(ops, maxMultiSize);
1463    // Would use forEach() but have to handle KeeperException
1464    for (List<ZKUtilOp> batch : batchedOps) {
1465      multiOrSequential(zkw, batch, runSequentialOnMultiFailure);
1466    }
1467  }
1468
1469  /**
1470   * Partition the list of {@code ops} by size (using {@link #estimateSize(ZKUtilOp)}).
1471   */
1472  static List<List<ZKUtilOp>> partitionOps(List<ZKUtilOp> ops, int maxPartitionSize) {
1473    List<List<ZKUtilOp>> partitionedOps = new ArrayList<>();
1474    List<ZKUtilOp> currentPartition = new ArrayList<>();
1475    int currentPartitionSize = 0;
1476    partitionedOps.add(currentPartition);
1477    Iterator<ZKUtilOp> iter = ops.iterator();
1478    while (iter.hasNext()) {
1479      ZKUtilOp currentOp = iter.next();
1480      int currentOpSize = estimateSize(currentOp);
1481
1482      // Roll a new partition if necessary
1483      // If the current partition is empty, put the element in there anyways.
1484      // We can roll a new partition if we get another element
1485      if (!currentPartition.isEmpty() && currentOpSize + currentPartitionSize > maxPartitionSize) {
1486        currentPartition = new ArrayList<>();
1487        partitionedOps.add(currentPartition);
1488        currentPartitionSize = 0;
1489      }
1490
1491      // Add the current op to the partition
1492      currentPartition.add(currentOp);
1493      // And record its size
1494      currentPartitionSize += currentOpSize;
1495    }
1496    return partitionedOps;
1497  }
1498
1499  static int estimateSize(ZKUtilOp op) {
1500    return Bytes.toBytes(op.getPath()).length;
1501  }
1502
1503  /**
1504   * BFS Traversal of all the children under path, with the entries in the list,
1505   * in the same order as that of the traversal. Lists all the children without
1506   * setting any watches.
1507   *
1508   * @param zkw
1509   *          - zk reference
1510   * @param znode
1511   *          - path of node
1512   * @return list of children znodes under the path
1513   * @throws KeeperException
1514   *           if unexpected ZooKeeper exception
1515   */
1516  private static List<String> listChildrenBFSNoWatch(ZKWatcher zkw,
1517      final String znode) throws KeeperException {
1518    Deque<String> queue = new LinkedList<>();
1519    List<String> tree = new ArrayList<>();
1520    queue.add(znode);
1521    while (true) {
1522      String node = queue.pollFirst();
1523      if (node == null) {
1524        break;
1525      }
1526      List<String> children = listChildrenNoWatch(zkw, node);
1527      if (children == null) {
1528        continue;
1529      }
1530      for (final String child : children) {
1531        final String childPath = node + "/" + child;
1532        queue.add(childPath);
1533        tree.add(childPath);
1534      }
1535    }
1536    return tree;
1537  }
1538
1539  /**
1540   * BFS Traversal of all the children under path, with the entries in the list,
1541   * in the same order as that of the traversal.
1542   * Lists all the children and set watches on to them.
1543   *
1544   * @param zkw
1545   *          - zk reference
1546   * @param znode
1547   *          - path of node
1548   * @return list of children znodes under the path
1549   * @throws KeeperException
1550   *           if unexpected ZooKeeper exception
1551   */
1552  private static List<String> listChildrenBFSAndWatchThem(ZKWatcher zkw, final String znode)
1553      throws KeeperException {
1554    Deque<String> queue = new LinkedList<>();
1555    List<String> tree = new ArrayList<>();
1556    queue.add(znode);
1557    while (true) {
1558      String node = queue.pollFirst();
1559      if (node == null) {
1560        break;
1561      }
1562      List<String> children = listChildrenAndWatchThem(zkw, node);
1563      if (children == null) {
1564        continue;
1565      }
1566      for (final String child : children) {
1567        final String childPath = node + "/" + child;
1568        queue.add(childPath);
1569        tree.add(childPath);
1570      }
1571    }
1572    return tree;
1573  }
1574
1575  /**
1576   * Represents an action taken by ZKUtil, e.g. createAndFailSilent.
1577   * These actions are higher-level than ZKOp actions, which represent
1578   * individual actions in the ZooKeeper API, like create.
1579   */
1580  public abstract static class ZKUtilOp {
1581    private String path;
1582
1583    @Override public String toString() {
1584      return this.getClass().getSimpleName() + ", path=" + this.path;
1585    }
1586
1587    private ZKUtilOp(String path) {
1588      this.path = path;
1589    }
1590
1591    /**
1592     * @return a createAndFailSilent ZKUtilOp
1593     */
1594    public static ZKUtilOp createAndFailSilent(String path, byte[] data) {
1595      return new CreateAndFailSilent(path, data);
1596    }
1597
1598    /**
1599     * @return a deleteNodeFailSilent ZKUtilOP
1600     */
1601    public static ZKUtilOp deleteNodeFailSilent(String path) {
1602      return new DeleteNodeFailSilent(path);
1603    }
1604
1605    /**
1606     * @return a setData ZKUtilOp
1607     */
1608    public static ZKUtilOp setData(String path, byte[] data) {
1609      return new SetData(path, data);
1610    }
1611
1612    /**
1613     * @return a setData ZKUtilOp
1614     */
1615    public static ZKUtilOp setData(String path, byte[] data, int version) {
1616      return new SetData(path, data, version);
1617    }
1618
1619    /**
1620     * @return path to znode where the ZKOp will occur
1621     */
1622    public String getPath() {
1623      return path;
1624    }
1625
1626    /**
1627     * ZKUtilOp representing createAndFailSilent in ZooKeeper
1628     * (attempt to create node, ignore error if already exists)
1629     */
1630    public static final class CreateAndFailSilent extends ZKUtilOp {
1631      private byte [] data;
1632
1633      private CreateAndFailSilent(String path, byte [] data) {
1634        super(path);
1635        this.data = data;
1636      }
1637
1638      public byte[] getData() {
1639        return data;
1640      }
1641
1642      @Override
1643      public boolean equals(Object o) {
1644        if (this == o) {
1645          return true;
1646        }
1647        if (!(o instanceof CreateAndFailSilent)) {
1648          return false;
1649        }
1650
1651        CreateAndFailSilent op = (CreateAndFailSilent) o;
1652        return getPath().equals(op.getPath()) && Arrays.equals(data, op.data);
1653      }
1654
1655      @Override
1656      public int hashCode() {
1657        int ret = 17 + getPath().hashCode() * 31;
1658        return ret * 31 + Bytes.hashCode(data);
1659      }
1660    }
1661
1662    /**
1663     * ZKUtilOp representing deleteNodeFailSilent in ZooKeeper
1664     * (attempt to delete node, ignore error if node doesn't exist)
1665     */
1666    public static final class DeleteNodeFailSilent extends ZKUtilOp {
1667      private DeleteNodeFailSilent(String path) {
1668        super(path);
1669      }
1670
1671      @Override
1672      public boolean equals(Object o) {
1673        if (this == o) {
1674          return true;
1675        }
1676        if (!(o instanceof DeleteNodeFailSilent)) {
1677          return false;
1678        }
1679
1680        return super.equals(o);
1681      }
1682
1683      @Override
1684      public int hashCode() {
1685        return getPath().hashCode();
1686      }
1687    }
1688
1689    /**
1690     * ZKUtilOp representing setData in ZooKeeper
1691     */
1692    public static final class SetData extends ZKUtilOp {
1693      private byte[] data;
1694      private int version = -1;
1695
1696      private SetData(String path, byte[] data) {
1697        super(path);
1698        this.data = data;
1699      }
1700
1701      private SetData(String path, byte[] data, int version) {
1702        super(path);
1703        this.data = data;
1704        this.version = version;
1705      }
1706
1707      public byte[] getData() {
1708        return data;
1709      }
1710
1711      public int getVersion() {
1712        return version;
1713      }
1714
1715      @Override
1716      public boolean equals(Object o) {
1717        if (this == o) {
1718          return true;
1719        }
1720        if (!(o instanceof SetData)) {
1721          return false;
1722        }
1723
1724        SetData op = (SetData) o;
1725        return getPath().equals(op.getPath()) && Arrays.equals(data, op.data)
1726            && getVersion() == op.getVersion();
1727      }
1728
1729      @Override
1730      public int hashCode() {
1731        int ret = getPath().hashCode();
1732        ret = ret * 31 + Bytes.hashCode(data);
1733        return ret * 31 + Integer.hashCode(version);
1734      }
1735    }
1736  }
1737
1738  /**
1739   * Convert from ZKUtilOp to ZKOp
1740   */
1741  private static Op toZooKeeperOp(ZKWatcher zkw, ZKUtilOp op) throws UnsupportedOperationException {
1742    if(op == null) {
1743      return null;
1744    }
1745
1746    if (op instanceof CreateAndFailSilent) {
1747      CreateAndFailSilent cafs = (CreateAndFailSilent)op;
1748      return Op.create(cafs.getPath(), cafs.getData(), createACL(zkw, cafs.getPath()),
1749        CreateMode.PERSISTENT);
1750    } else if (op instanceof DeleteNodeFailSilent) {
1751      DeleteNodeFailSilent dnfs = (DeleteNodeFailSilent)op;
1752      return Op.delete(dnfs.getPath(), -1);
1753    } else if (op instanceof SetData) {
1754      SetData sd = (SetData) op;
1755      return Op.setData(sd.getPath(), sd.getData(), sd.getVersion());
1756    } else {
1757      throw new UnsupportedOperationException("Unexpected ZKUtilOp type: "
1758        + op.getClass().getName());
1759    }
1760  }
1761
1762  /**
1763   * Use ZooKeeper's multi-update functionality.
1764   *
1765   * If all of the following are true:
1766   * - runSequentialOnMultiFailure is true
1767   * - on calling multi, we get a ZooKeeper exception that can be handled by a sequential call(*)
1768   * Then:
1769   * - we retry the operations one-by-one (sequentially)
1770   *
1771   * Note *: an example is receiving a NodeExistsException from a "create" call.  Without multi,
1772   * a user could call "createAndFailSilent" to ensure that a node exists if they don't care who
1773   * actually created the node (i.e. the NodeExistsException from ZooKeeper is caught).
1774   * This will cause all operations in the multi to fail, however, because
1775   * the NodeExistsException that zk.create throws will fail the multi transaction.
1776   * In this case, if the previous conditions hold, the commands are run sequentially, which should
1777   * result in the correct final state, but means that the operations will not run atomically.
1778   *
1779   * @throws KeeperException if a ZooKeeper operation fails
1780   */
1781  public static void multiOrSequential(ZKWatcher zkw, List<ZKUtilOp> ops,
1782                                       boolean runSequentialOnMultiFailure) throws KeeperException {
1783    if (zkw.getConfiguration().get("hbase.zookeeper.useMulti") != null) {
1784      LOG.warn("hbase.zookeeper.useMulti is deprecated. Default to true always.");
1785    }
1786    if (ops == null) {
1787      return;
1788    }
1789
1790    List<Op> zkOps = new LinkedList<>();
1791    for (ZKUtilOp op : ops) {
1792      zkOps.add(toZooKeeperOp(zkw, op));
1793    }
1794    try {
1795      zkw.getRecoverableZooKeeper().multi(zkOps);
1796    } catch (KeeperException ke) {
1797      switch (ke.code()) {
1798        case NODEEXISTS:
1799        case NONODE:
1800        case BADVERSION:
1801        case NOAUTH:
1802        case NOTEMPTY:
1803          // if we get an exception that could be solved by running sequentially
1804          // (and the client asked us to), then break out and run sequentially
1805          if (runSequentialOnMultiFailure) {
1806            LOG.info("multi exception: {}; running operations sequentially " +
1807              "(runSequentialOnMultiFailure=true); {}", ke.toString(),
1808              ops.stream().map(o -> o.toString()).collect(Collectors.joining(",")));
1809            processSequentially(zkw, ops);
1810            break;
1811          }
1812        default:
1813          throw ke;
1814      }
1815    } catch (InterruptedException ie) {
1816      zkw.interruptedException(ie);
1817    }
1818  }
1819
1820  private static void processSequentially(ZKWatcher zkw, List<ZKUtilOp> ops)
1821      throws KeeperException, NoNodeException {
1822    for (ZKUtilOp op : ops) {
1823      if (op instanceof CreateAndFailSilent) {
1824        createAndFailSilent(zkw, (CreateAndFailSilent) op);
1825      } else if (op instanceof DeleteNodeFailSilent) {
1826        deleteNodeFailSilent(zkw, (DeleteNodeFailSilent) op);
1827      } else if (op instanceof SetData) {
1828        setData(zkw, (SetData) op);
1829      } else {
1830        throw new UnsupportedOperationException("Unexpected ZKUtilOp type: "
1831            + op.getClass().getName());
1832      }
1833    }
1834  }
1835
1836  //
1837  // ZooKeeper cluster information
1838  //
1839
1840  /** @return String dump of everything in ZooKeeper. */
1841  public static String dump(ZKWatcher zkw) {
1842    StringBuilder sb = new StringBuilder();
1843    try {
1844      sb.append("HBase is rooted at ").append(zkw.getZNodePaths().baseZNode);
1845      sb.append("\nActive master address: ");
1846      try {
1847        sb.append(MasterAddressTracker.getMasterAddress(zkw));
1848      } catch (IOException e) {
1849        sb.append("<<FAILED LOOKUP: " + e.getMessage() + ">>");
1850      }
1851      sb.append("\nBackup master addresses:");
1852      final List<String> backupMasterChildrenNoWatchList = listChildrenNoWatch(zkw,
1853              zkw.getZNodePaths().backupMasterAddressesZNode);
1854      if (backupMasterChildrenNoWatchList != null) {
1855        for (String child : backupMasterChildrenNoWatchList) {
1856          sb.append("\n ").append(child);
1857        }
1858      }
1859      sb.append("\nRegion server holding hbase:meta: "
1860        + MetaTableLocator.getMetaRegionLocation(zkw));
1861      int numMetaReplicas = zkw.getMetaReplicaNodes().size();
1862      for (int i = 1; i < numMetaReplicas; i++) {
1863        sb.append("\nRegion server holding hbase:meta, replicaId " + i + " "
1864                    + MetaTableLocator.getMetaRegionLocation(zkw, i));
1865      }
1866      sb.append("\nRegion servers:");
1867      final List<String> rsChildrenNoWatchList =
1868              listChildrenNoWatch(zkw, zkw.getZNodePaths().rsZNode);
1869      if (rsChildrenNoWatchList != null) {
1870        for (String child : rsChildrenNoWatchList) {
1871          sb.append("\n ").append(child);
1872        }
1873      }
1874      try {
1875        getReplicationZnodesDump(zkw, sb);
1876      } catch (KeeperException ke) {
1877        LOG.warn("Couldn't get the replication znode dump", ke);
1878      }
1879      sb.append("\nQuorum Server Statistics:");
1880      String[] servers = zkw.getQuorum().split(",");
1881      for (String server : servers) {
1882        sb.append("\n ").append(server);
1883        try {
1884          String[] stat = getServerStats(server, ZKUtil.zkDumpConnectionTimeOut);
1885
1886          if (stat == null) {
1887            sb.append("[Error] invalid quorum server: " + server);
1888            break;
1889          }
1890
1891          for (String s : stat) {
1892            sb.append("\n  ").append(s);
1893          }
1894        } catch (Exception e) {
1895          sb.append("\n  ERROR: ").append(e.getMessage());
1896        }
1897      }
1898    } catch (KeeperException ke) {
1899      sb.append("\nFATAL ZooKeeper Exception!\n");
1900      sb.append("\n" + ke.getMessage());
1901    }
1902    return sb.toString();
1903  }
1904
1905  /**
1906   * Appends replication znodes to the passed StringBuilder.
1907   *
1908   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
1909   * @param sb the {@link StringBuilder} to append to
1910   * @throws KeeperException if a ZooKeeper operation fails
1911   */
1912  private static void getReplicationZnodesDump(ZKWatcher zkw, StringBuilder sb)
1913      throws KeeperException {
1914    String replicationZnode = zkw.getZNodePaths().replicationZNode;
1915
1916    if (ZKUtil.checkExists(zkw, replicationZnode) == -1) {
1917      return;
1918    }
1919
1920    // do a ls -r on this znode
1921    sb.append("\n").append(replicationZnode).append(": ");
1922    List<String> children = ZKUtil.listChildrenNoWatch(zkw, replicationZnode);
1923    if (children != null) {
1924      Collections.sort(children);
1925      for (String child : children) {
1926        String zNode = ZNodePaths.joinZNode(replicationZnode, child);
1927        if (zNode.equals(zkw.getZNodePaths().peersZNode)) {
1928          appendPeersZnodes(zkw, zNode, sb);
1929        } else if (zNode.equals(zkw.getZNodePaths().queuesZNode)) {
1930          appendRSZnodes(zkw, zNode, sb);
1931        } else if (zNode.equals(zkw.getZNodePaths().hfileRefsZNode)) {
1932          appendHFileRefsZNodes(zkw, zNode, sb);
1933        }
1934      }
1935    }
1936  }
1937
1938  private static void appendHFileRefsZNodes(ZKWatcher zkw, String hFileRefsZNode,
1939                                            StringBuilder sb) throws KeeperException {
1940    sb.append("\n").append(hFileRefsZNode).append(": ");
1941    final List<String> hFileRefChildrenNoWatchList =
1942            ZKUtil.listChildrenNoWatch(zkw, hFileRefsZNode);
1943    if (hFileRefChildrenNoWatchList != null) {
1944      for (String peerIdZNode : hFileRefChildrenNoWatchList) {
1945        String zNodeToProcess = ZNodePaths.joinZNode(hFileRefsZNode, peerIdZNode);
1946        sb.append("\n").append(zNodeToProcess).append(": ");
1947        List<String> peerHFileRefsZNodes = ZKUtil.listChildrenNoWatch(zkw, zNodeToProcess);
1948        if (peerHFileRefsZNodes != null) {
1949          sb.append(String.join(", ", peerHFileRefsZNodes));
1950        }
1951      }
1952    }
1953  }
1954
1955  /**
1956   * Returns a string with replication znodes and position of the replication log
1957   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
1958   * @return aq string of replication znodes and log positions
1959   */
1960  public static String getReplicationZnodesDump(ZKWatcher zkw) throws KeeperException {
1961    StringBuilder sb = new StringBuilder();
1962    getReplicationZnodesDump(zkw, sb);
1963    return sb.toString();
1964  }
1965
1966  private static void appendRSZnodes(ZKWatcher zkw, String znode, StringBuilder sb)
1967      throws KeeperException {
1968    List<String> stack = new LinkedList<>();
1969    stack.add(znode);
1970    do {
1971      String znodeToProcess = stack.remove(stack.size() - 1);
1972      sb.append("\n").append(znodeToProcess).append(": ");
1973      byte[] data;
1974      try {
1975        data = ZKUtil.getData(zkw, znodeToProcess);
1976      } catch (InterruptedException e) {
1977        zkw.interruptedException(e);
1978        return;
1979      }
1980      if (data != null && data.length > 0) { // log position
1981        long position = 0;
1982        try {
1983          position = ZKUtil.parseWALPositionFrom(ZKUtil.getData(zkw, znodeToProcess));
1984          sb.append(position);
1985        } catch (DeserializationException ignored) {
1986        } catch (InterruptedException e) {
1987          zkw.interruptedException(e);
1988          return;
1989        }
1990      }
1991      for (String zNodeChild : ZKUtil.listChildrenNoWatch(zkw, znodeToProcess)) {
1992        stack.add(ZNodePaths.joinZNode(znodeToProcess, zNodeChild));
1993      }
1994    } while (stack.size() > 0);
1995  }
1996
1997  private static void appendPeersZnodes(ZKWatcher zkw, String peersZnode,
1998                                        StringBuilder sb) throws KeeperException {
1999    int pblen = ProtobufUtil.lengthOfPBMagic();
2000    sb.append("\n").append(peersZnode).append(": ");
2001    for (String peerIdZnode : ZKUtil.listChildrenNoWatch(zkw, peersZnode)) {
2002      String znodeToProcess = ZNodePaths.joinZNode(peersZnode, peerIdZnode);
2003      byte[] data;
2004      try {
2005        data = ZKUtil.getData(zkw, znodeToProcess);
2006      } catch (InterruptedException e) {
2007        zkw.interruptedException(e);
2008        return;
2009      }
2010      // parse the data of the above peer znode.
2011      try {
2012        ReplicationProtos.ReplicationPeer.Builder builder =
2013          ReplicationProtos.ReplicationPeer.newBuilder();
2014        ProtobufUtil.mergeFrom(builder, data, pblen, data.length - pblen);
2015        String clusterKey = builder.getClusterkey();
2016        sb.append("\n").append(znodeToProcess).append(": ").append(clusterKey);
2017        // add the peer-state.
2018        appendPeerState(zkw, znodeToProcess, sb);
2019      } catch (IOException ipbe) {
2020        LOG.warn("Got Exception while parsing peer: " + znodeToProcess, ipbe);
2021      }
2022    }
2023  }
2024
2025  private static void appendPeerState(ZKWatcher zkw, String znodeToProcess, StringBuilder sb)
2026          throws KeeperException, InvalidProtocolBufferException {
2027    String peerState = zkw.getConfiguration().get("zookeeper.znode.replication.peers.state",
2028      "peer-state");
2029    int pblen = ProtobufUtil.lengthOfPBMagic();
2030    for (String child : ZKUtil.listChildrenNoWatch(zkw, znodeToProcess)) {
2031      if (!child.equals(peerState)) {
2032        continue;
2033      }
2034
2035      String peerStateZnode = ZNodePaths.joinZNode(znodeToProcess, child);
2036      sb.append("\n").append(peerStateZnode).append(": ");
2037      byte[] peerStateData;
2038      try {
2039        peerStateData = ZKUtil.getData(zkw, peerStateZnode);
2040        ReplicationProtos.ReplicationState.Builder builder =
2041            ReplicationProtos.ReplicationState.newBuilder();
2042        ProtobufUtil.mergeFrom(builder, peerStateData, pblen, peerStateData.length - pblen);
2043        sb.append(builder.getState().name());
2044      } catch (IOException ipbe) {
2045        LOG.warn("Got Exception while parsing peer: " + znodeToProcess, ipbe);
2046      } catch (InterruptedException e) {
2047        zkw.interruptedException(e);
2048        return;
2049      }
2050    }
2051  }
2052
2053  /**
2054   * Gets the statistics from the given server.
2055   *
2056   * @param server  The server to get the statistics from.
2057   * @param timeout  The socket timeout to use.
2058   * @return The array of response strings.
2059   * @throws IOException When the socket communication fails.
2060   */
2061  private static String[] getServerStats(String server, int timeout)
2062    throws IOException {
2063    String[] sp = server.split(":");
2064    if (sp.length == 0) {
2065      return null;
2066    }
2067
2068    String host = sp[0];
2069    int port = sp.length > 1 ? Integer.parseInt(sp[1])
2070        : HConstants.DEFAULT_ZOOKEEPER_CLIENT_PORT;
2071
2072    InetSocketAddress sockAddr = new InetSocketAddress(host, port);
2073    try (Socket socket = new Socket()) {
2074      socket.connect(sockAddr, timeout);
2075
2076      socket.setSoTimeout(timeout);
2077      try (PrintWriter out = new PrintWriter(new BufferedWriter(
2078          new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)), true);
2079          BufferedReader in = new BufferedReader(
2080              new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
2081        out.println("stat");
2082        out.flush();
2083        ArrayList<String> res = new ArrayList<>();
2084        while (true) {
2085          String line = in.readLine();
2086          if (line != null) {
2087            res.add(line);
2088          } else {
2089            break;
2090          }
2091        }
2092        return res.toArray(new String[res.size()]);
2093      }
2094    }
2095  }
2096
2097  private static void logRetrievedMsg(final ZKWatcher zkw,
2098      final String znode, final byte [] data, final boolean watcherSet) {
2099    if (!LOG.isTraceEnabled()) {
2100      return;
2101    }
2102
2103    LOG.trace(zkw.prefix("Retrieved " + ((data == null)? 0: data.length) +
2104      " byte(s) of data from znode " + znode +
2105      (watcherSet? " and set watcher; ": "; data=") +
2106      (data == null? "null": data.length == 0? "empty": (
2107          zkw.getZNodePaths().isMetaZNodePath(znode)?
2108            getServerNameOrEmptyString(data):
2109          znode.startsWith(zkw.getZNodePaths().backupMasterAddressesZNode)?
2110            getServerNameOrEmptyString(data):
2111          StringUtils.abbreviate(Bytes.toStringBinary(data), 32)))));
2112  }
2113
2114  private static String getServerNameOrEmptyString(final byte [] data) {
2115    try {
2116      return ProtobufUtil.parseServerNameFrom(data).toString();
2117    } catch (DeserializationException e) {
2118      return "";
2119    }
2120  }
2121
2122  /**
2123   * Waits for HBase installation's base (parent) znode to become available.
2124   * @throws IOException on ZK errors
2125   */
2126  public static void waitForBaseZNode(Configuration conf) throws IOException {
2127    LOG.info("Waiting until the base znode is available");
2128    String parentZNode = conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT,
2129        HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT);
2130    ZooKeeper zk = new ZooKeeper(ZKConfig.getZKQuorumServersString(conf),
2131        conf.getInt(HConstants.ZK_SESSION_TIMEOUT,
2132        HConstants.DEFAULT_ZK_SESSION_TIMEOUT), EmptyWatcher.instance);
2133
2134    final int maxTimeMs = 10000;
2135    final int maxNumAttempts = maxTimeMs / HConstants.SOCKET_RETRY_WAIT_MS;
2136
2137    KeeperException keeperEx = null;
2138    try {
2139      try {
2140        for (int attempt = 0; attempt < maxNumAttempts; ++attempt) {
2141          try {
2142            if (zk.exists(parentZNode, false) != null) {
2143              LOG.info("Parent znode exists: {}", parentZNode);
2144              keeperEx = null;
2145              break;
2146            }
2147          } catch (KeeperException e) {
2148            keeperEx = e;
2149          }
2150          Threads.sleepWithoutInterrupt(HConstants.SOCKET_RETRY_WAIT_MS);
2151        }
2152      } finally {
2153        zk.close();
2154      }
2155    } catch (InterruptedException ex) {
2156      Thread.currentThread().interrupt();
2157    }
2158
2159    if (keeperEx != null) {
2160      throw new IOException(keeperEx);
2161    }
2162  }
2163
2164  /**
2165   * Convert a {@link DeserializationException} to a more palatable {@link KeeperException}.
2166   * Used when can't let a {@link DeserializationException} out w/o changing public API.
2167   * @param e Exception to convert
2168   * @return Converted exception
2169   */
2170  public static KeeperException convert(final DeserializationException e) {
2171    KeeperException ke = new KeeperException.DataInconsistencyException();
2172    ke.initCause(e);
2173    return ke;
2174  }
2175
2176  /**
2177   * Recursively print the current state of ZK (non-transactional)
2178   * @param root name of the root directory in zk to print
2179   */
2180  public static void logZKTree(ZKWatcher zkw, String root) {
2181    if (!LOG.isDebugEnabled()) {
2182      return;
2183    }
2184
2185    LOG.debug("Current zk system:");
2186    String prefix = "|-";
2187    LOG.debug(prefix + root);
2188    try {
2189      logZKTree(zkw, root, prefix);
2190    } catch (KeeperException e) {
2191      throw new RuntimeException(e);
2192    }
2193  }
2194
2195  /**
2196   * Helper method to print the current state of the ZK tree.
2197   * @see #logZKTree(ZKWatcher, String)
2198   * @throws KeeperException if an unexpected exception occurs
2199   */
2200  private static void logZKTree(ZKWatcher zkw, String root, String prefix)
2201      throws KeeperException {
2202    List<String> children = ZKUtil.listChildrenNoWatch(zkw, root);
2203
2204    if (children == null) {
2205      return;
2206    }
2207
2208    for (String child : children) {
2209      LOG.debug(prefix + child);
2210      String node = ZNodePaths.joinZNode(root.equals("/") ? "" : root, child);
2211      logZKTree(zkw, node, prefix + "---");
2212    }
2213  }
2214
2215  /**
2216   * @param position the position to serialize
2217   * @return Serialized protobuf of <code>position</code> with pb magic prefix prepended suitable
2218   *         for use as content of an wal position in a replication queue.
2219   */
2220  public static byte[] positionToByteArray(final long position) {
2221    byte[] bytes = ReplicationProtos.ReplicationHLogPosition.newBuilder().setPosition(position)
2222        .build().toByteArray();
2223    return ProtobufUtil.prependPBMagic(bytes);
2224  }
2225
2226  /**
2227   * @param bytes - Content of a WAL position znode.
2228   * @return long - The current WAL position.
2229   * @throws DeserializationException if the WAL position cannot be parsed
2230   */
2231  public static long parseWALPositionFrom(final byte[] bytes) throws DeserializationException {
2232    if (bytes == null) {
2233      throw new DeserializationException("Unable to parse null WAL position.");
2234    }
2235    if (ProtobufUtil.isPBMagicPrefix(bytes)) {
2236      int pblen = ProtobufUtil.lengthOfPBMagic();
2237      ReplicationProtos.ReplicationHLogPosition.Builder builder =
2238          ReplicationProtos.ReplicationHLogPosition.newBuilder();
2239      ReplicationProtos.ReplicationHLogPosition position;
2240      try {
2241        ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen);
2242        position = builder.build();
2243      } catch (IOException e) {
2244        throw new DeserializationException(e);
2245      }
2246      return position.getPosition();
2247    } else {
2248      if (bytes.length > 0) {
2249        return Bytes.toLong(bytes);
2250      }
2251      return 0;
2252    }
2253  }
2254}