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 2012/12/23 07:35:44 UTC

[4/4] Create DB views to improve ListHostsCmd performance.

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/e7fa1a86/server/src/com/cloud/storage/StorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java
index 1f625d3..824af6a 100755
--- a/server/src/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/com/cloud/storage/StorageManagerImpl.java
@@ -431,8 +431,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
     }
 
     @Override
-    public boolean isLocalStorageActiveOnHost(Host host) {
-        List<StoragePoolHostVO> storagePoolHostRefs = _storagePoolHostDao.listByHostId(host.getId());
+    public boolean isLocalStorageActiveOnHost(Long hostId) {
+        List<StoragePoolHostVO> storagePoolHostRefs = _storagePoolHostDao.listByHostId(hostId);
         for (StoragePoolHostVO storagePoolHostRef : storagePoolHostRefs) {
             StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolHostRef.getPoolId());
             if (storagePool.getPoolType() == StoragePoolType.LVM || storagePool.getPoolType() == StoragePoolType.EXT) {
@@ -652,7 +652,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(volume, snapshot);
         if (volumeDetails != null) {
             createdVolume = volumeDetails.first();
-        	UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(), createdVolume.getDataCenterId(), createdVolume.getId(), createdVolume.getName(), 
+        	UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(), createdVolume.getDataCenterId(), createdVolume.getId(), createdVolume.getName(),
         			                                   createdVolume.getDiskOfferingId(), null, createdVolume.getSize());
         	_usageEventDao.persist(usageEvent);
         }
@@ -737,21 +737,21 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
     @DB
     public VolumeVO copyVolumeFromSecToPrimary(VolumeVO volume, VMInstanceVO vm, VMTemplateVO template, DataCenterVO dc, HostPodVO pod, Long clusterId, ServiceOfferingVO offering, DiskOfferingVO diskOffering,
             List<StoragePoolVO> avoids, long size, HypervisorType hyperType) throws NoTransitionException {
-    	
+
     	final HashSet<StoragePool> avoidPools = new HashSet<StoragePool>(avoids);
     	DiskProfile dskCh = createDiskCharacteristics(volume, template, dc, diskOffering);
     	dskCh.setHyperType(vm.getHypervisorType());
-    	// Find a suitable storage to create volume on 
+    	// Find a suitable storage to create volume on
 	StoragePoolVO destPool = findStoragePool(dskCh, dc, pod, clusterId, null, vm, avoidPools);
-    	
-    	// Copy the volume from secondary storage to the destination storage pool    	  	
+
+    	// Copy the volume from secondary storage to the destination storage pool
     	stateTransitTo(volume, Event.CopyRequested);
     	VolumeHostVO volumeHostVO = _volumeHostDao.findByVolumeId(volume.getId());
     	HostVO secStorage = _hostDao.findById(volumeHostVO.getHostId());
     	String secondaryStorageURL = secStorage.getStorageUrl();
     	String[] volumePath = volumeHostVO.getInstallPath().split("/");
     	String volumeUUID = volumePath[volumePath.length - 1].split("\\.")[0];
-    	
+
         CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(), volumeUUID, destPool, secondaryStorageURL, false, _copyvolumewait);
         CopyVolumeAnswer cvAnswer;
 		try {
@@ -764,23 +764,23 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         if (cvAnswer == null || !cvAnswer.getResult()) {
         	stateTransitTo(volume, Event.CopyFailed);
             throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool.");
-        }        
+        }
         Transaction txn = Transaction.currentTxn();
-        txn.start();        
+        txn.start();
         volume.setPath(cvAnswer.getVolumePath());
         volume.setFolder(destPool.getPath());
         volume.setPodId(destPool.getPodId());
-        volume.setPoolId(destPool.getId());        
+        volume.setPoolId(destPool.getId());
         volume.setPodId(destPool.getPodId());
-        stateTransitTo(volume, Event.CopySucceeded); 
+        stateTransitTo(volume, Event.CopySucceeded);
         UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), null, volume.getSize());
         _usageEventDao.persist(usageEvent);
         _volumeHostDao.remove(volumeHostVO.getId());
     	txn.commit();
 		return volume;
-    	
+
     }
