org.apache.accumulo.server.init.Initialize.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.accumulo.server.init.Initialize.java

Source

/*
 * 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.server.init;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily.DIRECTORY_COLUMN;
import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily.TIME_COLUMN;
import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;

import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.cli.Help;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.client.IteratorSetting.Column;
import org.apache.accumulo.core.client.impl.Namespaces;
import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.conf.SiteConfiguration;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.data.impl.KeyExtent;
import org.apache.accumulo.core.file.FileOperations;
import org.apache.accumulo.core.file.FileSKVWriter;
import org.apache.accumulo.core.iterators.Combiner;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
import org.apache.accumulo.core.iterators.user.VersioningIterator;
import org.apache.accumulo.core.master.state.tables.TableState;
import org.apache.accumulo.core.master.thrift.MasterGoalState;
import org.apache.accumulo.core.metadata.MetadataTable;
import org.apache.accumulo.core.metadata.RootTable;
import org.apache.accumulo.core.metadata.schema.DataFileValue;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.ReplicationSection;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.CurrentLocationColumnFamily;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.FutureLocationColumnFamily;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.LogColumnFamily;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily;
import org.apache.accumulo.core.replication.ReplicationConstants;
import org.apache.accumulo.core.replication.ReplicationSchema.StatusSection;
import org.apache.accumulo.core.replication.ReplicationSchema.WorkSection;
import org.apache.accumulo.core.replication.ReplicationTable;
import org.apache.accumulo.core.util.CachedConfiguration;
import org.apache.accumulo.core.util.ColumnFQ;
import org.apache.accumulo.core.util.LocalityGroupUtil;
import org.apache.accumulo.core.util.Pair;
import org.apache.accumulo.core.volume.VolumeConfiguration;
import org.apache.accumulo.core.zookeeper.ZooUtil;
import org.apache.accumulo.fate.zookeeper.IZooReaderWriter;
import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeExistsPolicy;
import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeMissingPolicy;
import org.apache.accumulo.server.Accumulo;
import org.apache.accumulo.server.AccumuloServerContext;
import org.apache.accumulo.server.ServerConstants;
import org.apache.accumulo.server.client.HdfsZooInstance;
import org.apache.accumulo.server.conf.ServerConfigurationFactory;
import org.apache.accumulo.server.constraints.MetadataConstraints;
import org.apache.accumulo.server.fs.VolumeManager;
import org.apache.accumulo.server.fs.VolumeManagerImpl;
import org.apache.accumulo.server.iterators.MetadataBulkLoadFilter;
import org.apache.accumulo.server.log.WalStateManager;
import org.apache.accumulo.server.replication.ReplicationUtil;
import org.apache.accumulo.server.replication.StatusCombiner;
import org.apache.accumulo.server.security.AuditedSecurityOperation;
import org.apache.accumulo.server.security.SecurityUtil;
import org.apache.accumulo.server.tables.TableManager;
import org.apache.accumulo.server.tablets.TabletTime;
import org.apache.accumulo.server.util.ReplicationTableUtil;
import org.apache.accumulo.server.util.TablePropUtil;
import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
import org.apache.accumulo.start.spi.KeywordExecutable;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.beust.jcommander.Parameter;
import com.google.auto.service.AutoService;
import com.google.common.base.Joiner;

import jline.console.ConsoleReader;

/**
 * This class is used to setup the directory structure and the root tablet to get an instance started
 *
 */
@AutoService(KeywordExecutable.class)
public class Initialize implements KeywordExecutable {
    private static final Logger log = LoggerFactory.getLogger(Initialize.class);
    private static final String DEFAULT_ROOT_USER = "root";
    private static final String TABLE_TABLETS_TABLET_DIR = "/table_info";

    private static ConsoleReader reader = null;
    private static IZooReaderWriter zoo = ZooReaderWriter.getInstance();

    private static ConsoleReader getConsoleReader() throws IOException {
        if (reader == null)
            reader = new ConsoleReader();
        return reader;
    }

    /**
     * Sets this class's ZooKeeper reader/writer.
     *
     * @param izoo
     *          reader/writer
     */
    static void setZooReaderWriter(IZooReaderWriter izoo) {
        zoo = izoo;
    }

    /**
     * Gets this class's ZooKeeper reader/writer.
     *
     * @return reader/writer
     */
    static IZooReaderWriter getZooReaderWriter() {
        return zoo;
    }

    private static HashMap<String, String> initialMetadataConf = new HashMap<String, String>();
    private static HashMap<String, String> initialMetadataCombinerConf = new HashMap<String, String>();
    private static HashMap<String, String> initialReplicationTableConf = new HashMap<String, String>();

