Java tutorial
/* * 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); } }