chungen0126 commented on code in PR #10159:
URL: https://github.com/apache/ozone/pull/10159#discussion_r3186916581


##########
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/local/LocalOzoneCluster.java:
##########
@@ -0,0 +1,333 @@
+/*
+ * 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.apache.hadoop.hdds.HddsConfigKeys.HDDS_DATANODE_CLIENT_ADDRESS_KEY;
+import static 
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DATANODE_CLIENT_BIND_HOST_KEY;
+import static 
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DATANODE_HTTP_ADDRESS_KEY;
+import static 
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DATANODE_HTTP_BIND_HOST_KEY;
+import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_CONTAINER_RATIS_ENABLED_KEY;
+import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_KEY;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_CONTAINER_IPC_PORT;
+import static 
org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_CONTAINER_RATIS_ADMIN_PORT;
+import static 
org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_CONTAINER_RATIS_DATANODE_STORAGE_DIR;
+import static 
org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_CONTAINER_RATIS_DATASTREAM_PORT;
+import static 
org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_CONTAINER_RATIS_IPC_PORT;
+import static 
org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_CONTAINER_RATIS_SERVER_PORT;
+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 java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+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.metrics2.lib.DefaultMetricsSystem;
+import org.apache.hadoop.ozone.HddsDatanodeService;
+import org.apache.hadoop.ozone.container.replication.ReplicationServer;
+import org.apache.hadoop.ozone.local.LocalOzoneClusterConfig.FormatMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Starts a local in-process Ozone cluster for development and testing.
+ *
+ * <p>This implementation manages the lifecycle of datanodes (and eventually
+ * SCM, OM, S3G) running within a single JVM process.</p>
+ */
+public final class LocalOzoneCluster implements LocalOzoneRuntime {
+
+  private static final Logger LOG =
+      LoggerFactory.getLogger(LocalOzoneCluster.class);
+
+  private static final String[] NO_ARGS = new String[0];
+  private static final String PORTS_STATE_FILE = "ports.properties";
+
+  private final LocalOzoneClusterConfig config;
+  private final OzoneConfiguration seedConfiguration;
+  private final AtomicBoolean closed = new AtomicBoolean();
+  private final List<HddsDatanodeService> datanodes = new ArrayList<>();
+
+  private boolean previousMetricsMiniClusterMode;
+  private boolean metricsMiniClusterModeEnabled;
+
+  /**
+   * Creates a new local Ozone cluster.
+   *
+   * @param config the cluster configuration
+   * @param seedConfiguration the base Ozone configuration
+   */
+  public LocalOzoneCluster(LocalOzoneClusterConfig config,
+      OzoneConfiguration seedConfiguration) {
+    this.config = Objects.requireNonNull(config, "config");
+    this.seedConfiguration = new OzoneConfiguration(
+        Objects.requireNonNull(seedConfiguration, "seedConfiguration"));
+  }
+
+  @Override
+  public void start() throws Exception {
+    enableMiniClusterMetricsMode();
+    prepareDataDirectory();
+
+    OzoneConfiguration baseConf = prepareBaseConfiguration();
+    startDatanodes(baseConf);
+
+    // TODO: Add SCM/OM startup and waitForClusterToBeReady() in future tasks
+    LOG.info("Local Ozone cluster started with {} datanode(s)",
+        datanodes.size());
+  }
+
+  @Override
+  public String getDisplayHost() {
+    return "0.0.0.0".equals(config.getHost())
+        ? LocalOzoneClusterConfig.DEFAULT_HOST
+        : config.getHost();
+  }
+
+  /**
+   * Returns the number of running datanodes.
+   */
+  public int getDatanodeCount() {
+    return datanodes.size();
+  }
+
+  @Override
+  public int getScmPort() {
+    // TODO: Return actual SCM port when SCM is implemented
+    return -1;
+  }
+
+  @Override
+  public int getOmPort() {
+    // TODO: Return actual OM port when OM is implemented
+    return -1;
+  }
+
+  @Override
+  public int getS3gPort() {
+    // TODO: Return actual S3G port when S3G is implemented
+    return -1;
+  }
+
+  @Override
+  public String getS3Endpoint() {
+    // TODO: Return actual S3 endpoint when S3G is implemented
+    return "";
+  }
+
+  @Override
+  public void close() throws Exception {
+    if (!closed.compareAndSet(false, true)) {
+      return;
+    }
+
+    try {
+      stopDatanodes();
+
+      if (config.isEphemeral()) {
+        deleteDirectory(config.getDataDir());
+        LOG.info("Deleted ephemeral data directory: {}", config.getDataDir());
+      }
+    } finally {
+      restoreMetricsMode();
+    }
+  }
+
+  /**
+   * Prepares the data directory, formatting if needed.
+   */
+  private void prepareDataDirectory() throws IOException {
+    if (config.getFormatMode() == FormatMode.ALWAYS) {
+      deleteDirectory(config.getDataDir());
+    }
+    Files.createDirectories(config.getDataDir());
+  }
+
+  /**
+   * Prepares the base Ozone configuration with local-safe defaults.
+   */
+  OzoneConfiguration prepareBaseConfiguration() throws IOException {
+    OzoneConfiguration conf = new OzoneConfiguration(seedConfiguration);
+
+    // Local-safe replication defaults: single replica, no Ratis
+    conf.set(OZONE_REPLICATION, ReplicationFactor.ONE.name());
+    conf.set(OZONE_REPLICATION_TYPE, ReplicationType.STAND_ALONE.name());
+    conf.setBoolean(HDDS_CONTAINER_RATIS_ENABLED_KEY, false);
+
+    // Root metadata directory
+    Path metadataDir = Files.createDirectories(
+        config.getDataDir().resolve("metadata"));
+    conf.set(OZONE_METADATA_DIRS, metadataDir.toString());
+
+    return conf;
+  }
+
+  /**
+   * Starts the configured number of datanodes.
+   */
+  private void startDatanodes(OzoneConfiguration baseConf) throws IOException {
+    PersistedPorts persistedPorts = PersistedPorts.load(
+        config.getDataDir().resolve(PORTS_STATE_FILE));
+
+    for (int index = 0; index < config.getDatanodes(); index++) {
+      OzoneConfiguration dnConf = createDatanodeConfiguration(

Review Comment:
   Thanks @YutaLin for the patch. Could we instantiate the `PortAllocator` here 
so that all datanodes can share the same state and avoid binding to the same 
ports.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to