You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ml...@apache.org on 2014/03/11 20:05:45 UTC

[06/13] CLOUDSTACK-4760 : Enabling GPU support for XenServer. CLOUDSTACK-4762 : Enabling VGPU support for XenServer.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/src/com/cloud/network/NetworkUsageManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/NetworkUsageManagerImpl.java b/server/src/com/cloud/network/NetworkUsageManagerImpl.java
index e9b0393..13eb210 100755
--- a/server/src/com/cloud/network/NetworkUsageManagerImpl.java
+++ b/server/src/com/cloud/network/NetworkUsageManagerImpl.java
@@ -57,6 +57,7 @@ import com.cloud.event.UsageEventVO;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.AgentUnavailableException;
 import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.gpu.dao.HostGpuGroupsDao;
 import com.cloud.host.DetailVO;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
@@ -116,6 +117,8 @@ public class NetworkUsageManagerImpl extends ManagerBase implements NetworkUsage
     @Inject
     HostDetailsDao _detailsDao;
     @Inject
+    HostGpuGroupsDao _hostGpuGroupsDao;
+    @Inject
     AccountManager _accountMgr;
     @Inject
     NetworkDao _networksDao = null;
@@ -537,6 +540,7 @@ public class NetworkUsageManagerImpl extends ManagerBase implements NetworkUsage
         long hostId = host.getId();
         _agentMgr.disconnectWithoutInvestigation(hostId, Status.Event.Remove);
         _detailsDao.deleteDetails(hostId);
+        _hostGpuGroupsDao.deleteGpuEntries(hostId);
         host.setGuid(null);
         _hostDao.update(hostId, host);
         _hostDao.remove(hostId);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/src/com/cloud/resource/ResourceManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java
index adad85c..2625885 100755
--- a/server/src/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/com/cloud/resource/ResourceManagerImpl.java
@@ -30,11 +30,6 @@ import javax.ejb.Local;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
-
-import com.google.gson.Gson;
-
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
 import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
@@ -51,10 +46,14 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
 import org.apache.commons.lang.ObjectUtils;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
+import com.cloud.agent.api.GetGPUStatsAnswer;
+import com.cloud.agent.api.GetGPUStatsCommand;
 import com.cloud.agent.api.GetHostStatsAnswer;
 import com.cloud.agent.api.GetHostStatsCommand;
 import com.cloud.agent.api.MaintainAnswer;
@@ -64,6 +63,7 @@ import com.cloud.agent.api.StartupCommand;
 import com.cloud.agent.api.StartupRoutingCommand;
 import com.cloud.agent.api.UnsupportedAnswer;
 import com.cloud.agent.api.UpdateHostPasswordCommand;
+import com.cloud.agent.api.to.GPUDeviceTO;
 import com.cloud.agent.transport.Request;
 import com.cloud.api.ApiDBUtils;
 import com.cloud.capacity.Capacity;
@@ -97,6 +97,11 @@ import com.cloud.exception.DiscoveryException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceInUseException;
+import com.cloud.gpu.GPU.vGPUType;
+import com.cloud.gpu.HostGpuGroupsVO;
+import com.cloud.gpu.VGPUTypesVO;
+import com.cloud.gpu.dao.HostGpuGroupsDao;
+import com.cloud.gpu.dao.VGPUTypesDao;
 import com.cloud.ha.HighAvailabilityManager;
 import com.cloud.ha.HighAvailabilityManager.WorkType;
 import com.cloud.host.DetailVO;
@@ -137,11 +142,14 @@ import com.cloud.utils.UriUtils;
 import com.cloud.utils.component.Manager;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.db.DB;
+import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.GenericSearchBuilder;
 import com.cloud.utils.db.GlobalLock;
+import com.cloud.utils.db.JoinBuilder;
 import com.cloud.utils.db.QueryBuilder;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.utils.db.SearchCriteria.Func;
 import com.cloud.utils.db.SearchCriteria.Op;
 import com.cloud.utils.db.Transaction;
