This is an automated email from the ASF dual-hosted git repository.
peterxcli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new ff5d03288c4 HDDS-15084. Core Runtime: SCM + OM (#10456)
ff5d03288c4 is described below
commit ff5d03288c48755f65c99bc425f676c2210ac85b
Author: Chun-Hung Tseng <[email protected]>
AuthorDate: Tue Jul 14 00:21:13 2026 +0200
HDDS-15084. Core Runtime: SCM + OM (#10456)
---
.../ozone/local/TestLocalOzoneClusterRuntime.java | 136 ++++++++++
hadoop-ozone/tools/pom.xml | 12 +
.../hadoop/ozone/local/LocalOzoneCluster.java | 294 +++++++++++++++++++--
.../hadoop/ozone/local/TestLocalOzoneCluster.java | 9 +-
4 files changed, 431 insertions(+), 20 deletions(-)
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/local/TestLocalOzoneClusterRuntime.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/local/TestLocalOzoneClusterRuntime.java
new file mode 100644
index 00000000000..2d2d762896a
--- /dev/null
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/local/TestLocalOzoneClusterRuntime.java
@@ -0,0 +1,136 @@
+/*
+ * 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.hadoop.ozone.local;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.UUID;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.TestDataUtil;
+import org.apache.hadoop.ozone.client.OzoneBucket;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.OzoneClientFactory;
+import org.apache.hadoop.ozone.client.OzoneVolume;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Integration tests for the SCM and OM portion of {@link LocalOzoneCluster}.
+ */
+class TestLocalOzoneClusterRuntime {
+
+ @TempDir
+ private Path tempDir;
+
+ @Test
+ void scmAndOmStartAndReuseExistingMetadata() throws Exception {
+ String volumeName = uniqueName("vol");
+ String bucketName = uniqueName("bucket");
+ Path dataDir = tempDir.resolve("local-ozone-runtime");
+ LocalOzoneClusterConfig config = LocalOzoneClusterConfig.builder(dataDir)
+ .setS3gEnabled(false)
+ .setStartupTimeout(Duration.ofMinutes(2))
+ .build();
+
+ startRuntimeAndCreateBucket(config, volumeName, bucketName);
+ restartRuntimeAndVerifyBucket(config, volumeName, bucketName);
+ }
+
+ @Test
+ void formatNeverRejectsUninitializedScmOmStorage() throws Exception {
+ Path dataDir = tempDir.resolve("local-ozone-runtime");
+ LocalOzoneClusterConfig initialConfig =
+ LocalOzoneClusterConfig.builder(dataDir).build();
+ try (LocalOzoneCluster cluster = new LocalOzoneCluster(initialConfig, new
OzoneConfiguration())) {
+ cluster.prepareConfiguration();
+ }
+
+ LocalOzoneClusterConfig neverFormatConfig =
+ LocalOzoneClusterConfig.builder(dataDir)
+ .setFormatMode(LocalOzoneClusterConfig.FormatMode.NEVER)
+ .setS3gEnabled(false)
+ .build();
+
+ IOException error = assertThrows(IOException.class, () -> {
+ try (LocalOzoneCluster cluster = new
LocalOzoneCluster(neverFormatConfig, new OzoneConfiguration())) {
+ cluster.start();
+ }
+ });
+
+ assertTrue(error.getMessage().contains("storage is not initialized"),
+ error.getMessage());
+ }
+
+ private void startRuntimeAndCreateBucket(LocalOzoneClusterConfig config,
+ String volumeName, String bucketName) throws Exception {
+ try (LocalOzoneCluster cluster = new LocalOzoneCluster(config, new
OzoneConfiguration())) {
+ OzoneConfiguration clientConf =
+ cluster.prepareConfiguration().getConfiguration();
+ cluster.start();
+
+ assertServicePortsReachable(cluster);
+
+ try (OzoneClient client = OzoneClientFactory.getRpcClient(clientConf)) {
+ TestDataUtil.createVolumeAndBucket(client, volumeName, bucketName);
+ }
+ }
+ }
+
+ private void restartRuntimeAndVerifyBucket(LocalOzoneClusterConfig config,
+ String volumeName, String bucketName) throws Exception {
+ try (LocalOzoneCluster cluster = new LocalOzoneCluster(config, new
OzoneConfiguration())) {
+ OzoneConfiguration clientConf =
+ cluster.prepareConfiguration().getConfiguration();
+ cluster.start();
+
+ assertServicePortsReachable(cluster);
+
+ try (OzoneClient client = OzoneClientFactory.getRpcClient(clientConf)) {
+ OzoneVolume volume = client.getObjectStore().getVolume(volumeName);
+ OzoneBucket bucket = volume.getBucket(bucketName);
+ assertEquals(bucketName, bucket.getName());
+ }
+ }
+ }
+
+ private static void assertServicePortsReachable(LocalOzoneCluster cluster)
+ throws IOException {
+ assertTrue(cluster.getScmPort() > 0);
+ assertTrue(cluster.getOmPort() > 0);
+ assertPortReachable(cluster.getDisplayHost(), cluster.getScmPort());
+ assertPortReachable(cluster.getDisplayHost(), cluster.getOmPort());
+ }
+
+ private static void assertPortReachable(String host, int port)
+ throws IOException {
+ try (Socket socket = new Socket()) {
+ socket.connect(new InetSocketAddress(host, port), 1_000);
+ }
+ }
+
+ private static String uniqueName(String prefix) {
+ return prefix + UUID.randomUUID().toString().replace("-", "");
+ }
+}
diff --git a/hadoop-ozone/tools/pom.xml b/hadoop-ozone/tools/pom.xml
index 683ce8663b6..bda0ebb3134 100644
--- a/hadoop-ozone/tools/pom.xml
+++ b/hadoop-ozone/tools/pom.xml
@@ -62,10 +62,22 @@
<groupId>org.apache.ozone</groupId>
<artifactId>hdds-config</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.ozone</groupId>
+ <artifactId>hdds-server-framework</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ozone</groupId>
+ <artifactId>hdds-server-scm</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.ozone</groupId>
<artifactId>ozone-common</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.ozone</groupId>
+ <artifactId>ozone-manager</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.ratis</groupId>
<artifactId>ratis-common</artifactId>
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/local/LocalOzoneCluster.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/local/LocalOzoneCluster.java
index 4b35fb12c9e..def3b5b634f 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/local/LocalOzoneCluster.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/local/LocalOzoneCluster.java
@@ -19,6 +19,7 @@
import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SCM_SAFEMODE_MIN_DATANODE;
import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SCM_SAFEMODE_PIPELINE_CREATION;
+import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SCM_WAIT_TIME_AFTER_SAFE_MODE_EXIT;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_CONTAINER_RATIS_ENABLED_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_BLOCK_CLIENT_ADDRESS_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_BLOCK_CLIENT_BIND_HOST_KEY;
@@ -26,7 +27,11 @@
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_CLIENT_BIND_HOST_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_DATANODE_ADDRESS_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_DATANODE_BIND_HOST_KEY;
+import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_DB_DIRS;
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_GRPC_PORT_KEY;
+import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HA_RATIS_SERVER_RPC_FIRST_ELECTION_TIMEOUT;
+import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HA_RATIS_SNAPSHOT_DIR;
+import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HA_RATIS_STORAGE_DIR;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HTTPS_ADDRESS_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HTTPS_BIND_HOST_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HTTP_ADDRESS_KEY;
@@ -35,13 +40,24 @@
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_RATIS_PORT_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_SECURITY_SERVICE_ADDRESS_KEY;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_SECURITY_SERVICE_BIND_HOST_KEY;
+import static org.apache.hadoop.hdds.server.http.BaseHttpServer.SERVER_DIR;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_HTTP_BASEDIR;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_METADATA_DIRS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_REPLICATION;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_REPLICATION_TYPE;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_RATIS_SNAPSHOT_DIR;
+import static org.apache.hadoop.ozone.common.Storage.StorageState.INITIALIZED;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DB_DIRS;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTPS_ADDRESS_KEY;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTPS_BIND_HOST_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_ADDRESS_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_BIND_HOST_KEY;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RATIS_MINIMUM_TIMEOUT_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RATIS_PORT_KEY;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RATIS_SNAPSHOT_DIR;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RATIS_STORAGE_DIR;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_SNAPSHOT_DIFF_DB_DIR;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_SERVER_DEFAULT_REPLICATION_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_SERVER_DEFAULT_REPLICATION_TYPE_KEY;
@@ -51,24 +67,36 @@
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.Duration;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import org.apache.hadoop.hdds.client.ReplicationFactor;
import org.apache.hadoop.hdds.client.ReplicationType;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.scm.proxy.SCMClientConfig;
+import org.apache.hadoop.hdds.scm.server.SCMStorageConfig;
+import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
+import org.apache.hadoop.hdds.utils.IOUtils;
+import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
+import org.apache.hadoop.ozone.OzoneSecurityUtil;
+import org.apache.hadoop.ozone.common.Storage;
+import org.apache.hadoop.ozone.om.OMStorage;
+import org.apache.hadoop.ozone.om.OzoneManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Prepares local filesystem state and configuration for {@code ozone local}.
+ * Starts the SCM and OM portion of the {@code ozone local} runtime.
*
- * <p>This runtime intentionally stops at filesystem and configuration
- * preparation. Starting SCM, OM, datanodes, and S3 Gateway belongs to later
- * HDDS-15084/HDDS-15086 work.</p>
+ * <p>Datanodes, S3 Gateway, Recon, and end-to-end key writes are added by
+ * later local runtime tickets.</p>
*/
public final class LocalOzoneCluster implements LocalOzoneRuntime {
@@ -78,6 +106,11 @@ public final class LocalOzoneCluster implements
LocalOzoneRuntime {
static final String PORTS_STATE_FILE_NAME = "ports.properties";
private static final String METADATA_DIR_NAME = "metadata";
+ private static final String SCM_DIR_NAME = "scm";
+ private static final String OM_DIR_NAME = "om";
+ private static final String OZONE_METADATA_DIR_NAME = "ozone-metadata";
+ private static final String DATA_DIR_NAME = "data";
+ private static final String RATIS_DIR_NAME = "ratis";
private static final String SCM_CLIENT_PORT_KEY = "scm.client";
private static final String SCM_BLOCK_PORT_KEY = "scm.block";
private static final String SCM_DATANODE_PORT_KEY = "scm.datanode";
@@ -88,7 +121,11 @@ public final class LocalOzoneCluster implements
LocalOzoneRuntime {
private static final String SCM_GRPC_PORT_KEY = "scm.grpc";
private static final String OM_RPC_PORT_KEY = "om.rpc";
private static final String OM_HTTP_PORT_KEY = "om.http";
+ private static final String OM_HTTPS_PORT_KEY = "om.https";
private static final String OM_RATIS_PORT_KEY = "om.ratis";
+ private static final int LOCAL_RATIS_RPC_TIMEOUT_SECONDS = 1;
+ private static final long SCM_CLIENT_MAX_RETRY_TIMEOUT_MILLIS = 30_000;
+ private static final long READINESS_POLL_INTERVAL_MILLIS = 500;
private static final int MAX_PORT = 65_535;
@@ -103,13 +140,19 @@ public final class LocalOzoneCluster implements
LocalOzoneRuntime {
SCM_GRPC_PORT_KEY,
OM_RPC_PORT_KEY,
OM_HTTP_PORT_KEY,
+ OM_HTTPS_PORT_KEY,
OM_RATIS_PORT_KEY
};
private final LocalOzoneClusterConfig config;
private final OzoneConfiguration seedConfiguration;
+ private boolean closed;
private PreparedConfiguration preparedConfiguration;
+ private StorageContainerManager scm;
+ private OzoneManager om;
+ private boolean previousMetricsMiniClusterMode;
+ private boolean metricsMiniClusterModeEnabled;
public LocalOzoneCluster(LocalOzoneClusterConfig config,
OzoneConfiguration seedConfiguration) {
@@ -119,8 +162,30 @@ public LocalOzoneCluster(LocalOzoneClusterConfig config,
}
@Override
- public void start() throws IOException {
- prepareConfiguration();
+ public void start() throws Exception {
+ if (closed) {
+ throw new IOException("Local Ozone cluster is already closed.");
+ }
+ if (scm != null && om != null) {
+ return;
+ }
+ if (scm != null || om != null) {
+ throw new IOException("Local Ozone cluster is partially started.");
+ }
+
+ try {
+ enableSameJvmMetricsMode();
+ PreparedConfiguration prepared = prepareConfiguration();
+ initializeStorage(prepared.getConfiguration());
+ startScm(prepared.getConfiguration());
+ startOm(prepared.getConfiguration());
+ waitForScmAndOmReadiness(config.getStartupTimeout());
+ } catch (Exception ex) {
+ // Roll back without latching closed: the caller's close() still owns the
+ // ephemeral data dir lifecycle.
+ stopServices();
+ throw ex;
+ }
}
PreparedConfiguration prepareConfiguration() throws IOException {
@@ -151,14 +216,12 @@ public String getDisplayHost() {
@Override
public int getScmPort() {
- return preparedConfiguration == null ? config.getScmPort()
- : preparedConfiguration.getScmPort();
+ return scm.getClientRpcAddress().getPort();
}
@Override
public int getOmPort() {
- return preparedConfiguration == null ? config.getOmPort()
- : preparedConfiguration.getOmPort();
+ return om.getOmRpcServerAddr().getPort();
}
@Override
@@ -173,14 +236,57 @@ public String getS3Endpoint() {
@Override
public void close() throws IOException {
- // Ephemeral mode owns the data directory lifecycle for short-lived runs.
- if (config.isEphemeral()) {
- deleteDirectory(config.getDataDir());
+ if (closed) {
+ return;
+ }
+ closed = true;
+
+ try {
+ stopServices();
+ } finally {
+ // Ephemeral mode owns the data directory lifecycle for short-lived runs.
+ if (config.isEphemeral()) {
+ try {
+ deleteDirectory(config.getDataDir());
+ } catch (IOException ex) {
+ LOG.warn("Failed to delete local Ozone data dir {} during shutdown.",
+ config.getDataDir(), ex);
+ }
+ }
+ }
+ }
+
+ private void stopServices() {
+ try {
+ // Shutdown is best-effort so one failed service cannot leak the other.
+ IOUtils.closeQuietly(this::stopOm, this::stopScm);
+ } finally {
+ restoreSameJvmMetricsMode();
+ }
+ }
+
+ private void stopOm() {
+ OzoneManager service = om;
+ om = null;
+ if (service != null && service.stop()) {
+ service.join();
+ }
+ }
+
+ private void stopScm() {
+ StorageContainerManager service = scm;
+ scm = null;
+ if (service != null) {
+ service.stop();
+ service.join();
}
}
private void configureLocalDefaults(OzoneConfiguration conf) {
conf.set(OZONE_METADATA_DIRS, metadataDir().toString());
+ // SCM and OM share one configuration and JVM, so the Jetty base dir is a
+ // single service-neutral location; Jetty keeps per-context temp dirs.
+ conf.setIfUnset(OZONE_HTTP_BASEDIR, metadataDir() + SERVER_DIR);
conf.set(OZONE_REPLICATION, ReplicationFactor.ONE.name());
conf.set(OZONE_REPLICATION_TYPE, ReplicationType.STAND_ALONE.name());
conf.set(OZONE_SERVER_DEFAULT_REPLICATION_KEY,
ReplicationFactor.ONE.name());
@@ -190,6 +296,15 @@ private void configureLocalDefaults(OzoneConfiguration
conf) {
conf.setBoolean(HDDS_SCM_SAFEMODE_PIPELINE_CREATION, false);
conf.setInt(HDDS_SCM_SAFEMODE_MIN_DATANODE,
Math.max(1, config.getDatanodes()));
+ conf.setTimeDuration(OZONE_OM_RATIS_MINIMUM_TIMEOUT_KEY,
+ LOCAL_RATIS_RPC_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ conf.set(HDDS_SCM_WAIT_TIME_AFTER_SAFE_MODE_EXIT, "3s");
+ conf.setIfUnset(OZONE_SCM_HA_RATIS_SERVER_RPC_FIRST_ELECTION_TIMEOUT,
+ "1s");
+
+ SCMClientConfig scmClientConfig = conf.getObject(SCMClientConfig.class);
+ scmClientConfig.setMaxRetryTimeout(SCM_CLIENT_MAX_RETRY_TIMEOUT_MILLIS);
+ conf.setFromObject(scmClientConfig);
}
private int configureScm(OzoneConfiguration conf,
@@ -234,9 +349,23 @@ private int configureScm(OzoneConfiguration conf,
conf.setInt(OZONE_SCM_GRPC_PORT_KEY, scmGrpcPort);
conf.setStrings(OZONE_SCM_NAMES,
address(config.getHost(), scmDatanodePort));
+ configureScmStorage(conf);
return scmClientPort;
}
+ private void configureScmStorage(OzoneConfiguration conf) throws IOException
{
+ Path scmDir = config.getDataDir().resolve(SCM_DIR_NAME);
+ Path scmMetadataDir = scmDir.resolve(OZONE_METADATA_DIR_NAME);
+ Files.createDirectories(scmDir.resolve(DATA_DIR_NAME));
+
+ conf.setIfUnset(OZONE_SCM_DB_DIRS,
+ scmDir.resolve(DATA_DIR_NAME).toString());
+ conf.setIfUnset(OZONE_SCM_HA_RATIS_STORAGE_DIR,
+ scmDir.resolve(RATIS_DIR_NAME).toString());
+ conf.setIfUnset(OZONE_SCM_HA_RATIS_SNAPSHOT_DIR,
+ scmMetadataDir.resolve(OZONE_RATIS_SNAPSHOT_DIR).toString());
+ }
+
private int configureOm(OzoneConfiguration conf,
PersistedPortState persistedPorts, PortAllocator portAllocator)
throws IOException {
@@ -244,16 +373,147 @@ private int configureOm(OzoneConfiguration conf,
config.getOmPort());
int omHttpPort = reservePort(portAllocator, persistedPorts,
OM_HTTP_PORT_KEY, 0);
+ int omHttpsPort = reservePort(portAllocator, persistedPorts,
+ OM_HTTPS_PORT_KEY, 0);
int omRatisPort = reservePort(portAllocator, persistedPorts,
OM_RATIS_PORT_KEY, 0);
conf.set(OZONE_OM_ADDRESS_KEY, address(config.getHost(), omRpcPort));
conf.set(OZONE_OM_HTTP_ADDRESS_KEY, address(config.getHost(), omHttpPort));
conf.set(OZONE_OM_HTTP_BIND_HOST_KEY, config.getBindHost());
+ conf.set(OZONE_OM_HTTPS_ADDRESS_KEY,
+ address(config.getHost(), omHttpsPort));
+ conf.set(OZONE_OM_HTTPS_BIND_HOST_KEY, config.getBindHost());
conf.setInt(OZONE_OM_RATIS_PORT_KEY, omRatisPort);
+ configureOmStorage(conf);
return omRpcPort;
}
+ private void configureOmStorage(OzoneConfiguration conf) throws IOException {
+ Path omDir = config.getDataDir().resolve(OM_DIR_NAME);
+ Path omMetadataDir = omDir.resolve(OZONE_METADATA_DIR_NAME);
+ Files.createDirectories(omDir.resolve(DATA_DIR_NAME));
+
+ conf.setIfUnset(OZONE_OM_DB_DIRS, omDir.resolve(DATA_DIR_NAME).toString());
+ conf.setIfUnset(OZONE_OM_RATIS_STORAGE_DIR,
+ omDir.resolve(RATIS_DIR_NAME).toString());
+ conf.setIfUnset(OZONE_OM_RATIS_SNAPSHOT_DIR,
+ omMetadataDir.resolve(OZONE_RATIS_SNAPSHOT_DIR).toString());
+ conf.setIfUnset(OZONE_OM_SNAPSHOT_DIFF_DB_DIR,
+ omMetadataDir.toString());
+ }
+
+ private void initializeStorage(OzoneConfiguration conf) throws IOException {
+ SCMStorageConfig scmStorage = new SCMStorageConfig(conf);
+ OMStorage omStorage = new OMStorage(conf);
+ String clusterId = resolveClusterId(scmStorage, omStorage);
+ String scmId = initializeScmStorage(conf, scmStorage, clusterId);
+ initializeOmStorage(conf, omStorage, clusterId, scmId);
+ }
+
+ private String resolveClusterId(SCMStorageConfig scmStorage,
+ OMStorage omStorage) throws IOException {
+ String scmClusterId = initializedClusterId(scmStorage);
+ String omClusterId = initializedClusterId(omStorage);
+ if (scmClusterId != null && omClusterId != null
+ && !scmClusterId.equals(omClusterId)) {
+ throw new IOException("Local Ozone SCM cluster ID " + scmClusterId
+ + " does not match OM cluster ID " + omClusterId + ".");
+ }
+ // Reuse an initialized component's cluster ID so local metadata survives
+ // restarting with a partially formatted data directory.
+ if (scmClusterId != null) {
+ return scmClusterId;
+ }
+ if (omClusterId != null) {
+ return omClusterId;
+ }
+ return UUID.randomUUID().toString();
+ }
+
+ private String initializeScmStorage(OzoneConfiguration conf,
+ SCMStorageConfig scmStorage, String clusterId) throws IOException {
+ if (scmStorage.getState() != INITIALIZED) {
+ requireStorageFormatting("SCM");
+ }
+ // scmInit formats storage before Ratis so a crash between the two is
+ // recoverable, and re-initializes Ratis for a half-formatted directory.
+ if (!StorageContainerManager.scmInit(conf, clusterId)) {
+ throw new IOException("SCM initialization failed for local Ozone; see
the SCM log for the cause.");
+ }
+ return new SCMStorageConfig(conf).getScmId();
+ }
+
+ private void initializeOmStorage(OzoneConfiguration conf, OMStorage
omStorage,
+ String clusterId, String scmId) throws IOException {
+ if (omStorage.getState() == INITIALIZED) {
+ if (!clusterId.equals(omStorage.getClusterID())) {
+ throw new IOException("Local Ozone OM cluster ID "
+ + omStorage.getClusterID() + " does not match SCM cluster ID "
+ + clusterId + ".");
+ }
+ return;
+ }
+
+ requireStorageFormatting("OM");
+ omStorage.setClusterId(clusterId);
+ omStorage.setOmId(UUID.randomUUID().toString());
+ if (OzoneSecurityUtil.isSecurityEnabled(conf)) {
+ OzoneManager.initializeSecurity(conf, omStorage, scmId);
+ }
+ omStorage.initialize();
+ }
+
+ private void requireStorageFormatting(String component) throws IOException {
+ if (config.getFormatMode() == LocalOzoneClusterConfig.FormatMode.NEVER) {
+ throw new IOException(component + " storage is not initialized. "
+ + "Format mode NEVER requires existing SCM and OM storage.");
+ }
+ }
+
+ private void startScm(OzoneConfiguration conf) throws Exception {
+ // Assign the field before start() so a failed start can still be rolled
+ // back by stopServices().
+ scm = StorageContainerManager.createSCM(conf);
+ scm.start();
+ }
+
+ private void startOm(OzoneConfiguration conf) throws Exception {
+ om = OzoneManager.createOm(conf);
+ om.start();
+ }
+
+ private void waitForScmAndOmReadiness(Duration timeout) throws Exception {
+ long deadlineNanos = System.nanoTime() + timeout.toNanos();
+ while (true) {
+ // Readiness is intentionally scoped to SCM and OM leadership until
+ // datanode and full safe-mode readiness are added in later tickets.
+ if (scm.checkLeader() && om.isLeaderReady()) {
+ return;
+ }
+ if (System.nanoTime() >= deadlineNanos) {
+ throw new TimeoutException("Timed out waiting " + timeout
+ + " for local SCM and OM leadership.");
+ }
+ Thread.sleep(READINESS_POLL_INTERVAL_MILLIS);
+ }
+ }
+
+ private void enableSameJvmMetricsMode() {
+ if (!metricsMiniClusterModeEnabled) {
+ previousMetricsMiniClusterMode =
DefaultMetricsSystem.inMiniClusterMode();
+ DefaultMetricsSystem.setMiniClusterMode(true);
+ metricsMiniClusterModeEnabled = true;
+ }
+ }
+
+ private void restoreSameJvmMetricsMode() {
+ if (metricsMiniClusterModeEnabled) {
+ DefaultMetricsSystem.setMiniClusterMode(previousMetricsMiniClusterMode);
+ metricsMiniClusterModeEnabled = false;
+ }
+ }
+
private void prepareStorageLayout() throws IOException {
Path dataDir = config.getDataDir();
if (Files.exists(dataDir) && !Files.isDirectory(dataDir)) {
@@ -334,7 +594,11 @@ private static String address(String host, int port) {
return host + ":" + port;
}
- private static void deleteDirectory(Path directory) {
+ private static String initializedClusterId(Storage storage) {
+ return storage.getState() == INITIALIZED ? storage.getClusterID() : null;
+ }
+
+ private static void deleteDirectory(Path directory) throws IOException {
if (!Files.exists(directory)) {
return;
}
@@ -344,8 +608,6 @@ private static void deleteDirectory(Path directory) {
for (Path path : deleteOrder) {
Files.deleteIfExists(path);
}
- } catch (IOException ex) {
- LOG.error("Failed to delete local Ozone directory {}", directory, ex);
}
}
diff --git
a/hadoop-ozone/tools/src/test/java/org/apache/hadoop/ozone/local/TestLocalOzoneCluster.java
b/hadoop-ozone/tools/src/test/java/org/apache/hadoop/ozone/local/TestLocalOzoneCluster.java
index c62c108ce71..8ecc3a9db98 100644
---
a/hadoop-ozone/tools/src/test/java/org/apache/hadoop/ozone/local/TestLocalOzoneCluster.java
+++
b/hadoop-ozone/tools/src/test/java/org/apache/hadoop/ozone/local/TestLocalOzoneCluster.java
@@ -59,18 +59,19 @@ class TestLocalOzoneCluster {
private Path tempDir;
@Test
- void startPreparesConfiguration() throws Exception {
+ void prepareConfigurationExposesPreparedPorts() throws Exception {
Path dataDir = tempDir.resolve("local-ozone");
LocalOzoneClusterConfig config =
LocalOzoneClusterConfig.builder(dataDir).build();
try (LocalOzoneCluster cluster = newCluster(config)) {
- cluster.start();
+ LocalOzoneCluster.PreparedConfiguration prepared =
+ cluster.prepareConfiguration();
assertTrue(Files.isDirectory(metadataDir(dataDir)));
assertTrue(Files.isRegularFile(portStateFile(dataDir)));
- assertTrue(cluster.getScmPort() > 0);
- assertTrue(cluster.getOmPort() > 0);
+ assertTrue(prepared.getScmPort() > 0);
+ assertTrue(prepared.getOmPort() > 0);
assertEquals(-1, cluster.getS3gPort());
assertEquals("", cluster.getS3Endpoint());
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]