    static {
        initialMetadataConf.put(Property.TABLE_FILE_COMPRESSED_BLOCK_SIZE.getKey(), "32K");
        initialMetadataConf.put(Property.TABLE_FILE_REPLICATION.getKey(), "5");
        initialMetadataConf.put(Property.TABLE_DURABILITY.getKey(), "sync");
        initialMetadataConf.put(Property.TABLE_MAJC_RATIO.getKey(), "1");
        initialMetadataConf.put(Property.TABLE_SPLIT_THRESHOLD.getKey(), "64M");
        initialMetadataConf.put(Property.TABLE_CONSTRAINT_PREFIX.getKey() + "1",
                MetadataConstraints.class.getName());
        initialMetadataConf.put(Property.TABLE_ITERATOR_PREFIX.getKey() + "scan.vers",
                "10," + VersioningIterator.class.getName());
        initialMetadataConf.put(Property.TABLE_ITERATOR_PREFIX.getKey() + "scan.vers.opt.maxVersions", "1");
        initialMetadataConf.put(Property.TABLE_ITERATOR_PREFIX.getKey() + "minc.vers",
                "10," + VersioningIterator.class.getName());
        initialMetadataConf.put(Property.TABLE_ITERATOR_PREFIX.getKey() + "minc.vers.opt.maxVersions", "1");
        initialMetadataConf.put(Property.TABLE_ITERATOR_PREFIX.getKey() + "majc.vers",
                "10," + VersioningIterator.class.getName());
        initialMetadataConf.put(Property.TABLE_ITERATOR_PREFIX.getKey() + "majc.vers.opt.maxVersions", "1");
        initialMetadataConf.put(Property.TABLE_ITERATOR_PREFIX.getKey() + "majc.bulkLoadFilter",
                "20," + MetadataBulkLoadFilter.class.getName());
        initialMetadataConf.put(Property.TABLE_FAILURES_IGNORE.getKey(), "false");
        initialMetadataConf.put(Property.TABLE_LOCALITY_GROUP_PREFIX.getKey() + "tablet",
                String.format("%s,%s", TabletColumnFamily.NAME, CurrentLocationColumnFamily.NAME));
        initialMetadataConf.put(Property.TABLE_LOCALITY_GROUP_PREFIX.getKey() + "server",
                String.format("%s,%s,%s,%s", DataFileColumnFamily.NAME, LogColumnFamily.NAME,
                        ServerColumnFamily.NAME, FutureLocationColumnFamily.NAME));
        initialMetadataConf.put(Property.TABLE_LOCALITY_GROUPS.getKey(), "tablet,server");
        initialMetadataConf.put(Property.TABLE_DEFAULT_SCANTIME_VISIBILITY.getKey(), "");
        initialMetadataConf.put(Property.TABLE_INDEXCACHE_ENABLED.getKey(), "true");
        initialMetadataConf.put(Property.TABLE_BLOCKCACHE_ENABLED.getKey(), "true");

        // ACCUMULO-3077 Set the combiner on accumulo.metadata during init to reduce the likelihood of a race
        // condition where a tserver compacts away Status updates because it didn't see the Combiner configured
        IteratorSetting setting = new IteratorSetting(9, ReplicationTableUtil.COMBINER_NAME, StatusCombiner.class);
        Combiner.setColumns(setting, Collections.singletonList(new Column(ReplicationSection.COLF)));
        for (IteratorScope scope : IteratorScope.values()) {
            String root = String.format("%s%s.%s", Property.TABLE_ITERATOR_PREFIX, scope.name().toLowerCase(),
                    setting.getName());
            for (Entry<String, String> prop : setting.getOptions().entrySet()) {
                initialMetadataCombinerConf.put(root + ".opt." + prop.getKey(), prop.getValue());
            }
            initialMetadataCombinerConf.put(root, setting.getPriority() + "," + setting.getIteratorClass());
        }

        // add combiners to replication table
        setting = new IteratorSetting(30, ReplicationTable.COMBINER_NAME, StatusCombiner.class);
        setting.setPriority(30);
        Combiner.setColumns(setting, Arrays.asList(new Column(StatusSection.NAME), new Column(WorkSection.NAME)));
        for (IteratorScope scope : EnumSet.allOf(IteratorScope.class)) {
            String root = String.format("%s%s.%s", Property.TABLE_ITERATOR_PREFIX, scope.name().toLowerCase(),
                    setting.getName());
            for (Entry<String, String> prop : setting.getOptions().entrySet()) {
                initialReplicationTableConf.put(root + ".opt." + prop.getKey(), prop.getValue());
            }
            initialReplicationTableConf.put(root, setting.getPriority() + "," + setting.getIteratorClass());
        }
        // add locality groups to replication table
        for (Entry<String, Set<Text>> g : ReplicationTable.LOCALITY_GROUPS.entrySet()) {
            initialReplicationTableConf.put(Property.TABLE_LOCALITY_GROUP_PREFIX + g.getKey(),
                    LocalityGroupUtil.encodeColumnFamilies(g.getValue()));
        }
        initialReplicationTableConf.put(Property.TABLE_LOCALITY_GROUPS.getKey(),
                Joiner.on(",").join(ReplicationTable.LOCALITY_GROUPS.keySet()));
        // add formatter to replication table
        initialReplicationTableConf.put(Property.TABLE_FORMATTER_CLASS.getKey(),
                ReplicationUtil.STATUS_FORMATTER_CLASS_NAME);
    }