@@ -158,6 +166,7 @@ import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.dao.VMInstanceDao;
+import com.google.gson.Gson;
 
 @Component
 @Local({ResourceManager.class, ResourceService.class})
@@ -193,6 +202,10 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
     @Inject
     private GuestOSCategoryDao _guestOSCategoryDao;
     @Inject
+    protected HostGpuGroupsDao _hostGpuGroupsDao;
+    @Inject
+    protected VGPUTypesDao _vgpuTypesDao;
+    @Inject
     private PrimaryDataStoreDao _storagePoolDao;
     @Inject
     private DataCenterIpAddressDao _privateIPAddressDao;
@@ -244,6 +257,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
 
     private GenericSearchBuilder<HostVO, String> _hypervisorsInDC;
 
+    private SearchBuilder<HostGpuGroupsVO> _gpuAvailability;
+
     private void insertListener(Integer event, ResourceListener listener) {
         List<ResourceListener> lst = _lifeCycleListeners.get(event);
         if (lst == null) {
@@ -827,6 +842,9 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
                 // delete host details
                 _hostDetailsDao.deleteDetails(hostId);
 
+                // if host is GPU enabled, delete GPU entries
+                _hostGpuGroupsDao.deleteGpuEntries(hostId);
+
                 host.setGuid(null);
                 Long clusterId = host.getClusterId();
                 host.setClusterId(null);
@@ -1329,6 +1347,14 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
         _hypervisorsInDC.and("type", _hypervisorsInDC.entity().getType(), SearchCriteria.Op.EQ);
         _hypervisorsInDC.done();
 
+        _gpuAvailability = _hostGpuGroupsDao.createSearchBuilder();
+        _gpuAvailability.and("hostId", _gpuAvailability.entity().getHostId(), Op.EQ);
+        SearchBuilder<VGPUTypesVO> join1 = _vgpuTypesDao.createSearchBuilder();
+        join1.and("vgpuType", join1.entity().getVgpuType(), Op.EQ);
+        join1.and("remainingCapacity", join1.entity().getRemainingCapacity(), Op.GT);
+        _gpuAvailability.join("groupId", join1, _gpuAvailability.entity().getId(), join1.entity().getGpuGroupId(), JoinBuilder.JoinType.INNER);
+        _gpuAvailability.done();
+
         return true;
     }
 
@@ -1958,6 +1984,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
         host.setSpeed(ssCmd.getSpeed());
         host.setHypervisorType(hyType);
         host.setHypervisorVersion(ssCmd.getHypervisorVersion());
+        host.setGpuGroups(ssCmd.getGpuGroupDetails());
         return host;
     }
 
@@ -2474,6 +2501,66 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
     }
 
     @Override
