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/04/23 19:18:46 UTC

git commit: updated refs/heads/http_post to 495a729

Updated Branches:
  refs/heads/http_post [created] 495a72928


CLOUDSTACK-1086: DeployVirtualMachine userdata enhancements

Description:

	Currently, userdata sent over to the DeployVMCmd and
	updateVMCmd commands can be upto 2K in length, whether
	sent over GET or POST. We remove this limitation for
	POST to change this limit to 32K. Also enabling lazy
	load on userdata to improve performance during reads
	of large sized userdata from user VM records.

Signed-off-by: Min Chen <mi...@citrix.com>


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

Branch: refs/heads/http_post
Commit: 495a7292839d37551ad5c35253f02870fd44e876
Parents: 2712ddd
Author: Vijayendra Bhamidipati <vi...@citrix.com>
Authored: Wed Apr 17 05:36:33 2013 -0700
Committer: Min Chen <mi...@citrix.com>
Committed: Tue Apr 23 10:02:53 2013 -0700

----------------------------------------------------------------------
 api/src/com/cloud/vm/UserVmService.java            |   27 ++-
 api/src/org/apache/cloudstack/api/BaseCmd.java     |   24 +++
 .../api/command/user/vm/DeployVMCmd.java           |   12 +-
 .../api/command/user/vm/UpdateVMCmd.java           |    2 +-
 core/src/com/cloud/vm/UserVmVO.java                |    6 +-
 server/src/com/cloud/api/ApiDispatcher.java        |    9 -
 server/src/com/cloud/api/ApiServer.java            |  155 ++++++++++-----
 server/src/com/cloud/api/ApiServerService.java     |    1 +
 server/src/com/cloud/api/ApiServlet.java           |    6 +-
 server/src/com/cloud/vm/UserVmManagerImpl.java     |   96 ++++++----
 .../test/com/cloud/vm/MockUserVmManagerImpl.java   |   48 +++--
 .../test/com/cloud/vm/dao/UserVmDaoImplTest.java   |   43 +++-
 .../cloud/vm/dao/UserVmDaoTestConfiguration.java   |   50 +++++
 server/test/resources/UserVMDaoTestContext.xml     |   44 ++++
 setup/db/db/schema-410to420.sql                    |    4 +-
 .../component/test_deploy_vm_with_userdata.py      |  110 ++++++++++
 16 files changed, 503 insertions(+), 134 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/api/src/com/cloud/vm/UserVmService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java
index aa21136..7e89cd3 100755
--- a/api/src/com/cloud/vm/UserVmService.java
+++ b/api/src/com/cloud/vm/UserVmService.java
@@ -21,9 +21,22 @@ import java.util.Map;
 
 import javax.naming.InsufficientResourcesException;
 
+import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
 import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
 import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
-import org.apache.cloudstack.api.command.user.vm.*;
+import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
+import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
+import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
+import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
+import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
+import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
+import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd;
 import com.cloud.dc.DataCenter;