    static boolean checkInit(Configuration conf, VolumeManager fs, SiteConfiguration sconf) throws IOException {
        @SuppressWarnings("deprecation")
        String fsUri = sconf.get(Property.INSTANCE_DFS_URI);
        if (fsUri.equals(""))
            fsUri = FileSystem.getDefaultUri(conf).toString();
        log.info("Hadoop Filesystem is " + fsUri);
        log.info("Accumulo data dirs are "
                + Arrays.asList(VolumeConfiguration.getVolumeUris(SiteConfiguration.getInstance())));
        log.info("Zookeeper server is " + sconf.get(Property.INSTANCE_ZK_HOST));
        log.info(
                "Checking if Zookeeper is available. If this hangs, then you need to make sure zookeeper is running");
        if (!zookeeperAvailable()) {
            // ACCUMULO-3651 Changed level to error and added FATAL to message for slf4j compatibility
            log.error("FATAL Zookeeper needs to be up and running in order to init. Exiting ...");
            return false;
        }
        if (sconf.get(Property.INSTANCE_SECRET).equals(Property.INSTANCE_SECRET.getDefaultValue())) {
            ConsoleReader c = getConsoleReader();
            c.beep();
            c.println();
            c.println();
            c.println(
                    "Warning!!! Your instance secret is still set to the default, this is not secure. We highly recommend you change it.");
            c.println();
            c.println();
            c.println("You can change the instance secret in accumulo by using:");
            c.println("   bin/accumulo " + org.apache.accumulo.server.util.ChangeSecret.class.getName()
                    + " oldPassword newPassword.");
            c.println(
                    "You will also need to edit your secret in your configuration file by adding the property instance.secret to your conf/accumulo-site.xml. "
                            + "Without this accumulo will not operate correctly");
        }
        try {
            if (isInitialized(fs)) {
                printInitializeFailureMessages(sconf);
                return false;
            }
        } catch (IOException e) {
            throw new IOException("Failed to check if filesystem already initialized", e);
        }

        return true;
    }

    static void printInitializeFailureMessages(SiteConfiguration sconf) {
        @SuppressWarnings("deprecation")
        Property INSTANCE_DFS_DIR = Property.INSTANCE_DFS_DIR;
        @SuppressWarnings("deprecation")
        Property INSTANCE_DFS_URI = Property.INSTANCE_DFS_URI;
        String instanceDfsDir = sconf.get(INSTANCE_DFS_DIR);
        // ACCUMULO-3651 Changed level to error and added FATAL to message for slf4j compatibility
        log.error("FATAL It appears the directories "
                + Arrays.asList(VolumeConfiguration.getVolumeUris(SiteConfiguration.getInstance()))
                + " were previously initialized.");
        String instanceVolumes = sconf.get(Property.INSTANCE_VOLUMES);
        String instanceDfsUri = sconf.get(INSTANCE_DFS_URI);

        // ACCUMULO-3651 Changed level to error and added FATAL to message for slf4j compatibility

        if (!instanceVolumes.isEmpty()) {
            log.error("FATAL: Change the property " + Property.INSTANCE_VOLUMES + " to use different filesystems,");
        } else if (!instanceDfsDir.isEmpty()) {
            log.error("FATAL: Change the property " + INSTANCE_DFS_URI + " to use a different filesystem,");
        } else {
            log.error("FATAL: You are using the default URI for the filesystem. Set the property "
                    + Property.INSTANCE_VOLUMES + " to use a different filesystem,");
        }
        log.error("FATAL: or change the property " + INSTANCE_DFS_DIR + " to use a different directory.");
        log.error("FATAL: The current value of " + INSTANCE_DFS_URI + " is |" + instanceDfsUri + "|");
        log.error("FATAL: The current value of " + INSTANCE_DFS_DIR + " is |" + instanceDfsDir + "|");
        log.error("FATAL: The current value of " + Property.INSTANCE_VOLUMES + " is |" + instanceVolumes + "|");
    }