+    public List<HostGpuGroupsVO> listAvailableGPUDevice(long hostId, String vgpuType) {
+        if (vgpuType == null) {
+            vgpuType = vGPUType.passthrough.getType();
+        }
+        Filter searchFilter = new Filter(VGPUTypesVO.class, "remainingCapacity", false, null, null);
+        SearchCriteria<HostGpuGroupsVO> sc = _gpuAvailability.create();
+        sc.setParameters("hostId", hostId);
+        sc.setJoinParameters("groupId", "vgpuType", vgpuType);
+        sc.setJoinParameters("groupId", "remainingCapacity", 0);
+        return _hostGpuGroupsDao.customSearch(sc, searchFilter);
+    }
+
+    @Override
+    public boolean isGPUDeviceAvailable(long hostId, String vgpuType) {
+        if(!listAvailableGPUDevice(hostId, vgpuType).isEmpty()) {
+            return true;
+        } else {
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("Host ID: "+ hostId +" does not have GPU device available");
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public GPUDeviceTO getGPUDevice(long hostId, String vgpuType) {
+        HostGpuGroupsVO gpuDevice = listAvailableGPUDevice(hostId, vgpuType).get(0);
+        return new GPUDeviceTO(gpuDevice.getGroupName(), vgpuType, null);
+    }
+
+    @Override
+    public void updateGPUDetails(long hostId, HashMap<String, HashMap<String, Long>> groupDetails) {
+        // Update GPU group capacity
+        TransactionLegacy txn = TransactionLegacy.currentTxn();
+        txn.start();
+        _hostGpuGroupsDao.persist(hostId, new ArrayList<String>(groupDetails.keySet()));
+        _vgpuTypesDao.persist(hostId, groupDetails);
+        txn.commit();
+    }
+
+    @Override
+    public HashMap<String, HashMap<String, Long>> getGPUStatistics(HostVO host) {
+        Answer answer = _agentMgr.easySend(host.getId(), new GetGPUStatsCommand(host.getGuid(), host.getName()));
+        if (answer != null && (answer instanceof UnsupportedAnswer)) {
+            return null;
+        }
+        if (answer == null || !answer.getResult()) {
+            String msg = "Unable to obtain GPU stats for host " + host.getName();
+            s_logger.warn(msg);
+            return null;
+        } else {
+            // now construct the result object
+            if (answer instanceof GetGPUStatsAnswer) {
+                return ((GetGPUStatsAnswer)answer).getGroupDetails();
+            }
+        }
+        return null;
+    }
+
+    @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_HOST_RESERVATION_RELEASE, eventDescription = "releasing host reservation", async = true)
     public boolean releaseHostReservation(final Long hostId) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/src/com/cloud/server/ManagementServerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index 49a9eb5..663d4e5 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -508,6 +508,7 @@ import com.cloud.exception.OperationTimedoutException;
 import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.exception.VirtualMachineMigrationException;
+import com.cloud.gpu.GPU;
 import com.cloud.ha.HighAvailabilityManager;
 import com.cloud.host.DetailVO;
 import com.cloud.host.Host;
@@ -539,6 +540,7 @@ import com.cloud.server.ResourceTag.ResourceObjectType;
 import com.cloud.server.auth.UserAuthenticator;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.service.dao.ServiceOfferingDetailsDao;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.GuestOS;
 import com.cloud.storage.GuestOSCategoryVO;
@@ -700,6 +702,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
     private ResourceTagDao _resourceTagDao;
     @Inject
     private ImageStoreDao _imgStoreDao;
+    @Inject
+    private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
+
 
     @Inject
     private ProjectManager _projectMgr;
@@ -1059,6 +1064,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
             throw ex;
         }
 
+        if(_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
+            s_logger.info(" Live Migration of GPU enabled VM : " + vm.getInstanceName()+ " is not supported");
+            // Return empty list.
+            return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>>(new Pair<List <? extends Host>,
+                    Integer>(new ArrayList<HostVO>(), new Integer(0)), new ArrayList<Host>(), new HashMap<Host, Boolean>());
+        }
+
         if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM)
                 && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv)) {
             if (s_logger.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/src/com/cloud/server/StatsCollector.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java
index 548587c..067ed00 100755
--- a/server/src/com/cloud/server/StatsCollector.java
+++ b/server/src/com/cloud/server/StatsCollector.java
@@ -54,6 +54,7 @@ import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.cluster.ManagementServerHostVO;
 import com.cloud.cluster.dao.ManagementServerHostDao;
 import com.cloud.exception.StorageUnavailableException;
+import com.cloud.gpu.dao.HostGpuGroupsDao;
 import com.cloud.host.Host;
 import com.cloud.host.HostStats;
 import com.cloud.host.HostVO;
@@ -175,6 +176,8 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
     private AutoScaleVmProfileDao _asProfileDao;
     @Inject
     private ServiceOfferingDao _serviceOfferingDao;
+    @Inject
+    private HostGpuGroupsDao _hostGpuGroupsDao;
 
     private ConcurrentHashMap<Long, HostStats> _hostStats = new ConcurrentHashMap<Long, HostStats>();
     private final ConcurrentHashMap<Long, VmStats> _VmStats = new ConcurrentHashMap<Long, VmStats>();
@@ -188,6 +191,7 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
     long volumeStatsInterval = -1L;
     long autoScaleStatsInterval = -1L;
     int vmDiskStatsInterval = 0;
+    List<Long> hostIds = null;
 
     private ScheduledExecutorService _diskStatsUpdateExecutor;
     private int _usageAggregationRange = 1440;
@@ -325,6 +329,23 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
                     }
                 }
                 _hostStats = hostStats;