@@ -185,8 +198,8 @@ public interface UserVmService {
      */
     UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList, Account owner, String hostName,
             String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
-            String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIp,
-            String keyboard, List<Long> affinityGroupIdList)
+	    HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
+	    IpAddresses defaultIp, String keyboard, List<Long> affinityGroupIdList)
             throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -257,8 +270,8 @@ public interface UserVmService {
      * @throws InsufficientResourcesException
      */
     UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, List<Long> securityGroupIdList,
-            Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
-            IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
+            Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair,
+	    Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
             throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -327,8 +340,8 @@ public interface UserVmService {
      */
     UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner, String hostName,
             String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
-            String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps,
-            String keyboard, List<Long> affinityGroupIdList)
+	    HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
+	    IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
             throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/api/src/org/apache/cloudstack/api/BaseCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java
index 42c0680..200675d 100644
--- a/api/src/org/apache/cloudstack/api/BaseCmd.java
+++ b/api/src/org/apache/cloudstack/api/BaseCmd.java
@@ -95,6 +95,11 @@ public abstract class BaseCmd {
     private Object _responseObject = null;
     private Map<String, String> fullUrlParams;
 
+    public enum HTTPMethod {
+        GET, POST, PUT, DELETE
+    }
+    private HTTPMethod httpMethod;
+
     @Parameter(name = "response", type = CommandType.STRING)
     private String responseType;
 
@@ -140,6 +145,25 @@ public abstract class BaseCmd {
     public void configure() {
     }
 
+    public HTTPMethod getHttpMethod() {
+        return httpMethod;
+    }
+
+    public void setHttpMethod(String method) {
+        if (method != null) {
+            if (method.equalsIgnoreCase("GET"))
+                httpMethod = HTTPMethod.GET;
+            else if (method.equalsIgnoreCase("PUT"))
+                httpMethod = HTTPMethod.PUT;
+            else if (method.equalsIgnoreCase("POST"))
+                httpMethod = HTTPMethod.POST;
+            else if (method.equalsIgnoreCase("DELETE"))
+                httpMethod = HTTPMethod.DELETE;
+        } else {
+            httpMethod = HTTPMethod.GET;
+	}
+    }
+
     public String getResponseType() {
         if (responseType == null) {
             return RESPONSE_TYPE_XML;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 70c0159..3ed08d2 100755
--- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -128,7 +128,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
     @Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, description="the hypervisor on which to deploy the virtual machine")
     private String hypervisor;
 
-    @Parameter(name=ApiConstants.USER_DATA, type=CommandType.STRING, description="an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Currently only HTTP GET is supported. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding.", length=2048)
+    @Parameter(name=ApiConstants.USER_DATA, type=CommandType.STRING, description="an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.", length=32768)
     private String userData;
 
     @Parameter(name=ApiConstants.SSH_KEYPAIR, type=CommandType.STRING, description="name of the ssh key pair used to login to the virtual machine")
@@ -312,8 +312,8 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
                         throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + ips.get("networkid"));
                     }
                 }
-                String requestedIp = (String) ips.get("ip");
-                String requestedIpv6 = (String) ips.get("ipv6");
+                String requestedIp = ips.get("ip");
+                String requestedIpv6 = ips.get("ipv6");
                 if (requestedIpv6 != null) {
                 	requestedIpv6 = requestedIpv6.toLowerCase();
                 }
@@ -481,18 +481,18 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
                     throw new InvalidParameterValueException("Can't specify network Ids in Basic zone");
                 } else {
                     vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(), owner, name,
-                                displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList());
+                            displayName, diskOfferingId, size, group, getHypervisor(), this.getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList());
                 }
             } else {
                 if (zone.isSecurityGroupEnabled())  {
                     vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), getSecurityGroupIdList(),
-                                owner, name, displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList());
+                            owner, name, displayName, diskOfferingId, size, group, getHypervisor(), this.getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList());
                 } else {
                     if (getSecurityGroupIdList() != null && !getSecurityGroupIdList().isEmpty()) {
                         throw new InvalidParameterValueException("Can't create vm with security groups; security group feature is not enabled per zone");
                     }
                     vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, getNetworkIds(), owner, name, displayName,
-                                diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList());
+                            diskOfferingId, size, group, getHypervisor(), this.getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList());
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
index ff8fff1..bbf9b25 100644
--- a/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
@@ -61,7 +61,7 @@ public class UpdateVMCmd extends BaseCmd{
             description="the ID of the OS type that best represents this VM.")
     private Long osTypeId;
 
-    @Parameter(name=ApiConstants.USER_DATA, type=CommandType.STRING, description="an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Currently only HTTP GET is supported. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding.", length=2048)
+    @Parameter(name=ApiConstants.USER_DATA, type=CommandType.STRING, description="an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.", length=32768)
     private String userData;
 
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/core/src/com/cloud/vm/UserVmVO.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/vm/UserVmVO.java b/core/src/com/cloud/vm/UserVmVO.java
index a16eaf9..2e9bdf5 100755
--- a/core/src/com/cloud/vm/UserVmVO.java
+++ b/core/src/com/cloud/vm/UserVmVO.java
@@ -18,9 +18,11 @@ package com.cloud.vm;
 
 import java.util.HashMap;
 
+import javax.persistence.Basic;
 import javax.persistence.Column;
 import javax.persistence.DiscriminatorValue;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.PrimaryKeyJoinColumn;
 import javax.persistence.Table;
 
@@ -36,7 +38,8 @@ public class UserVmVO extends VMInstanceVO implements UserVm {
     @Column(name="iso_id", nullable=true, length=17)
     private Long isoId = null;
     
-    @Column(name="user_data", updatable=true, nullable=true, length=2048)
+    @Column(name="user_data", updatable=true, nullable=true, length=32768)
+    @Basic(fetch = FetchType.LAZY)
     private String userData;
     
     @Column(name="display_name", updatable=true, nullable=true)
@@ -119,6 +122,7 @@ public class UserVmVO extends VMInstanceVO implements UserVm {
         return details != null ? details.get(name) : null;
     }
     
+    @Override
     public void setAccountId(long accountId){
         this.accountId = accountId;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/src/com/cloud/api/ApiDispatcher.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java
index 925d90a..b4437ce 100755
--- a/server/src/com/cloud/api/ApiDispatcher.java
+++ b/server/src/com/cloud/api/ApiDispatcher.java
@@ -27,7 +27,6 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 
@@ -56,21 +55,14 @@ import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
-import com.cloud.async.AsyncCommandQueued;
 import com.cloud.async.AsyncJobManager;
 import com.cloud.dao.EntityManager;
-import com.cloud.exception.AccountLimitException;
-import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.user.UserContext;
 import com.cloud.utils.DateUtil;
 import com.cloud.utils.ReflectUtil;
-import com.cloud.utils.component.ComponentContext;
 import com.cloud.utils.exception.CSExceptionErrorCode;
 import com.cloud.utils.exception.CloudRuntimeException;
 
@@ -160,7 +152,6 @@ public class ApiDispatcher {
                     }
                 }
             }