    public boolean doInit(Opts opts, Configuration conf, VolumeManager fs) throws IOException {
        if (!checkInit(conf, fs, SiteConfiguration.getInstance())) {
            return false;
        }

        // prompt user for instance name and root password early, in case they
        // abort, we don't leave an inconsistent HDFS/ZooKeeper structure
        String instanceNamePath;
        try {
            instanceNamePath = getInstanceNamePath(opts);
        } catch (Exception e) {
            log.error("FATAL: Failed to talk to zookeeper", e);
            return false;
        }

        String rootUser;
        try {
            rootUser = getRootUserName(opts);
        } catch (Exception e) {
            log.error("FATAL: Failed to obtain user for administrative privileges");
            return false;
        }

        // Don't prompt for a password when we're running SASL(Kerberos)
        final AccumuloConfiguration siteConf = SiteConfiguration.getInstance();
        if (siteConf.getBoolean(Property.INSTANCE_RPC_SASL_ENABLED)) {
            opts.rootpass = UUID.randomUUID().toString().getBytes(UTF_8);
        } else {
            opts.rootpass = getRootPassword(opts, rootUser);
        }

        return initialize(opts, instanceNamePath, fs, rootUser);
    }

    private boolean initialize(Opts opts, String instanceNamePath, VolumeManager fs, String rootUser) {

        UUID uuid = UUID.randomUUID();
        // the actual disk locations of the root table and tablets
        String[] configuredVolumes = VolumeConfiguration.getVolumeUris(SiteConfiguration.getInstance());
        final String rootTabletDir = new Path(
                fs.choose(Optional.<String>empty(), configuredVolumes) + Path.SEPARATOR + ServerConstants.TABLE_DIR
                        + Path.SEPARATOR + RootTable.ID + RootTable.ROOT_TABLET_LOCATION).toString();

        try {
            initZooKeeper(opts, uuid.toString(), instanceNamePath, rootTabletDir);
        } catch (Exception e) {
            log.error("FATAL: Failed to initialize zookeeper", e);
            return false;
        }

        try {
            initFileSystem(opts, fs, uuid, rootTabletDir);
        } catch (Exception e) {
            log.error("FATAL Failed to initialize filesystem", e);

            if (SiteConfiguration.getInstance().get(Property.INSTANCE_VOLUMES).trim().equals("")) {
                Configuration fsConf = CachedConfiguration.getInstance();

                final String defaultFsUri = "file:///";
                String fsDefaultName = fsConf.get("fs.default.name", defaultFsUri),
                        fsDefaultFS = fsConf.get("fs.defaultFS", defaultFsUri);

                // Try to determine when we couldn't find an appropriate core-site.xml on the classpath
                if (defaultFsUri.equals(fsDefaultName) && defaultFsUri.equals(fsDefaultFS)) {
                    log.error("FATAL: Default filesystem value ('fs.defaultFS' or 'fs.default.name') of '"
                            + defaultFsUri + "' was found in the Hadoop configuration");
                    log.error(
                            "FATAL: Please ensure that the Hadoop core-site.xml is on the classpath using 'general.classpaths' in accumulo-site.xml");
                }
            }

            return false;
        }

        final ServerConfigurationFactory confFactory = new ServerConfigurationFactory(
                HdfsZooInstance.getInstance());

        // When we're using Kerberos authentication, we need valid credentials to perform initialization. If the user provided some, use them.
        // If they did not, fall back to the credentials present in accumulo-site.xml that the servers will use themselves.
        try {
            final SiteConfiguration siteConf = confFactory.getSiteConfiguration();
            if (siteConf.getBoolean(Property.INSTANCE_RPC_SASL_ENABLED)) {
                final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
                // We don't have any valid creds to talk to HDFS
                if (!ugi.hasKerberosCredentials()) {
                    final String accumuloKeytab = siteConf.get(Property.GENERAL_KERBEROS_KEYTAB),
                            accumuloPrincipal = siteConf.get(Property.GENERAL_KERBEROS_PRINCIPAL);

                    // Fail if the site configuration doesn't contain appropriate credentials to login as servers
                    if (StringUtils.isBlank(accumuloKeytab) || StringUtils.isBlank(accumuloPrincipal)) {
                        log.error(
                                "FATAL: No Kerberos credentials provided, and Accumulo is not properly configured for server login");
                        return false;
                    }

                    log.info("Logging in as " + accumuloPrincipal + " with " + accumuloKeytab);

                    // Login using the keytab as the 'accumulo' user
                    UserGroupInformation.loginUserFromKeytab(accumuloPrincipal, accumuloKeytab);
                }
            }
        } catch (IOException e) {
            log.error("FATAL: Failed to get the Kerberos user", e);
            return false;
        }

        try {
            AccumuloServerContext context = new AccumuloServerContext(confFactory);
            initSecurity(context, opts, uuid.toString(), rootUser);
        } catch (Exception e) {
            log.error("FATAL: Failed to initialize security", e);
            return false;
        }
        return true;
    }

    private static boolean zookeeperAvailable() {
        try {
            return zoo.exists("/");
        } catch (KeeperException e) {
            return false;
        } catch (InterruptedException e) {
            return false;
        }
    }