+                // Get a subset of hosts with GPU support from the list of "hosts"
+                List<HostVO> gpuEnabledHosts = new ArrayList<HostVO>();
+                if (hostIds != null) {
+                    for (HostVO host : hosts) {
+                        if (hostIds.contains(host.getId())) {
+                            gpuEnabledHosts.add(host);
+                        }
+                    }
+                } else {
+                    // Check for all the hosts managed by CloudStack.
+                    gpuEnabledHosts = hosts;
+                }
+                for (HostVO host : gpuEnabledHosts) {
+                    HashMap<String, HashMap<String, Long>> groupDetails = _resourceMgr.getGPUStatistics(host);
+                    _resourceMgr.updateGPUDetails(host.getId(), groupDetails);
+                }
+                hostIds = _hostGpuGroupsDao.listHostIds();
             } catch (Throwable t) {
                 s_logger.error("Error trying to retrieve host stats", t);
             }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/src/com/cloud/storage/VolumeApiServiceImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
index 17461c0..acc922f 100644
--- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
@@ -92,12 +92,14 @@ import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.StorageUnavailableException;
+import com.cloud.gpu.GPU;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorCapabilitiesVO;
 import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
 import com.cloud.org.Grouping;
+import com.cloud.service.dao.ServiceOfferingDetailsDao;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.SnapshotDao;
@@ -171,6 +173,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
     @Inject
     private SnapshotDao _snapshotDao;
     @Inject
+    protected ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
+    @Inject
     StoragePoolDetailsDao storagePoolDetailsDao;
     @Inject
     private UserVmDao _userVmDao;
@@ -1466,6 +1470,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
         }
 
         if (vm != null && vm.getState() == State.Running) {
+            // Check if the VM is GPU enabled.
+            if(_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
+                throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported");
+            }
             // Check if the underlying hypervisor supports storage motion.
             Long hostId = vm.getHostId();
             if (hostId != null) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/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 d1df3c1..be00aa8 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -137,6 +137,7 @@ import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.exception.StorageUnavailableException;
 import com.cloud.exception.VirtualMachineMigrationException;
+import com.cloud.gpu.GPU;
 import com.cloud.ha.HighAvailabilityManager;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
@@ -3853,6 +3854,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
             ex.addProxyObject(vm.getUuid(), "vmId");
             throw ex;
         }
+
+        if(serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
+            throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported");
+        }
+
         if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM)
                 && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv)
                 && !vm.getHypervisorType().equals(HypervisorType.Simulator)) {
@@ -4164,6 +4170,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
             throw ex;
         }
 
+        if(serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
+            throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported");
+        }
+
         if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM)
                 && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Simulator)) {
             throw new InvalidParameterValueException("Unsupported hypervisor type for vm migration, we support" + " XenServer/VMware/KVM only");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/test/com/cloud/resource/MockResourceManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/resource/MockResourceManagerImpl.java b/server/test/com/cloud/resource/MockResourceManagerImpl.java
index 5599e8c..e6bf9a2 100644
--- a/server/test/com/cloud/resource/MockResourceManagerImpl.java
+++ b/server/test/com/cloud/resource/MockResourceManagerImpl.java
@@ -17,6 +17,7 @@
 
 package com.cloud.resource;
 
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -35,6 +36,7 @@ import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd;
 
 import com.cloud.agent.api.StartupCommand;
 import com.cloud.agent.api.StartupRoutingCommand;
+import com.cloud.agent.api.to.GPUDeviceTO;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.HostPodVO;
 import com.cloud.dc.PodCluster;
@@ -42,6 +44,7 @@ import com.cloud.exception.AgentUnavailableException;
 import com.cloud.exception.DiscoveryException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.ResourceInUseException;
+import com.cloud.gpu.HostGpuGroupsVO;
 import com.cloud.host.Host;
 import com.cloud.host.Host.Type;
 import com.cloud.host.HostStats;
@@ -554,4 +557,32 @@ public class MockResourceManagerImpl extends ManagerBase implements ResourceMana
         return false;
     }
 
