You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mc...@apache.org on 2013/01/10 18:52:13 UTC

[1/9] git commit: Add API throttling config items and APILimitChecker Adapter interface, add api limit checking in APIServer flow.

Add API throttling config items and APILimitChecker Adapter interface,
add api limit checking in APIServer flow.

Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/8d98daa1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/8d98daa1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/8d98daa1

Branch: refs/heads/api_limit
Commit: 8d98daa1beb46fe61022727b25c9e5b75db5e630
Parents: dcbb0ec
Author: Min Chen <mi...@citrix.com>
Authored: Wed Jan 9 16:11:23 2013 -0800
Committer: Min Chen <mi...@citrix.com>
Committed: Wed Jan 9 16:11:23 2013 -0800

----------------------------------------------------------------------
 .../org/apache/cloudstack/acl/APILimitChecker.java |   28 +++++++++++++++
 api/src/org/apache/cloudstack/api/BaseCmd.java     |    1 +
 server/src/com/cloud/api/ApiServer.java            |   27 ++++++++++++++
 server/src/com/cloud/configuration/Config.java     |    6 +++-
 4 files changed, 61 insertions(+), 1 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8d98daa1/api/src/org/apache/cloudstack/acl/APILimitChecker.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/acl/APILimitChecker.java b/api/src/org/apache/cloudstack/acl/APILimitChecker.java
new file mode 100644
index 0000000..3a1db70
--- /dev/null
+++ b/api/src/org/apache/cloudstack/acl/APILimitChecker.java
@@ -0,0 +1,28 @@
+// 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.cloudstack.acl;
+
+import com.cloud.user.Account;
+import com.cloud.utils.component.Adapter;
+
+/**
+ * APILimitChecker checks if we should block an API request based on pre-set account based api limit.
+ */
+public interface APILimitChecker extends Adapter {
+    // Interface for checking if the account is over its api limit
+    boolean isUnderLimit(Account account);
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8d98daa1/api/src/org/apache/cloudstack/api/BaseCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java
index d964e70..ae13012 100644
--- a/api/src/org/apache/cloudstack/api/BaseCmd.java
+++ b/api/src/org/apache/cloudstack/api/BaseCmd.java
@@ -89,6 +89,7 @@ public abstract class BaseCmd {
     public static final int PARAM_ERROR = 431;
     public static final int UNSUPPORTED_ACTION_ERROR = 432;
     public static final int PAGE_LIMIT_EXCEED = 433;
+    public static final int API_LIMIT_EXCEED = 434;
 
     // Server error codes
     public static final int INTERNAL_ERROR = 530;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8d98daa1/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index 56cef12..2bb7fdd 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -52,6 +52,7 @@ import javax.servlet.http.HttpSession;
 
 import com.cloud.utils.ReflectUtil;
 import org.apache.cloudstack.acl.APIAccessChecker;
+import org.apache.cloudstack.acl.APILimitChecker;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.*;
 import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
@@ -141,6 +142,7 @@ public class ApiServer implements HttpRequestHandler {
     private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName());
 
     public static boolean encodeApiResponse = false;
+    public static boolean apiThrottlingEnabled = true;
     public static String jsonContentType = "text/javascript";
     private ApiDispatcher _dispatcher;
 
@@ -148,6 +150,8 @@ public class ApiServer implements HttpRequestHandler {
     @Inject private DomainManager _domainMgr = null;
     @Inject private AsyncJobManager _asyncMgr = null;
 
+    @Inject(adapter = APILimitChecker.class)
+    protected Adapters<APILimitChecker> _apiLimitCheckers;
     @Inject(adapter = APIAccessChecker.class)
     protected Adapters<APIAccessChecker> _apiAccessCheckers;
     @Inject(adapter = ApiDiscoveryService.class)
@@ -217,6 +221,7 @@ public class ApiServer implements HttpRequestHandler {
         if (jsonType != null) {
             jsonContentType = jsonType;
         }
+        apiThrottlingEnabled = Boolean.valueOf(configDao.getValue(Config.ApiLimitEnabled.key()));
 
         if (apiPort != null) {
             ListenerThread listenerThread = new ListenerThread(this, apiPort);
@@ -552,6 +557,14 @@ public class ApiServer implements HttpRequestHandler {
             // if userId not null, that mean that user is logged in
             if (userId != null) {
             	User user = ApiDBUtils.findUserById(userId);
+            	if (apiThrottlingEnabled){
+            	    // go through each API limit checker
+            	    if (!isRequestAllowed(user)) {
+            	        //FIXME: more detailed message regarding when he/she can retry
+                        s_logger.warn("The given user has reached his/her account api limit, please retry later");
+                        throw new ServerApiException(BaseCmd.API_LIMIT_EXCEED, "The given user has reached his/her account api limit");
+            	    }
+            	}
                 if (!isCommandAvailable(user, commandName)) {
                     s_logger.warn("The given command:" + commandName + " does not exist or it is not available for user");
                     throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user");
@@ -791,6 +804,20 @@ public class ApiServer implements HttpRequestHandler {
         return true;
     }
 
+    private boolean isRequestAllowed(User user) {
+        Account account = ApiDBUtils.findAccountById(user.getAccountId());
+        if ( _accountMgr.isRootAdmin(account.getType()) ){
+            // no api throttling for root admin
+            return true;
+        }
+        for (APILimitChecker apiChecker : _apiLimitCheckers) {
+            // Fail the checking if any checker fails to verify
+            if (!apiChecker.isUnderLimit(account))
+                return false;
+        }
+        return true;
+    }
+
     private boolean isCommandAvailable(User user, String commandName) {
         for (APIAccessChecker apiChecker : _apiAccessCheckers) {
             // Fail the checking if any checker fails to verify

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8d98daa1/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index b91fbdd..ae7651c 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -358,8 +358,12 @@ public enum Config {
     DetailBatchQuerySize("Advanced", ManagementServer.class, Integer.class, "detail.batch.query.size", "2000", "Default entity detail batch query size for listing", null),
 
 	ConcurrentSnapshotsThresholdPerHost("Advanced", ManagementServer.class, Long.class, "concurrent.snapshots.threshold.perhost",
-	                null, "Limits number of snapshots that can be handled by the host concurrently; default is NULL - unlimited", null);
+	                null, "Limits number of snapshots that can be handled by the host concurrently; default is NULL - unlimited", null),
 
+	// API throttling
+    ApiLimitInterval("Advanced", ManagementServer.class, Long.class, "api.throttling.interval", "1", "The default time interval in seconds used to set account based api limit", null),
+    ApiLimitMax("Advanced", ManagementServer.class, Long.class, "api.throttling.max", "25", "The max number of API requests within api.throttling.interval duration", null),
+    ApiLimitEnabled("Advanced", ManagementServer.class, Boolean.class, "api.throttling.enabled", "true", "If true, api throttline feature is enabled", "true,false");
 
 	private final String _category;
 	private final Class<?> _componentClass;