helix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zzh...@apache.org
Subject [1/2] git commit: HELIX-46: Add REST/cli admin command for message constraints
Date Tue, 26 Mar 2013 23:48:35 GMT
Updated Branches:
  refs/heads/master 404f3d802 -> e61be451d


HELIX-46: Add REST/cli admin command for message constraints


Project: http://git-wip-us.apache.org/repos/asf/incubator-helix/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-helix/commit/7a868187
Tree: http://git-wip-us.apache.org/repos/asf/incubator-helix/tree/7a868187
Diff: http://git-wip-us.apache.org/repos/asf/incubator-helix/diff/7a868187

Branch: refs/heads/master
Commit: 7a8681872bc6a44217e89552bd6e2d6b22ac7d4d
Parents: 142dab5
Author: zzhang <zzhang5@uci.edu>
Authored: Tue Mar 26 16:42:12 2013 -0700
Committer: zzhang <zzhang5@uci.edu>
Committed: Tue Mar 26 16:42:12 2013 -0700

----------------------------------------------------------------------
 .../apache/helix/webapp/RestAdminApplication.java  |    3 +
 .../helix/webapp/resources/ConstraintResource.java |  158 +++++++++
 .../helix/webapp/resources/JsonParameters.java     |   42 ++--
 .../src/main/java/org/apache/helix/HelixAdmin.java |   25 +-
 .../org/apache/helix/manager/zk/ZKHelixAdmin.java  |   21 +-
 .../java/org/apache/helix/tools/ClusterSetup.java  |  266 ++++++++++----
 .../main/java/org/apache/helix/util/HelixUtil.java |   33 ++
 .../helix/integration/TestMessageThrottle.java     |    2 +-
 .../helix/integration/TestSchedulerMessage.java    |   19 +-
 .../apache/helix/manager/zk/TestZkHelixAdmin.java  |   10 +-
 10 files changed, 454 insertions(+), 125 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-admin-webapp/src/main/java/org/apache/helix/webapp/RestAdminApplication.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/RestAdminApplication.java
b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/RestAdminApplication.java
index 724c31f..f753efa 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/RestAdminApplication.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/RestAdminApplication.java
@@ -30,6 +30,7 @@ import org.apache.commons.cli.ParseException;
 import org.apache.helix.webapp.resources.ClusterResource;
 import org.apache.helix.webapp.resources.ClustersResource;
 import org.apache.helix.webapp.resources.ConfigResource;
+import org.apache.helix.webapp.resources.ConstraintResource;
 import org.apache.helix.webapp.resources.ControllerResource;
 import org.apache.helix.webapp.resources.ControllerStatusUpdateResource;
 import org.apache.helix.webapp.resources.CurrentStateResource;
@@ -107,6 +108,8 @@ public class RestAdminApplication extends Application
     router.attach("/clusters/{clusterName}/configs/{scope}", ConfigResource.class);
     router.attach("/clusters/{clusterName}/configs/{scope}/{scopeKey1}", ConfigResource.class);
     router.attach("/clusters/{clusterName}/configs/{scope}/{scopeKey1}/{scopeKey2}", ConfigResource.class);
