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/18 00:17:48 UTC

[12/13] git commit: Fix some bugs and add java integration test for api rate limit plugin.

Fix some bugs and add java integration test for api rate limit plugin.

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

Branch: refs/heads/api_limit
Commit: 86ada92ffa20d7624ff3c411ac24ea9dbd9e0196
Parents: c1a540c
Author: Min Chen <mi...@citrix.com>
Authored: Thu Jan 17 15:13:51 2013 -0800
Committer: Min Chen <mi...@citrix.com>
Committed: Thu Jan 17 15:13:51 2013 -0800

----------------------------------------------------------------------
 client/tomcatconf/components.xml.in                |    2 +-
 plugins/api/rate-limit/pom.xml                     |   22 ++
 .../api/command/user/ratelimit/GetApiLimitCmd.java |    3 +-
 .../cloudstack/ratelimit/integration/APITest.java  |  211 ++++++++++++++
 .../ratelimit/integration/LoginResponse.java       |  142 ++++++++++
 .../integration/RateLimitIntegrationTest.java      |  214 +++++++++++++++
 server/test/com/cloud/api/APITest.java             |   39 ++-
 server/test/com/cloud/api/ListPerfTest.java        |  140 ----------
 8 files changed, 622 insertions(+), 151 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/client/tomcatconf/components.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in
index e19b418..8157051 100755
--- a/client/tomcatconf/components.xml.in
+++ b/client/tomcatconf/components.xml.in
@@ -185,7 +185,7 @@ under the License.
         <pluggableservice name="ApiDiscoveryService" key="org.apache.cloudstack.discovery.ApiDiscoveryService" class="org.apache.cloudstack.discovery.ApiDiscoveryServiceImpl"/>
         <pluggableservice name="VirtualRouterElementService" key="com.cloud.network.element.VirtualRouterElementService" class="com.cloud.network.element.VirtualRouterElement"/>
         <pluggableservice name="NiciraNvpElementService" key="com.cloud.network.element.NiciraNvpElementService" class="com.cloud.network.element.NiciraNvpElement"/>
-        <pluggableservice name="ApiRateLimitService" key="org.apache.cloudstack.api.ratelimit.ApiRateLimitService" class="org.apache.cloudstack.ratelimit.ApiRateLimitServiceImpl"/>
+        <pluggableservice name="ApiRateLimitService" key="org.apache.cloudstack.ratelimit.ApiRateLimitService" class="org.apache.cloudstack.ratelimit.ApiRateLimitServiceImpl"/>
         <dao name="OvsTunnelInterfaceDao" class="com.cloud.network.ovs.dao.OvsTunnelInterfaceDaoImpl" singleton="false"/>
         <dao name="OvsTunnelAccountDao" class="com.cloud.network.ovs.dao.OvsTunnelNetworkDaoImpl" singleton="false"/>
         <dao name="NiciraNvpDao" class="com.cloud.network.dao.NiciraNvpDaoImpl" singleton="false"/>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/plugins/api/rate-limit/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/api/rate-limit/pom.xml b/plugins/api/rate-limit/pom.xml
index 416c901..1f03309 100644
--- a/plugins/api/rate-limit/pom.xml
+++ b/plugins/api/rate-limit/pom.xml
@@ -26,4 +26,26 @@
     <version>4.1.0-SNAPSHOT</version>
     <relativePath>../../pom.xml</relativePath>
   </parent>