-    
+
     @Override
     @DB
     public VolumeVO createVolume(VolumeVO volume, VMInstanceVO vm, VMTemplateVO template, DataCenterVO dc, HostPodVO pod, Long clusterId, ServiceOfferingVO offering, DiskOfferingVO diskOffering,
@@ -975,7 +975,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 
         value = configDao.getValue(Config.RecreateSystemVmEnabled.key());
         _recreateSystemVmEnabled = Boolean.parseBoolean(value);
-        
+
         value = configDao.getValue(Config.StorageTemplateCleanupEnabled.key());
         _templateCleanupEnabled = (value == null ? true : Boolean.parseBoolean(value));
 
@@ -1517,7 +1517,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         }
         if(sPool.getStatus() != StoragePoolStatus.Maintenance){
             s_logger.warn("Unable to delete storage id: " + id +" due to it is not in Maintenance state");
-            throw new InvalidParameterValueException("Unable to delete storage due to it is not in Maintenance state, id: " + id);           
+            throw new InvalidParameterValueException("Unable to delete storage due to it is not in Maintenance state, id: " + id);
         }
         if (sPool.getPoolType().equals(StoragePoolType.LVM) || sPool.getPoolType().equals(StoragePoolType.EXT)) {
             s_logger.warn("Unable to delete local storage id:" + id);
@@ -1546,7 +1546,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                 		" for this pool");
             }
         }
-        
+
 
         // First get the host_id from storage_pool_host_ref for given pool id
         StoragePoolVO lock = _storagePoolDao.acquireInLockTable(sPool.getId());
@@ -1740,10 +1740,10 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         return _volsDao.findById(volume.getId());
     }
 
-    
+
     /*
      * Upload the volume to secondary storage.
-     * 
+     *
      */
     @Override
     @DB
@@ -1755,13 +1755,13 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         String volumeName = cmd.getVolumeName();
         String url = cmd.getUrl();
         String format = cmd.getFormat();
-        
+
     	validateVolume(caller, ownerId, zoneId, volumeName, url, format);
     	VolumeVO volume = persistVolume(caller, ownerId, zoneId, volumeName, url, cmd.getFormat());
     	_downloadMonitor.downloadVolumeToStorage(volume, zoneId, url, cmd.getChecksum(), ImageFormat.valueOf(format.toUpperCase()));
-		return volume;    	
+		return volume;
     }
-    
+
     private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format) throws ResourceAllocationException{
 
         // permission check
@@ -1769,7 +1769,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 
         // Check that the resource limit for volumes won't be exceeded
         _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.volume);
-        
+
 
         // Verify that zone exists
         DataCenterVO zone = _dcDao.findById(zoneId);
@@ -1781,22 +1781,22 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
             throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
         }
-        
+
 		if (url.toLowerCase().contains("file://")) {
 			throw new InvalidParameterValueException("File:// type urls are currently unsupported");
 		}
-		
+
 		ImageFormat imgfmt = ImageFormat.valueOf(format.toUpperCase());
 		if (imgfmt == null) {
 			throw new IllegalArgumentException("Image format is incorrect " + format + ". Supported formats are " + EnumUtils.listValues(ImageFormat.values()));
 		}
-		
+
         String userSpecifiedName = volumeName;
         if (userSpecifiedName == null) {
             userSpecifiedName = getRandomVolumeName();
         }
 		if((!url.toLowerCase().endsWith("vhd"))&&(!url.toLowerCase().endsWith("vhd.zip"))
-		        &&(!url.toLowerCase().endsWith("vhd.bz2"))&&(!url.toLowerCase().endsWith("vhd.gz")) 
+		        &&(!url.toLowerCase().endsWith("vhd.bz2"))&&(!url.toLowerCase().endsWith("vhd.gz"))
 		        &&(!url.toLowerCase().endsWith("qcow2"))&&(!url.toLowerCase().endsWith("qcow2.zip"))
 		        &&(!url.toLowerCase().endsWith("qcow2.bz2"))&&(!url.toLowerCase().endsWith("qcow2.gz"))
 		        &&(!url.toLowerCase().endsWith("ova"))&&(!url.toLowerCase().endsWith("ova.zip"))
@@ -1804,7 +1804,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 		        &&(!url.toLowerCase().endsWith("img"))&&(!url.toLowerCase().endsWith("raw"))){
 		        throw new InvalidParameterValueException("Please specify a valid " + format.toLowerCase());
 		    }