    private static void initDirs(VolumeManager fs, UUID uuid, String[] baseDirs, boolean print) throws IOException {
        for (String baseDir : baseDirs) {
            fs.mkdirs(new Path(new Path(baseDir, ServerConstants.VERSION_DIR), "" + ServerConstants.DATA_VERSION));

            // create an instance id
            Path iidLocation = new Path(baseDir, ServerConstants.INSTANCE_ID_DIR);
            fs.mkdirs(iidLocation);
            fs.createNewFile(new Path(iidLocation, uuid.toString()));
            if (print)
                log.info("Initialized volume " + baseDir);
        }
    }

    private void initFileSystem(Opts opts, VolumeManager fs, UUID uuid, String rootTabletDir) throws IOException {
        initDirs(fs, uuid, VolumeConfiguration.getVolumeUris(SiteConfiguration.getInstance()), false);

        // initialize initial system tables config in zookeeper
        initSystemTablesConfig();

        String tableMetadataTabletDir = fs.choose(Optional.<String>empty(), ServerConstants.getBaseUris())
                + Constants.HDFS_TABLES_DIR + Path.SEPARATOR + MetadataTable.ID + TABLE_TABLETS_TABLET_DIR;
        String replicationTableDefaultTabletDir = fs.choose(Optional.<String>empty(), ServerConstants.getBaseUris())
                + Constants.HDFS_TABLES_DIR + Path.SEPARATOR + ReplicationTable.ID
                + Constants.DEFAULT_TABLET_LOCATION;
        String defaultMetadataTabletDir = fs.choose(Optional.<String>empty(), ServerConstants.getBaseUris())
                + Constants.HDFS_TABLES_DIR + Path.SEPARATOR + MetadataTable.ID + Constants.DEFAULT_TABLET_LOCATION;

        // create table and default tablets directories
        createDirectories(fs, rootTabletDir, tableMetadataTabletDir, defaultMetadataTabletDir,
                replicationTableDefaultTabletDir);

        String ext = FileOperations.getNewFileExtension(AccumuloConfiguration.getDefaultConfiguration());

        // populate the metadata tables tablet with info about the replication table's one initial tablet
        String metadataFileName = tableMetadataTabletDir + Path.SEPARATOR + "0_1." + ext;
        Tablet replicationTablet = new Tablet(ReplicationTable.ID, replicationTableDefaultTabletDir, null, null);
        createMetadataFile(fs, metadataFileName, replicationTablet);

        // populate the root tablet with info about the metadata table's two initial tablets
        String rootTabletFileName = rootTabletDir + Path.SEPARATOR + "00000_00000." + ext;
        Text splitPoint = TabletsSection.getRange().getEndKey().getRow();
        Tablet tablesTablet = new Tablet(MetadataTable.ID, tableMetadataTabletDir, null, splitPoint,
                metadataFileName);
        Tablet defaultTablet = new Tablet(MetadataTable.ID, defaultMetadataTabletDir, splitPoint, null);
        createMetadataFile(fs, rootTabletFileName, tablesTablet, defaultTablet);
    }

    private static class Tablet {
        String tableId, dir;
        Text prevEndRow, endRow;
        String[] files;

        Tablet(String tableId, String dir, Text prevEndRow, Text endRow, String... files) {
            this.tableId = tableId;
            this.dir = dir;
            this.prevEndRow = prevEndRow;
            this.endRow = endRow;
            this.files = files;
        }
    }

    private static void createMetadataFile(VolumeManager volmanager, String fileName, Tablet... tablets)
            throws IOException {
        // sort file contents in memory, then play back to the file
        TreeMap<Key, Value> sorted = new TreeMap<>();
        for (Tablet tablet : tablets) {
            createEntriesForTablet(sorted, tablet);
        }
        FileSystem fs = volmanager.getVolumeByPath(new Path(fileName)).getFileSystem();
        FileSKVWriter tabletWriter = FileOperations.getInstance().newWriterBuilder()
                .forFile(fileName, fs, fs.getConf())
                .withTableConfiguration(AccumuloConfiguration.getDefaultConfiguration()).build();
        tabletWriter.startDefaultLocalityGroup();

        for (Entry<Key, Value> entry : sorted.entrySet()) {
            tabletWriter.append(entry.getKey(), entry.getValue());
        }

        tabletWriter.close();
    }

    private static void createEntriesForTablet(TreeMap<Key, Value> map, Tablet tablet) {
        Value EMPTY_SIZE = new DataFileValue(0, 0).encodeAsValue();
        Text extent = new Text(KeyExtent.getMetadataEntry(tablet.tableId, tablet.endRow));
        addEntry(map, extent, DIRECTORY_COLUMN, new Value(tablet.dir.getBytes(UTF_8)));
        addEntry(map, extent, TIME_COLUMN, new Value((TabletTime.LOGICAL_TIME_ID + "0").getBytes(UTF_8)));
        addEntry(map, extent, PREV_ROW_COLUMN, KeyExtent.encodePrevEndRow(tablet.prevEndRow));
        for (String file : tablet.files) {
            addEntry(map, extent, new ColumnFQ(DataFileColumnFamily.NAME, new Text(file)), EMPTY_SIZE);
        }
    }