+    router.attach("/clusters/{clusterName}/constraints/{constraintType}", ConstraintResource.class);
+    router.attach("/clusters/{clusterName}/constraints/{constraintType}/{constraintId}",
ConstraintResource.class);
     router.attach("/zkPath", ZkPathResource.class).setMatchingMode(Template.MODE_STARTS_WITH);
     router.attach("/zkChild", ZkChildResource.class).setMatchingMode(Template.MODE_STARTS_WITH);
 

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
new file mode 100644
index 0000000..16131f4
--- /dev/null
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
@@ -0,0 +1,158 @@
+package org.apache.helix.webapp.resources;
+
+/*
+ * 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 java.util.Map;
+
+import org.apache.helix.HelixAdmin;
+import org.apache.helix.ZNRecord;
+import org.apache.helix.manager.zk.ZKHelixAdmin;
+import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.model.ClusterConstraints.ConstraintType;
+import org.apache.helix.tools.ClusterSetup;
+import org.apache.helix.webapp.RestAdminApplication;
+import org.apache.log4j.Logger;
+import org.restlet.Context;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.Resource;
+import org.restlet.resource.StringRepresentation;
+import org.restlet.resource.Variant;
+
+public class ConstraintResource extends Resource {
+
+  private final static Logger LOG = Logger.getLogger(ConstraintResource.class);
+
+  public ConstraintResource(Context context, Request request, Response response)
+  {
+    super(context, request, response);
+    getVariants().add(new Variant(MediaType.TEXT_PLAIN));
+    getVariants().add(new Variant(MediaType.APPLICATION_JSON));
+    setModifiable(true);
+  }
+
+  // TODO move to a util function
+  String getValue(String key)
+  {
+    return (String) getRequest().getAttributes().get(key);
+  }
+
+  @Override
+  public Representation represent(Variant variant)
+  {
+    StringRepresentation representation = null;
+    String clusterName = getValue("clusterName");
+    String constraintTypeStr = getValue("constraintType").toUpperCase();
+    String constraintId = getValue("constraintId");
+
+    try {
+      ConstraintType constraintType = ConstraintType.valueOf(constraintTypeStr);
+      ZkClient zkClient =
+          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+      // ClusterSetup setupTool = new ClusterSetup(zkClient);
+      HelixAdmin admin = new ZKHelixAdmin(zkClient);  // setupTool.getClusterManagementTool();
+  
+      ZNRecord record = admin.getConstraints(clusterName, constraintType).getRecord();
+      if (constraintId == null) {
+        // get all message constraints
+        representation = new StringRepresentation(ClusterRepresentationUtil.ZNRecordToJson(record),
+                                         MediaType.APPLICATION_JSON);
+      } else {
+        // get a specific constraint
+        Map<String, String> constraint = record.getMapField(constraintId);
+        if (constraint == null) {
+          representation = new StringRepresentation("No constraint of type: " 
+              + constraintType + " associated with id: " + constraintId, MediaType.APPLICATION_JSON);
+        } else {
+          ZNRecord subRecord = new ZNRecord(record.getId());
+          subRecord.setMapField(constraintId, constraint);
+          representation = new StringRepresentation(ClusterRepresentationUtil.ZNRecordToJson(subRecord),
+              MediaType.APPLICATION_JSON);
+        }
+      }
+    }
+    catch (IllegalArgumentException e) {
+      representation = new StringRepresentation("constraint-type: " + constraintTypeStr +
" not recognized.", 
+          MediaType.APPLICATION_JSON);
+    }
+    catch (Exception e)
+    {
+      String error = ClusterRepresentationUtil.getErrorAsJsonStringFromException(e);
+      representation = new StringRepresentation(error, MediaType.APPLICATION_JSON);
+      LOG.error("", e);
+    }
+
+    return representation;
+  }
+  
+  @Override
+  public void acceptRepresentation(Representation entity)
+  {
+    String clusterName = getValue("clusterName");
+    String constraintTypeStr = getValue("constraintType").toUpperCase();
+    String constraintId = getValue("constraintId");
+
+    try {
+      ZkClient zkClient =
+          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+      ClusterSetup setupTool = new ClusterSetup(zkClient);
+      JsonParameters jsonParameters = new JsonParameters(entity);
+     
+      String constraintAttrStr = jsonParameters.getParameter(JsonParameters.CONSTRAINT_ATTRIBUTES);
+      setupTool.setConstraint(clusterName, constraintTypeStr, constraintId, constraintAttrStr);
+
+    }
+    catch (Exception e)
+    {
+      LOG.error("Error in posting " + entity, e);
+      getResponse().setEntity(ClusterRepresentationUtil.getErrorAsJsonStringFromException(e),
+                              MediaType.APPLICATION_JSON);
+      getResponse().setStatus(Status.SUCCESS_OK);
+    }
+  }
+  
+  @Override
+  public void removeRepresentations()
+  {
+    String clusterName = getValue("clusterName");
+    String constraintTypeStr = getValue("constraintType").toUpperCase();
+    String constraintId = getValue("constraintId");
+
+    try {
+      ZkClient zkClient =
+          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+      ClusterSetup setupTool = new ClusterSetup(zkClient);
+     
+      setupTool.removeConstraint(clusterName, constraintTypeStr, constraintId);
+
+    }
+    catch (Exception e)
+    {
+      LOG.error("Error in deleting ", e);
+      getResponse().setEntity(ClusterRepresentationUtil.getErrorAsJsonStringFromException(e),
+                              MediaType.APPLICATION_JSON);
+      getResponse().setStatus(Status.SUCCESS_OK);
+    }
+    
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
index fc77dcf..fc25364 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
@@ -39,28 +39,30 @@ import org.restlet.resource.Representation;
 public class JsonParameters
 {
   // json parameter key
-  public static final String             JSON_PARAMETERS     = "jsonParameters";
+  public static final String           JSON_PARAMETERS       = "jsonParameters";
 
   // json parameter map keys
-  public static final String             PARTITION           = "partition";
-  public static final String             RESOURCE            = "resource";
-  public static final String             MANAGEMENT_COMMAND  = "command";
-  public static final String             ENABLED             = "enabled";
-  public static final String             GRAND_CLUSTER       = "grandCluster";
-  public static final String             REPLICAS            = "replicas";
-  public static final String             RESOURCE_KEY_PREFIX = "key";
-  public static final String             INSTANCE_NAME       = "instanceName";
-  public static final String             INSTANCE_NAMES      = "instanceNames";
-  public static final String             OLD_INSTANCE        = "oldInstance";
-  public static final String             NEW_INSTANCE        = "newInstance";
-  public static final String             CONFIGS             = "configs";
-  public static final String             CLUSTER_NAME        = "clusterName";
-  public static final String             PARTITIONS          = "partitions";
-  public static final String             RESOURCE_GROUP_NAME = "resourceGroupName";
-  public static final String             STATE_MODEL_DEF_REF = "stateModelDefRef";
-  public static final String             IDEAL_STATE_MODE    = "mode";
-  public static final String             MAX_PARTITIONS_PER_NODE = "maxPartitionsPerNode";
-  public static final String             BUCKET_SIZE         = "bucketSize";
+  public static final String           PARTITION             = "partition";
+  public static final String           RESOURCE              = "resource";
+  public static final String           MANAGEMENT_COMMAND    = "command";
+  public static final String           ENABLED               = "enabled";
+  public static final String           GRAND_CLUSTER         = "grandCluster";
+  public static final String           REPLICAS              = "replicas";
+  public static final String           RESOURCE_KEY_PREFIX   = "key";
+  public static final String           INSTANCE_NAME         = "instanceName";
+  public static final String           INSTANCE_NAMES        = "instanceNames";
+  public static final String           OLD_INSTANCE          = "oldInstance";
+  public static final String           NEW_INSTANCE          = "newInstance";
+  public static final String           CONFIGS               = "configs";
+  public static final String           CONSTRAINT_ATTRIBUTES = "constraintAttributes";
+//  public static final String           CONSTRAINT_ID         = "constraintId";
+  public static final String           CLUSTER_NAME          = "clusterName";
+  public static final String           PARTITIONS            = "partitions";
+  public static final String           RESOURCE_GROUP_NAME   = "resourceGroupName";
+  public static final String           STATE_MODEL_DEF_REF   = "stateModelDefRef";
+  public static final String           IDEAL_STATE_MODE      = "mode";
+  public static final String           MAX_PARTITIONS_PER_NODE = "maxPartitionsPerNode";
+  public static final String           BUCKET_SIZE           = "bucketSize";
   
 
   // zk commands

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
----------------------------------------------------------------------
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 682b8b7..8437ea1 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.helix.model.*;
+import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.model.ConfigScope.ConfigScopeProperty;
 
 
@@ -404,30 +405,38 @@ public interface HelixAdmin
                         String stateModelDefFile) throws IOException;
 
   /**
-   * Add a message constraint item
+   * Add a constraint item; create if not exist
    * 
+   * @param clusterName
+   * @param constraintType
    * @param constraintId
    * @param constraintItem
    */