-			
+
 			if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith(".vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url.toLowerCase().endsWith("vhd.gz") ))
 				|| (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith(".qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz") ))
 				|| (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith(".ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url.toLowerCase().endsWith("ova.gz")))
@@ -1812,14 +1812,14 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 		        throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + format.toLowerCase());
 			}
         validateUrl(url);
-               
+
     	return false;
     }
-    
+
     private String validateUrl(String url){
 		try {
 			URI uri = new URI(url);
-			if ((uri.getScheme() == null) || (!uri.getScheme().equalsIgnoreCase("http") 
+			if ((uri.getScheme() == null) || (!uri.getScheme().equalsIgnoreCase("http")
 				&& !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme().equalsIgnoreCase("file"))) {
 				throw new IllegalArgumentException("Unsupported scheme for url: " + url);
 			}
@@ -1840,16 +1840,16 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 			} catch (UnknownHostException uhe) {
 				throw new IllegalArgumentException("Unable to resolve " + host);
 			}
-			
+
 			return uri.toString();
 		} catch (URISyntaxException e) {
 			throw new IllegalArgumentException("Invalid URL " + url);
 		}
-    	
+
     }
-    
+
     private VolumeVO persistVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format) {
-    	
+
         Transaction txn = Transaction.currentTxn();
         txn.start();
 
@@ -1860,7 +1860,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         volume.setAccountId(ownerId);
         volume.setDomainId(((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId()));
         long diskOfferingId = _diskOfferingDao.findByUniqueName("Cloud.com-Custom").getId();
-        volume.setDiskOfferingId(diskOfferingId);        
+        volume.setDiskOfferingId(diskOfferingId);
         //volume.setSize(size);
         volume.setInstanceId(null);
         volume.setUpdated(new Date());
@@ -1881,8 +1881,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         txn.commit();
 		return volume;
 	}
-    	    
-    
+
+
     /*
      * Just allocate a volume in the database, don't send the createvolume cmd to hypervisor. The volume will be finally
      * created
@@ -1984,7 +1984,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
              * throw new UnsupportedServiceException("operation not supported, snapshot with id " + snapshotId +
              * " is created from ROOT volume");
              * }
-             * 
+             *
              */
         }
 
@@ -2189,7 +2189,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         s_logger.debug("Successfully set Capacity - " + totalOverProvCapacity + " for capacity type - " + capacityType + " , DataCenterId - "
                 + storagePool.getDataCenterId() + ", HostOrPoolId - " + storagePool.getId() + ", PodId " + storagePool.getPodId());
     }
-    
+
     @Override
     public List<Long> getUpHostsInPool(long poolId) {
         SearchCriteria<Long> sc = UpHostsInPoolSearch.create();
@@ -2290,7 +2290,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                             s_logger.warn("Unable to destroy " + vol.getId(), e);
                         }
                     }
-                    
+
                     // remove snapshots in Error state
                     List<SnapshotVO> snapshots = _snapshotDao.listAllByStatus(Snapshot.Status.Error);
                     for (SnapshotVO snapshotVO : snapshots) {
@@ -2300,7 +2300,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                             s_logger.warn("Unable to destroy " + snapshotVO.getId(), e);
                         }
                     }
-                    
+
                 } finally {
                     scanLock.unlock();
                 }
@@ -2439,7 +2439,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                     s_logger.warn("problem cleaning up snapshots in secondary storage " + secondaryStorageHost, e2);
                 }
             }
-            
+
             //CleanUp volumes on Secondary Storage.
             for (HostVO secondaryStorageHost : secondaryStorageHosts) {
                 try {
@@ -2467,7 +2467,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                             _volumeHostDao.remove(destroyedVolumeHostVO.getId());
                         }
                     }
-                
+
                 }catch (Exception e2) {
                     s_logger.warn("problem cleaning up volumes in secondary storage " + secondaryStorageHost, e2);
                 }