+    @Override
+    public boolean isGPUDeviceAvailable(long hostId, String vgpuType) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public GPUDeviceTO getGPUDevice(long hostId, String vgpuType) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<HostGpuGroupsVO> listAvailableGPUDevice(long hostId, String vgpuType) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public void updateGPUDetails(long hostId, HashMap<String, HashMap<String, Long>> deviceDetails) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public HashMap<String, HashMap<String, Long>> getGPUStatistics(HostVO host) {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
index 751a3bd..fb63766 100644
--- a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
+++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
@@ -78,7 +78,9 @@ import com.cloud.exception.AffinityConflictException;
 import com.cloud.exception.InsufficientServerCapacityException;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.resource.ResourceManager;
 import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.dao.ServiceOfferingDetailsDao;
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.GuestOSCategoryDao;
@@ -239,6 +241,16 @@ public class DeploymentPlanningManagerImplTest {
         }
 
         @Bean
+        public ResourceManager resourceManager() {
+            return Mockito.mock(ResourceManager.class);
+        }
+
+        @Bean
+        public ServiceOfferingDetailsDao serviceOfferingDetailsDao() {
+            return Mockito.mock(ServiceOfferingDetailsDao.class);
+        }
+
+        @Bean
         public DataStoreManager cataStoreManager() {
             return Mockito.mock(DataStoreManager.class);
         }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/server/test/resources/createNetworkOffering.xml
----------------------------------------------------------------------
diff --git a/server/test/resources/createNetworkOffering.xml b/server/test/resources/createNetworkOffering.xml
index c6228da..6ae1978 100644
--- a/server/test/resources/createNetworkOffering.xml
+++ b/server/test/resources/createNetworkOffering.xml
@@ -43,7 +43,9 @@
     </bean>
 
     <bean class="org.apache.cloudstack.networkoffering.ChildTestConfiguration" />
-    <bean id="UservmDetailsDaoImpl" class="com.cloud.vm.dao.UserVmDetailsDaoImpl" />
+    <bean id="UservmDetailsDaoImpl" class="com.cloud.vm.dao.UserVmDetailsDaoImpl" />
+    <bean id="hostGpuGroupsDaoImpl" class="com.cloud.gpu.dao.HostGpuGroupsDaoImpl" />
+    <bean id="vGPUTypesDaoImpl" class="com.cloud.gpu.dao.VGPUTypesDaoImpl" />
     <bean id="usageEventDaoImpl" class="com.cloud.event.dao.UsageEventDaoImpl" />
     <bean id="usageEventDetailsDaoImpl" class="com.cloud.event.dao.UsageEventDetailsDaoImpl" />
 </beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/setup/db/db/schema-430to440.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql
index be49b83..ee4cf21 100644
--- a/setup/db/db/schema-430to440.sql
+++ b/setup/db/db/schema-430to440.sql
@@ -592,4 +592,22 @@ CREATE VIEW `cloud`.`event_view` AS
         `cloud`.`event` eve ON event.start_id = eve.id;
 
 
+DROP TABLE IF EXISTS `cloud`.`host_gpu_groups`;
+CREATE TABLE `cloud`.`host_gpu_groups` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `group_name` varchar(255) NOT NULL,
+  `host_id` bigint(20) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_host_gpu_groups__host_id` FOREIGN KEY (`host_id`) REFERENCES `host` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB CHARSET=utf8;
+
+DROP TABLE IF EXISTS `cloud`.`vgpu_types`;
+CREATE TABLE `cloud`.`vgpu_types` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `gpu_group_id` bigint(20) unsigned NOT NULL,
+  `vgpu_type` varchar(40) NOT NULL COMMENT 'vgpu type supported by this gpu group',
+  `remaining_vm_capacity` bigint(20) unsigned DEFAULT NULL COMMENT 'remaining vgpu can be created with this vgpu_type on the given gpu group',
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_vgpu_types__gpu_group_id` FOREIGN KEY (`gpu_group_id`) REFERENCES `host_gpu_groups` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB CHARSET=utf8;
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/test/integration/smoke/test_deploy_vgpu_enabled_vm.py
----------------------------------------------------------------------
diff --git a/test/integration/smoke/test_deploy_vgpu_enabled_vm.py b/test/integration/smoke/test_deploy_vgpu_enabled_vm.py
new file mode 100644
index 0000000..a09e87e
--- /dev/null
+++ b/test/integration/smoke/test_deploy_vgpu_enabled_vm.py
@@ -0,0 +1,227 @@
+# 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.
+
+#Test from the Marvin - Testing in Python wiki
+
+#All tests inherit from cloudstackTestCase
+from marvin.cloudstackTestCase import cloudstackTestCase
+
+#Import Integration Libraries
+
+#base - contains all resources as entities and defines create, delete, list operations on them
+from marvin.integration.lib.base import Account, VirtualMachine, ServiceOffering
+
+#utils - utility classes for common cleanup, external library wrappers etc
+from marvin.integration.lib.utils import cleanup_resources
+
+#common - commonly used methods for all tests are listed here
+from marvin.integration.lib.common import get_zone, get_domain, get_template
+
+from nose.plugins.attrib import attr
+
+class Services:
+    """Test VM Life Cycle Services
+    """
+
+    def __init__(self):
+        self.services = {
+                "disk_offering":{
+                    "displaytext": "Small",
+                    "name": "Small",
+                    "disksize": 1
+                },
+                "account": {
+                    "email": "test@test.com",
+                    "firstname": "Test",
+                    "lastname": "User",
+                    "username": "test",
+                    # Random characters are appended in create account to
+                    # ensure unique username generated each time
+                    "password": "password",
+                },
+                "vgpu260q":   # Create a virtual machine instance with vgpu type as 260q
+                {
+                    "displayname": "testserver",
+                    "username": "root", # VM creds for SSH
+                    "password": "password",
+                    "ssh_port": 22,
+                    "hypervisor": 'XenServer',
+                    "privateport": 22,
+                    "publicport": 22,
+                    "protocol": 'TCP',
+                },
+                "vgpu140q":   # Create a virtual machine instance with vgpu type as 140q
+                {
+                    "displayname": "testserver",
+                    "username": "root",
+                    "password": "password",
+                    "ssh_port": 22,
+                    "hypervisor": 'XenServer',
+                    "privateport": 22,
+                    "publicport": 22,
+                    "protocol": 'TCP',
+                },
+                "service_offerings":
+                {
+                 "vgpu260qwin":
+                   {
+                        "name": "Windows Instance with vGPU260Q",
+                        "displaytext": "Windows Instance with vGPU260Q",
+                        "cpunumber": 2,
+                        "cpuspeed": 1600, # in MHz
+                        "memory": 3072, # In MBs
+                    },
+                 "vgpu140qwin":
+                    {
+                     # Small service offering ID to for change VM
+                     # service offering from medium to small
+                        "name": "Windows Instance with vGPU140Q",
+                        "displaytext": "Windows Instance with vGPU140Q",
+                        "cpunumber": 2,
+                        "cpuspeed": 1600,
+                        "memory": 3072,
+                    }
+                },
+            "diskdevice": ['/dev/vdc',  '/dev/vdb', '/dev/hdb', '/dev/hdc', '/dev/xvdd', '/dev/cdrom', '/dev/sr0', '/dev/cdrom1' ],
+            # Disk device where ISO is attached to instance
+            "mount_dir": "/mnt/tmp",
+            "sleep": 60,
+            "timeout": 10,
+            #Migrate VM to hostid
+            "ostype": 'Windows 7 (32-bit)',
+            # CentOS 5.3 (64-bit)
+        }
+
+
+class TestDeployvGPUenabledVM(cloudstackTestCase):
+    """Test deploy a vGPU enabled VM into a user account
+    """
+
+    def setUp(self):
+        self.services = Services().services
+        self.apiclient = self.testClient.getApiClient()
+
+        # Get Zone, Domain and Default Built-in template
+        self.domain = get_domain(self.apiclient, self.services)
+        self.zone = get_zone(self.apiclient, self.services)
+        self.services["mode"] = self.zone.networktype
+        # Before running this test, register a windows template with ostype as 'Windows 7 (32-bit)'
+        self.template = get_template(self.apiclient, self.zone.id, self.services["ostype"], templatetype='USER')
+
+        #create a user account
+        self.account = Account.create(
+            self.apiclient,
+            self.services["account"],
+            domainid=self.domain.id
+        )
+
+        self.services["vgpu260q"]["zoneid"] = self.zone.id
+        self.services["vgpu260q"]["template"] = self.template.id
+
+        self.services["vgpu140q"]["zoneid"] = self.zone.id
+        self.services["vgpu140q"]["template"] = self.template.id
+        #create a service offering
+        self.service_offering = ServiceOffering.create(
+                self.apiclient,
+                self.services["service_offerings"]["vgpu260qwin"],
+                serviceofferingdetails={'pciDevice': 'VGPU'}
+        )
+        #build cleanup list
+        self.cleanup = [
+            self.service_offering,
+            self.account
+        ]
+
+    @attr(tags = ['advanced', 'simulator', 'basic', 'vgpu'])
+    def test_deploy_vgpu_enabled_vm(self):
+        """Test Deploy Virtual Machine
+
+        # Validate the following:
+        # 1. Virtual Machine is accessible via SSH
+        # 2. Virtual Machine is vGPU enabled (via SSH)
+        # 3. listVirtualMachines returns accurate information
+        """
+        self.virtual_machine = VirtualMachine.create(
+            self.apiclient,
+            self.services["vgpu260q"],
+            accountid=self.account.name,
+            domainid=self.account.domainid,
+            serviceofferingid=self.service_offering.id,
+            mode=self.services['mode']
+        )
+
+        list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id)
+
+        self.debug(
+            "Verify listVirtualMachines response for virtual machine: %s"\
+            % self.virtual_machine.id
+        )
+
+        self.assertEqual(
+            isinstance(list_vms, list),
+            True,
+            "List VM response was not a valid list"
+        )
+        self.assertNotEqual(
+            len(list_vms),
+            0,
+            "List VM response was empty"
+        )
+
+        vm = list_vms[0]
+        self.assertEqual(
+            vm.id,
+            self.virtual_machine.id,
+            "Virtual Machine ids do not match"
+        )
+        self.assertEqual(
+            vm.name,
+            self.virtual_machine.name,
+            "Virtual Machine names do not match"
+        )
+        self.assertEqual(
+            vm.state,
+            "Running",
+            msg="VM is not in Running state"
+        )
+        list_hosts = list_hosts(
+               self.apiclient,
+               id=vm.hostid
+               )
+        hostip = list_hosts[0].ipaddress
+        try:
+            sshClient = SshClient(host=hostip, port=22, user='root',passwd=self.services["host_password"])
+            res = sshClient.execute("xe vgpu-list vm-name-label=%s params=type-uuid %s" % (
+                                   vm.instancename
+                                 ))
+            self.debug("SSH result: %s" % res)
+        except Exception as e:
+            self.fail("SSH Access failed for %s: %s" % \
+                      (hostip, e)
+                      )
+        result = str(res)
+        self.assertEqual(
+                    result.count("type-uuid"),
+                    1,
+                    "VM is vGPU enabled."
+                    )
+
+    def tearDown(self):
+        try:
+            cleanup_resources(self.apiclient, self.cleanup)
+        except Exception as e:
+            self.debug("Warning! Exception in tearDown: %s" % e)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/tools/marvin/marvin/integration/lib/base.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py
index 7449d8c..27a26b8 100755
--- a/tools/marvin/marvin/integration/lib/base.py
+++ b/tools/marvin/marvin/integration/lib/base.py
@@ -1462,6 +1462,9 @@ class ServiceOffering:
         if "deploymentplanner" in services:
             cmd.deploymentplanner = services["deploymentplanner"]
 
