From 7e9836dfd0eec25f63103f9239d45bcfcf156dd6 Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Wed, 6 Jul 2011 16:08:55 -0700 Subject: [PATCH] Some operations on the lock table allowed through jmx --- .../cloud/deploy/DataCenterDeployment.java | 1 + .../src/com/cloud/deploy/FirstFitPlanner.java | 74 ++++++++++--------- .../src/com/cloud/utils/db/Merovingian2.java | 73 ++++++++++++++---- .../com/cloud/utils/db/MerovingianMBean.java | 9 ++- 4 files changed, 107 insertions(+), 50 deletions(-) diff --git a/api/src/com/cloud/deploy/DataCenterDeployment.java b/api/src/com/cloud/deploy/DataCenterDeployment.java index bc646959fbe..cac3839999d 100644 --- a/api/src/com/cloud/deploy/DataCenterDeployment.java +++ b/api/src/com/cloud/deploy/DataCenterDeployment.java @@ -23,6 +23,7 @@ public class DataCenterDeployment implements DeploymentPlan { Long _clusterId; Long _poolId; Long _hostId; + boolean _recreateDisks; public DataCenterDeployment(long dataCenterId) { this(dataCenterId, null, null, null, null); diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java index 7ccb88ff396..9a5e9934375 100644 --- a/server/src/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/com/cloud/deploy/FirstFitPlanner.java @@ -47,8 +47,8 @@ import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; -import com.cloud.host.dao.HostDetailsDao; import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; @@ -84,7 +84,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { @Inject protected HostPodDao _podDao; @Inject protected ClusterDao _clusterDao; @Inject protected HostDetailsDao _hostDetailsDao = null; - @Inject protected GuestOSDao _guestOSDao = null; + @Inject protected GuestOSDao _guestOSDao = null; @Inject protected GuestOSCategoryDao _guestOSCategoryDao = null; @Inject protected DiskOfferingDao _diskOfferingDao; @Inject protected StoragePoolHostDao _poolHostDao; @@ -118,24 +118,28 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { float cpuOverprovisioningFactor = NumbersUtil.parseFloat(opFactor, 1); - s_logger.debug("In FirstFitPlanner:: plan"); - - s_logger.debug("Trying to allocate a host and storage pools from dc:" + plan.getDataCenterId() + ", pod:" + plan.getPodId() + ",cluster:" + plan.getClusterId() + - ", requested cpu: " + cpu_requested + ", requested ram: " + ram_requested); - - s_logger.debug("Is ROOT volume READY (pool already allocated)?: " + (plan.getPoolId()!=null ? "Yes": "No")); - + if (s_logger.isDebugEnabled()) { + s_logger.debug("In FirstFitPlanner:: plan"); + + s_logger.debug("Trying to allocate a host and storage pools from dc:" + plan.getDataCenterId() + ", pod:" + plan.getPodId() + ",cluster:" + plan.getClusterId() + + ", requested cpu: " + cpu_requested + ", requested ram: " + ram_requested); + + s_logger.debug("Is ROOT volume READY (pool already allocated)?: " + (plan.getPoolId()!=null ? "Yes": "No")); + } + if(plan.getHostId() != null){ Long hostIdSpecified = plan.getHostId(); - if(s_logger.isDebugEnabled()){ + if (s_logger.isDebugEnabled()){ s_logger.debug("DeploymentPlan has host_id specified, making no checks on this host, looks like admin test: "+hostIdSpecified); } HostVO host = _hostDao.findById(hostIdSpecified); - if(host == null){ - s_logger.debug("The specified host cannot be found"); - }else{ - s_logger.debug("Looking for suitable pools for this host under zone: "+host.getDataCenterId() +", pod: "+ host.getPodId()+", cluster: "+ host.getClusterId()); - } + if (s_logger.isDebugEnabled()) { + if(host == null){ + s_logger.debug("The specified host cannot be found"); + }else{ + s_logger.debug("Looking for suitable pools for this host under zone: "+host.getDataCenterId() +", pod: "+ host.getPodId()+", cluster: "+ host.getClusterId()); + } + } //search for storage under the zone, pod, cluster of the host. DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), hostIdSpecified, plan.getPoolId()); @@ -154,21 +158,21 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { Pod pod = _podDao.findById(host.getPodId()); Cluster cluster = _clusterDao.findById(host.getClusterId()); Map storageVolMap = potentialResources.second(); - // remove the reused vol<->pool from destination, since we don't have to prepare this volume. + // remove the reused vol<->pool from destination, since we don't have to prepare this volume. for(Volume vol : readyAndReusedVolumes){ storageVolMap.remove(vol); - } + } DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap); s_logger.debug("Returning Deployment Destination: "+ dest); return dest; } - } + } s_logger.debug("Cannnot deploy to specified host, returning."); return null; } if (vm.getLastHostId() != null) { - s_logger.debug("This VM has last host_id specified, trying to choose the same host: " +vm.getLastHostId()); + s_logger.debug("This VM has last host_id specified, trying to choose the same host: " +vm.getLastHostId()); HostVO host = _hostDao.findById(vm.getLastHostId()); if(host == null){ @@ -178,13 +182,13 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { //check zone/pod/cluster are enabled if(isEnabledForAllocation(host.getDataCenterId(), host.getPodId(), host.getClusterId())){ if(_capacityMgr.checkIfHostHasCapacity(host.getId(), cpu_requested, ram_requested, true, cpuOverprovisioningFactor)){ - s_logger.debug("The last host of this VM is UP and has enough capacity"); + s_logger.debug("The last host of this VM is UP and has enough capacity"); s_logger.debug("Now checking for suitable pools under zone: "+host.getDataCenterId() +", pod: "+ host.getPodId()+", cluster: "+ host.getClusterId()); //search for storage under the zone, pod, cluster of the last host. - DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), host.getId(), plan.getPoolId()); + DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), host.getId(), plan.getPoolId()); Pair>, List> result = findSuitablePoolsForVolumes(vmProfile, lastPlan, avoid, RETURN_UPTO_ALL); Map> suitableVolumeStoragePools = result.first(); - List readyAndReusedVolumes = result.second(); + List readyAndReusedVolumes = result.second(); //choose the potential pool for this VM for this host if(!suitableVolumeStoragePools.isEmpty()){ List suitableHosts = new ArrayList(); @@ -198,7 +202,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { // remove the reused vol<->pool from destination, since we don't have to prepare this volume. for(Volume vol : readyAndReusedVolumes){ storageVolMap.remove(vol); - } + } DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap); s_logger.debug("Returning Deployment Destination: "+ dest); return dest; @@ -219,14 +223,14 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { if(!isEnabledForAllocation(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId())){ s_logger.debug("Cannot deploy to specified plan, allocation state is disabled, returning."); return null; - } + } List clusterList = new ArrayList(); if (plan.getClusterId() != null) { Long clusterIdSpecified = plan.getClusterId(); s_logger.debug("Searching resources only under specified Cluster: "+ clusterIdSpecified); ClusterVO cluster = _clusterDao.findById(plan.getClusterId()); - if (cluster != null ){ + if (cluster != null ){ clusterList.add(clusterIdSpecified); return checkClustersforDestination(clusterList, vmProfile, plan, avoid, dc, _allocationAlgorithm); }else{ @@ -241,7 +245,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { HostPodVO pod = _podDao.findById(podIdSpecified); if (pod != null) { - //list clusters under this pod by cpu and ram capacity + //list clusters under this pod by cpu and ram capacity clusterList = listClustersByCapacity(podIdSpecified, cpu_requested, ram_requested, avoid, false, cpuOverprovisioningFactor); if(!clusterList.isEmpty()){ if(avoid.getClustersToAvoid() != null){ @@ -256,7 +260,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { if (s_logger.isDebugEnabled()) { s_logger.debug("Removing from the clusterId list these clusters that are disabled: "+ disabledClusters); } - clusterList.removeAll(disabledClusters); + clusterList.removeAll(disabledClusters); } DeployDestination dest = checkClustersforDestination(clusterList, vmProfile, plan, avoid, dc, _allocationAlgorithm); @@ -279,7 +283,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { }else{ //consider all clusters under this zone. s_logger.debug("Searching all possible resources under this Zone: "+ plan.getDataCenterId()); - //list clusters under this zone by cpu and ram capacity + //list clusters under this zone by cpu and ram capacity List prioritizedClusterIds = listClustersByCapacity(plan.getDataCenterId(), cpu_requested, ram_requested, avoid, true, cpuOverprovisioningFactor); if(!prioritizedClusterIds.isEmpty()){ if(avoid.getClustersToAvoid() != null){ @@ -302,7 +306,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { return null; } if(!prioritizedClusterIds.isEmpty()){ - boolean applyUserConcentrationPodHeuristic = Boolean.parseBoolean(_configDao.getValue(Config.UseUserConcentratedPodAllocation.key())); + boolean applyUserConcentrationPodHeuristic = Boolean.parseBoolean(_configDao.getValue(Config.UseUserConcentratedPodAllocation.key())); if(applyUserConcentrationPodHeuristic && vmProfile.getOwner() != null){ //user has VMs in certain pods. - prioritize those pods first //UserConcentratedPod strategy @@ -397,7 +401,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { Pod pod = _podDao.findById(clusterVO.getPodId()); Host host = _hostDao.findById(potentialResources.first().getId()); Map storageVolMap = potentialResources.second(); - // remove the reused vol<->pool from destination, since we don't have to prepare this volume. + // remove the reused vol<->pool from destination, since we don't have to prepare this volume. for(Volume vol : readyAndReusedVolumes){ storageVolMap.remove(vol); } @@ -464,13 +468,13 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } return prioritizedPods; - } + } protected List listClustersByCapacity(long id, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone, float cpuOverprovisioningFactor){ //look at the aggregate available cpu and ram per cluster //although an aggregate value may be false indicator that a cluster can host a vm, it will at the least eliminate those clusters which definitely cannot - //we need clusters having enough cpu AND RAM + //we need clusters having enough cpu AND RAM if (s_logger.isDebugEnabled()) { s_logger.debug("Listing clusters that have enough aggregate CPU and RAM capacity under this "+(isZone ? "Zone: " : "Pod: " )+id); } @@ -543,7 +547,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, avoid, returnUpTo); if (suitableHosts != null && !suitableHosts.isEmpty()) { break; - } + } } if(suitableHosts.isEmpty()){ @@ -573,7 +577,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { long exstPoolDcId = pool.getDataCenterId(); Long exstPoolPodId = pool.getPodId(); Long exstPoolClusterId = pool.getClusterId(); - if(plan.getDataCenterId() == exstPoolDcId && plan.getPodId() == exstPoolPodId && plan.getClusterId() == exstPoolClusterId){ + if(plan.getDataCenterId() == exstPoolDcId && plan.getPodId() == exstPoolPodId && plan.getClusterId() == exstPoolClusterId){ s_logger.debug("Planner need not allocate a pool for this volume since its READY"); suitablePools.add(pool); suitableVolumeStoragePools.put(toBeCreated, suitablePools); @@ -635,7 +639,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } return new Pair>, List>(suitableVolumeStoragePools, readyAndReusedVolumes); - } + } @Override diff --git a/utils/src/com/cloud/utils/db/Merovingian2.java b/utils/src/com/cloud/utils/db/Merovingian2.java index 3e3f5f48498..f27f3a637e7 100644 --- a/utils/src/com/cloud/utils/db/Merovingian2.java +++ b/utils/src/com/cloud/utils/db/Merovingian2.java @@ -50,13 +50,11 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { private static final String SELECT_SQL = "SELECT op_lock.key, mac, ip, thread, acquired_on, waiters FROM op_lock"; private static final String INQUIRE_SQL = SELECT_SQL + " WHERE op_lock.key=?"; private static final String DECREMENT_SQL = "UPDATE op_lock SET waiters=waiters-1 where op_lock.key=? AND op_lock.mac=? AND op_lock.ip=? AND op_lock.thread=?"; - private static final String RELEASE_SQL = "DELETE FROM op_lock WHERE op_lock.key = ? AND op_lock.mac=? AND waiters=0"; + private static final String RELEASE_LOCK_SQL = "DELETE FROM op_lock WHERE op_lock.key = ?"; + private static final String RELEASE_SQL = RELEASE_LOCK_SQL + " AND op_lock.mac=? AND waiters=0"; private static final String CLEANUP_MGMT_LOCKS_SQL = "DELETE FROM op_lock WHERE op_lock.mac = ?"; private static final String SELECT_MGMT_LOCKS_SQL = SELECT_SQL + " WHERE mac=?"; private static final String SELECT_THREAD_LOCKS_SQL = SELECT_SQL + " WHERE mac=? AND ip=?"; - private static final String SELECT_OWNER_SQL = "SELECT mac, ip, thread FROM op_lock WHERE op_lock.key=?"; - private static final String DEADLOCK_DETECT_SQL = "SELECT l2.key FROM op_lock l2 WHERE l2.mac=? AND l2.ip=? AND l2.thread=? AND l2.key in " + - "(SELECT l1.key from op_lock l1 WHERE l1.mac=? AND l1.ip=? AND l1.thread=?)"; private static final String CLEANUP_THREAD_LOCKS_SQL = "DELETE FROM op_lock WHERE mac=? AND ip=? AND thread=?"; TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT"); @@ -64,19 +62,16 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { private long _msId; private static Merovingian2 s_instance = null; - ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("LockMasterConnectionKeepAlive")); - Connection _conn; + ScheduledExecutorService _executor = null; + Connection _conn = null; private Merovingian2(long msId) { super(MerovingianMBean.class, false); _msId = msId; - try { - _conn = Transaction.getStandaloneConnectionWithException(); - _conn.setAutoCommit(true); - } catch (SQLException e) { - throw new CloudRuntimeException("Unable to initialize a connection to the database for locking purposes", e); + String result = resetDbConnection(); + if (!result.equalsIgnoreCase("Success")) { + throw new CloudRuntimeException("Unable to initialize a connection to the database for locking purposes due to " + result); } - _executor.schedule(new KeepAliveTask(), 10, TimeUnit.SECONDS); } public static synchronized Merovingian2 createLockMaster(long msId) { @@ -95,6 +90,42 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { return s_instance; } + @Override + public String resetDbConnection() { + if (_conn != null) { + try { + _conn.close(); + } catch (Throwable th) { + s_logger.error("Unable to close connection", th); + } + } + + try { + _conn = Transaction.getStandaloneConnectionWithException(); + _conn.setAutoCommit(true); + } catch (SQLException e) { + s_logger.error("Unable to get a new db connection", e); + return "Unable to initialize a connection to the database for locking purposes: " + e; + } + + if (_conn == null) { + return "Unable to initialize a connection to the database for locking purposes, shutdown this server!"; + } + + if (_executor != null) { + try { + _executor.shutdown(); + } catch (Throwable th) { + s_logger.error("Unable to shutdown the executor", th); + } + } + + _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("LockMasterConnectionKeepAlive")); + _executor.schedule(new KeepAliveTask(), 10, TimeUnit.SECONDS); + + return "Success"; + } + public boolean acquire(String key, int timeInSeconds) { Thread th = Thread.currentThread(); String threadName = th.getName(); @@ -222,7 +253,8 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { public void cleanupThisServer() { cleanupForServer(_msId); } - + + @Override public void cleanupForServer(long msId) { PreparedStatement pstmt = null; try { @@ -397,6 +429,20 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { } } + @Override + public boolean releaseLockAsLastResortAndIReallyKnowWhatIAmDoing(String key) { + PreparedStatement pstmt = null; + try { + pstmt = _conn.prepareStatement(RELEASE_LOCK_SQL); + pstmt.setString(1, key); + int rows = pstmt.executeUpdate(); + return rows > 0; + } catch (SQLException e) { + s_logger.error("Unable to release lock " + key, e); + return false; + } + } + protected class KeepAliveTask implements Runnable { @Override public void run() { @@ -416,4 +462,5 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { } } } + } diff --git a/utils/src/com/cloud/utils/db/MerovingianMBean.java b/utils/src/com/cloud/utils/db/MerovingianMBean.java index 32c6e268ea5..62a9a0cca0d 100644 --- a/utils/src/com/cloud/utils/db/MerovingianMBean.java +++ b/utils/src/com/cloud/utils/db/MerovingianMBean.java @@ -23,8 +23,13 @@ import java.util.Map; public interface MerovingianMBean { - public List> getAllLocks(); + List> getAllLocks(); - public List> getLocksAcquiredByThisServer(); + List> getLocksAcquiredByThisServer(); + boolean releaseLockAsLastResortAndIReallyKnowWhatIAmDoing(String key); + + String resetDbConnection(); + + void cleanupForServer(long msId); }