You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by jx...@apache.org on 2022/03/29 18:08:40 UTC

[helix] branch helix-vm-freeze updated: Add instance disable reason (#1993)

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

jxue pushed a commit to branch helix-vm-freeze
in repository https://gitbox.apache.org/repos/asf/helix.git


The following commit(s) were added to refs/heads/helix-vm-freeze by this push:
     new ead15a3  Add instance disable reason  (#1993)
ead15a3 is described below

commit ead15a3a25e20da15c98200942ed7320bb457189
Author: xyuanlu <xy...@gmail.com>
AuthorDate: Tue Mar 29 11:08:32 2022 -0700

    Add instance disable reason  (#1993)
    
    Add instance disable reason
---
 .../apache/helix/constants/InstanceConstants.java  | 10 +++++
 .../src/main/java/org/apache/helix/HelixAdmin.java | 11 +++++
 .../org/apache/helix/manager/zk/ZKHelixAdmin.java  | 23 ++++++++--
 .../org/apache/helix/model/InstanceConfig.java     | 49 ++++++++++++++++++++++
 .../apache/helix/manager/zk/TestZkHelixAdmin.java  | 17 +++++++-
 .../java/org/apache/helix/mock/MockHelixAdmin.java | 19 ++++++++-
 .../org/apache/helix/model/TestInstanceConfig.java | 39 +++++++++++++----
 .../resources/helix/PerInstanceAccessor.java       | 41 +++++++++++-------
 .../helix/rest/server/TestPerInstanceAccessor.java | 31 +++++++++++++-
 9 files changed, 209 insertions(+), 31 deletions(-)

diff --git a/helix-common/src/main/java/org/apache/helix/constants/InstanceConstants.java b/helix-common/src/main/java/org/apache/helix/constants/InstanceConstants.java
new file mode 100644
index 0000000..74dc837
--- /dev/null
+++ b/helix-common/src/main/java/org/apache/helix/constants/InstanceConstants.java
@@ -0,0 +1,10 @@
+package org.apache.helix.constants;
+
+public class InstanceConstants {
+  public enum InstanceDisabledType {
+    CLOUD_EVENT,
+    USER_OPERATION,
+    DEFAULT_INSTANCE_DISABLE_TYPE,
+    INSTANCE_NOT_DISABLED
+  }
+}
diff --git a/helix-core/src/main/java/org/apache/helix/HelixAdmin.java b/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
index 05bd60e..32158d5 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import org.apache.helix.api.status.ClusterManagementMode;
 import org.apache.helix.api.status.ClusterManagementModeRequest;
 import org.apache.helix.api.topology.ClusterTopology;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.model.CloudConfig;
 import org.apache.helix.model.ClusterConstraints;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
@@ -281,6 +282,16 @@ public interface HelixAdmin {
   void enableInstance(String clusterName, String instanceName, boolean enabled);
 
   /**
+   * @param clusterName
+   * @param instanceName
+   * @param enabled
+   * @param reason set additional string description on why the instance is disabled when
+   *          <code>enabled</code> is false.
+   */
+  void enableInstance(String clusterName, String instanceName, boolean enabled,
+      InstanceConstants.InstanceDisabledType disabledType, String reason);
+
+  /**
    * Batch enable/disable instances in a cluster
    * By default, all the instances are enabled
    * @param clusterName
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
index 3faa9c4..b0eec58 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
@@ -56,6 +56,7 @@ import org.apache.helix.api.exceptions.HelixConflictException;
 import org.apache.helix.api.status.ClusterManagementMode;
 import org.apache.helix.api.status.ClusterManagementModeRequest;
 import org.apache.helix.api.topology.ClusterTopology;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.controller.rebalancer.strategy.RebalanceStrategy;
 import org.apache.helix.controller.rebalancer.util.WagedValidationUtil;
 import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
@@ -341,10 +342,16 @@ public class ZKHelixAdmin implements HelixAdmin {
   @Override
   public void enableInstance(final String clusterName, final String instanceName,
       final boolean enabled) {
+    enableInstance(clusterName, instanceName, enabled, null, null);
+  }
+
+  @Override
+  public void enableInstance(final String clusterName, final String instanceName,
+      final boolean enabled, InstanceConstants.InstanceDisabledType disabledType, String reason) {
     logger.info("{} instance {} in cluster {}.", enabled ? "Enable" : "Disable", instanceName,
         clusterName);
     BaseDataAccessor<ZNRecord> baseAccessor = new ZkBaseDataAccessor<>(_zkClient);
-    enableSingleInstance(clusterName, instanceName, enabled, baseAccessor);
+    enableSingleInstance(clusterName, instanceName, enabled, baseAccessor, disabledType, reason);
     // TODO: Reenable this after storage node bug fixed.
     // enableBatchInstances(clusterName, Collections.singletonList(instanceName), enabled, baseAccessor);
 
@@ -352,6 +359,7 @@ public class ZKHelixAdmin implements HelixAdmin {
 
   @Override
   public void enableInstance(String clusterName, List<String> instances, boolean enabled) {
+    // TODO: Considering adding another batched API with  disabled type and reason.
     // TODO: Reenable this after storage node bug fixed.
     if (true) {
       throw new HelixException("Current batch enable/disable instances are temporarily disabled!");
@@ -361,7 +369,7 @@ public class ZKHelixAdmin implements HelixAdmin {
     BaseDataAccessor<ZNRecord> baseAccessor = new ZkBaseDataAccessor<>(_zkClient);
     if (enabled) {
       for (String instance : instances) {
-        enableSingleInstance(clusterName, instance, enabled, baseAccessor);
+        enableSingleInstance(clusterName, instance, enabled, baseAccessor, null, null);
       }
     }
     enableBatchInstances(clusterName, instances, enabled, baseAccessor);
@@ -1858,7 +1866,8 @@ public class ZKHelixAdmin implements HelixAdmin {
   }
 
   private void enableSingleInstance(final String clusterName, final String instanceName,
-      final boolean enabled, BaseDataAccessor<ZNRecord> baseAccessor) {
+      final boolean enabled, BaseDataAccessor<ZNRecord> baseAccessor,
+      InstanceConstants.InstanceDisabledType disabledType, String reason) {
     String path = PropertyPathBuilder.instanceConfig(clusterName, instanceName);
 
     if (!baseAccessor.exists(path, 0)) {
@@ -1876,6 +1885,14 @@ public class ZKHelixAdmin implements HelixAdmin {
 
         InstanceConfig config = new InstanceConfig(currentData);
         config.setInstanceEnabled(enabled);
+        if (!enabled) {
+          if (reason != null) {
+            config.setInstanceDisabledReason(reason);
+          }
+          if (disabledType != null) {
+            config.setInstanceDisabledType(disabledType);
+          }
+        }
         return config.getRecord();
       }
     }, AccessOption.PERSISTENT);
diff --git a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
index 3ab3ea0..45dc6b4 100644
--- a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
@@ -31,6 +31,7 @@ import java.util.stream.Collectors;
 
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.controller.rebalancer.topology.Topology;
 import org.apache.helix.util.HelixUtil;
 import org.apache.helix.zookeeper.datamodel.ZNRecord;
@@ -50,6 +51,8 @@ public class InstanceConfig extends HelixProperty {
     HELIX_ZONE_ID,
     HELIX_ENABLED,
     HELIX_ENABLED_TIMESTAMP,
+    HELIX_DISABLED_REASON,
+    HELIX_DISABLED_TYPE,
     HELIX_DISABLED_PARTITION,
     TAG_LIST,
     INSTANCE_WEIGHT,
@@ -275,6 +278,7 @@ public class InstanceConfig extends HelixProperty {
 
   /**
    * Set the enabled state of the instance
+   * If user enables the instance, HELIX_DISABLED_REASON filed will be removed.
    *
    * @param enabled true to enable, false to disable
    */
@@ -282,6 +286,51 @@ public class InstanceConfig extends HelixProperty {
     _record.setBooleanField(InstanceConfigProperty.HELIX_ENABLED.toString(), enabled);
     _record.setLongField(InstanceConfigProperty.HELIX_ENABLED_TIMESTAMP.name(),
         System.currentTimeMillis());
+    if (enabled) {
+      _record.getSimpleFields().remove(InstanceConfigProperty.HELIX_DISABLED_REASON.toString());
+      _record.getSimpleFields().remove(InstanceConfigProperty.HELIX_DISABLED_TYPE.toString());
+    }
+  }
+
+  /**
+   * Set the instance disabled reason when instance is disabled.
+   * It will be a no-op when instance is enabled.
+   */
+  public void setInstanceDisabledReason(String disabledReason) {
+     if (!getInstanceEnabled()) {
+     _record.setSimpleField(InstanceConfigProperty.HELIX_DISABLED_REASON.toString(), disabledReason);
+     }
+  }
+
+  /**
+   * Set the instance disabled type when instance is disabled.
+   * It will be a no-op when instance is enabled.
+   */
+  public void setInstanceDisabledType(InstanceConstants.InstanceDisabledType disabledType) {
+    if (!getInstanceEnabled()) {
+      _record.setSimpleField(InstanceConfigProperty.HELIX_DISABLED_TYPE.toString(),
+          disabledType.toString());
+    }
+  }
+
+  /**
+   * @return Return instance disabled reason. Default is am empty string.
+   */
+  public String getInstanceDisabledReason() {
+    return _record.getStringField(InstanceConfigProperty.HELIX_DISABLED_REASON.toString(), "");
+  }
+
+  /**
+   *
+   * @return Return instance disabled type (org.apache.helix.constants.InstanceConstants.InstanceDisabledType)
+   *         Default is am empty string.
+   */
+  public String getInstanceDisabledType() {
+    if (getInstanceEnabled()) {
+      return InstanceConstants.InstanceDisabledType.INSTANCE_NOT_DISABLED.toString();
+    }
+    return _record.getStringField(InstanceConfigProperty.HELIX_DISABLED_TYPE.toString(),
+        InstanceConstants.InstanceDisabledType.DEFAULT_INSTANCE_DISABLE_TYPE.toString());
   }
 
   /**
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
index 5aae53c..18bafd2 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
@@ -47,8 +47,10 @@ import org.apache.helix.TestHelper;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.api.exceptions.HelixConflictException;
 import org.apache.helix.api.status.ClusterManagementMode;
+import org.apache.helix.api.status.ClusterManagementModeRequest;
 import org.apache.helix.api.topology.ClusterTopology;
 import org.apache.helix.cloud.constants.CloudProvider;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
 import org.apache.helix.examples.MasterSlaveStateModelFactory;
 import org.apache.helix.integration.manager.ClusterControllerManager;
@@ -73,7 +75,6 @@ import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.model.builder.ConstraintItemBuilder;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
-import org.apache.helix.api.status.ClusterManagementModeRequest;
 import org.apache.helix.participant.StateMachineEngine;
 import org.apache.helix.tools.StateModelConfigGenerator;
 import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
@@ -176,6 +177,20 @@ public class TestZkHelixAdmin extends ZkUnitTestBase {
       // OK
     }
 
+    Assert.assertTrue(
+        tool.getInstanceConfig(clusterName, instanceName).getInstanceDisabledReason().isEmpty());
+    String disableReason = "Reason";
+    tool.enableInstance(clusterName, instanceName, false,
+        InstanceConstants.InstanceDisabledType.CLOUD_EVENT, disableReason);
+    Assert.assertTrue(tool.getInstanceConfig(clusterName, instanceName).getInstanceDisabledReason()
+        .equals(disableReason));
+    tool.enableInstance(clusterName, instanceName, true,
+        InstanceConstants.InstanceDisabledType.CLOUD_EVENT, disableReason);
+    Assert.assertTrue(
+        tool.getInstanceConfig(clusterName, instanceName).getInstanceDisabledReason().isEmpty());
+    Assert.assertEquals(tool.getInstanceConfig(clusterName, instanceName).getInstanceDisabledType(),
+        InstanceConstants.InstanceDisabledType.INSTANCE_NOT_DISABLED.toString());
+
     dummyList.remove("bar");
     dummyList.add("baz");
     config = tool.getInstanceConfig(clusterName, instanceName);
diff --git a/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java b/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java
index f7a1034..6390edf 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java
@@ -31,7 +31,9 @@ import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
 import org.apache.helix.api.status.ClusterManagementMode;
+import org.apache.helix.api.status.ClusterManagementModeRequest;
 import org.apache.helix.api.topology.ClusterTopology;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.model.CloudConfig;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ClusterConstraints;
@@ -45,7 +47,6 @@ import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.MaintenanceSignal;
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.model.StateModelDefinition;
-import org.apache.helix.api.status.ClusterManagementModeRequest;
 import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 public class MockHelixAdmin implements HelixAdmin {
@@ -267,6 +268,12 @@ public class MockHelixAdmin implements HelixAdmin {
 
   @Override
   public void enableInstance(String clusterName, String instanceName, boolean enabled) {
+    enableInstance(clusterName, instanceName, enabled, null, null);
+  }
+
+  @Override
+  public void enableInstance(String clusterName, String instanceName, boolean enabled,
+      InstanceConstants.InstanceDisabledType disabledType, String reason) {
     String instanceConfigsPath = PropertyPathBuilder.instanceConfig(clusterName);
     if (!_baseDataAccessor.exists(instanceConfigsPath, 0)) {
       _baseDataAccessor.create(instanceConfigsPath, new ZNRecord(instanceName), 0);
@@ -274,9 +281,17 @@ public class MockHelixAdmin implements HelixAdmin {
 
     String instanceConfigPath = instanceConfigsPath + "/" + instanceName;
 
-    ZNRecord  record = (ZNRecord) _baseDataAccessor.get(instanceConfigPath, null, 0);
+    ZNRecord record = (ZNRecord) _baseDataAccessor.get(instanceConfigPath, null, 0);
     InstanceConfig instanceConfig = new InstanceConfig(record);
     instanceConfig.setInstanceEnabled(enabled);
+    if (!enabled) {
+      if (reason != null) {
+        instanceConfig.setInstanceDisabledReason(reason);
+      }
+      if (disabledType != null) {
+        instanceConfig.setInstanceDisabledType(disabledType);
+      }
+    }
     _baseDataAccessor.set(instanceConfigPath, instanceConfig.getRecord(), 0);
   }
 
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java b/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java
index 6ec6a4a..96f50e4 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java
@@ -23,17 +23,11 @@ import java.util.Collections;
 import java.util.Map;
 
 import com.google.common.collect.ImmutableMap;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-/**
- * Created with IntelliJ IDEA.
- * User: zzhang
- * Date: 3/19/13
- * Time: 5:28 PM
- * To change this template use File | Settings | File Templates.
- */
 public class TestInstanceConfig {
   @Test
   public void testNotCheckingHostPortExistence() {
@@ -45,7 +39,8 @@ public class TestInstanceConfig {
   @Test
   public void testGetParsedDomain() {
     InstanceConfig instanceConfig = new InstanceConfig(new ZNRecord("id"));
-    instanceConfig.setDomain("cluster=myCluster,zone=myZone1,rack=myRack,host=hostname,instance=instance001");
+    instanceConfig
+        .setDomain("cluster=myCluster,zone=myZone1,rack=myRack,host=hostname,instance=instance001");
 
     Map<String, String> parsedDomain = instanceConfig.getDomainAsMap();
     Assert.assertEquals(parsedDomain.size(), 5);
@@ -53,6 +48,34 @@ public class TestInstanceConfig {
   }
 
   @Test
+  public void testSetInstanceEnableWithReason() {
+    InstanceConfig instanceConfig = new InstanceConfig(new ZNRecord("id"));
+    instanceConfig.setInstanceEnabled(true);
+    instanceConfig.setInstanceDisabledReason("NoShowReason");
+    instanceConfig.setInstanceDisabledType(InstanceConstants.InstanceDisabledType.USER_OPERATION);
+
+    Assert.assertEquals(instanceConfig.getRecord().getSimpleFields()
+        .get(InstanceConfig.InstanceConfigProperty.HELIX_ENABLED.toString()), "true");
+    Assert.assertEquals(instanceConfig.getRecord().getSimpleFields()
+        .get(InstanceConfig.InstanceConfigProperty.HELIX_DISABLED_REASON.toString()), null);
+    Assert.assertEquals(instanceConfig.getRecord().getSimpleFields()
+        .get(InstanceConfig.InstanceConfigProperty.HELIX_DISABLED_TYPE.toString()), null);
+
+
+    instanceConfig.setInstanceEnabled(false);
+    String reasonCode = "ReasonCode";
+    instanceConfig.setInstanceDisabledReason(reasonCode);
+    instanceConfig.setInstanceDisabledType(InstanceConstants.InstanceDisabledType.USER_OPERATION);
+    Assert.assertEquals(instanceConfig.getRecord().getSimpleFields()
+        .get(InstanceConfig.InstanceConfigProperty.HELIX_ENABLED.toString()), "false");
+    Assert.assertEquals(instanceConfig.getRecord().getSimpleFields()
+        .get(InstanceConfig.InstanceConfigProperty.HELIX_DISABLED_REASON.toString()), reasonCode);
+    Assert.assertEquals(instanceConfig.getInstanceDisabledReason(), reasonCode);
+    Assert.assertEquals(instanceConfig.getInstanceDisabledType(),
+        InstanceConstants.InstanceDisabledType.USER_OPERATION.toString());
+  }
+
+  @Test
   public void testGetParsedDomainEmptyDomain() {
     InstanceConfig instanceConfig = new InstanceConfig(new ZNRecord("id"));
 
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
index f9d171e..d77e743 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
@@ -48,6 +48,7 @@ import org.apache.helix.ConfigAccessor;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.Error;
@@ -369,7 +370,8 @@ public class PerInstanceAccessor extends AbstractHelixResource {
   @POST
   public Response updateInstance(@PathParam("clusterId") String clusterId,
       @PathParam("instanceName") String instanceName, @QueryParam("command") String command,
-      String content) {
+      @QueryParam("instanceDisabledType") String disabledType,
+      @QueryParam("instanceDisabledReason") String disabledReason, String content) {
     Command cmd;
     try {
       cmd = Command.valueOf(command);
@@ -385,21 +387,30 @@ public class PerInstanceAccessor extends AbstractHelixResource {
       }
 
       switch (cmd) {
-      case enable:
-        admin.enableInstance(clusterId, instanceName, true);
-        break;
-      case disable:
-        admin.enableInstance(clusterId, instanceName, false);
-        break;
+        case enable:
+          admin.enableInstance(clusterId, instanceName, true);
+          break;
+        case disable:
+          InstanceConstants.InstanceDisabledType disabledTypeEnum = null;
+          if (disabledType != null) {
+            try {
+              disabledTypeEnum = InstanceConstants.InstanceDisabledType.valueOf(disabledType);
+            } catch (IllegalArgumentException ex) {
+              return badRequest("Invalid instanceDisabledType!");
+            }
+          }
+          admin.enableInstance(clusterId, instanceName, false, disabledTypeEnum, disabledReason);
+          break;
 
-      case reset:
-      case resetPartitions:
-        if (!validInstance(node, instanceName)) {
-          return badRequest("Instance names are not match!");
-        }
-        admin.resetPartition(clusterId, instanceName,
-            node.get(PerInstanceProperties.resource.name()).textValue(), (List<String>) OBJECT_MAPPER
-                .readValue(node.get(PerInstanceProperties.partitions.name()).toString(),
+        case reset:
+        case resetPartitions:
+          if (!validInstance(node, instanceName)) {
+            return badRequest("Instance names are not match!");
+          }
+          admin.resetPartition(clusterId, instanceName,
+              node.get(PerInstanceProperties.resource.name()).textValue(),
+              (List<String>) OBJECT_MAPPER
+                  .readValue(node.get(PerInstanceProperties.partitions.name()).toString(),
                     OBJECT_MAPPER.getTypeFactory()
                         .constructCollectionType(List.class, String.class)));
         break;
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
index 6ca7bf6..2fb1f1e 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
@@ -30,6 +30,7 @@ import java.util.Set;
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -38,6 +39,7 @@ import com.google.common.collect.ImmutableMap;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.TestHelper;
+import org.apache.helix.constants.InstanceConstants;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.InstanceConfig;
@@ -357,16 +359,41 @@ public class TestPerInstanceAccessor extends AbstractTestClass {
     // Disable instance
     Entity entity = Entity.entity("", MediaType.APPLICATION_JSON_TYPE);
 
-    new JerseyUriRequestBuilder("clusters/{}/instances/{}?command=disable")
+    new JerseyUriRequestBuilder(
+        "clusters/{}/instances/{}?command=disable&instanceDisabledReason=reason1")
         .format(CLUSTER_NAME, INSTANCE_NAME).post(this, entity);
 
     Assert.assertFalse(
         _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceEnabled());
+    Assert.assertEquals(
+        _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceDisabledType(),
+        InstanceConstants.InstanceDisabledType.DEFAULT_INSTANCE_DISABLE_TYPE.toString());
+    Assert.assertEquals(
+        _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceDisabledReason(),
+        "reason1");
 
     // Enable instance
-    new JerseyUriRequestBuilder("clusters/{}/instances/{}?command=enable")
+    new JerseyUriRequestBuilder(
+        "clusters/{}/instances/{}?command=enable&instanceDisabledType=USER_OPERATION&instanceDisabledReason=reason1")
         .format(CLUSTER_NAME, INSTANCE_NAME).post(this, entity);
+    Assert.assertTrue(
+        _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceEnabled());
+    Assert.assertEquals(
+        _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceDisabledType(),
+        InstanceConstants.InstanceDisabledType.INSTANCE_NOT_DISABLED.toString());
+    Assert.assertEquals(
+        _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceDisabledReason(),
+        "");
 
+    // disable instance with no reason input
+    new JerseyUriRequestBuilder("clusters/{}/instances/{}?command=disable")
+        .format(CLUSTER_NAME, INSTANCE_NAME).post(this, entity);
+
+    Assert.assertFalse(
+        _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceEnabled());
+
+    new JerseyUriRequestBuilder("clusters/{}/instances/{}?command=enable")
+        .format(CLUSTER_NAME, INSTANCE_NAME).post(this, entity);
     Assert.assertTrue(
         _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME).getInstanceEnabled());