+        if "serviceofferingdetails" in services:
+            cmd.serviceofferingdetails.append({services['serviceofferingdetails']})
+
         if "isvolatile" in services:
             cmd.isvolatile = services["isvolatile"]
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/ui/scripts/configuration.js
----------------------------------------------------------------------
diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js
index e3c35af..8666042 100644
--- a/ui/scripts/configuration.js
+++ b/ui/scripts/configuration.js
@@ -361,6 +361,71 @@
                                         }
                                     },
 
+                                    pciDevice: {
+                                        label: 'GPU Type',
+                                        select: function(args) {
+                                            var items = [];
+                                            items.push({
+                                                id: '',
+                                                description: ''
+                                            });
+                                            items.push({
+                                                id: 'GPU_Passthrough',
+                                                description: 'GPU-Passthrough'
+                                            });
+                                            items.push({
+                                                id: 'VGPU',
+                                                description: 'VGPU'
+                                            });
+                                            args.response.success({
+                                                data: items
+                                            });
+                                            args.$select.change(function() {
+                                                var $form = $(this).closest('form');
+                                                var $fields = $form.find('.field');
+                                                if (($(this).val() == "") || $(this).val() == "GPU-Passthrough") {
+                                                  $form.find('[rel=vgpuType]').hide();
+                                                } else if ($(this).val() == "VGPU") {
+                                                  $form.find('[rel=vgpuType]').css('display', 'block');
+                                                }
+                                            });
+                                        }
+                                    },
+
+                                    vgpuType: {
+                                        label: 'VGPU Type',
+                                        select: function(args) {
+                                            var items = [];
+                                            items.push({
+                                                id: '',
+                                                description: ''
+                                            });
+                                            items.push({
+                                                id: 'GRID K100',
+                                                description: 'GRID K100'
+                                            });
+                                            items.push({
+                                                id: 'GRID K140Q',
+                                                description: 'GRID K140Q'
+                                            });
+                                            items.push({
+                                                id: 'GRID K200',
+                                                description: 'GRID K200'
+                                            });
+                                            items.push({
+                                                id: 'GRID K240Q',
+                                                description: 'GRID K240Q'
+                                            });
+                                            items.push({
+                                                id: 'GRID K260Q',
+                                                description: 'GRID K260Q'
+                                            });
+                                            args.response.success({
+                                                data: items
+                                            });
+                                        }
+                                    },
+
                                     domainId: {
                                         label: 'label.domain',
                                         docID: 'helpComputeOfferingDomain',
@@ -428,6 +493,14 @@
                                     array1.push("&serviceofferingdetails[0].ImplicitDedicationMode" + "=" + args.data.plannerMode);
                                 }
 
+                                if (args.data.pciDevice != "") {
+                                    array1.push("&serviceofferingdetails[1].pciDevice" + "=" + args.data.pciDevice);
+                                }
+
+                                if (args.data.pciDevice == "VGPU") {
+                                    array1.push("&serviceofferingdetails[2].vgpuType" + "=" + args.data.vgpuType);
+                                }
+
                                 if (args.data.networkRate != null && args.data.networkRate.length > 0) {
                                     $.extend(data, {
                                         networkrate: args.data.networkRate

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c7d31fe2/ui/scripts/instances.js
----------------------------------------------------------------------
diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js
index e5b2e85..10d2591 100644
--- a/ui/scripts/instances.js
+++ b/ui/scripts/instances.js
@@ -1804,7 +1804,9 @@
                             memory: {
                             	label: 'label.memory.mb'
                             },
-                            
+                            vgpu: {
+                                label: 'VGPU'
+                            },
                             haenable: {
                                 label: 'label.ha.enabled',
                                 converter: cloudStack.converters.toBooleanText