CLOUDSTACK-1484: provide api.throttling.enabled gloabl configuration settings to enable/disable api throttling feature.

This commit is contained in:
Min Chen 2013-03-04 16:31:58 -08:00
parent 5750fd9631
commit 9c5c4753e9
9 changed files with 80 additions and 5 deletions

View File

@ -52,8 +52,12 @@ public class ListCapabilitiesCmd extends BaseCmd {
response.setProjectInviteRequired((Boolean)capabilities.get("projectInviteRequired"));
response.setAllowUsersCreateProjects((Boolean)capabilities.get("allowusercreateprojects"));
response.setDiskOffMaxSize((Long)capabilities.get("customDiskOffMaxSize"));
response.setApiLimitInterval((Integer)capabilities.get("apiLimitInterval"));
response.setApiLimitMax((Integer)capabilities.get("apiLimitMax"));
if (capabilities.containsKey("apiLimitInterval")) {
response.setApiLimitInterval((Integer) capabilities.get("apiLimitInterval"));
}
if (capabilities.containsKey("apiLimitMax")) {
response.setApiLimitMax((Integer) capabilities.get("apiLimitMax"));
}
response.setObjectName("capability");
response.setResponseName(getCommandName());
this.setResponseObject(response);

View File

@ -29,6 +29,8 @@ import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.ratelimit.ApiRateLimitService;
import org.apache.log4j.Logger;
import com.cloud.configuration.Config;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.user.Account;
import com.cloud.user.UserContext;
@ -43,6 +45,9 @@ public class ResetApiLimitCmd extends BaseCmd {
@Inject
ApiRateLimitService _apiLimitService;
@Inject
ConfigurationDao _configDao;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@ -89,6 +94,10 @@ public class ResetApiLimitCmd extends BaseCmd {
@Override
public void execute(){
boolean apiLimitEnabled = Boolean.parseBoolean(_configDao.getValue(Config.ApiLimitEnabled.key()));
if ( !apiLimitEnabled ){
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "This api is only available when api.throttling.enabled = true.");
}
boolean result = _apiLimitService.resetApiLimit(this.accountId);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());

View File

@ -21,6 +21,7 @@ import java.util.List;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
@ -35,6 +36,9 @@ import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.ratelimit.ApiRateLimitService;
import com.cloud.configuration.Config;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
@ -55,6 +59,9 @@ public class GetApiLimitCmd extends BaseCmd {
@Inject
ApiRateLimitService _apiLimitService;
@Inject
ConfigurationDao _configDao;
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -76,6 +83,10 @@ public class GetApiLimitCmd extends BaseCmd {
@Override
public void execute(){
boolean apiLimitEnabled = Boolean.parseBoolean(_configDao.getValue(Config.ApiLimitEnabled.key()));
if ( !apiLimitEnabled ){
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "This api is only available when api.throttling.enabled = true.");
}
Account caller = UserContext.current().getCaller();
ApiLimitResponse response = _apiLimitService.searchApiLimit(caller);
response.setResponseName(getCommandName());

View File

@ -33,4 +33,6 @@ public interface ApiRateLimitService extends PluggableService{
public void setTimeToLive(int timeToLive);
public void setMaxAllowed(int max);
public void setEnabled(boolean enabled);
}

View File

@ -49,6 +49,11 @@ import org.springframework.stereotype.Component;
public class ApiRateLimitServiceImpl extends AdapterBase implements APIChecker, ApiRateLimitService {
private static final Logger s_logger = Logger.getLogger(ApiRateLimitServiceImpl.class);
/**
* True if api rate limiting is enabled
*/
private boolean enabled = false;
/**
* Fixed time duration where api rate limit is set, in seconds
*/
@ -73,6 +78,10 @@ public class ApiRateLimitServiceImpl extends AdapterBase implements APIChecker,
if (_store == null) {
// get global configured duration and max values
String isEnabled = _configDao.getValue(Config.ApiLimitEnabled.key());
if ( isEnabled != null ){
enabled = Boolean.parseBoolean(isEnabled);
}
String duration = _configDao.getValue(Config.ApiLimitInterval.key());
if (duration != null) {
timeToLive = Integer.parseInt(duration);
@ -140,6 +149,10 @@ public class ApiRateLimitServiceImpl extends AdapterBase implements APIChecker,
@Override
public boolean checkAccess(User user, String apiCommandName) throws PermissionDeniedException {
// check if api rate limiting is enabled or not
if (!enabled){
return true;
}
Long accountId = user.getAccountId();
Account account = _accountService.getAccount(accountId);
if ( _accountService.isRootAdmin(account.getType())){
@ -192,5 +205,11 @@ public class ApiRateLimitServiceImpl extends AdapterBase implements APIChecker,
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View File

@ -55,6 +55,7 @@ public class ApiRateLimitTest {
when(_configDao.getValue(Config.ApiLimitInterval.key())).thenReturn(null);
when(_configDao.getValue(Config.ApiLimitMax.key())).thenReturn(null);
when(_configDao.getValue(Config.ApiLimitCacheSize.key())).thenReturn(null);
when(_configDao.getValue(Config.ApiLimitEnabled.key())).thenReturn("true"); // enable api rate limiting
_limitService._configDao = _configDao;
_limitService.configure("ApiRateLimitTest", Collections.<String, Object> emptyMap());
@ -106,6 +107,8 @@ public class ApiRateLimitTest {
+ " accesses take less than a second to perform", isUnderLimit(key));
}
@Test
public void canDoReasonableNumberOfApiAccessPerSecond() throws Exception {
int allowedRequests = 200;
@ -232,4 +235,26 @@ public class ApiRateLimitTest {
}
@Test
public void disableApiLimit() throws Exception {
try {
int allowedRequests = 200;
_limitService.setMaxAllowed(allowedRequests);
_limitService.setTimeToLive(1);
_limitService.setEnabled(false);
User key = createFakeUser();
for (int i = 0; i < allowedRequests + 1; i++) {
assertTrue("We should allow more than " + allowedRequests + " requests per second when api throttling is disabled.",
isUnderLimit(key));
}
} finally {
_limitService.setEnabled(true); // enable api throttling to avoid
// impacting other testcases
}
}
}

View File

@ -372,7 +372,8 @@ public enum Config {
IntervalToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "interval.baremetal.securitygroup.agent.echo", "10", "Interval to echo baremetal security group agent, in seconds", null),
TimeoutToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "timeout.baremetal.securitygroup.agent.echo", "3600", "Timeout to echo baremetal security group agent, in seconds, the provisioning process will be treated as a failure", null),
ApiLimitInterval("Advanced", ManagementServer.class, Integer.class, "api.throttling.interval", "1", "Time interval (in seconds) to reset API count", null),
ApiLimitEnabled("Advanced", ManagementServer.class, Boolean.class, "api.throttling.enabled", "true", "Enable/disable Api rate limit", null),
ApiLimitInterval("Advanced", ManagementServer.class, Integer.class, "api.throttling.interval", "1", "Time interval (in seconds) to reset API count", null),
ApiLimitMax("Advanced", ManagementServer.class, Integer.class, "api.throttling.max", "25", "Max allowed number of APIs within fixed interval", null),
ApiLimitCacheSize("Advanced", ManagementServer.class, Integer.class, "api.throttling.cachesize", "50000", "Account based API count cache size", null),

View File

@ -2535,6 +2535,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
String userPublicTemplateEnabled = _configs.get(Config.AllowPublicUserTemplates.key());
// add some parameters UI needs to handle API throttling
boolean apiLimitEnabled = Boolean.parseBoolean(_configDao.getValue(Config.ApiLimitEnabled.key()));
Integer apiLimitInterval = Integer.valueOf(_configDao.getValue(Config.ApiLimitInterval.key()));
Integer apiLimitMax = Integer.valueOf(_configDao.getValue(Config.ApiLimitMax.key()));
@ -2546,8 +2547,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
capabilities.put("projectInviteRequired", _projectMgr.projectInviteRequired());
capabilities.put("allowusercreateprojects", _projectMgr.allowUserToCreateProject());
capabilities.put("customDiskOffMaxSize", diskOffMaxSize);
capabilities.put("apiLimitInterval", apiLimitInterval);
capabilities.put("apiLimitMax", apiLimitMax);
if (apiLimitEnabled) {
capabilities.put("apiLimitInterval", apiLimitInterval);
capabilities.put("apiLimitMax", apiLimitMax);
}
return capabilities;
}

View File

@ -241,6 +241,7 @@ UPDATE `cloud`.`volumes` set uuid=id WHERE uuid is NULL;
-- UPDATE `cloud`.`conditions` set uuid=id WHERE uuid is NULL;
INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'detail.batch.query.size', '2000', 'Default entity detail batch query size for listing');
INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'api.throttling.enabled', 'false', 'Enable/Disable Api rate limit');
INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'api.throttling.interval', '1', 'Time interval (in seconds) to reset API count');
INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'api.throttling.max', '25', 'Max allowed number of APIs within fixed interval');
INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'api.throttling.cachesize', '50000', 'Account based API count cache size');