-  void addMessageConstraint(String clusterName,
-                            String constraintId,
-                            ConstraintItem constraintItem);
+  void setConstraint(String clusterName,
+                     ConstraintType constraintType,
+                     String constraintId,
+                     ConstraintItem constraintItem);
 
   /**
-   * Remove a message constraint item
+   * Remove a constraint item
    * 
    * @param clusterName
+   * @param constraintType
    * @param constraintId
    */
-  void removeMessageConstraint(String clusterName, String constraintId);
+  void removeConstraint(String clusterName, 
+                        ConstraintType constraintType, 
+                        String constraintId);
   
   /**
-   * Get all message constraints for a cluster
+   * Get all constraints for a type
    * 
    * @param clusterName
+   * @param constraintType
    * @return
    */
-  ClusterConstraints getMessageConstraints(String clusterName);
+  ClusterConstraints getConstraints(String clusterName,
+                                    ConstraintType constraintType);
   
   /**
    * 

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
----------------------------------------------------------------------
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 38da4e7..c8c23d6 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
@@ -1280,15 +1280,16 @@ public class ZKHelixAdmin implements HelixAdmin
   }
 
   @Override
-  public void addMessageConstraint(String clusterName,
-                                   final String constraintId,
-                                   final ConstraintItem constraintItem)
+  public void setConstraint(String clusterName,
+                            final ConstraintType constraintType,
+                            final String constraintId,
+                            final ConstraintItem constraintItem)
   {
     ZkBaseDataAccessor<ZNRecord> baseAccessor =
         new ZkBaseDataAccessor<ZNRecord>(_zkClient);
 
     Builder keyBuilder = new Builder(clusterName);
-    String path = keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()).getPath();
+    String path = keyBuilder.constraint(constraintType.toString()).getPath();
 
     baseAccessor.update(path, new DataUpdater<ZNRecord>()
     {
@@ -1296,7 +1297,7 @@ public class ZKHelixAdmin implements HelixAdmin
       public ZNRecord update(ZNRecord currentData)
       {
         ClusterConstraints constraints = currentData == null? 
-            new ClusterConstraints(ConstraintType.MESSAGE_CONSTRAINT) : new ClusterConstraints(currentData);
+            new ClusterConstraints(constraintType) : new ClusterConstraints(currentData);
 
         constraints.addConstraintItem(constraintId, constraintItem);
         return constraints.getRecord();
@@ -1305,13 +1306,15 @@ public class ZKHelixAdmin implements HelixAdmin
   }
   
   @Override
-  public void removeMessageConstraint(String clusterName, final String constraintId)
+  public void removeConstraint(String clusterName, 
+                               final ConstraintType constraintType,
+                               final String constraintId)
   {
     ZkBaseDataAccessor<ZNRecord> baseAccessor =
         new ZkBaseDataAccessor<ZNRecord>(_zkClient);
 
     Builder keyBuilder = new Builder(clusterName);
-    String path = keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()).getPath();
+    String path = keyBuilder.constraint(constraintType.toString()).getPath();
 
     baseAccessor.update(path, new DataUpdater<ZNRecord>()
     {
@@ -1330,13 +1333,13 @@ public class ZKHelixAdmin implements HelixAdmin
   }
   
   @Override
-  public ClusterConstraints getMessageConstraints(String clusterName)
+  public ClusterConstraints getConstraints(String clusterName, ConstraintType constraintType)
   {
     HelixDataAccessor accessor =
         new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(_zkClient));
 
     Builder keyBuilder = new Builder(clusterName);
-    return accessor.getProperty(keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()));
+    return accessor.getProperty(keyBuilder.constraint(constraintType.toString()));
   }
   
   /**

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
index b4dec19..04a72d7 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
@@ -24,6 +24,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -41,6 +42,7 @@ import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.helix.model.ConfigScope;
 import org.apache.helix.model.builder.ConfigScopeBuilder;
+import org.apache.helix.model.builder.ConstraintItemBuilder;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixException;
 import org.apache.helix.ZNRecord;
@@ -51,12 +53,17 @@ import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.model.ClusterConstraints;
+import org.apache.helix.model.ClusterConstraints.ConstraintType;
+import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.model.IdealState.IdealStateModeProperty;
+import org.apache.helix.store.PropertyJsonSerializer;
+import org.apache.helix.util.HelixUtil;
 import org.apache.helix.util.ZKClientPool;
 import org.apache.log4j.Logger;
 import org.codehaus.jackson.JsonGenerationException;
@@ -120,10 +127,15 @@ public class ClusterSetup
   public static final String dropStat = "dropStat";
   public static final String dropAlert = "dropAlert";
 
-  // get/set configs
+  // get/set/remove configs
   public static final String getConfig = "getConfig";
   public static final String setConfig = "setConfig";
   public static final String removeConfig = "removeConfig";
+  
+  // get/set/remove constraints
+  public static final String getConstraints = "getConstraints";
+  public static final String setConstraint = "setConstraint";
+  public static final String removeConstraint = "removeConstraint";
 
   static Logger _logger = Logger.getLogger(ClusterSetup.class);
   String _zkServerAddress;
@@ -148,22 +160,22 @@ public class ClusterSetup
   {
     _admin.addCluster(clusterName, overwritePrevious);
 
-    StateModelConfigGenerator generator = new StateModelConfigGenerator();
+    // StateModelConfigGenerator generator = new StateModelConfigGenerator();
     addStateModelDef(clusterName,
                      "MasterSlave",
-                     new StateModelDefinition(generator.generateConfigForMasterSlave()));
+                     new StateModelDefinition(StateModelConfigGenerator.generateConfigForMasterSlave()));
     addStateModelDef(clusterName,
                      "LeaderStandby",
-                     new StateModelDefinition(generator.generateConfigForLeaderStandby()));
+                     new StateModelDefinition(StateModelConfigGenerator.generateConfigForLeaderStandby()));
     addStateModelDef(clusterName,
                      "StorageSchemata",
-                     new StateModelDefinition(generator.generateConfigForStorageSchemata()));
+                     new StateModelDefinition(StateModelConfigGenerator.generateConfigForStorageSchemata()));
     addStateModelDef(clusterName,
                      "OnlineOffline",
-                     new StateModelDefinition(generator.generateConfigForOnlineOffline()));
+                     new StateModelDefinition(StateModelConfigGenerator.generateConfigForOnlineOffline()));
     addStateModelDef(clusterName,
                      "ScheduledTask",
-                     new StateModelDefinition(generator.generateConfigForScheduledTaskQueue()));
+                     new StateModelDefinition(StateModelConfigGenerator.generateConfigForScheduledTaskQueue()));
   }
 
   public void activateCluster(String clusterName, String grandCluster, boolean enable)
@@ -540,39 +552,29 @@ public class ClusterSetup
     _admin.rebalance(clusterName, resourceName, replica, keyPrefix);
   }
 
+  
   /**
-   * setConfig
+   * set config
    * 
-   * @param scopesStr
-   *          : scope=value, ... where scope=CLUSTER, RESOURCE, PARTICIPANT, PARTITION
-   * @param propertiesStr
-   *          : key=value, ... which represents a Map<String, String>
+   * @param scopesKeyValuePairs : csv-formated scope key-value pair. e.g CLUSTER=MyCluster,RESOURCE=MyDB,...

+   *                      where scope-key could be: CLUSTER, RESOURCE, PARTICIPANT, and PARTITION
+   * @param keyValuePairs : csv-formatted key-value pairs. e.g. k1=v1,k2=v2,...
    */
-  public void setConfig(String scopesStr, String propertiesStr)
+  public void setConfig(String scopesKeyValuePairs, String keyValuePairs)
   {
-    ConfigScope scope = new ConfigScopeBuilder().build(scopesStr);
-
-    // parse properties
-    String[] properties = propertiesStr.split("[\\s,]");
-    Map<String, String> propertiesMap = new TreeMap<String, String>();
-    for (String property : properties)
-    {
-      int idx = property.indexOf('=');
-      if (idx == -1)
-      {
-        logger.error("Invalid property string: " + property);
-        continue;
-      }
+    ConfigScope scope = new ConfigScopeBuilder().build(scopesKeyValuePairs);
 
-      String key = property.substring(0, idx);
-      String value = property.substring(idx + 1);
-      propertiesMap.put(key, value);
-    }
-    logger.debug("propertiesMap: " + propertiesMap);
-
-    _admin.setConfig(scope, propertiesMap);
+    Map<String, String> keyValueMap = HelixUtil.parseCsvFormatedKeyValuePairs(keyValuePairs);
+    _admin.setConfig(scope, keyValueMap);
   }
 
+  /**
+   * remove config
+   * 
+   * @param scopesStr : comma-separated scope key-value pair. e.g CLUSTER=MyCluster,RESOURCE=MyDB,...

+   *                      where scope-key could be: CLUSTER, RESOURCE, PARTICIPANT, and PARTITION
+   * @param keysStr : comma-separated keys. e.g. k1,k2...
+   */
   public void removeConfig(String scopesStr, String keysStr)
   {
     ConfigScope scope = new ConfigScopeBuilder().build(scopesStr);
@@ -584,8 +586,17 @@ public class ClusterSetup
     _admin.removeConfig(scope, keysSet);
   }
 
