mirror of https://github.com/apache/cloudstack.git
Add API throttling config items and APILimitChecker Adapter interface,
add api limit checking in APIServer flow.
This commit is contained in:
parent
dcbb0ecef5
commit
8d98daa1be
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue