You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by ca...@apache.org on 2023/02/03 08:09:00 UTC

[ozone] branch master updated: HDDS-3591. ozone.administrators support dynamic changes through the cli. (#4221)

This is an automated email from the ASF dual-hosted git repository.

captainzmc 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 15cc89b602 HDDS-3591. ozone.administrators support dynamic changes through the cli. (#4221)
15cc89b602 is described below

commit 15cc89b6024948faebd9ec89a917bc07ed851581
Author: Hongbing Wang <28...@qq.com>
AuthorDate: Fri Feb 3 16:08:55 2023 +0800

    HDDS-3591. ozone.administrators support dynamic changes through the cli. (#4221)
    
    * HDDS-3591. ozone.administrators support dynamic changes through the cli.
---
 .../docs/content/feature/Reconfigurability.md      |  65 ++++++++
 .../docs/content/feature/Reconfigurability.zh.md   |  62 +++++++
 .../hadoop/hdds/protocol/ReconfigureProtocol.java  |  56 +++++++
 .../ReconfigureProtocolClientSideTranslatorPB.java | 178 +++++++++++++++++++++
 .../hdds/protocolPB/ReconfigureProtocolPB.java     |  36 +++++
 .../ReconfigureProtocolServerSideTranslatorPB.java | 128 +++++++++++++++
 .../org/apache/hadoop/hdds/server/OzoneAdmins.java |  13 +-
 .../hadoop/hdds/server/ServiceRuntimeInfoImpl.java |  24 ++-
 hadoop-hdds/interface-client/pom.xml               |   2 +
 .../src/main/proto/ReconfigureProtocol.proto       |  80 +++++++++
 .../hadoop/ozone/reconfig/TestOmReconfigure.java   | 100 ++++++++++++
 .../apache/hadoop/ozone/om/OzoneConfigUtil.java    |  14 +-
 .../org/apache/hadoop/ozone/om/OzoneManager.java   | 104 +++++++++++-
 .../ozone/admin/reconfig/ReconfigureCommands.java  |  75 +++++++++
 .../reconfig/ReconfigurePropertiesSubcommand.java  |  53 ++++++
 .../admin/reconfig/ReconfigureStartSubcommand.java |  49 ++++++
 .../reconfig/ReconfigureStatusSubcommand.java      |  89 +++++++++++
 .../admin/reconfig/ReconfigureSubCommandUtil.java  |  46 ++++++
 .../hadoop/ozone/admin/reconfig/package-info.java  |  23 +++
 19 files changed, 1181 insertions(+), 16 deletions(-)

diff --git a/hadoop-hdds/docs/content/feature/Reconfigurability.md b/hadoop-hdds/docs/content/feature/Reconfigurability.md
new file mode 100644
index 0000000000..c822b75096
--- /dev/null
+++ b/hadoop-hdds/docs/content/feature/Reconfigurability.md
@@ -0,0 +1,65 @@
+---
+title: "Reconfigurability"
+weight: 11
+menu:
+   main:
+      parent: Features
+summary: Dynamic reloading configuration.
+---
+<!---
+  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.
+-->
+
+Ozone supports dynamic loading of certain properties without restarting the service. 
+If a property is reconfigurable, you can modify it in the configuration file (`ozone-site.xml`) and then invoke the command to flush it to memory.
+
+## OM Reconfigurability
+
+**Reconfigurable properties**
+key | description
+-----------------------------------|-----------------------------------------
+ozone.administrators | OM startup user will be added to admin by default
+
+```shell
+ozone admin reconfig --address=<ip:port> start|status|properties
+```
+
+The meaning of command options:
+- **--address**: RPC address for one server
+- Three operations are provided:
+  - `start`:      Execute the reconfig operation asynchronously
+  - `status`:     Check reconfig status
+  - `properties`: List reconfigurable properties
+
+>For example, modify `ozone.administrators` in ozone-site.xml and execute:
+>
+> $ `ozone admin reconfig --address=hadoop1:9862 start`<br>
+Started OM reconfiguration task on node [hadoop1:9862].
+>
+>$ `ozone admin reconfig --address=hadoop1:9862 status`<br>
+Reconfiguring status for node [hadoop1:9862]: started at Wed Dec 28 19:04:44 CST 2022 and finished at Wed Dec 28 19:04:44 CST 2022.<br>
+SUCCESS: Changed property ozone.administrators<br>
+From: "hadoop"<br>
+To: "hadoop,bigdata"
+>
+> $ `ozone admin reconfig -address=hadoop1:9862 properties`<br>
+Node [hadoop1:9862] Reconfigurable properties:<br>
+ozone.administrators
+
+
+## SCM Reconfigurability
+
+Add later if needed.
\ No newline at end of file
diff --git a/hadoop-hdds/docs/content/feature/Reconfigurability.zh.md b/hadoop-hdds/docs/content/feature/Reconfigurability.zh.md
new file mode 100644
index 0000000000..177878a522
--- /dev/null
+++ b/hadoop-hdds/docs/content/feature/Reconfigurability.zh.md
@@ -0,0 +1,62 @@
+---
+title: "动态加载配置"
+weight: 8
+summary: 动态加载配置
+menu:
+   main:
+      parent: 特性
+---
+<!---
+  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.
+-->
+
+Ozone支持在不重启服务的情况下动态加载某些配置。如果某个属性支持热加载,就可以先在配置文件(`ozone-site.xml`)中修改它然后调用命令使之刷新到内存。
+
+## OM动态配置
+
+**支持动态加载的属性**
+配置项 | 描述
+-----------------------------------|-----------------------------------------
+ozone.administrators | OM启动用户将默认成为一个管理员
+
+```shell
+ozone admin reconfig --address=<ip:port> start|status|properties
+```
+
+命令项的含义:
+- **--address**: 一台服务所在的主机与客户端通信的RPC地址
+- 提供3中操作:
+    - `start`:      开始异步执行动态加载配置
+    - `status`:     查看最近一次动态加载的状态
+    - `properties`: 列出支持动态加载的配置项
+
+>例如, 在`ozone-site.xml`文件中修改`ozone.administrators`的值并执行:
+>
+> $ `ozone admin reconfig --address=hadoop1:9862 start`<br>
+Started reconfiguration task on node [hadoop1:9862].
+>
+>$ `ozone admin reconfig --address=hadoop1:9862 status`<br>
+Reconfiguring status for node [hadoop1:9862]: started at Wed Dec 28 19:04:44 CST 2022 and finished at Wed Dec 28 19:04:44 CST 2022.<br>
+SUCCESS: Changed property ozone.administrators<br>
+From: "hadoop"<br>
+To: "hadoop,bigdata"
+>
+> $ `ozone admin reconfig --address=hadoop1:9862 properties`<br>
+Node [hadoop1:9862] Reconfigurable properties:<br>
+ozone.administrators
+
+## SCM动态配置
+
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/ReconfigureProtocol.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/ReconfigureProtocol.java
new file mode 100644
index 0000000000..8d0dfbcabe
--- /dev/null
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/ReconfigureProtocol.java
@@ -0,0 +1,56 @@
+/**
+ * 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.hdds.protocol;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.ReconfigurationTaskStatus;
+import org.apache.hadoop.io.retry.Idempotent;
+
+import java.io.IOException;
+import java.util.List;
+
+/**********************************************************************
+ * ReconfigureProtocol is used by ozone admin to reload configuration.
+ **********************************************************************/
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+public interface ReconfigureProtocol {
+
+  long VERSIONID = 1L;
+
+  /**
+   * Asynchronously reload configuration on disk and apply changes.
+   */
+  @Idempotent
+  void startReconfigure() throws IOException;
+
+  /**
+   * Get the status of the previously issued reconfig task.
+   * @see ReconfigurationTaskStatus
+   */
+  @Idempotent
+  ReconfigurationTaskStatus getReconfigureStatus() throws IOException;
+
+  /**
+   * Get a list of allowed properties for reconfiguration.
+   */
+  @Idempotent
+  List<String> listReconfigureProperties() throws IOException;
+}
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolClientSideTranslatorPB.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolClientSideTranslatorPB.java
new file mode 100644
index 0000000000..30f4aa3740
--- /dev/null
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolClientSideTranslatorPB.java
@@ -0,0 +1,178 @@
+/**
+ * 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.hdds.protocolPB;
+
+import com.google.common.collect.Maps;
+import com.google.protobuf.RpcController;
+import com.google.protobuf.ServiceException;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.conf.ReconfigurationTaskStatus;
+import org.apache.hadoop.conf.ReconfigurationUtil.PropertyChange;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.ReconfigureProtocol;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.GetConfigurationChangeProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.GetReconfigureStatusResponseProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.GetReconfigureStatusRequestProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.ListReconfigurePropertiesRequestProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.ListReconfigurePropertiesResponseProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.StartReconfigureRequestProto;
+import org.apache.hadoop.hdds.utils.LegacyHadoopConfigurationSource;
+import org.apache.hadoop.ipc.ProtobufHelper;
+import org.apache.hadoop.ipc.ProtobufRpcEngine;
+import org.apache.hadoop.ipc.ProtocolMetaInterface;
+import org.apache.hadoop.ipc.ProtocolTranslator;
+import org.apache.hadoop.ipc.RPC;
+import org.apache.hadoop.ipc.RpcClientUtil;
+import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * This class is the client side translator to translate the requests made on
+ * {@link ReconfigureProtocol} interfaces to the RPC server implementing
+ * {@link ReconfigureProtocolPB}.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Stable
+public class ReconfigureProtocolClientSideTranslatorPB implements
+    ProtocolMetaInterface, ReconfigureProtocol, ProtocolTranslator, Closeable {
+  public static final Logger LOG = LoggerFactory
+      .getLogger(ReconfigureProtocolClientSideTranslatorPB.class);
+
+  private static final RpcController NULL_CONTROLLER = null;
+  private static final StartReconfigureRequestProto VOID_START_RECONFIG =
+      StartReconfigureRequestProto.newBuilder().build();
+
+  private static final ListReconfigurePropertiesRequestProto
+      VOID_LIST_RECONFIGURABLE_PROPERTIES =
+      ListReconfigurePropertiesRequestProto.newBuilder().build();
+
+  private static final GetReconfigureStatusRequestProto
+      VOID_GET_RECONFIG_STATUS =
+      GetReconfigureStatusRequestProto.newBuilder().build();
+
+  private final ReconfigureProtocolPB rpcProxy;
+
+  public ReconfigureProtocolClientSideTranslatorPB(InetSocketAddress addr,
+      UserGroupInformation ugi, OzoneConfiguration conf)
+      throws IOException {
+    rpcProxy = createReconfigureProtocolProxy(addr, ugi, conf);
+  }
+
+  static ReconfigureProtocolPB createReconfigureProtocolProxy(
+      InetSocketAddress addr, UserGroupInformation ugi,
+      OzoneConfiguration conf) throws IOException {
+
+    RPC.setProtocolEngine(OzoneConfiguration.of(conf),
+        ReconfigureProtocolPB.class, ProtobufRpcEngine.class);
+    Configuration hadoopConf = LegacyHadoopConfigurationSource
+        .asHadoopConfiguration(conf);
+    return RPC.getProtocolProxy(
+            ReconfigureProtocolPB.class,
+            RPC.getProtocolVersion(ReconfigureProtocolPB.class),
+            addr, ugi, hadoopConf,
+            NetUtils.getDefaultSocketFactory(hadoopConf))
+        .getProxy();
+  }
+
+  @Override
+  public void close() throws IOException {
+    RPC.stopProxy(rpcProxy);
+  }
+
+  @Override
+  public Object getUnderlyingProxyObject() {
+    return rpcProxy;
+  }
+
+  @Override
+  public void startReconfigure() throws IOException {
+    try {
+      rpcProxy.startReconfigure(NULL_CONTROLLER, VOID_START_RECONFIG);
+    } catch (ServiceException e) {
+      throw ProtobufHelper.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public ReconfigurationTaskStatus getReconfigureStatus()
+      throws IOException {
+    try {
+      GetReconfigureStatusResponseProto response = rpcProxy
+          .getReconfigureStatus(NULL_CONTROLLER, VOID_GET_RECONFIG_STATUS);
+      return getReconfigureStatus(response);
+    } catch (ServiceException e) {
+      throw ProtobufHelper.getRemoteException(e);
+    }
+  }
+
+  private ReconfigurationTaskStatus getReconfigureStatus(
+      GetReconfigureStatusResponseProto response) {
+    Map<PropertyChange, Optional<String>> statusMap = null;
+    long startTime;
+    long endTime = 0;
+
+    startTime = response.getStartTime();
+    if (response.hasEndTime()) {
+      endTime = response.getEndTime();
+    }
+    if (response.getChangesCount() > 0) {
+      statusMap = Maps.newHashMap();
+      for (GetConfigurationChangeProto change : response.getChangesList()) {
+        PropertyChange pc = new PropertyChange(change.getName(),
+            change.getNewValue(), change.getOldValue());
+        String errorMessage = null;
+        if (change.hasErrorMessage()) {
+          errorMessage = change.getErrorMessage();
+        }
+        statusMap.put(pc, Optional.ofNullable(errorMessage));
+      }
+    }
+    return new ReconfigurationTaskStatus(startTime, endTime, statusMap);
+  }
+  
+  @Override
+  public List<String> listReconfigureProperties() throws IOException {
+    ListReconfigurePropertiesResponseProto response;
+    try {
+      response = rpcProxy.listReconfigureProperties(NULL_CONTROLLER,
+          VOID_LIST_RECONFIGURABLE_PROPERTIES);
+      return response.getNameList();
+    } catch (ServiceException e) {
+      throw ProtobufHelper.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public boolean isMethodSupported(String methodName) throws IOException {
+    return RpcClientUtil.isMethodSupported(rpcProxy,
+        ReconfigureProtocolPB.class,
+        RPC.RpcKind.RPC_PROTOCOL_BUFFER,
+        RPC.getProtocolVersion(ReconfigureProtocolPB.class),
+        methodName);
+  }
+}
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolPB.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolPB.java
new file mode 100644
index 0000000000..e1702ce0ad
--- /dev/null
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolPB.java
@@ -0,0 +1,36 @@
+/**
+ * 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.hdds.protocolPB;
+
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.ReconfigureProtocolService;
+import org.apache.hadoop.hdds.scm.ScmConfig;
+import org.apache.hadoop.ipc.ProtocolInfo;
+import org.apache.hadoop.security.KerberosInfo;
+
+/**
+ * Protocol that clients use to communicate with the OM/SCM to do
+ * reconfiguration on the fly.
+ */
+@ProtocolInfo(
+    protocolName = "org.apache.hadoop.hdds.protocol.ReconfigureProtocol",
+    protocolVersion = 1)
+@KerberosInfo(serverPrincipal = ScmConfig.ConfigStrings
+    .HDDS_SCM_KERBEROS_PRINCIPAL_KEY)
+public interface ReconfigureProtocolPB extends
+    ReconfigureProtocolService.BlockingInterface {
+}
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolServerSideTranslatorPB.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolServerSideTranslatorPB.java
new file mode 100644
index 0000000000..5273c91513
--- /dev/null
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/ReconfigureProtocolServerSideTranslatorPB.java
@@ -0,0 +1,128 @@
+/**
+ * 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.hdds.protocolPB;
+
+import com.google.protobuf.RpcController;
+import com.google.protobuf.ServiceException;
+import org.apache.hadoop.conf.ReconfigurationTaskStatus;
+import org.apache.hadoop.conf.ReconfigurationUtil.PropertyChange;
+import org.apache.hadoop.hdds.protocol.ReconfigureProtocol;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.GetConfigurationChangeProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.GetReconfigureStatusRequestProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.GetReconfigureStatusResponseProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.ListReconfigurePropertiesRequestProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.ListReconfigurePropertiesResponseProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.StartReconfigureRequestProto;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.StartReconfigureResponseProto;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * This class is used on the server side. Calls come across the wire for the
+ * for protocol {@link ReconfigureProtocolPB}.
+ * This class translates the PB data types
+ * to the native data types used inside the NN/DN as specified in the generic
+ * ReconfigureProtocol.
+ */
+public class ReconfigureProtocolServerSideTranslatorPB implements
+    ReconfigureProtocolPB {
+
+  private final ReconfigureProtocol impl;
+
+  private static final StartReconfigureResponseProto START_RECONFIG_RESP =
+      StartReconfigureResponseProto.newBuilder().build();
+
+  public ReconfigureProtocolServerSideTranslatorPB(
+      ReconfigureProtocol impl) {
+    this.impl = impl;
+  }
+
+  @Override
+  public StartReconfigureResponseProto startReconfigure(
+      RpcController controller, StartReconfigureRequestProto request)
+      throws ServiceException {
+    try {
+      impl.startReconfigure();
+    } catch (IOException e) {
+      throw new ServiceException(e);
+    }
+    return START_RECONFIG_RESP;
+  }
+
+  @Override
+  public ListReconfigurePropertiesResponseProto listReconfigureProperties(
+      RpcController controller, ListReconfigurePropertiesRequestProto request)
+      throws ServiceException {
+    try {
+      return listReconfigureProperties(impl.listReconfigureProperties());
+    } catch (IOException e) {
+      throw new ServiceException(e);
+    }
+  }
+
+  private ListReconfigurePropertiesResponseProto listReconfigureProperties(
+      List<String> reconfigurableProperties) {
+    ListReconfigurePropertiesResponseProto.Builder builder =
+        ListReconfigurePropertiesResponseProto.newBuilder();
+    builder.addAllName(reconfigurableProperties);
+    return builder.build();
+  }
+
+  @Override
+  public GetReconfigureStatusResponseProto getReconfigureStatus(
+      RpcController unused, GetReconfigureStatusRequestProto request)
+      throws ServiceException {
+    try {
+      return getReconfigureStatus(impl.getReconfigureStatus());
+    } catch (IOException e) {
+      throw new ServiceException(e);
+    }
+  }
+
+  private GetReconfigureStatusResponseProto getReconfigureStatus(
+      ReconfigurationTaskStatus status) {
+    GetReconfigureStatusResponseProto.Builder builder =
+        GetReconfigureStatusResponseProto.newBuilder();
+
+    builder.setStartTime(status.getStartTime());
+    if (status.stopped()) {
+      builder.setEndTime(status.getEndTime());
+      assert status.getStatus() != null;
+      for (Map.Entry<PropertyChange, Optional<String>> result : status
+          .getStatus().entrySet()) {
+        GetConfigurationChangeProto.Builder changeBuilder = 
+            GetConfigurationChangeProto.newBuilder();
+        PropertyChange change = result.getKey();
+        changeBuilder.setName(change.prop);
+        changeBuilder.setOldValue(change.oldVal != null ? change.oldVal : "");
+        if (change.newVal != null) {
+          changeBuilder.setNewValue(change.newVal);
+        }
+        if (result.getValue().isPresent()) {
+          // Get full stack trace.
+          changeBuilder.setErrorMessage(result.getValue().get());
+        }
+        builder.addChanges(changeBuilder);
+      }
+    }
+    return builder.build();
+  }
+}
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java
index 342e10a1b9..a705783d19 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneAdmins.java
@@ -38,7 +38,7 @@ public class OzoneAdmins {
   /**
    * Ozone super user / admin username list.
    */
-  private final Set<String> adminUsernames;
+  private volatile Set<String> adminUsernames;
   /**
    * Ozone super user / admin group list.
    */
@@ -50,9 +50,7 @@ public class OzoneAdmins {
 
   public OzoneAdmins(Collection<String> adminUsernames,
       Collection<String> adminGroups) {
-    this.adminUsernames = adminUsernames != null ?
-        Collections.unmodifiableSet(new LinkedHashSet<>(adminUsernames)) :
-        Collections.emptySet();
+    setAdminUsernames(adminUsernames);
     this.adminGroups = adminGroups != null ?
         Collections.unmodifiableSet(new LinkedHashSet<>(adminGroups)) :
         Collections.emptySet();
@@ -84,4 +82,11 @@ public class OzoneAdmins {
   public Set<String> getAdminUsernames() {
     return adminUsernames;
   }
+
+  public void setAdminUsernames(Collection<String> adminUsernames) {
+    this.adminUsernames = adminUsernames != null ?
+        Collections.unmodifiableSet(new LinkedHashSet<>(adminUsernames)) :
+        Collections.emptySet();
+  }
+
 }
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/ServiceRuntimeInfoImpl.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/ServiceRuntimeInfoImpl.java
index 2dffc6f902..42d63dffe0 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/ServiceRuntimeInfoImpl.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/ServiceRuntimeInfoImpl.java
@@ -17,13 +17,20 @@
 
 package org.apache.hadoop.hdds.server;
 
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.conf.ReconfigurableBase;
+import org.apache.hadoop.conf.ReconfigurationException;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.utils.VersionInfo;
 
+import java.util.Collection;
+
 /**
  * Helper base class to report the standard version and runtime information.
  *
  */
-public class ServiceRuntimeInfoImpl implements ServiceRuntimeInfo {
+public class ServiceRuntimeInfoImpl extends ReconfigurableBase
+    implements ServiceRuntimeInfo {
 
   private long startedTimeInMillis;
   private final VersionInfo versionInfo;
@@ -57,4 +64,19 @@ public class ServiceRuntimeInfoImpl implements ServiceRuntimeInfo {
     startedTimeInMillis = System.currentTimeMillis();
   }
 
+  @Override
+  protected Configuration getNewConf() {
+    return new OzoneConfiguration();
+  }
+
+  @Override
+  public Collection<String> getReconfigurableProperties() {
+    return null;
+  }
+
+  @Override
+  protected String reconfigurePropertyImpl(String property, String newVal)
+      throws ReconfigurationException {
+    throw new ReconfigurationException();
+  }
 }
diff --git a/hadoop-hdds/interface-client/pom.xml b/hadoop-hdds/interface-client/pom.xml
index eac95a00e1..3fe96d3d90 100644
--- a/hadoop-hdds/interface-client/pom.xml
+++ b/hadoop-hdds/interface-client/pom.xml
@@ -103,6 +103,7 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
               <protoSourceRoot>${basedir}/src/main/proto/</protoSourceRoot>
               <includes>
                 <include>hdds.proto</include>
+                <include>ReconfigureProtocol.proto</include>
               </includes>
               <outputDirectory>target/generated-sources/java</outputDirectory>
               <clearOutputDirectory>false</clearOutputDirectory>
@@ -121,6 +122,7 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
               <protoSourceRoot>${basedir}/src/main/proto/</protoSourceRoot>
               <includes>
                 <include>hdds.proto</include>
+                <include>ReconfigureProtocol.proto</include>
               </includes>
               <outputDirectory>target/generated-sources/java/proto3</outputDirectory>
               <clearOutputDirectory>false</clearOutputDirectory>
diff --git a/hadoop-hdds/interface-client/src/main/proto/ReconfigureProtocol.proto b/hadoop-hdds/interface-client/src/main/proto/ReconfigureProtocol.proto
new file mode 100644
index 0000000000..4bc78c3056
--- /dev/null
+++ b/hadoop-hdds/interface-client/src/main/proto/ReconfigureProtocol.proto
@@ -0,0 +1,80 @@
+/**
+ * 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.
+ */
+
+/**
+ * These .proto interfaces are private and unstable.
+ * Please see http://wiki.apache.org/hadoop/Compatibility
+ * for what changes are allowed for an *unstable* .proto interface.
+ */
+
+// This file contains protocol buffers that are used to reconfigure OM and SCM
+// by ozone admin.
+
+syntax="proto2";
+option java_package = "org.apache.hadoop.hdds.protocol.proto";
+option java_outer_classname = "ReconfigureProtocolProtos";
+option java_generic_services = true;
+option java_generate_equals_and_hash = true;
+package hadoop.hdds;
+
+/** Asks OM/SCM to reload configuration file. */
+message StartReconfigureRequestProto {
+}
+
+message StartReconfigureResponseProto {
+}
+
+/** Query the running status of reconfiguration process */
+message GetReconfigureStatusRequestProto {
+}
+
+message GetConfigurationChangeProto {
+  required string name = 1;
+  required string oldValue = 2;
+  optional string newValue = 3;
+  optional string errorMessage = 4;  // It is empty if success.
+}
+
+message GetReconfigureStatusResponseProto {
+  required int64 startTime = 1;
+  optional int64 endTime = 2;
+  repeated GetConfigurationChangeProto changes = 3;
+}
+
+/** Query the reconfigurable properties on OM/SCM. */
+message ListReconfigurePropertiesRequestProto {
+}
+
+message ListReconfigurePropertiesResponseProto {
+  repeated string name = 1;
+}
+
+/**
+ * Protocol used from client to the OM.
+ * See the request and response for details of rpc call.
+ */
+service ReconfigureProtocolService {
+  rpc getReconfigureStatus(GetReconfigureStatusRequestProto)
+      returns(GetReconfigureStatusResponseProto);
+
+  rpc startReconfigure(StartReconfigureRequestProto)
+      returns(StartReconfigureResponseProto);
+
+  rpc listReconfigureProperties(ListReconfigurePropertiesRequestProto)
+      returns(ListReconfigurePropertiesResponseProto);
+}
\ No newline at end of file
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/reconfig/TestOmReconfigure.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/reconfig/TestOmReconfigure.java
new file mode 100644
index 0000000000..3ce6cab55b
--- /dev/null
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/reconfig/TestOmReconfigure.java
@@ -0,0 +1,100 @@
+package org.apache.hadoop.ozone.reconfig;
+
+/*
+ * 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.
+ *
+ */
+
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import java.util.UUID;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.MiniOzoneCluster;
+import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+
+/**
+ * Tests for OM Reconfigure.
+ */
+public class TestOmReconfigure {
+
+  /**
+   * Set a timeout for each test.
+   */
+  @Rule
+  public Timeout timeout = new Timeout(300000);
+  private OzoneConfiguration conf;
+  private MiniOzoneHAClusterImpl cluster;
+  private OzoneManager ozoneManager;
+
+  /**
+   * Create a MiniDFSCluster for testing.
+   */
+  @Before
+  public void setup() throws Exception {
+
+    conf = new OzoneConfiguration();
+    String omServiceId = UUID.randomUUID().toString();
+    cluster = (MiniOzoneHAClusterImpl) MiniOzoneCluster.newOMHABuilder(conf)
+        .setClusterId(UUID.randomUUID().toString())
+        .setScmId(UUID.randomUUID().toString())
+        .setOMServiceId(omServiceId)
+        .setNumOfOzoneManagers(3)
+        .setNumDatanodes(1)
+        .build();
+
+    cluster.waitForClusterToBeReady();
+    ozoneManager = cluster.getOzoneManager();
+
+  }
+
+  /**
+   * Shutdown MiniDFSCluster.
+   */
+  @After
+  public void shutdown() {
+    if (cluster != null) {
+      cluster.shutdown();
+    }
+  }
+
+  /**
+   * Test reconfigure om "ozone.administrators".
+   */
+  @Test
+  public void testOmAdminUsersReconfigure() throws Exception {
+    String userA = "mockUserA";
+    String userB = "mockUserB";
+    conf.set(OZONE_ADMINISTRATORS, userA);
+    ozoneManager.reconfigurePropertyImpl(OZONE_ADMINISTRATORS, userA);
+    assertTrue(userA + " should be an admin user",
+        ozoneManager.getOmAdminUsernames().contains(userA));
+
+    conf.set(OZONE_ADMINISTRATORS, userB);
+    ozoneManager.reconfigurePropertyImpl(OZONE_ADMINISTRATORS, userB);
+    assertFalse(userA + " should NOT be an admin user",
+        ozoneManager.getOmAdminUsernames().contains(userA));
+    assertTrue(userB + " should be an admin user",
+        ozoneManager.getOmAdminUsernames().contains(userB));
+  }
+
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java
index b73a3b7173..ce6c59dd72 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneConfigUtil.java
@@ -44,14 +44,14 @@ public final class OzoneConfigUtil {
 
   /**
    * Return list of OzoneAdministrators from config.
+   * The service startup user will default to an admin.
    */
-  static Collection<String> getOzoneAdminsFromConfig(OzoneConfiguration conf)
-          throws IOException {
-    Collection<String> ozAdmins =
-            conf.getTrimmedStringCollection(OZONE_ADMINISTRATORS);
-    String omSPN = UserGroupInformation.getCurrentUser().getShortUserName();
-    if (!ozAdmins.contains(omSPN)) {
-      ozAdmins.add(omSPN);
+  static Collection<String> getOzoneAdminsFromConfig(OzoneConfiguration conf,
+      String starterUser) {
+    Collection<String> ozAdmins = conf.getTrimmedStringCollection(
+        OZONE_ADMINISTRATORS);
+    if (!ozAdmins.contains(starterUser)) {
+      ozAdmins.add(starterUser);
     }
     return ozAdmins;
   }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 169e87fa39..67803786df 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -43,6 +43,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.SortedSet;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.ConcurrentHashMap;
@@ -50,7 +51,11 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Lists;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.conf.ReconfigurationException;
+import org.apache.hadoop.conf.ReconfigurationTaskStatus;
 import org.apache.hadoop.conf.StorageUnit;
 import org.apache.hadoop.crypto.key.KeyProvider;
 import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
@@ -64,7 +69,11 @@ import org.apache.hadoop.hdds.client.ReplicationConfig;
 import org.apache.hadoop.hdds.client.ReplicationType;
 import org.apache.hadoop.hdds.conf.ConfigurationException;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.ReconfigureProtocol;
 import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
+import org.apache.hadoop.hdds.protocol.proto.ReconfigureProtocolProtos.ReconfigureProtocolService;
+import org.apache.hadoop.hdds.protocolPB.ReconfigureProtocolPB;
+import org.apache.hadoop.hdds.protocolPB.ReconfigureProtocolServerSideTranslatorPB;
 import org.apache.hadoop.hdds.scm.ScmInfo;
 import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
 import org.apache.hadoop.hdds.server.OzoneAdmins;
@@ -220,6 +229,7 @@ import static org.apache.hadoop.ozone.OzoneConfigKeys.DFS_CONTAINER_RATIS_ENABLE
 import static org.apache.hadoop.ozone.OzoneConfigKeys.DFS_CONTAINER_RATIS_ENABLED_KEY;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED_DEFAULT;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_FLEXIBLE_FQDN_RESOLUTION_ENABLED;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_FLEXIBLE_FQDN_RESOLUTION_ENABLED_DEFAULT;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_KEY_PREALLOCATION_BLOCKS_MAX;
@@ -291,7 +301,7 @@ import org.slf4j.LoggerFactory;
 @InterfaceAudience.LimitedPrivate({"HDFS", "CBLOCK", "OZONE", "HBASE"})
 public final class OzoneManager extends ServiceRuntimeInfoImpl
     implements OzoneManagerProtocol, OMInterServiceProtocol,
-    OMMXBean, Auditor {
+    ReconfigureProtocol, OMMXBean, Auditor {
   public static final Logger LOG =
       LoggerFactory.getLogger(OzoneManager.class);
 
@@ -329,6 +339,7 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
   /**
    * OM super user / admin list.
    */
+  private final String omStarterUser;
   private final OzoneAdmins omAdmins;
   private final OzoneAdmins s3OzoneAdmins;
 
@@ -435,6 +446,12 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
   private OmMetadataReader omMetadataReader;
   private OmSnapshotManager omSnapshotManager;
 
+  /** A list of property that are reconfigurable at runtime. */
+  private final SortedSet<String> reconfigurableProperties =
+      ImmutableSortedSet.of(
+          OZONE_ADMINISTRATORS
+      );
+
   @SuppressWarnings("methodlength")
   private OzoneManager(OzoneConfiguration conf, StartupOption startupOption)
       throws IOException, AuthenticationException {
@@ -587,10 +604,12 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
 
     metrics = OMMetrics.create();
     // Get admin list
+    omStarterUser = UserGroupInformation.getCurrentUser().getShortUserName();
     Collection<String> omAdminUsernames =
-        OzoneConfigUtil.getOzoneAdminsFromConfig(configuration);
+        OzoneConfigUtil.getOzoneAdminsFromConfig(configuration, omStarterUser);
     Collection<String> omAdminGroups =
         OzoneConfigUtil.getOzoneAdminsGroupsFromConfig(configuration);
+    LOG.info("OM start with adminUsers: {}", omAdminUsernames);
     omAdmins = new OzoneAdmins(omAdminUsernames, omAdminGroups);
 
     Collection<String> s3AdminUsernames =
@@ -1132,8 +1151,14 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
         OzoneManagerAdminService.newReflectiveBlockingService(
             omMetadataServerProtocol);
 
+    ReconfigureProtocolServerSideTranslatorPB reconfigureServerProtocol
+        = new ReconfigureProtocolServerSideTranslatorPB(this);
+    BlockingService reconfigureService =
+        ReconfigureProtocolService.newReflectiveBlockingService(
+            reconfigureServerProtocol);
+
     return startRpcServer(configuration, omNodeRpcAddr, omService,
-        omInterService, omAdminService, handlerCount);
+        omInterService, omAdminService, reconfigureService, handlerCount);
   }
 
   /**
@@ -1144,6 +1169,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
    *                              (OzoneManagerProtocolPB impl)
    * @param interOMProtocolService RPC protocol for inter OM communication
    *                               (OMInterServiceProtocolPB impl)
+   * @param reconfigureProtocolService RPC protocol for reconfigure
+   *    *                              (ReconfigureProtocolPB impl)
    * @param handlerCount RPC server handler count
    * @return RPC server
    * @throws IOException if there is an I/O error while creating RPC server
@@ -1152,6 +1179,7 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
       InetSocketAddress addr, BlockingService clientProtocolService,
       BlockingService interOMProtocolService,
       BlockingService omAdminProtocolService,
+      BlockingService reconfigureProtocolService,
       int handlerCount)
       throws IOException {
     RPC.Server rpcServer = new RPC.Builder(conf)
@@ -1168,6 +1196,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
         interOMProtocolService, rpcServer);
     HddsServerUtil.addPBProtocol(conf, OMAdminProtocolPB.class,
         omAdminProtocolService, rpcServer);
+    HddsServerUtil.addPBProtocol(conf, ReconfigureProtocolPB.class,
+        reconfigureProtocolService, rpcServer);
 
     if (conf.getBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION,
         false)) {
@@ -3701,6 +3731,12 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
     return LOG;
   }
 
+  // ReconfigurableBase get base configuration
+  @Override
+  public Configuration getConf() {
+    return getConfiguration();
+  }
+
   public OzoneConfiguration getConfiguration() {
     return configuration;
   }
@@ -3843,7 +3879,7 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
   /**
    * Return the list of Ozone administrators in effect.
    */
-  Collection<String> getOmAdminUsernames() {
+  public Collection<String> getOmAdminUsernames() {
     return omAdmins.getAdminUsernames();
   }
 
@@ -3859,6 +3895,17 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
     return callerUgi != null && omAdmins.isAdmin(callerUgi);
   }
 
+  /**
+   * Check ozone admin privilege, throws exception if not admin.
+   */
+  public void checkAdminUserPrivilege(String operation) throws IOException {
+    final UserGroupInformation ugi = getRemoteUser();
+    if (!isAdmin(ugi)) {
+      throw new OMException("Only Ozone admins are allowed to " + operation,
+          PERMISSION_DENIED);
+    }
+  }
+
   public boolean isS3Admin(UserGroupInformation callerUgi) {
     return callerUgi != null && s3OzoneAdmins.isAdmin(callerUgi);
   }
@@ -4325,4 +4372,53 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
     return omSnapshotManager.getSnapshotDiffReport(volume, bucket,
         fromSnapshot, toSnapshot);
   }
+
+  @Override // ReconfigureProtocol
+  public void startReconfigure() throws IOException {
+    String operationName = "startOmReconfiguration";
+    checkAdminUserPrivilege(operationName);
+    startReconfigurationTask();
+  }
+
+  @Override // ReconfigureProtocol
+  public ReconfigurationTaskStatus getReconfigureStatus()
+      throws IOException {
+    String operationName = "getOmReconfigurationStatus";
+    checkAdminUserPrivilege(operationName);
+    return getReconfigurationTaskStatus();
+  }
+
+  @Override // ReconfigureProtocol
+  public List<String> listReconfigureProperties() throws IOException {
+    String operationName = "listOmReconfigurableProperties";
+    checkAdminUserPrivilege(operationName);
+    return Lists.newArrayList(getReconfigurableProperties());
+  }
+
+  @Override // ReconfigurableBase
+  public Collection<String> getReconfigurableProperties() {
+    return reconfigurableProperties;
+  }
+
+  @Override // ReconfigurableBase
+  public String reconfigurePropertyImpl(String property, String newVal)
+      throws ReconfigurationException {
+    if (property.equals(OZONE_ADMINISTRATORS)) {
+      return reconfOzoneAdmins(newVal);
+    } else {
+      throw new ReconfigurationException(property, newVal,
+          getConfiguration().get(property));
+    }
+  }
+
+  private String reconfOzoneAdmins(String newVal) {
+    getConfiguration().set(OZONE_ADMINISTRATORS, newVal);
+    Collection<String> admins =
+        OzoneConfigUtil.getOzoneAdminsFromConfig(getConfiguration(),
+            omStarterUser);
+    omAdmins.setAdminUsernames(admins);
+    LOG.info("Load conf {} : {}, and now admins are: {}", OZONE_ADMINISTRATORS,
+        newVal, admins);
+    return String.valueOf(newVal);
+  }
 }
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureCommands.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureCommands.java
new file mode 100644
index 0000000000..f47c439711
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureCommands.java
@@ -0,0 +1,75 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.admin.reconfig;
+
+import org.apache.hadoop.hdds.cli.GenericCli;
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import org.apache.hadoop.hdds.cli.OzoneAdmin;
+import org.apache.hadoop.hdds.cli.SubcommandWithParent;
+import org.kohsuke.MetaInfServices;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Spec;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Subcommand to group reconfigure OM related operations.
+ */
+@Command(
+    name = "reconfig",
+    description = "Dynamically reconfigure server without restarting it",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class,
+    subcommands = {
+        ReconfigureStartSubcommand.class, 
+        ReconfigureStatusSubcommand.class,
+        ReconfigurePropertiesSubcommand.class
+    })
+@MetaInfServices(SubcommandWithParent.class)
+public class ReconfigureCommands implements Callable<Void>,
+    SubcommandWithParent {
+
+  @CommandLine.ParentCommand
+  private OzoneAdmin parent;
+
+  @Spec
+  private CommandSpec spec;
+
+  @CommandLine.Option(names = {"--address"},
+      description = "node address: <ip:port> or <hostname:port>",
+      required = true)
+  private String address;
+
+  @Override
+  public Void call() throws Exception {
+    GenericCli.missingSubcommand(spec);
+    return null;
+  }
+
+  public String getAddress() {
+    return address;
+  }
+
+  @Override
+  public Class<?> getParentType() {
+    return OzoneAdmin.class;
+  }
+
+}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigurePropertiesSubcommand.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigurePropertiesSubcommand.java
new file mode 100644
index 0000000000..1206d6117e
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigurePropertiesSubcommand.java
@@ -0,0 +1,53 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.admin.reconfig;
+
+import org.apache.hadoop.hdds.protocol.ReconfigureProtocol;
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Handler of ozone admin reconfig properties command.
+ */
+@Command(
+    name = "properties",
+    description = "List reconfigurable properties",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class)
+public class ReconfigurePropertiesSubcommand implements Callable<Void> {
+
+  @CommandLine.ParentCommand
+  private ReconfigureCommands parent;
+
+  @Override
+  public Void call() throws Exception {
+    ReconfigureProtocol reconfigProxy = ReconfigureSubCommandUtil
+        .getSingleNodeReconfigureProxy(parent.getAddress());
+    List<String> properties = reconfigProxy.listReconfigureProperties();
+    System.out.printf("Node [%s] Reconfigurable properties:%n",
+        parent.getAddress());
+    for (String name : properties) {
+      System.out.println(name);
+    }
+    return null;
+  }
+
+}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureStartSubcommand.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureStartSubcommand.java
new file mode 100644
index 0000000000..7f3866154f
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureStartSubcommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.admin.reconfig;
+
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import org.apache.hadoop.hdds.protocol.ReconfigureProtocol;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import java.util.concurrent.Callable;
+
+/**
+ * Handler of ozone admin reconfig start command.
+ */
+@Command(
+    name = "start",
+    description = "Start reconfig asynchronously",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class)
+public class ReconfigureStartSubcommand implements Callable<Void> {
+
+  @CommandLine.ParentCommand
+  private ReconfigureCommands parent;
+
+  @Override
+  public Void call() throws Exception {
+    ReconfigureProtocol reconfigProxy = ReconfigureSubCommandUtil
+        .getSingleNodeReconfigureProxy(parent.getAddress());
+    reconfigProxy.startReconfigure();
+    System.out.printf("Started reconfiguration task on node [%s].%n",
+        parent.getAddress());
+    return null;
+  }
+
+}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureStatusSubcommand.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureStatusSubcommand.java
new file mode 100644
index 0000000000..e6f2da9700
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureStatusSubcommand.java
@@ -0,0 +1,89 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.admin.reconfig;
+
+import org.apache.hadoop.conf.ReconfigurationTaskStatus;
+import org.apache.hadoop.conf.ReconfigurationUtil;
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import org.apache.hadoop.hdds.protocol.ReconfigureProtocol;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+/**
+ * Handler of ozone admin reconfig status command.
+ */
+@Command(
+    name = "status",
+    description = "Check reconfig status",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class)
+public class ReconfigureStatusSubcommand implements Callable<Void> {
+
+  @CommandLine.ParentCommand
+  private ReconfigureCommands parent;
+
+  @Override
+  public Void call() throws Exception {
+    ReconfigureProtocol reconfigProxy = ReconfigureSubCommandUtil
+        .getSingleNodeReconfigureProxy(parent.getAddress());
+    ReconfigurationTaskStatus status = reconfigProxy.getReconfigureStatus();
+    System.out.printf("Reconfiguring status for node [%s]: ",
+        parent.getAddress());
+    printReconfigurationStatus(status);
+    return null;
+  }
+
+  private void printReconfigurationStatus(ReconfigurationTaskStatus status) {
+    if (!status.hasTask()) {
+      System.out.println("no task was found.");
+      return;
+    }
+    System.out.print("started at " + new Date(status.getStartTime()));
+    if (!status.stopped()) {
+      System.out.println(" and is still running.");
+      return;
+    }
+    System.out.printf(" and finished at %s.%n", new Date(status.getEndTime()));
+    if (status.getStatus() == null) {
+      // Nothing to report.
+      return;
+    }
+    for (Map.Entry<ReconfigurationUtil.PropertyChange, Optional<String>>
+        result : status.getStatus().entrySet()) {
+      if (!result.getValue().isPresent()) {
+        System.out.printf(
+            "SUCCESS: Changed property %s%n\tFrom: \"%s\"%n\tTo: \"%s\"%n",
+            result.getKey().prop, result.getKey().oldVal,
+            result.getKey().newVal);
+      } else {
+        final String errorMsg = result.getValue().get();
+        System.out.printf(
+            "FAILED: Change property %s%n\tFrom: \"%s\"%n\tTo: \"%s\"%n",
+            result.getKey().prop, result.getKey().oldVal,
+            result.getKey().newVal);
+        System.out.println("\tError: " + errorMsg + ".");
+      }
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureSubCommandUtil.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureSubCommandUtil.java
new file mode 100644
index 0000000000..44c74ab2dc
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/ReconfigureSubCommandUtil.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.admin.reconfig;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.ReconfigureProtocol;
+import org.apache.hadoop.hdds.protocolPB.ReconfigureProtocolClientSideTranslatorPB;
+import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.security.UserGroupInformation;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+/**
+ * Reconfigure subcommand utils.
+ */
+final class ReconfigureSubCommandUtil {
+
+  private ReconfigureSubCommandUtil() {
+  }
+
+  public static ReconfigureProtocol getSingleNodeReconfigureProxy(
+      String address) throws IOException {
+    OzoneConfiguration ozoneConf = new OzoneConfiguration();
+    UserGroupInformation user = UserGroupInformation.getCurrentUser();
+    InetSocketAddress nodeAddr = NetUtils.createSocketAddr(address);
+    return new ReconfigureProtocolClientSideTranslatorPB(
+        nodeAddr, user, ozoneConf);
+  }
+
+}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/package-info.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/package-info.java
new file mode 100644
index 0000000000..fa2d33830a
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.
+ * <p>
+ */
+
+/**
+ * Reconfigure related Admin tools.
+ */
+package org.apache.hadoop.ozone.admin.reconfig;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org