@@ -2899,14 +2899,14 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
             throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM.");
         }
 
-        // Check that volume is completely Uploaded 
+        // Check that volume is completely Uploaded
         if (volume.getState() == Volume.State.UploadOp){
         	VolumeHostVO volumeHost = _volumeHostDao.findByVolumeId(volume.getId());
             if (volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS){
             	throw new InvalidParameterValueException("Please specify a volume that is not uploading");
-            }            
+            }
         }
-        
+
         // Check that the volume is not already destroyed
         if (volume.getState() != Volume.State.Destroy) {
             if (!destroyVolume(volume)) {
@@ -3258,7 +3258,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         if (s_logger.isDebugEnabled()) {
             s_logger.debug("Checking if we need to prepare " + vols.size() + " volumes for " + vm);
         }
-        
+
         boolean recreate = _recreateSystemVmEnabled;
 
         List<VolumeVO> recreateVols = new ArrayList<VolumeVO>(vols.size());
@@ -3270,7 +3270,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
             }
             if (assignedPool == null && recreate) {
             	assignedPool = _storagePoolDao.findById(vol.getPoolId());
-            	
+
             }
             if (assignedPool != null || recreate) {
                 Volume.State state = vol.getState();
@@ -3311,7 +3311,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                             StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId());
                             vm.addDisk(new VolumeTO(vol, pool));
                         }
-                        
+
                     }
                 }
             } else {
@@ -3333,7 +3333,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
             	existingPool = _storagePoolDao.findById(vol.getPoolId());
             	s_logger.debug("existing pool: " + existingPool.getId());
             }