+  <build>
+    <defaultGoal>install</defaultGoal>
+    <sourceDirectory>src</sourceDirectory>
+    <testSourceDirectory>test</testSourceDirectory>
+    <testResources>
+      <testResource>
+        <directory>test/resources</directory>
+      </testResource>
+    </testResources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <argLine>-Xmx1024m</argLine>
+          <excludes>
+            <exclude>org/apache/cloudstack/ratelimit/integration/*</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>  
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
----------------------------------------------------------------------
diff --git a/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java b/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
index ad1fb28..2b7b8e6 100644
--- a/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
+++ b/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
@@ -46,7 +46,7 @@ import com.cloud.user.UserContext;
 import com.cloud.utils.exception.CloudRuntimeException;
 
 @APICommand(name = "getApiLimit", responseObject=ApiLimitResponse.class, description="Get API limit count for the caller")
-public class GetApiLimitCmd extends BaseListCmd {
+public class GetApiLimitCmd extends BaseCmd {
     private static final Logger s_logger = Logger.getLogger(GetApiLimitCmd.class.getName());
 
     private static final String s_name = "getapilimitresponse";
@@ -81,6 +81,7 @@ public class GetApiLimitCmd extends BaseListCmd {
         Account caller = UserContext.current().getCaller();
         ApiLimitResponse response = _apiLimitService.searchApiLimit(caller);
         response.setResponseName(getCommandName());
+        response.setObjectName("apilimit");
         this.setResponseObject(response);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/APITest.java
----------------------------------------------------------------------
diff --git a/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/APITest.java b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/APITest.java
new file mode 100644
index 0000000..7701b15
--- /dev/null
+++ b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/APITest.java
@@ -0,0 +1,211 @@
+// 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
+// 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.ratelimit.integration;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.InputStreamReader;
+import java.math.BigInteger;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.cloudstack.api.response.SuccessResponse;
+
+import com.cloud.api.ApiGsonHelper;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.google.gson.Gson;
+
+/**
+ * Base class for API Test
+ *
+ * @author Min Chen
+ *
+ */
+public abstract class APITest {
+
+    protected String rootUrl = "http://localhost:8080/client/api";
+    protected String sessionKey = null;
+    protected String cookieToSent = null;
+
+
+    /**
+     * Sending an api request through Http GET
+     * @param command command name
+     * @param params command query parameters in a HashMap
+     * @return http request response string
+     */
+    protected String sendRequest(String command, HashMap<String, String> params){
+        try {
+            // Construct query string
+            StringBuilder sBuilder = new StringBuilder();
+            sBuilder.append("command=");
+            sBuilder.append(command);
+            if ( params != null && params.size() > 0){
+                Iterator<String> keys = params.keySet().iterator();
+                while (keys.hasNext()){
+                    String key = keys.next();
+                    sBuilder.append("&");
+                    sBuilder.append(key);
+                    sBuilder.append("=");
+                    sBuilder.append(URLEncoder.encode(params.get(key), "UTF-8"));
+                }
+            }
+
+            // Construct request url
+            String reqUrl = rootUrl + "?" + sBuilder.toString();
+
+            // Send Http GET request
+            URL url = new URL(reqUrl);
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setRequestMethod("GET");
+
+            if ( !command.equals("login") && cookieToSent != null){
+                // add the cookie to a request
+                conn.setRequestProperty("Cookie", cookieToSent);
+            }
+            conn.connect();
+
+
+            if ( command.equals("login")){
+                // if it is login call, store cookie
+                String headerName=null;
+                for (int i=1; (headerName = conn.getHeaderFieldKey(i))!=null; i++) {
+                    if (headerName.equals("Set-Cookie")) {
+                        String cookie = conn.getHeaderField(i);
+                        cookie = cookie.substring(0, cookie.indexOf(";"));
+                        String cookieName = cookie.substring(0, cookie.indexOf("="));
+                        String cookieValue = cookie.substring(cookie.indexOf("=") + 1, cookie.length());
+                        cookieToSent = cookieName + "=" + cookieValue;
+                    }
+                }
+            }
+
+            // Get the response
+            StringBuilder response = new StringBuilder();
+            BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+            String line;
+            try {
+                while ((line = rd.readLine()) != null) {
+                    response.append(line);
+                }
+            } catch (EOFException ex) {
+                // ignore this exception
+                System.out.println("EOF exception due to java bug");
+            }
+            rd.close();
+
+
+
+            return response.toString();
+
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Problem with sending api request", e);
+        }
+    }
+
+    protected String createMD5String(String password) {
+        MessageDigest md5;
+        try {
+            md5 = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new CloudRuntimeException("Error", e);
+        }
+
+        md5.reset();
+        BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes()));
+
+        // make sure our MD5 hash value is 32 digits long...
+        StringBuffer sb = new StringBuffer();
+        String pwStr = pwInt.toString(16);
+        int padding = 32 - pwStr.length();
+        for (int i = 0; i < padding; i++) {
+            sb.append('0');
+        }
+        sb.append(pwStr);
+        return sb.toString();
+    }
+
+
+    protected Object fromSerializedString(String result, Class<?> repCls) {
+        try {
+            if (result != null && !result.isEmpty()) {
+                // get real content
+                int start;
+                int end;
+                if (repCls == LoginResponse.class || repCls == SuccessResponse.class) {
+
+                    start = result.indexOf('{', result.indexOf('{') + 1); // find
+                                                                          // the
+                                                                          // second
+                                                                          // {
+
+                    end = result.lastIndexOf('}', result.lastIndexOf('}') - 1); // find
+                                                                                // the
+                                                                                // second
+                                                                                // }
+                                                                                // backwards
+
+                } else {
+                    // get real content
+                    start = result.indexOf('{', result.indexOf('{', result.indexOf('{') + 1) + 1); // find
+                                                                                                   // the
+                                                                                                   // third
+                                                                                                   // {
+                    end = result.lastIndexOf('}', result.lastIndexOf('}', result.lastIndexOf('}') - 1) - 1); // find
+                                                                                                             // the
+                                                                                                             // third
+                                                                                                             // }
+                                                                                                             // backwards
+                }
+                if (start < 0 || end < 0) {
+                    throw new CloudRuntimeException("Response format is wrong: " + result);
+                }
+                String content = result.substring(start, end + 1);
+                Gson gson = ApiGsonHelper.getBuilder().create();
+                return gson.fromJson(content, repCls);
+            }
+            return null;
+        } catch (RuntimeException e) {
+            throw new CloudRuntimeException("Caught runtime exception when doing GSON deserialization on: " + result, e);
+        }
+    }
+
+    /**
+     * Login call
+     * @param username user name
+     * @param password password (plain password, we will do MD5 hash here for you)
+     * @return login response string
+     */
+    protected void login(String username, String password)
+    {
+        //String md5Psw = createMD5String(password);
+        // send login request
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("username", username);
+        params.put("password", password);
+        String result = this.sendRequest("login", params);
+        LoginResponse loginResp = (LoginResponse)fromSerializedString(result, LoginResponse.class);
+        sessionKey = loginResp.getSessionkey();
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/LoginResponse.java
----------------------------------------------------------------------
diff --git a/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/LoginResponse.java b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/LoginResponse.java
new file mode 100644
index 0000000..719f39c
--- /dev/null
+++ b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/LoginResponse.java
@@ -0,0 +1,142 @@
+// 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
+// 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.ratelimit.integration;
+
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Login Response object
+ *
+ * @author Min Chen
+ *
+ */
+public class LoginResponse extends BaseResponse {
+
+    @SerializedName("timeout")
+    @Param(description = "session timeout period")
+    private String timeout;
+
+    @SerializedName("sessionkey")
+    @Param(description = "login session key")
+    private String sessionkey;
+
+    @SerializedName("username")
+    @Param(description = "login username")
+    private String username;
+
+    @SerializedName("userid")
+    @Param(description = "login user internal uuid")
+    private String userid;
+
+    @SerializedName("firstname")
+    @Param(description = "login user firstname")
+    private String firstname;
+
+    @SerializedName("lastname")
+    @Param(description = "login user lastname")
+    private String lastname;
+
+    @SerializedName("account")
+    @Param(description = "login user account type")
+    private String account;
+
+    @SerializedName("domainid")
+    @Param(description = "login user domain id")
+    private String domainid;
+
+    @SerializedName("type")
+    @Param(description = "login user type")
+    private int type;
+
+    public String getTimeout() {
+        return timeout;
+    }
+
+    public void setTimeout(String timeout) {
+        this.timeout = timeout;
+    }
+
+    public String getSessionkey() {
+        return sessionkey;
+    }
+
+    public void setSessionkey(String sessionkey) {
+        this.sessionkey = sessionkey;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getUserid() {
+        return userid;
+    }
+
+    public void setUserid(String userid) {
+        this.userid = userid;
+    }
+
+    public String getFirstname() {
+        return firstname;
+    }
+
+    public void setFirstname(String firstname) {
+        this.firstname = firstname;
+    }
+
+    public String getLastname() {
+        return lastname;
+    }
+
+    public void setLastname(String lastname) {
+        this.lastname = lastname;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getDomainid() {
+        return domainid;
+    }
+
+    public void setDomainid(String domainid) {
+        this.domainid = domainid;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/RateLimitIntegrationTest.java
----------------------------------------------------------------------
diff --git a/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/RateLimitIntegrationTest.java b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/RateLimitIntegrationTest.java
new file mode 100644
index 0000000..72d354c
--- /dev/null
+++ b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/integration/RateLimitIntegrationTest.java
@@ -0,0 +1,214 @@
+// 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
+// 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.ratelimit.integration;
+
+import static org.junit.Assert.*;
+
+import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.cloudstack.api.response.ApiLimitResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+
+
+/**
+ * Test fixture to do integration rate limit test.
+ * Currently we commented out this test suite since it requires a real MS and Db running.
+ *
+ * @author Min Chen
+ *
+ */
+public class RateLimitIntegrationTest extends APITest {
+
+    private static int apiMax = 25;         // assuming ApiRateLimitService set api.throttling.max = 25
+
+    @Before
+    public void setup(){
+        // always reset count for each testcase
+        login("admin", "password");
+
+        // issue reset api limit calls
+        final HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("sessionkey", sessionKey);
+        String resetResult =  sendRequest("resetApiLimit", params);
+        assertNotNull("Reset count failed!", fromSerializedString(resetResult, SuccessResponse.class));
+
+    }
+
+
+    @Test
+    public void testNoApiLimitOnRootAdmin() throws Exception {
+        // issue list Accounts calls
+        final HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        // assuming ApiRateLimitService set api.throttling.max = 25
+        int clientCount = 26;
+        Runnable[] clients = new Runnable[clientCount];
+        final boolean[] isUsable = new boolean[clientCount];
+
+        final CountDownLatch startGate = new CountDownLatch(1);
+
+        final CountDownLatch endGate = new CountDownLatch(clientCount);
+
+
+        for (int i = 0; i < isUsable.length; ++i) {
+            final int j = i;
+            clients[j] = new Runnable() {
+
+                /**
+                 * {@inheritDoc}
+                 */
+                @Override
+                public void run() {
+                    try {
+                        startGate.await();
+
+                        sendRequest("listAccounts", params);
+
+                        isUsable[j] = true;
+
+                    } catch (CloudRuntimeException e){
+                        isUsable[j] = false;
+                        e.printStackTrace();
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    } finally {
+                        endGate.countDown();
+                    }
+                }
+            };
+        }
+
+        ExecutorService executor = Executors.newFixedThreadPool(clientCount);
+
+        for (Runnable runnable : clients) {
+            executor.execute(runnable);
+        }
+
+        startGate.countDown();
+
+        endGate.await();
+
+        int rejectCount = 0;
+        for ( int i = 0; i < isUsable.length; ++i){
+            if ( !isUsable[i])
+                rejectCount++;
+        }
+
+        assertEquals("No request should be rejected!", 0, rejectCount);
+
+    }
+
+
+    @Test
+    public void testApiLimitOnUser() throws Exception {
+        // log in using normal user
+        login("demo", "password");
+        // issue list Accounts calls
+        final HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+
+        int clientCount = apiMax + 1;
+        Runnable[] clients = new Runnable[clientCount];
+        final boolean[] isUsable = new boolean[clientCount];
+
+        final CountDownLatch startGate = new CountDownLatch(1);
+
+        final CountDownLatch endGate = new CountDownLatch(clientCount);
+
+
+        for (int i = 0; i < isUsable.length; ++i) {
+            final int j = i;
+            clients[j] = new Runnable() {
+
+                /**
+                 * {@inheritDoc}
+                 */
+                @Override
+                public void run() {
+                    try {
+                        startGate.await();
+
+                        sendRequest("listAccounts", params);
+
+                        isUsable[j] = true;
+
+                    } catch (CloudRuntimeException e){
+                        isUsable[j] = false;
+                        e.printStackTrace();
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    } finally {
+                        endGate.countDown();
+                    }
+                }
+            };
+        }
+
+        ExecutorService executor = Executors.newFixedThreadPool(clientCount);
+
+        for (Runnable runnable : clients) {
+            executor.execute(runnable);
+        }
+
+        startGate.countDown();
+
+        endGate.await();
+
+        int rejectCount = 0;
+        for ( int i = 0; i < isUsable.length; ++i){
+            if ( !isUsable[i])
+                rejectCount++;
+        }
+
+        assertEquals("Only one request should be rejected!", 1, rejectCount);
+
+    }
+
+    @Test
+    public void testGetApiLimitOnUser() throws Exception {
+        // log in using normal user
+        login("demo", "password");
+
+        // issue an api call
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        sendRequest("listAccounts", params);
+
+        // issue get api limit calls
+        final HashMap<String, String> params2 = new HashMap<String, String>();
+        params2.put("response", "json");
+        params2.put("sessionkey", sessionKey);
+        String getResult =  sendRequest("getApiLimit", params2);
+        ApiLimitResponse getLimitResp = (ApiLimitResponse)fromSerializedString(getResult, ApiLimitResponse.class);
+        assertEquals("Issued api count is incorrect!", 2, getLimitResp.getApiIssued() ); // should be 2 apis issues plus this getlimit api
+        assertEquals("Allowed api count is incorrect!", apiMax -2, getLimitResp.getApiAllowed());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/server/test/com/cloud/api/APITest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/api/APITest.java b/server/test/com/cloud/api/APITest.java
index 69c488f..0b040ab 100644
--- a/server/test/com/cloud/api/APITest.java
+++ b/server/test/com/cloud/api/APITest.java
@@ -19,17 +19,17 @@ package com.cloud.api;
 import java.io.BufferedReader;
 import java.io.EOFException;
 import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
 import java.math.BigInteger;
 import java.net.HttpURLConnection;
 import java.net.URL;
-import java.net.URLConnection;
 import java.net.URLEncoder;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
 import java.util.Iterator;
 
+import org.apache.cloudstack.api.response.SuccessResponse;
+
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.google.gson.Gson;
 
@@ -147,17 +147,38 @@ public abstract class APITest {
     protected Object fromSerializedString(String result, Class<?> repCls) {
         try {
             if (result != null && !result.isEmpty()) {
-
                 // get real content
-                int start = result.indexOf('{', result.indexOf('{') + 1); // find the second {
-                if ( start < 0 ){
-                    throw new CloudRuntimeException("Response format is wrong: " + result);
+                int start;
+                int end;
+                if (repCls == LoginResponse.class || repCls == SuccessResponse.class) {
+
+                    start = result.indexOf('{', result.indexOf('{') + 1); // find
+                                                                          // the
+                                                                          // second
+                                                                          // {
+
+                    end = result.lastIndexOf('}', result.lastIndexOf('}') - 1); // find
+                                                                                // the
+                                                                                // second
+                                                                                // }
+                                                                                // backwards
+
+                } else {
+                    // get real content
+                    start = result.indexOf('{', result.indexOf('{', result.indexOf('{') + 1) + 1); // find
+                                                                                                   // the
+                                                                                                   // third
+                                                                                                   // {
+                    end = result.lastIndexOf('}', result.lastIndexOf('}', result.lastIndexOf('}') - 1) - 1); // find
+                                                                                                             // the
+                                                                                                             // third
+                                                                                                             // }
+                                                                                                             // backwards
                 }
-                int end = result.lastIndexOf('}', result.lastIndexOf('}')-1); // find the second } backwards
-                if ( end < 0 ){
+                if (start < 0 || end < 0) {
                     throw new CloudRuntimeException("Response format is wrong: " + result);
                 }
-                String content = result.substring(start, end+1);
+                String content = result.substring(start, end + 1);
                 Gson gson = ApiGsonHelper.getBuilder().create();
                 return gson.fromJson(content, repCls);
             }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/86ada92f/server/test/com/cloud/api/ListPerfTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/api/ListPerfTest.java b/server/test/com/cloud/api/ListPerfTest.java
index c6fda9b..b8cb97e 100644
--- a/server/test/com/cloud/api/ListPerfTest.java
+++ b/server/test/com/cloud/api/ListPerfTest.java
@@ -170,144 +170,4 @@ public class ListPerfTest extends APITest {
 
     }
 
-    @Test
-    public void testNoApiLimitOnRootAdmin() throws Exception {
-        // issue list Accounts calls
-        final HashMap<String, String> params = new HashMap<String, String>();
-        params.put("response", "json");
-        params.put("listAll", "true");
-        params.put("sessionkey", sessionKey);
-        // assuming ApiRateLimitService set api.throttling.max = 25
-        int clientCount = 26;
-        Runnable[] clients = new Runnable[clientCount];
-        final boolean[] isUsable = new boolean[clientCount];
-
-        final CountDownLatch startGate = new CountDownLatch(1);
-
-        final CountDownLatch endGate = new CountDownLatch(clientCount);
-
-
-        for (int i = 0; i < isUsable.length; ++i) {
-            final int j = i;
-            clients[j] = new Runnable() {
-
-                /**
-                 * {@inheritDoc}
-                 */
-                @Override
-                public void run() {
-                    try {
-                        startGate.await();
-
-                        sendRequest("listAccounts", params);
-
-                        isUsable[j] = true;
-
-                    } catch (CloudRuntimeException e){
-                        isUsable[j] = false;
-                        e.printStackTrace();
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    } finally {
-                        endGate.countDown();
-                    }
-                }
-            };
-        }
-
-        ExecutorService executor = Executors.newFixedThreadPool(clientCount);
-
-        for (Runnable runnable : clients) {
-            executor.execute(runnable);
-        }
-
-        startGate.countDown();
-
-        endGate.await();
-
-        int rejectCount = 0;
-        for ( int i = 0; i < isUsable.length; ++i){
-            if ( !isUsable[i])
-                rejectCount++;
-        }
-
-        assertEquals("No request should be rejected!", 0, rejectCount);
-
-    }
-
-
-    @Test
-    public void testApiLimitOnUser() throws Exception {
-        // log in using normal user
-        login("demo", "password");
-        // issue list Accounts calls
-        final HashMap<String, String> params = new HashMap<String, String>();
-        params.put("response", "json");
-        params.put("listAll", "true");
-        params.put("sessionkey", sessionKey);
-        // assuming ApiRateLimitService set api.throttling.max = 25
-        int clientCount = 26;
-        Runnable[] clients = new Runnable[clientCount];
-        final boolean[] isUsable = new boolean[clientCount];
-
-        final CountDownLatch startGate = new CountDownLatch(1);
-
-        final CountDownLatch endGate = new CountDownLatch(clientCount);
-
-
-        for (int i = 0; i < isUsable.length; ++i) {
-            final int j = i;
-            clients[j] = new Runnable() {
-
-                /**
-                 * {@inheritDoc}
-                 */
-                @Override
-                public void run() {
-                    try {
-                        startGate.await();
-
-                        sendRequest("listAccounts", params);
-
-                        isUsable[j] = true;
-
-                    } catch (CloudRuntimeException e){
-                        isUsable[j] = false;
-                        e.printStackTrace();
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    } finally {
-                        endGate.countDown();
-                    }
-                }
-            };
-        }
-
-        ExecutorService executor = Executors.newFixedThreadPool(clientCount);
-
-        for (Runnable runnable : clients) {
-            executor.execute(runnable);
-        }
-
-        startGate.countDown();
-
-        endGate.await();
-
-        int rejectCount = 0;
-        for ( int i = 0; i < isUsable.length; ++i){
-            if ( !isUsable[i])
-                rejectCount++;
-        }
-
-        assertEquals("Only one request should be rejected!", 1, rejectCount);
-
-        // issue get api limit calls
-        final HashMap<String, String> params2 = new HashMap<String, String>();
-        params2.put("response", "json");
-        params2.put("sessionkey", sessionKey);
-        String getResult =  sendRequest("getApiLimit", params2);
-        //ApiLimitResponse loginResp = (ApiLimitResponse)fromSerializedString(getResult, ApiLimitResponse.class);
-
-    }
-
 }