-
             cmd.execute();
 
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index d842819..dd5fd4d 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -16,30 +16,51 @@
 // under the License.
 package com.cloud.api;
 
-import com.cloud.api.response.ApiResponseSerializer;
-import com.cloud.async.AsyncCommandQueued;
-import com.cloud.async.AsyncJob;
-import com.cloud.async.AsyncJobManager;
-import com.cloud.async.AsyncJobVO;
-import com.cloud.configuration.Config;
-import com.cloud.configuration.ConfigurationVO;
-import com.cloud.configuration.dao.ConfigurationDao;
-import com.cloud.domain.Domain;
-import com.cloud.domain.DomainVO;
-import com.cloud.event.ActionEventUtils;
-import com.cloud.exception.*;
-import com.cloud.user.*;
-import com.cloud.utils.NumbersUtil;
-import com.cloud.utils.Pair;
-import com.cloud.utils.StringUtils;
-import com.cloud.utils.component.ComponentContext;
-import com.cloud.utils.component.PluggableService;
-import com.cloud.utils.concurrency.NamedThreadFactory;
-import com.cloud.utils.db.SearchCriteria;
-import com.cloud.utils.db.Transaction;
-import com.cloud.utils.exception.CloudRuntimeException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.security.SecureRandom;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
 import org.apache.cloudstack.acl.APIChecker;
-import org.apache.cloudstack.api.*;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.ResponseObject;
+import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
 import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd;
 import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd;
@@ -61,7 +82,13 @@ import org.apache.cloudstack.api.response.ExceptionResponse;
 import org.apache.cloudstack.api.response.ListResponse;
 import org.apache.cloudstack.region.RegionManager;
 import org.apache.commons.codec.binary.Base64;
-import org.apache.http.*;
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.HttpStatus;
+import org.apache.http.NameValuePair;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.entity.BasicHttpEntity;
 import org.apache.http.impl.DefaultHttpResponseFactory;
@@ -72,29 +99,54 @@ import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.CoreConnectionPNames;
 import org.apache.http.params.CoreProtocolPNames;
 import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.*;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.HttpRequestHandlerRegistry;
+import org.apache.http.protocol.HttpService;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
-import javax.annotation.PostConstruct;
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import javax.inject.Inject;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.*;
-import java.security.SecureRandom;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
+import com.cloud.api.response.ApiResponseSerializer;
+import com.cloud.async.AsyncCommandQueued;
+import com.cloud.async.AsyncJob;
+import com.cloud.async.AsyncJobManager;
+import com.cloud.async.AsyncJobVO;
+import com.cloud.configuration.Config;
+import com.cloud.configuration.ConfigurationVO;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.domain.Domain;
+import com.cloud.domain.DomainVO;
+import com.cloud.event.ActionEventUtils;
+import com.cloud.exception.AccountLimitException;
+import com.cloud.exception.CloudAuthenticationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.exception.RequestLimitException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.DomainManager;
+import com.cloud.user.User;
+import com.cloud.user.UserAccount;
+import com.cloud.user.UserContext;
+import com.cloud.user.UserVO;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.Pair;
+import com.cloud.utils.StringUtils;
+import com.cloud.utils.component.ComponentContext;
+import com.cloud.utils.component.PluggableService;
+import com.cloud.utils.concurrency.NamedThreadFactory;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.exception.CloudRuntimeException;
 
 @Component
 public class ApiServer implements HttpRequestHandler, ApiServerService {
@@ -113,7 +165,7 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
     @Inject List<PluggableService> _pluggableServices;
     @Inject List<APIChecker> _apiAccessCheckers;
 
-    @Inject private RegionManager _regionMgr = null;
+    @Inject private final RegionManager _regionMgr = null;
 
     private static int _workerCount = 0;
     private static ApiServer s_instance = null;
@@ -227,6 +279,9 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
                 parameterMap.put(param.getName(), new String[] { param.getValue() });
             }
 
+	    // Get the type of http method being used.
+            parameterMap.put("httpmethod", new String[] { request.getRequestLine().getMethod() });
+
             // Check responseType, if not among valid types, fallback to JSON
             if (!(responseType.equals(BaseCmd.RESPONSE_TYPE_JSON) || responseType.equals(BaseCmd.RESPONSE_TYPE_XML)))
                 responseType = BaseCmd.RESPONSE_TYPE_XML;
@@ -254,10 +309,12 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
         }
     }
 
+    @Override
     @SuppressWarnings("rawtypes")
     public String handleRequest(Map params, String responseType, StringBuffer auditTrailSb) throws ServerApiException {
         String response = null;
         String[] command = null;
+
         try {
             command = (String[]) params.get("command");
             if (command == null) {
@@ -299,6 +356,7 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
                     cmdObj.configure();
                     cmdObj.setFullUrlParams(paramMap);
                     cmdObj.setResponseType(responseType);
+		    cmdObj.setHttpMethod(paramMap.get("httpmethod").toString());
 
                     // This is where the command is either serialized, or directly dispatched
                     response = queueCommand(cmdObj, paramMap);
@@ -530,6 +588,7 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
         }
     }
 
+    @Override
     public boolean verifyRequest(Map<String, Object[]> requestParameters, Long userId) throws ServerApiException {
         try {
             String apiKey = null;
@@ -692,10 +751,12 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
         return false;
     }
 
+    @Override
     public Long fetchDomainId(String domainUUID) {
         return _domainMgr.getDomain(domainUUID).getId();
     }
 
+    @Override
     public void loginUser(HttpSession session, String username, String password, Long domainId, String domainPath, String loginIpAddress ,Map<String, Object[]> requestParameters) throws CloudAuthenticationException {
         // We will always use domainId first. If that does not exist, we will use domain name. If THAT doesn't exist
         // we will default to ROOT
@@ -770,11 +831,13 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
         throw new CloudAuthenticationException("Failed to authenticate user " + username + " in domain " + domainId + "; please provide valid credentials");
     }
 
+    @Override
     public void logoutUser(long userId) {
         _accountMgr.logoutUser(Long.valueOf(userId));
         return;
     }
 
+    @Override
     public boolean verifyUser(Long userId) {
         User user = _accountMgr.getUserIncludingRemoved(userId);
         Account account = null;
@@ -931,6 +994,7 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
         }
     }
 
+    @Override
     public String getSerializedApiError(int errorCode, String errorText, Map<String, Object[]> apiCommandParams, String responseType) {
         String responseName = null;
         Class<?> cmdClass = null;
@@ -965,6 +1029,7 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
         return responseText;
     }
 
+    @Override
     public String getSerializedApiError(ServerApiException ex, Map<String, Object[]> apiCommandParams, String responseType) {
         String responseName = null;
         Class<?> cmdClass = null;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/src/com/cloud/api/ApiServerService.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServerService.java b/server/src/com/cloud/api/ApiServerService.java
index 12d8b52..dac81c6 100644
--- a/server/src/com/cloud/api/ApiServerService.java
+++ b/server/src/com/cloud/api/ApiServerService.java
@@ -21,6 +21,7 @@ import java.util.Map;
 import javax.servlet.http.HttpSession;
 
 import org.apache.cloudstack.api.ServerApiException;
+
 import com.cloud.exception.CloudAuthenticationException;
 
 public interface ApiServerService {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/src/com/cloud/api/ApiServlet.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java
index 03bfb5f..7525b61 100755
--- a/server/src/com/cloud/api/ApiServlet.java
+++ b/server/src/com/cloud/api/ApiServlet.java
@@ -299,8 +299,10 @@ public class ApiServlet extends HttpServlet {
                 auditTrailSb.insert(0, "(userId=" + UserContext.current().getCallerUserId() + " accountId="
                         + UserContext.current().getCaller().getId() + " sessionId=" + (session != null ? session.getId() : null) + ")");
 
-                    String response = _apiServer.handleRequest(params, responseType, auditTrailSb);
-                    writeResponse(resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType);
+                // Add the HTTP method (GET/POST/PUT/DELETE) as well into the params map.
+                params.put("httpmethod", new String[] { req.getMethod() });
+                String response = _apiServer.handleRequest(params, responseType, auditTrailSb);
+                writeResponse(resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType);
             } else {
                 if (session != null) {
                     try {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 1843f60..451f2a0 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -32,15 +32,27 @@ import javax.ejb.Local;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import com.cloud.api.ApiDBUtils;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.affinity.AffinityGroupVO;
 import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
 import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
+import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
 import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
 import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
-import org.apache.cloudstack.api.command.user.vm.*;
+import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
+import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
+import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
+import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
+import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
+import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
+import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd;
 import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity;
@@ -67,6 +79,7 @@ import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.agent.api.to.VolumeTO;
 import com.cloud.agent.manager.Commands;
 import com.cloud.alert.AlertManager;
+import com.cloud.api.ApiDBUtils;
 import com.cloud.api.query.dao.UserVmJoinDao;
 import com.cloud.api.query.vo.UserVmJoinVO;
 import com.cloud.async.AsyncJobManager;
@@ -405,6 +418,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
     private int _createprivatetemplatefromvolumewait;
     private int _createprivatetemplatefromsnapshotwait;
     private final int MAX_VM_NAME_LEN = 80;
+    private final int MAX_HTTP_GET_LENGTH = 2 * MAX_USER_DATA_LENGTH_BYTES;
+    private final int MAX_HTTP_POST_LENGTH = 16 * MAX_USER_DATA_LENGTH_BYTES;
 
     @Inject
     protected OrchestrationService _orchSrvc;
@@ -1604,7 +1619,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         if (userData != null) {
             // check and replace newlines
             userData = userData.replace("\\n", "");
-            validateUserData(userData);
+            validateUserData(userData, cmd.getHttpMethod());
             // update userData on domain router.
             updateUserdata = true;
         } else {
@@ -1926,10 +1941,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 
     @Override
     public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList, Account owner,
- String hostName,
-            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
-            String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps,
-            String keyboard, List<Long> affinityGroupIdList)
+            String hostName, String displayName, Long diskOfferingId, Long diskSize, String group,
+	    HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair,
+	    Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard,
+	    List<Long> affinityGroupIdList)
                     throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
 
         Account caller = UserContext.current().getCaller();
@@ -1979,18 +1994,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId,
-                diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller,
-                requestedIps, defaultIps, keyboard, affinityGroupIdList);
+                diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor,
+		caller, requestedIps, defaultIps, keyboard, affinityGroupIdList);
     }
 
     @Override
     public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
-            List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData,
- String sshKeyPair, Map<Long, IpAddresses> requestedIps,
-            IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
-            throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
-            StorageUnavailableException,
-            ResourceAllocationException {
+            List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId,
+	    Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
+            String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard,
+	    List<Long> affinityGroupIdList) throws InsufficientCapacityException, ConcurrentOperationException,
+	    ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
 
         Account caller = UserContext.current().getCaller();
         List<NetworkVO> networkList = new ArrayList<NetworkVO>();
@@ -2096,15 +2110,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId,
-                diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller,
-                requestedIps, defaultIps, keyboard, affinityGroupIdList);
+                diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor,
+		caller, requestedIps, defaultIps, keyboard, affinityGroupIdList);
     }
 
     @Override
     public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner, String hostName,
             String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
-            String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps,
-            String keyboard, List<Long> affinityGroupIdList)
+	    HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
+	    IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
                     throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
 
         Account caller = UserContext.current().getCaller();
@@ -2213,8 +2227,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId,
-                diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps,
-                keyboard, affinityGroupIdList);
+		diskSize, networkList, null, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps,
+		defaultIps, keyboard, affinityGroupIdList);
     }
 
 
@@ -2227,9 +2241,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 
     @DB @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true)
     protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, String hostName, String displayName, Account owner, Long diskOfferingId,
-            Long diskSize, List<NetworkVO> networkList, List<Long> securityGroupIdList, String group, String userData,
-            String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, IpAddresses> requestedIps,
-            IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
+            Long diskSize, List<NetworkVO> networkList, List<Long> securityGroupIdList, String group, HTTPMethod httpmethod,
+	    String userData, String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, IpAddresses> requestedIps,
+	    IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
                     throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException {
 
         _accountMgr.checkAccess(caller, null, true, owner);
@@ -2337,7 +2351,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         }
 
         // check if the user data is correct
-        validateUserData(userData);
+        validateUserData(userData, httpmethod);
 
         // Find an SSH public key corresponding to the key pair name, if one is
         // given
@@ -2601,22 +2615,36 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         return vm;
     }
 
-    private void validateUserData(String userData) {
+    private void validateUserData(String userData, HTTPMethod httpmethod) {
         byte[] decodedUserData = null;
         if (userData != null) {
             if (!Base64.isBase64(userData)) {
                 throw new InvalidParameterValueException(
                         "User data is not base64 encoded");
             }
-            if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) {
-                throw new InvalidParameterValueException(
-                        "User data is too long");
-            }
-            decodedUserData = Base64.decodeBase64(userData.getBytes());
-            if (decodedUserData.length > MAX_USER_DATA_LENGTH_BYTES) {
-                throw new InvalidParameterValueException(
-                        "User data is too long");
+            // If GET, use 4K. If POST, support upto 32K.
+            if (httpmethod.equals(HTTPMethod.GET)) {
+                if (userData.length() >= MAX_HTTP_GET_LENGTH) {
+                    throw new InvalidParameterValueException(
+                            "User data is too long for an http GET request");
+                }
+                decodedUserData = Base64.decodeBase64(userData.getBytes());
+                if (decodedUserData.length > MAX_HTTP_GET_LENGTH) {
+                    throw new InvalidParameterValueException(
+                        "User data is too long for GET request");
+                }
+            } else if (httpmethod.equals(HTTPMethod.POST)) {
+                if (userData.length() >= MAX_HTTP_POST_LENGTH) {
+                    throw new InvalidParameterValueException(
+                            "User data is too long for an http POST request");
+                }
+                decodedUserData = Base64.decodeBase64(userData.getBytes());
+                if (decodedUserData.length > MAX_HTTP_POST_LENGTH) {
+                    throw new InvalidParameterValueException(
+                        "User data is too long for POST request");
+                }
             }
+
             if (decodedUserData.length < 1) {
                 throw new InvalidParameterValueException(
                         "User data is too short");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/test/com/cloud/vm/MockUserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
index 0d0a8f4..d886fd8 100644
--- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java
+++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
@@ -23,9 +23,22 @@ import java.util.Map;
 import javax.ejb.Local;
 import javax.naming.ConfigurationException;
 
+import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
 import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
 import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
-import org.apache.cloudstack.api.command.user.vm.*;
+import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
+import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
+import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
+import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
+import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
+import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
+import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd;
 import org.springframework.stereotype.Component;
@@ -40,13 +53,13 @@ import com.cloud.dc.DataCenter;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.ManagementServerException;
+import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.exception.StorageUnavailableException;
 import com.cloud.exception.VirtualMachineMigrationException;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.PermissionDeniedException;
 import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.Network;
@@ -59,10 +72,9 @@ import com.cloud.template.VirtualMachineTemplate;
 import com.cloud.user.Account;
 import com.cloud.uservm.UserVm;
 import com.cloud.utils.Pair;
-import com.cloud.utils.component.Manager;
 import com.cloud.utils.component.ManagerBase;
-import com.cloud.utils.exception.ExecutionException;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.exception.ExecutionException;
 
 @Component
 @Local(value = { UserVmManager.class, UserVmService.class })
@@ -329,10 +341,10 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager,
 
     @Override
     public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList, Account owner,
-            String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
- IpAddresses defaultIp,
-            String keyboard, List<Long> affinityGroupIdList) throws InsufficientCapacityException,
-            ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException,
+            String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
+	    HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
+            IpAddresses defaultIp, String keyboard, List<Long> affinityGroupIdList)
+	    throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException,
             ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
@@ -340,21 +352,21 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager,
 
     @Override
     public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
-            List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData,
- String sshKeyPair, Map<Long, IpAddresses> requestedIps,
-            IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList)
-            throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
-            StorageUnavailableException, ResourceAllocationException {
+            List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize,
+	    String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
+            String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps,
+	    String keyboard, List<Long> affinityGroupIdList) throws InsufficientCapacityException,
+	    ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
     public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner, String hostName,
-            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps,
-            String keyboard, List<Long> affinityGroupIdList) throws InsufficientCapacityException,
-            ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException,
-            ResourceAllocationException {
+            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
+	    HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
+	    IpAddresses defaultIps, String keyboard, List<Long> affinityGroupIdList) throws InsufficientCapacityException,
+	    ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java b/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java
index 0936180..12d08ec 100644
--- a/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java
+++ b/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java
@@ -20,24 +20,47 @@ import javax.inject.Inject;
 
 import junit.framework.TestCase;
 
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Test;
 
+import com.cloud.hypervisor.Hypervisor;
 import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VirtualMachine;
 