    private static void addEntry(TreeMap<Key, Value> map, Text row, ColumnFQ col, Value value) {
        map.put(new Key(row, col.getColumnFamily(), col.getColumnQualifier(), 0), value);
    }

    private static void createDirectories(VolumeManager fs, String... dirs) throws IOException {
        for (String s : dirs) {
            Path dir = new Path(s);
            try {
                FileStatus fstat = fs.getFileStatus(dir);
                if (!fstat.isDirectory()) {
                    log.error("FATAL: location " + dir + " exists but is not a directory");
                    return;
                }
            } catch (FileNotFoundException fnfe) {
                // attempt to create directory, since it doesn't exist
                if (!fs.mkdirs(dir)) {
                    log.error("FATAL: unable to create directory " + dir);
                    return;
                }
            }
        }
    }

    private static void initZooKeeper(Opts opts, String uuid, String instanceNamePath, String rootTabletDir)
            throws KeeperException, InterruptedException {
        // setup basic data in zookeeper
        zoo.putPersistentData(Constants.ZROOT, new byte[0], -1, NodeExistsPolicy.SKIP, Ids.OPEN_ACL_UNSAFE);
        zoo.putPersistentData(Constants.ZROOT + Constants.ZINSTANCES, new byte[0], -1, NodeExistsPolicy.SKIP,
                Ids.OPEN_ACL_UNSAFE);

        // setup instance name
        if (opts.clearInstanceName)
            zoo.recursiveDelete(instanceNamePath, NodeMissingPolicy.SKIP);
        zoo.putPersistentData(instanceNamePath, uuid.getBytes(UTF_8), NodeExistsPolicy.FAIL);

        final byte[] EMPTY_BYTE_ARRAY = new byte[0], ZERO_CHAR_ARRAY = new byte[] { '0' };

        // setup the instance
        String zkInstanceRoot = Constants.ZROOT + "/" + uuid;
        zoo.putPersistentData(zkInstanceRoot, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZTABLES, Constants.ZTABLES_INITIAL_ID,
                NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZNAMESPACES, new byte[0], NodeExistsPolicy.FAIL);
        TableManager.prepareNewNamespaceState(uuid, Namespaces.DEFAULT_NAMESPACE_ID, Namespaces.DEFAULT_NAMESPACE,
                NodeExistsPolicy.FAIL);
        TableManager.prepareNewNamespaceState(uuid, Namespaces.ACCUMULO_NAMESPACE_ID, Namespaces.ACCUMULO_NAMESPACE,
                NodeExistsPolicy.FAIL);
        TableManager.prepareNewTableState(uuid, RootTable.ID, Namespaces.ACCUMULO_NAMESPACE_ID, RootTable.NAME,
                TableState.ONLINE, NodeExistsPolicy.FAIL);
        TableManager.prepareNewTableState(uuid, MetadataTable.ID, Namespaces.ACCUMULO_NAMESPACE_ID,
                MetadataTable.NAME, TableState.ONLINE, NodeExistsPolicy.FAIL);
        TableManager.prepareNewTableState(uuid, ReplicationTable.ID, Namespaces.ACCUMULO_NAMESPACE_ID,
                ReplicationTable.NAME, TableState.OFFLINE, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZTSERVERS, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZPROBLEMS, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + RootTable.ZROOT_TABLET, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + RootTable.ZROOT_TABLET_WALOGS, EMPTY_BYTE_ARRAY,
                NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + RootTable.ZROOT_TABLET_CURRENT_LOGS, EMPTY_BYTE_ARRAY,
                NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + RootTable.ZROOT_TABLET_PATH, rootTabletDir.getBytes(UTF_8),
                NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZMASTERS, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZMASTER_LOCK, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZMASTER_GOAL_STATE,
                MasterGoalState.NORMAL.toString().getBytes(UTF_8), NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZGC, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZGC_LOCK, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZCONFIG, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZTABLE_LOCKS, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZHDFS_RESERVATIONS, EMPTY_BYTE_ARRAY,
                NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZNEXT_FILE, ZERO_CHAR_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZRECOVERY, ZERO_CHAR_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZMONITOR, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + Constants.ZMONITOR_LOCK, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + ReplicationConstants.ZOO_BASE, EMPTY_BYTE_ARRAY,
                NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + ReplicationConstants.ZOO_TSERVERS, EMPTY_BYTE_ARRAY,
                NodeExistsPolicy.FAIL);
        zoo.putPersistentData(zkInstanceRoot + WalStateManager.ZWALS, EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
    }

    private String getInstanceNamePath(Opts opts) throws IOException, KeeperException, InterruptedException {
        // setup the instance name
        String instanceName, instanceNamePath = null;
        boolean exists = true;
        do {
            if (opts.cliInstanceName == null) {
                instanceName = getConsoleReader().readLine("Instance name : ");
            } else {
                instanceName = opts.cliInstanceName;
            }
            if (instanceName == null)
                System.exit(0);
            instanceName = instanceName.trim();
            if (instanceName.length() == 0)
                continue;
            instanceNamePath = Constants.ZROOT + Constants.ZINSTANCES + "/" + instanceName;
            if (opts.clearInstanceName) {
                exists = false;
                break;
            } else if (exists = zoo.exists(instanceNamePath)) {
                String decision = getConsoleReader().readLine("Instance name \"" + instanceName
                        + "\" exists. Delete existing entry from zookeeper? [Y/N] : ");
                if (decision == null)
                    System.exit(0);
                if (decision.length() == 1 && decision.toLowerCase(Locale.ENGLISH).charAt(0) == 'y') {
                    opts.clearInstanceName = true;
                    exists = false;
                }
            }
        } while (exists);
        return instanceNamePath;
    }

    private String getRootUserName(Opts opts) throws IOException {
        AccumuloConfiguration conf = SiteConfiguration.getInstance();
        final String keytab = conf.get(Property.GENERAL_KERBEROS_KEYTAB);
        if (keytab.equals(Property.GENERAL_KERBEROS_KEYTAB.getDefaultValue())
                || !conf.getBoolean(Property.INSTANCE_RPC_SASL_ENABLED)) {
            return DEFAULT_ROOT_USER;
        }

        ConsoleReader c = getConsoleReader();
        c.println("Running against secured HDFS");

        if (null != opts.rootUser) {
            return opts.rootUser;
        }

        do {
            String user = c.readLine("Principal (user) to grant administrative privileges to : ");
            if (user == null) {
                // should not happen
                System.exit(1);
            }
            if (!user.isEmpty()) {
                return user;
            }
        } while (true);
    }

    private byte[] getRootPassword(Opts opts, String rootUser) throws IOException {
        if (opts.cliPassword != null) {
            return opts.cliPassword.getBytes(UTF_8);
        }
        String rootpass;
        String confirmpass;
        do {
            rootpass = getConsoleReader().readLine("Enter initial password for " + rootUser
                    + " (this may not be applicable for your security setup): ", '*');
            if (rootpass == null)
                System.exit(0);
            confirmpass = getConsoleReader().readLine("Confirm initial password for " + rootUser + ": ", '*');
            if (confirmpass == null)
                System.exit(0);
            if (!rootpass.equals(confirmpass))
                log.error("Passwords do not match");
        } while (!rootpass.equals(confirmpass));
        return rootpass.getBytes(UTF_8);
    }

    private static void initSecurity(AccumuloServerContext context, Opts opts, String iid, String rootUser)
            throws AccumuloSecurityException, ThriftSecurityException, IOException {
        AuditedSecurityOperation.getInstance(context, true).initializeSecurity(context.rpcCreds(), rootUser,
                opts.rootpass);
    }

    public static void initSystemTablesConfig() throws IOException {
        try {
            Configuration conf = CachedConfiguration.getInstance();
            int max = conf.getInt("dfs.replication.max", 512);
            // Hadoop 0.23 switched the min value configuration name
            int min = Math.max(conf.getInt("dfs.replication.min", 1),
                    conf.getInt("dfs.namenode.replication.min", 1));
            if (max < 5)
                setMetadataReplication(max, "max");
            if (min > 5)
                setMetadataReplication(min, "min");
            for (Entry<String, String> entry : initialMetadataConf.entrySet()) {
                if (!TablePropUtil.setTableProperty(RootTable.ID, entry.getKey(), entry.getValue()))
                    throw new IOException("Cannot create per-table property " + entry.getKey());
                if (!TablePropUtil.setTableProperty(MetadataTable.ID, entry.getKey(), entry.getValue()))
                    throw new IOException("Cannot create per-table property " + entry.getKey());
            }
            // Only add combiner config to accumulo.metadata table (ACCUMULO-3077)
            for (Entry<String, String> entry : initialMetadataCombinerConf.entrySet()) {
                if (!TablePropUtil.setTableProperty(MetadataTable.ID, entry.getKey(), entry.getValue()))
                    throw new IOException("Cannot create per-table property " + entry.getKey());
            }

            // add configuration to the replication table
            for (Entry<String, String> entry : initialReplicationTableConf.entrySet()) {
                if (!TablePropUtil.setTableProperty(ReplicationTable.ID, entry.getKey(), entry.getValue()))
                    throw new IOException("Cannot create per-table property " + entry.getKey());
            }
        } catch (Exception e) {
            log.error("FATAL: Error talking to ZooKeeper", e);
            throw new IOException(e);
        }
    }

    private static void setMetadataReplication(int replication, String reason) throws IOException {
        String rep = getConsoleReader()
                .readLine("Your HDFS replication " + reason + " is not compatible with our default "
                        + MetadataTable.NAME + " replication of 5. What do you want to set your "
                        + MetadataTable.NAME + " replication to? (" + replication + ") ");
        if (rep == null || rep.length() == 0)
            rep = Integer.toString(replication);
        else
            // Lets make sure it's a number
            Integer.parseInt(rep);
        initialMetadataConf.put(Property.TABLE_FILE_REPLICATION.getKey(), rep);
    }

    public static boolean isInitialized(VolumeManager fs) throws IOException {
        for (String baseDir : VolumeConfiguration.getVolumeUris(SiteConfiguration.getInstance())) {
            if (fs.exists(new Path(baseDir, ServerConstants.INSTANCE_ID_DIR))
                    || fs.exists(new Path(baseDir, ServerConstants.VERSION_DIR)))
                return true;
        }

        return false;
    }

    private static void addVolumes(VolumeManager fs) throws IOException {

        String[] volumeURIs = VolumeConfiguration.getVolumeUris(SiteConfiguration.getInstance());

        HashSet<String> initializedDirs = new HashSet<String>();
        initializedDirs.addAll(Arrays.asList(ServerConstants.checkBaseUris(volumeURIs, true)));

        HashSet<String> uinitializedDirs = new HashSet<String>();
        uinitializedDirs.addAll(Arrays.asList(volumeURIs));
        uinitializedDirs.removeAll(initializedDirs);

        Path aBasePath = new Path(initializedDirs.iterator().next());
        Path iidPath = new Path(aBasePath, ServerConstants.INSTANCE_ID_DIR);
        Path versionPath = new Path(aBasePath, ServerConstants.VERSION_DIR);

        UUID uuid = UUID.fromString(ZooUtil.getInstanceIDFromHdfs(iidPath, SiteConfiguration.getInstance()));
        for (Pair<Path, Path> replacementVolume : ServerConstants.getVolumeReplacements()) {
            if (aBasePath.equals(replacementVolume.getFirst()))
                log.error(aBasePath + " is set to be replaced in " + Property.INSTANCE_VOLUMES_REPLACEMENTS
                        + " and should not appear in " + Property.INSTANCE_VOLUMES
                        + ". It is highly recommended that this property be removed as data could still be written to this volume.");
        }

        if (ServerConstants.DATA_VERSION != Accumulo.getAccumuloPersistentVersion(
                versionPath.getFileSystem(CachedConfiguration.getInstance()), versionPath)) {
            throw new IOException("Accumulo " + Constants.VERSION + " cannot initialize data version "
                    + Accumulo.getAccumuloPersistentVersion(fs));
        }

        initDirs(fs, uuid, uinitializedDirs.toArray(new String[uinitializedDirs.size()]), true);
    }

    static class Opts extends Help {
        @Parameter(names = "--add-volumes", description = "Initialize any uninitialized volumes listed in instance.volumes")
        boolean addVolumes = false;
        @Parameter(names = "--reset-security", description = "just update the security information")
        boolean resetSecurity = false;
        @Parameter(names = "--clear-instance-name", description = "delete any existing instance name without prompting")
        boolean clearInstanceName = false;
        @Parameter(names = "--instance-name", description = "the instance name, if not provided, will prompt")
        String cliInstanceName;
        @Parameter(names = "--password", description = "set the password on the command line")
        String cliPassword;
        @Parameter(names = { "-u", "--user" }, description = "the name of the user to grant system permissions to")
        String rootUser = null;

        byte[] rootpass = null;
    }

    @Override
    public String keyword() {
        return "init";
    }

    @Override
    public void execute(final String[] args) {
        Opts opts = new Opts();
        opts.parseArgs(Initialize.class.getName(), args);

        try {
            AccumuloConfiguration acuConf = SiteConfiguration.getInstance();
            SecurityUtil.serverLogin(acuConf);
            Configuration conf = CachedConfiguration.getInstance();

            VolumeManager fs = VolumeManagerImpl.get(acuConf);

            if (opts.resetSecurity) {
                AccumuloServerContext context = new AccumuloServerContext(
                        new ServerConfigurationFactory(HdfsZooInstance.getInstance()));
                if (isInitialized(fs)) {
                    final String rootUser = getRootUserName(opts);
                    opts.rootpass = getRootPassword(opts, rootUser);
                    initSecurity(context, opts, HdfsZooInstance.getInstance().getInstanceID(), rootUser);
                } else {
                    log.error("FATAL: Attempted to reset security on accumulo before it was initialized");
                }
            }

            if (opts.addVolumes) {
                addVolumes(fs);
            }

            if (!opts.resetSecurity && !opts.addVolumes)
                if (!doInit(opts, conf, fs))
                    System.exit(-1);
        } catch (Exception e) {
            log.error("Fatal exception", e);
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        new Initialize().execute(args);
    }
}