+  /**
+   * get config
+   * 
+   * @param scopesStr : comma-separated scope key-value pair. e.g CLUSTER=MyCluster,RESOURCE=MyDB,...

+   *                      where scope-key could be: CLUSTER, RESOURCE, PARTICIPANT, and PARTITION
+   * @param keysStr :  comma-separated keys. e.g. k1,k2...
+   * @return : json-formated key-value pair. e.g. {k1=v1,k2=v2,...}
+   */
   public String getConfig(String scopesStr, String keysStr)
   {
+    
     ConfigScope scope = new ConfigScopeBuilder().build(scopesStr);
 
     // parse keys
@@ -593,31 +604,65 @@ public class ClusterSetup
     Set<String> keysSet = new HashSet<String>(Arrays.asList(keys));
 
     Map<String, String> propertiesMap = _admin.getConfig(scope, keysSet);
-    StringBuffer sb = new StringBuffer();
-    for (String key : keys)
-    {
-      if (propertiesMap.containsKey(key))
-      {
-        if (sb.length() > 0)
-        {
-          sb.append("," + key + "=" + propertiesMap.get(key));
-        }
-        else
-        {
-          // sb.length()==0 means the first key=value
-          sb.append(key + "=" + propertiesMap.get(key));
-        }
-      }
-      else
-      {
-        logger.error("Config doesn't exist for key: " + key);
-      }
+    ZNRecord record = new ZNRecord(scopesStr);
+    record.setMapField(scopesStr, propertiesMap);
+    ZNRecordSerializer serializer = new ZNRecordSerializer();
+    return new String(serializer.serialize(record));
+  }
+
+  /**
+   * set constraint
+   * 
+   * @param clusterName
+   * @param constraintType
+   * @param constraintId
+   * @param constraintAttributesMap : csv-formated constraint key-value pairs
+   */
+  public void setConstraint(String clusterName, String constraintType, String constraintId,
String constraintAttributesMap) {
+    if (clusterName == null || constraintType == null || constraintId == null || constraintAttributesMap
== null) {
+      throw new IllegalArgumentException("fail to set constraint. missing clusterName|constraintType|constraintId|constraintAttributesMap");
+    }
+    
+    ConstraintType type = ConstraintType.valueOf(constraintType);
+    ConstraintItemBuilder builder = new ConstraintItemBuilder();
+    Map<String, String> constraintAttributes = HelixUtil.parseCsvFormatedKeyValuePairs(constraintAttributesMap);
+    ConstraintItem constraintItem = builder.addConstraintAttributes(constraintAttributes).build();
+    _admin.setConstraint(clusterName, type, constraintId, constraintItem);
+  }
+  
+  /**
+   * remove constraint
+   * 
+   * @param clusterName
+   * @param constraintType
+   * @param constraintId
+   */
+  public void removeConstraint(String clusterName, String constraintType, String constraintId)
{
+    if (clusterName == null || constraintType == null || constraintId == null) {
+      throw new IllegalArgumentException("fail to remove constraint. missing clusterName|constraintType|constraintId");
     }
 
-    System.out.println(sb.toString());
-    return sb.toString();
+    ConstraintType type = ConstraintType.valueOf(constraintType);
+    _admin.removeConstraint(clusterName, type, constraintId);
   }
+  
+  /**
+   * get constraints associated with given type
+   * 
+   * @param constraintType : constraint-type. e.g. MESSAGE_CONSTRAINT
+   * @return json-formated constraints
+   */
+  public String getConstraints(String clusterName, String constraintType) {
+    if (clusterName == null || constraintType == null) {
+      throw new IllegalArgumentException("fail to get constraint. missing clusterName|constraintType");
+    }
 
+    ConstraintType type = ConstraintType.valueOf(constraintType);
+    ClusterConstraints constraints = _admin.getConstraints(clusterName, type);
+    ZNRecordSerializer serializer = new ZNRecordSerializer();
+    return new String(serializer.serialize(constraints.getRecord()));
+  }
+  
   /**
    * Sets up a cluster with 6 Instances[localhost:8900 to localhost:8905], 1
    * resource[EspressoDB] with a replication factor of 3
@@ -957,19 +1002,58 @@ public class ClusterSetup
     dropAlertOption.setRequired(false);
     dropAlertOption.setArgName("clusterName alertName");
 
-    // set/get configs option
-    Option setConfOption =
-        OptionBuilder.withLongOpt(setConfig).withDescription("Set a config").create();
-    setConfOption.setArgs(2);
-    setConfOption.setRequired(false);
-    setConfOption.setArgName("ConfigScope(e.g. CLUSTER=cluster,RESOURCE=rc,...) KeyValueMap(e.g.
k1=v1,k2=v2,...)");
+    // TODO need deal with resource-names containing ","
+    // set/get/remove configs options
+    Option setConfOption = 
+        OptionBuilder.hasArgs(2)
+                     .isRequired(false)
+                     .withArgName("ConfigScope(e.g. CLUSTER=cluster,RESOURCE=rc,...) KeyValueMap(e.g.
k1=v1,k2=v2,...)")
+                     .withLongOpt(setConfig)
+                     .withDescription("Set a config")
+                     .create();
+
+    Option getConfOption = 
+        OptionBuilder.hasArgs(2)
+                     .isRequired(false)
+                     .withArgName("ConfigScope(e.g. CLUSTER=cluster,RESOURCE=rc,...) KeySet(e.g.
k1,k2,...)")
+                     .withLongOpt(getConfig)
+                     .withDescription("Get a config")
+                     .create();
+    
+    Option removeConfOption = 
+        OptionBuilder.hasArgs(2)
+                     .isRequired(false)
+                     .withArgName("ConfigScope(e.g. CLUSTER=cluster,RESOURCE=rc,...) KeySet(e.g.
k1,k2,...)")
+                     .withLongOpt(getConfig)
+                     .withDescription("Remove a config")
+                     .create();
+    
+    // set/get/remove constraints options
+    Option setConstraintOption = 
+        OptionBuilder.hasArgs(4)
+                     .isRequired(false)
+                     .withArgName("clusterName ConstraintType(e.g. MESSAGE_CONSTRAINT) ConstraintId
ConstraintAttributesMap(e.g. attribute1=valule1,attribute2=value2,...)")
+                     .withLongOpt(setConstraint)
+                     .withDescription("Set a constraint associated with a give id. create
if not exist")
+                     .create();
 
-    Option getConfOption =
-        OptionBuilder.withLongOpt(getConfig).withDescription("Get a config").create();
-    getConfOption.setArgs(2);
-    getConfOption.setRequired(false);
-    getConfOption.setArgName("ConfigScope(e.g. CLUSTER=cluster,RESOURCE=rc,...) KeySet(e.g.
k1,k2,...)");
+    Option getConstraintsOption = 
+        OptionBuilder.hasArgs(2)
+                     .isRequired(false)
+                     .withArgName("clusterName ConstraintType(e.g. MESSAGE_CONSTRAINT)")
+                     .withLongOpt(getConstraints)
+                     .withDescription("Get constraints associated with given type")
+                     .create();
 
+    Option removeConstraintOption = 
+        OptionBuilder.hasArgs(3)
+                     .isRequired(false)
+                     .withArgName("clusterName ConstraintType(e.g. MESSAGE_CONSTRAINT) ConstraintId")
+                     .withLongOpt(removeConstraint)
+                     .withDescription("Remove a constraint associated with given id")
+                     .create();
+
+    
     OptionGroup group = new OptionGroup();
     group.setRequired(true);
     group.addOption(rebalanceOption);
@@ -1013,6 +1097,10 @@ public class ClusterSetup
     group.addOption(getConfOption);
     group.addOption(addResourcePropertyOption);
     group.addOption(removeResourcePropertyOption);
+    group.addOption(removeConfOption);
+    group.addOption(setConstraintOption);
+    group.addOption(getConstraintsOption);
+    group.addOption(removeConstraintOption);
 
     Options options = new Options();
     options.addOption(helpOption);
@@ -1509,18 +1597,44 @@ public class ClusterSetup
 
       setupTool.swapInstance(clusterName, oldInstanceName, newInstanceName);
     }
-    else if (cmd.hasOption(setConfig))
-    {
-      String scopeStr = cmd.getOptionValues(setConfig)[0];
-      String propertiesStr = cmd.getOptionValues(setConfig)[1];
+    // set/get/remove config options
+    else if (cmd.hasOption(setConfig)) {
+      String values[] = cmd.getOptionValues(setConfig);
+      String scopeStr = values[0];
+      String propertiesStr = values[1];
       setupTool.setConfig(scopeStr, propertiesStr);
-    }
-    else if (cmd.hasOption(getConfig))
-    {
-      String scopeStr = cmd.getOptionValues(getConfig)[0];
-      String keysStr = cmd.getOptionValues(getConfig)[1];
-      setupTool.getConfig(scopeStr, keysStr);
-    }
+    } else if (cmd.hasOption(getConfig)) {
+      String values[] = cmd.getOptionValues(getConfig);
+      String scopeStr = values[0];
+      String keySetStr = values[1];
+      setupTool.getConfig(scopeStr, keySetStr);
+    }  else if (cmd.hasOption(removeConfig)) {
+      String values[] = cmd.getOptionValues(removeConfig);
+      String scoepStr = values[0];
+      String keySetStr = values[1];
+      setupTool.removeConfig(scoepStr, keySetStr);
+    }
+    // set/get/remove constraint options
+    else if (cmd.hasOption(setConstraint)) {
+      String values[] = cmd.getOptionValues(setConstraint);
+      String clusterName = values[0];
+      String constraintType = values[1];
+      String constraintId = values[2];
+      String constraintAttributesMap = values[3];
+      setupTool.setConstraint(clusterName, constraintType, constraintId, constraintAttributesMap);
+    } else if (cmd.hasOption(getConstraints)) {
+      String values[] = cmd.getOptionValues(getConstraints);
+      String clusterName = values[0];
+      String constraintType = values[1];
+      setupTool.getConstraints(clusterName, constraintType);
+    } else if (cmd.hasOption(removeConstraint)) {
+      String values[] = cmd.getOptionValues(removeConstraint);
+      String clusterName = values[0];
+      String constraintType = values[1];
+      String constraintId = values[2];
+      setupTool.removeConstraint(clusterName, constraintType, constraintId);
+    }
+    // help option
     else if (cmd.hasOption(help))
     {
       printUsage(cliOptions);

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java b/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java
index 1d41f8f..34ffdc4 100644
--- a/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java
@@ -19,11 +19,18 @@ package org.apache.helix.util;
  * under the License.
  */
 
+import java.util.Map;
+import java.util.TreeMap;
+
 import org.apache.helix.PropertyPathConfig;
 import org.apache.helix.PropertyType;
+import org.apache.helix.store.PropertyJsonSerializer;
+import org.apache.log4j.Logger;
 
 public final class HelixUtil
 {
+  static private Logger LOG = Logger.getLogger(HelixUtil.class);
+
   private HelixUtil()
   {
   }
@@ -198,4 +205,30 @@ public final class HelixUtil
   public static String getZkName(String path) {
       return path.substring(path.lastIndexOf('/') + 1);
   }
+  
+  /**
+   * parse a csv-formated key-value pairs
+   * 
+   * @param keyValuePairs : csv-formatted key-value pairs. e.g. k1=v1,k2=v2,...
+   * @return 
+   */
+  public static Map<String, String> parseCsvFormatedKeyValuePairs(String keyValuePairs)
{
+    String[] pairs = keyValuePairs.split("[\\s,]");
+    Map<String, String> keyValueMap = new TreeMap<String, String>();
+    for (String pair : pairs)
+    {
+      int idx = pair.indexOf('=');
+      if (idx == -1)
+      {
+        LOG.error("Invalid key-value pair: " + pair + ". Igonore it.");
+        continue;
+      }
+
+      String key = pair.substring(0, idx);
+      String value = pair.substring(idx + 1);
+      keyValueMap.put(key, value);
+    }
+    return keyValueMap;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
b/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
index 8fa56c6..4cf8c80 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
@@ -88,7 +88,7 @@ public class TestMessageThrottle extends ZkIntegrationTestBase
 //    // constraints.put("TRANSITION", "OFFLINE-SLAVE");
 //    constraints.put("CONSTRAINT_VALUE", "1");
 //    constraints.put("INSTANCE", ".*");
-    admin.addMessageConstraint(clusterName, "constraint1", builder.build());
+    admin.setConstraint(clusterName, ConstraintType.MESSAGE_CONSTRAINT, "constraint1", builder.build());
     
 
     final ZKHelixDataAccessor accessor =

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
b/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
index bae97d5..cc4f9ea 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
@@ -45,6 +45,7 @@ import org.apache.helix.messaging.AsyncCallback;
 import org.apache.helix.messaging.handling.HelixTaskResult;
 import org.apache.helix.messaging.handling.MessageHandler;
 import org.apache.helix.messaging.handling.MessageHandlerFactory;
+import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Message.MessageState;
@@ -734,8 +735,10 @@ public class TestSchedulerMessage extends ZkStandAloneCMTestBaseWithPropertyServ
     constraints.put("TRANSITION", "OFFLINE-COMPLETED");
     constraints.put("CONSTRAINT_VALUE", "1");
     constraints.put("INSTANCE", ".*");
-    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",

-        new ConstraintItem(constraints));
+    manager.getClusterManagmentTool().setConstraint(manager.getClusterName(), 
+                                                    ConstraintType.MESSAGE_CONSTRAINT, 
+                                                    "constraint1", 
+                                                    new ConstraintItem(constraints));
     
     MockAsyncCallback callback = new MockAsyncCallback();
     cr.setInstanceName("localhost_%");
@@ -909,8 +912,10 @@ public class TestSchedulerMessage extends ZkStandAloneCMTestBaseWithPropertyServ
     constraints.put("TRANSITION", "OFFLINE-COMPLETED");
     constraints.put("CONSTRAINT_VALUE", "1");
     constraints.put("INSTANCE", ".*");
-    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",

-        new ConstraintItem(constraints));
+    manager.getClusterManagmentTool().setConstraint(manager.getClusterName(), 
+                                                    ConstraintType.MESSAGE_CONSTRAINT,
+                                                    "constraint1", 
+                                                    new ConstraintItem(constraints));
     
     // Send scheduler message
     crString = sw.toString();
@@ -982,8 +987,10 @@ public class TestSchedulerMessage extends ZkStandAloneCMTestBaseWithPropertyServ
     }
     Assert.assertEquals(count, _PARTITIONS * 3);
     