-
 public class UserVmDaoImplTest extends TestCase {
     @Inject UserVmDao dao;
-     
-	public void testPersist() {
-        
-        dao.expunge(1000l);
-        
-        UserVmVO vo = new UserVmVO(1000l, "instancename", "displayname", 1, HypervisorType.XenServer, 1, true, true, 1, 1, 1, "userdata", "name", null);
+
+    public void makeAndVerifyEntry(Long vmId, String instanceName, String displayName, long templateId, boolean userdataFlag, Hypervisor.HypervisorType hypervisor,
+            long guestOsId, boolean haEnabled, boolean limitCpuUse, long domainId, long accountId, long serviceOfferingId, String name, Long diskOfferingId) {
+
+        dao.expunge(vmId);
+        String userdata;
+
+        if (userdataFlag) {
+            // Generate large userdata to simulate 32k of random string data for userdata submitted through HTTP POST requests.
+            userdata = RandomStringUtils.randomAlphanumeric(32*1024);
+        } else {
+            // Generate normal sized userdata to simulate 2k of random string data.
+            userdata = RandomStringUtils.randomAlphanumeric(2*1024);
+        }
+
+        // Persist the data.
+        UserVmVO vo = new UserVmVO(vmId, instanceName, displayName, templateId, hypervisor, guestOsId, haEnabled, limitCpuUse, domainId, accountId, serviceOfferingId, userdata, name, diskOfferingId);
         dao.persist(vo);
-        
-        vo = dao.findById(1000l);
+
+        vo = dao.findById(vmId);
         assert (vo.getType() == VirtualMachine.Type.User) : "Incorrect type " + vo.getType();
+
+        // Check whether the userdata matches what we generated.
+        assert (vo.getUserData().equals(userdata)) : "User data retrieved does not match userdata generated as input";
+
+    }
+
+    @Test
+    public void testPersist() {
+        Long vmId = 2222l;
+        makeAndVerifyEntry(vmId, "vm1", "vmdisp1", 1l, false, Hypervisor.HypervisorType.KVM, 1l, false, true, 1l, 1l, 1l, "uservm1", 1l);
+        makeAndVerifyEntry(vmId, "vm1", "vmdisp1", 1l, true, Hypervisor.HypervisorType.KVM, 1l, false, true, 1l, 1l, 1l, "uservm1", 1l);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/test/com/cloud/vm/dao/UserVmDaoTestConfiguration.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/dao/UserVmDaoTestConfiguration.java b/server/test/com/cloud/vm/dao/UserVmDaoTestConfiguration.java
new file mode 100644
index 0000000..6a63fab
--- /dev/null
+++ b/server/test/com/cloud/vm/dao/UserVmDaoTestConfiguration.java
@@ -0,0 +1,50 @@
+// 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.vm.dao;
+
+import java.io.IOException;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.ComponentScan.Filter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.FilterType;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.core.type.filter.TypeFilter;
+
+import com.cloud.utils.component.SpringComponentScanUtils;
+
+@Configuration
+@ComponentScan(basePackageClasses={
+        UserVmDaoImpl.class},
+        includeFilters={@Filter(value=UserVmDaoTestConfiguration.Library.class, type=FilterType.CUSTOM)},
+        useDefaultFilters=false
+        )
+
+public class UserVmDaoTestConfiguration {
+    public static class Library implements TypeFilter {
+
+        @Override
+        public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {
+            mdr.getClassMetadata().getClassName();
+            ComponentScan cs = UserVmDaoTestConfiguration.class.getAnnotation(ComponentScan.class);
+            return SpringComponentScanUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs);
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/server/test/resources/UserVMDaoTestContext.xml
----------------------------------------------------------------------
diff --git a/server/test/resources/UserVMDaoTestContext.xml b/server/test/resources/UserVMDaoTestContext.xml
new file mode 100644
index 0000000..6045f59
--- /dev/null
+++ b/server/test/resources/UserVMDaoTestContext.xml
@@ -0,0 +1,44 @@
+<!-- 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. -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+  xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
+  xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+                      http://www.springframework.org/schema/tx
+                      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
+                      http://www.springframework.org/schema/aop
+                      http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
+                      http://www.springframework.org/schema/context
+                      http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+  <context:annotation-config />
+
+  <!-- @DB support -->
+  <bean id="componentContext" class="com.cloud.utils.component.ComponentContext" />
+
+  <bean id="transactionContextBuilder" class="com.cloud.utils.db.TransactionContextBuilder" />
+  <bean id="actionEventInterceptor" class="com.cloud.event.ActionEventInterceptor" />
+  <bean id="instantiatePostProcessor" class="com.cloud.utils.component.ComponentInstantiationPostProcessor">
+    <property name="Interceptors">
+        <list>
+            <ref bean="transactionContextBuilder" />
+            <ref bean="actionEventInterceptor" />
+        </list>
+    </property>
+  </bean>
+
+  <bean id="UVMTestConfiguration"
+    class="com.cloud.vm.dao.UserVmDaoTestConfiguration" />
+  <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" >
+    <property name="requiredParameterValue" value="false" />
+  </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index 10cdbba..39853d5 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -296,6 +296,8 @@ UPDATE configuration SET value='KVM,XenServer,VMware,BareMetal,Ovm,LXC' WHERE na
 INSERT INTO `cloud`.`vm_template` (id, unique_name, name, public, created, type, hvm, bits, account_id, url, checksum, enable_password, display_text, format, guest_os_id, featured, cross_zones, hypervisor_type)
      VALUES (10, 'routing-10', 'SystemVM Template (LXC)', 0, now(), 'SYSTEM', 0, 64, 1, 'http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2', '2755de1f9ef2ce4d6f2bee2efbb4da92', 0, 'SystemVM Template (LXC)', 'QCOW2', 15, 0, 1, 'LXC');
 
+ALTER TABLE `cloud`.`user_vm` MODIFY user_data TEXT(32768);
+
 -- END: support for LXC
 
 CREATE TABLE `cloud`.`vm_snapshots` (
@@ -777,4 +779,4 @@ CREATE VIEW `cloud`.`account_view` AS
             and async_job.instance_type = 'Account'
             and async_job.job_status = 0;
 
-alter table `cloud_usage`.`usage_network_offering` add column nic_id bigint(20) unsigned NOT NULL;             
\ No newline at end of file
+alter table `cloud_usage`.`usage_network_offering` add column nic_id bigint(20) unsigned NOT NULL;             

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/495a7292/test/integration/component/test_deploy_vm_with_userdata.py
----------------------------------------------------------------------
diff --git a/test/integration/component/test_deploy_vm_with_userdata.py b/test/integration/component/test_deploy_vm_with_userdata.py
new file mode 100644
index 0000000..d9808a9
--- /dev/null
+++ b/test/integration/component/test_deploy_vm_with_userdata.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+
+import marvin
+from marvin import cloudstackTestCase
+from marvin.cloudstackTestCase import *
+from marvin.integration.lib.base import *
+
+import unittest
+import hashlib
+import random
+import os
+import string
+
+class TestDeployVmWithUserData(cloudstackTestCase):
+    """
+    This test deploys a virtual machine into a user account
+    using the small service offering and builtin template
+    """
+    def setUp(self):
+        password = "password"
+
+        self.apiClient = self.testClient.getApiClient() #Get ourselves an API client
+
+        self.acct = createAccount.createAccountCmd() #The createAccount command
+        self.acct.accounttype = 0                    #We need a regular user. admins have accounttype=1
+        self.acct.firstname = 'firstname'
+        self.acct.lastname = 'lastname'
+        self.acct.password = password
+        self.acct.username = 'user1'
+        self.acct.email = 'user1@user.com'
+        self.acct.account = 'user1'
+        self.acct.domainid = 1                       #The default ROOT domain
+        self.acctResponse = self.apiClient.createAccount(self.acct)
+
+        self.debug("Successfully created account: %s, user: %s, id: \
+                   %s"%(self.acctResponse.account.account, \
+                        self.acctResponse.account.username, \
+                        self.acctResponse.account.id))
+
+        # Generate userdata of 2500 bytes. This is larger than the 2048 bytes limit.
+        # CS however allows for upto 4K bytes in the code. So this must succeed.
+        # Overall, the query length must not exceed 4K, for then the json decoder
+        # will fail this operation at the marvin client side itself.
+        user_data = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(2500))
+
+        self.virtual_machine = {
+          "displayname": "Test VM",
+          "username": "root",
+          "password": "password",
+          "ssh_port": 22,
+          "hypervisor": 'VMware',
+          "privateport": 22,
+          "publicport": 22,
+          "protocol": 'TCP',
+        }
+        #self.virtual_machine["userdata"] = base64.b64encode(user_data)
+        self.virtual_machine["userdata"] = user_data 
+
+    def test_DeployVm(self):
+        """
+        Let's start by defining the attributes of our VM that we will be
+        deploying on CloudStack. We will be assuming a single zone is available
+        and is configured and all templates are Ready
+        """
+        deployVmCmd = deployVirtualMachine.deployVirtualMachineCmd()
+        deployVmCmd.zoneid = 1
+        deployVmCmd.account = self.acct.account
+        deployVmCmd.domainid = self.acct.domainid
+        deployVmCmd.templateid = 7
+        deployVmCmd.serviceofferingid = 1
+
+        # Userdata is passed in the virtual_machine dictionary.
+        deployVmResponse = VirtualMachine.create(
+          self.apiClient,
+          self.virtual_machine,
+          accountid=self.acct.account,
+          domainid=self.acct.domainid,
+          serviceofferingid=deployVmCmd.serviceofferingid,
+          templateid=deployVmCmd.templateid,
+          zoneid=deployVmCmd.zoneid
+        )
+
+        # At this point our VM is expected to be Running. Let's find out what
+        # listVirtualMachines tells us about VMs in this account
+
+        listVmCmd = listVirtualMachines.listVirtualMachinesCmd()
+        listVmCmd.id = deployVmResponse.id
+        listVmResponse = self.apiClient.listVirtualMachines(listVmCmd)
+
+        self.assertNotEqual(len(listVmResponse), 0, "Check if the list API \
+                            returns a non-empty response")
+
+        vm = listVmResponse[0]
+
+        self.assertEqual(vm.id, deployVmResponse.id, "Check if the VM returned \
+                         is the same as the one we deployed")
+
+
+        self.assertEqual(vm.state, "Running", "Check if VM has reached \
+                         a state of running")
+
+    def tearDown(self):
+        """
+        Delete the account created. This will clear the VM belonging to that account as well.
+        """
+        deleteAcct = deleteAccount.deleteAccountCmd()
+        deleteAcct.id = self.acctResponse.account.id
+        self.apiClient.deleteAccount(deleteAcct)
+
+