-            
+
             if (vol.getState() == Volume.State.Allocated || vol.getState() == Volume.State.Creating) {
                 newVol = vol;
             } else {
@@ -3422,7 +3422,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         if (toBeCreated.getTemplateId() != null) {
             template = _templateDao.findById(toBeCreated.getTemplateId());
         }
-        
+
         StoragePool pool = null;
         if (sPool != null) {
         	pool = sPool;
@@ -3506,27 +3506,27 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         if (s_logger.isDebugEnabled()) {
             s_logger.debug("Expunging " + vol);
         }
-        
+
         //Find out if the volume is present on secondary storage
         VolumeHostVO volumeHost = _volumeHostDao.findByVolumeId(vol.getId());
         if(volumeHost != null){
         	if (volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED){
         		HostVO ssHost = _hostDao.findById(volumeHost.getHostId());
-        		DeleteVolumeCommand dtCommand = new DeleteVolumeCommand(ssHost.getStorageUrl(), volumeHost.getInstallPath());            
+        		DeleteVolumeCommand dtCommand = new DeleteVolumeCommand(ssHost.getStorageUrl(), volumeHost.getInstallPath());
         		Answer answer = _agentMgr.sendToSecStorage(ssHost, dtCommand);
         		if (answer == null || !answer.getResult()) {
         			s_logger.debug("Failed to delete " + volumeHost + " due to " + ((answer == null) ? "answer is null" : answer.getDetails()));
         			return;
         		}
-        	}else if(volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS){									
+        	}else if(volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS){
 					s_logger.debug("Volume: " + vol.getName() + " is currently being uploaded; cant' delete it.");
 					throw new CloudRuntimeException("Please specify a volume that is not currently being uploaded.");
         	}
             _volumeHostDao.remove(volumeHost.getId());
             _volumeDao.remove(vol.getId());
-            return;             
+            return;
         }
-        
+
         String vmName = null;
         if (vol.getVolumeType() == Type.ROOT && vol.getInstanceId() != null) {
             VirtualMachine vm = _vmInstanceDao.findByIdIncludingRemoved(vol.getInstanceId());
@@ -3894,7 +3894,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         vmSearch.and("type", vmSearch.entity().getType(), SearchCriteria.Op.NIN);
         vmSearch.or("nulltype", vmSearch.entity().getType(), SearchCriteria.Op.NULL);
         sb.join("vmSearch", vmSearch, sb.entity().getInstanceId(), vmSearch.entity().getId(), JoinBuilder.JoinType.LEFTOUTER);
-        
+
         if (tags != null && !tags.isEmpty()) {
             SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
             for (int count=0; count < tags.size(); count++) {
@@ -3924,7 +3924,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         }
 
         sc.setJoinParameters("diskOfferingSearch", "systemUse", 1);
-        
+
         if (tags != null && !tags.isEmpty()) {
             int count = 0;
             sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.Volume.toString());
@@ -3957,7 +3957,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 
         // Only return volumes that are not destroyed
         sc.setParameters("state", Volume.State.Destroy);
-        
+
         Pair<List<VolumeVO>, Integer> volumes = _volumeDao.searchAndCount(sc, searchFilter);
 
         return new Pair<List<? extends Volume>, Integer>(volumes.first(), volumes.second());
@@ -3979,14 +3979,14 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
             return null;
         }
     }
-    
+
     @Override
     public HypervisorType getHypervisorTypeFromFormat(ImageFormat format) {
-        
+
     	if(format == null) {
             return HypervisorType.None;
     	}
-    	
+
         if (format == ImageFormat.VHD) {
             return HypervisorType.XenServer;
         } else if (format == ImageFormat.OVA) {
@@ -4075,5 +4075,5 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         }
         return true;
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/e7fa1a86/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
new file mode 100644
index 0000000..69c488f
--- /dev/null
+++ b/server/test/com/cloud/api/APITest.java
@@ -0,0 +1,189 @@
+// 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 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 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 = result.indexOf('{', result.indexOf('{') + 1); // find the second {
+                if ( start < 0 ){
+                    throw new CloudRuntimeException("Response format is wrong: " + result);
+                }
+                int end = result.lastIndexOf('}', result.lastIndexOf('}')-1); // find the second } backwards
+                if ( 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/e7fa1a86/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
new file mode 100644
index 0000000..47abcae
--- /dev/null
+++ b/server/test/com/cloud/api/ListPerfTest.java
@@ -0,0 +1,109 @@
+// 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 com.cloud.api;
+
+import java.util.HashMap;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Test fixture to do performance test for list command
+ * Currently we commented out this test suite since it requires a real MS and Db running.
+ *
+ * @author Min Chen
+ *
+ */
+public class ListPerfTest extends APITest {
+
+
+
+    @Before
+    public void setup(){
+        // always login for each testcase
+        login("admin", "password");
+    }
+
+    @Test
+    public void testListVM(){
+        // issue list VM calls
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        long before = System.currentTimeMillis();
+        String result = this.sendRequest("listVirtualMachines", params);
+        long after = System.currentTimeMillis();
+        System.out.println("Time taken to list VM: " + (after - before) + " ms");
+
+    }
+
+    @Test
+    public void testListVMXML(){
+        // issue list VM calls
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        long before = System.currentTimeMillis();
+        String result = this.sendRequest("listVirtualMachines", params);
+        long after = System.currentTimeMillis();
+        System.out.println("Time taken to list VM: " + (after - before) + " ms");
+
+    }
+
+    @Test
+    public void testListRouter(){
+        // issue list VM calls
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        long before = System.currentTimeMillis();
+        String result = this.sendRequest("listRouters", params);
+        long after = System.currentTimeMillis();
+        System.out.println("Time taken to list Routers: " + (after - before) + " ms");
+
+    }
+
+    @Test
+    public void testListRouterXML(){
+        // issue list VM calls
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        long before = System.currentTimeMillis();
+        String result = this.sendRequest("listRouters", params);
+        long after = System.currentTimeMillis();
+        System.out.println("Time taken to list Routers: " + (after - before) + " ms");
+
+    }
+
+    @Test
+    public void testListHosts(){
+        // issue list Hosts calls
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        long before = System.currentTimeMillis();
+        String result = this.sendRequest("listHosts", params);
+        long after = System.currentTimeMillis();
+        System.out.println("Time taken to list Hosts: " + (after - before) + " ms");
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/e7fa1a86/server/test/com/cloud/api/LoginResponse.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/api/LoginResponse.java b/server/test/com/cloud/api/LoginResponse.java
new file mode 100644
index 0000000..097ae42
--- /dev/null
+++ b/server/test/com/cloud/api/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 com.cloud.api;
+
+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;
+    }
+
+
+
+}