-    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",

-        new ConstraintItem(new TreeMap<String, String>()));
+    manager.getClusterManagmentTool().setConstraint(manager.getClusterName(), 
+                                                    ConstraintType.MESSAGE_CONSTRAINT,
+                                                    "constraint1", 
+                                                    new ConstraintItem(new TreeMap<String,
String>()));
     
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/7a868187/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
----------------------------------------------------------------------
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 bc42493..fd3c495 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
@@ -246,12 +246,12 @@ public class TestZkHelixAdmin extends ZkUnitTestBase
       Assert.assertTrue(ZKUtil.isClusterSetup(clusterName, _gZkClient), "Cluster should be
setup");
 
       // test admin.getMessageConstraints()
-      ClusterConstraints constraints = tool.getMessageConstraints(clusterName);
+      ClusterConstraints constraints = tool.getConstraints(clusterName, ConstraintType.MESSAGE_CONSTRAINT);
       Assert.assertNull(constraints, "message-constraint should NOT exist for cluster: "
+ className);
 
       // remove non-exist constraint
       try {
-        tool.removeMessageConstraint(clusterName, "constraint1");
+        tool.removeConstraint(clusterName, ConstraintType.MESSAGE_CONSTRAINT, "constraint1");
         // will leave a null message-constraint znode on zk
       } catch (Exception e) {
         Assert.fail("Should not throw exception when remove a non-exist constraint.");
@@ -261,7 +261,7 @@ public class TestZkHelixAdmin extends ZkUnitTestBase
       ConstraintItemBuilder builder = new ConstraintItemBuilder();
       builder.addConstraintAttribute(ConstraintAttribute.RESOURCE.toString(), "MyDB")
              .addConstraintAttribute(ConstraintAttribute.CONSTRAINT_VALUE.toString(), "1");
-      tool.addMessageConstraint(clusterName, "constraint1", builder.build());
+      tool.setConstraint(clusterName, ConstraintType.MESSAGE_CONSTRAINT, "constraint1", builder.build());
 
       HelixDataAccessor accessor = new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(_gZkClient));
       PropertyKey.Builder keyBuilder = new PropertyKey.Builder(clusterName);
@@ -273,7 +273,7 @@ public class TestZkHelixAdmin extends ZkUnitTestBase
       Assert.assertEquals(item.getAttributeValue(ConstraintAttribute.RESOURCE), "MyDB");
       
       // test admin.getMessageConstraints()
-      constraints = tool.getMessageConstraints(clusterName);
+      constraints = tool.getConstraints(clusterName, ConstraintType.MESSAGE_CONSTRAINT);
       Assert.assertNotNull(constraints, "message-constraint should exist");
       item = constraints.getConstraintItem("constraint1");
       Assert.assertNotNull(item, "message-constraint for constraint1 should exist");
@@ -282,7 +282,7 @@ public class TestZkHelixAdmin extends ZkUnitTestBase
       
 
       // remove a exist message-constraint
-      tool.removeMessageConstraint(clusterName, "constraint1");
+      tool.removeConstraint(clusterName, ConstraintType.MESSAGE_CONSTRAINT, "constraint1");
       constraints = accessor.getProperty(keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()));
       Assert.assertNotNull(constraints, "message-constraint should exist");
       item = constraints.getConstraintItem("constraint1");


Mime
View raw message