From f6a79c603f37535c636ae2cb6a5053e60d3ea3cb Mon Sep 17 00:00:00 2001 From: alena Date: Tue, 13 Sep 2011 10:46:04 -0700 Subject: [PATCH] bug 11036: resource count update is refactored status 11036: resolved fixed 1) Use row locks instead of global lock when update resource_count table. When update resource_count for account, make sure that we lock account+all related domains 2) Insert resource_count records for account/domain at the moment when account/domain is created. 3) As a part of DB upgrade, insert missing resource_count records for all non-removed accounts/domains Conflicts: core/src/com/cloud/alert/AlertManager.java server/test/com/cloud/agent/MockAgentManagerImpl.java --- .../cloud/api/commands/CreateDomainCmd.java | 2 +- .../cloud/configuration/ResourceLimit.java | 2 + .../com/cloud/server/ManagementService.java | 9 - api/src/com/cloud/user/AccountService.java | 7 +- client/tomcatconf/components.xml.in | 3 + core/src/com/cloud/alert/AlertManager.java | 3 +- .../cloud/configuration/ResourceCountVO.java | 5 + server/src/com/cloud/api/ApiDBUtils.java | 2 +- .../ConfigurationManagerImpl.java | 3 +- .../configuration/dao/ResourceCountDao.java | 27 +- .../dao/ResourceCountDaoImpl.java | 135 ++++--- .../configuration/dao/ResourceLimitDao.java | 2 + .../dao/ResourceLimitDaoImpl.java | 13 +- server/src/com/cloud/domain/DomainVO.java | 7 +- .../src/com/cloud/domain/dao/DomainDao.java | 4 +- .../com/cloud/domain/dao/DomainDaoImpl.java | 16 + .../VirtualNetworkApplianceManagerImpl.java | 4 +- .../cloud/server/ConfigurationServerImpl.java | 148 +++++--- .../cloud/server/ManagementServerImpl.java | 54 --- .../storage/snapshot/SnapshotManagerImpl.java | 2 +- server/src/com/cloud/test/DatabaseConfig.java | 116 ------ .../cloud/upgrade/dao/Upgrade2211to2212.java | 74 ++++ server/src/com/cloud/user/AccountManager.java | 4 +- .../com/cloud/user/AccountManagerImpl.java | 356 +++++++++++------- .../com/cloud/agent/MockAgentManagerImpl.java | 2 - .../cloud/user/MockAccountManagerImpl.java | 8 +- setup/db/create-schema.sql | 4 +- setup/db/db/schema-2211to2212.sql | 4 + 28 files changed, 562 insertions(+), 454 deletions(-) diff --git a/api/src/com/cloud/api/commands/CreateDomainCmd.java b/api/src/com/cloud/api/commands/CreateDomainCmd.java index 1ca6fd4d4f9..5606b6070ca 100644 --- a/api/src/com/cloud/api/commands/CreateDomainCmd.java +++ b/api/src/com/cloud/api/commands/CreateDomainCmd.java @@ -81,7 +81,7 @@ public class CreateDomainCmd extends BaseCmd { @Override public void execute(){ UserContext.current().setEventDetails("Domain Name: "+getDomainName()+((getParentDomainId()!=null)?", Parent DomainId :"+getParentDomainId():"")); - Domain domain = _mgr.createDomain(this); + Domain domain = _accountService.createDomain(this); if (domain != null) { DomainResponse response = _responseGenerator.createDomainResponse(domain); response.setResponseName(getCommandName()); diff --git a/api/src/com/cloud/configuration/ResourceLimit.java b/api/src/com/cloud/configuration/ResourceLimit.java index e3dfb5e32e0..4bb3a7fe04d 100644 --- a/api/src/com/cloud/configuration/ResourceLimit.java +++ b/api/src/com/cloud/configuration/ResourceLimit.java @@ -19,6 +19,8 @@ package com.cloud.configuration; public interface ResourceLimit { + + public static enum OwnerType {Account, Domain} public Long getId(); diff --git a/api/src/com/cloud/server/ManagementService.java b/api/src/com/cloud/server/ManagementService.java index e81a6e437d4..aa72d9e2b4c 100755 --- a/api/src/com/cloud/server/ManagementService.java +++ b/api/src/com/cloud/server/ManagementService.java @@ -251,15 +251,6 @@ public interface ManagementService { List searchForDomainChildren(ListDomainChildrenCmd cmd); - /** - * create a new domain - * - * @param command - * - the create command defining the name to use and the id of the parent domain under which to create the new - * domain. - */ - Domain createDomain(CreateDomainCmd command); - /** * delete a domain with the given domainId * diff --git a/api/src/com/cloud/user/AccountService.java b/api/src/com/cloud/user/AccountService.java index 6da5b6dc657..fac18591d10 100644 --- a/api/src/com/cloud/user/AccountService.java +++ b/api/src/com/cloud/user/AccountService.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Set; import com.cloud.api.commands.CreateAccountCmd; +import com.cloud.api.commands.CreateDomainCmd; import com.cloud.api.commands.CreateUserCmd; import com.cloud.api.commands.DeleteAccountCmd; import com.cloud.api.commands.DeleteUserCmd; @@ -31,11 +32,11 @@ import com.cloud.api.commands.EnableUserCmd; import com.cloud.api.commands.ListResourceLimitsCmd; import com.cloud.api.commands.LockUserCmd; import com.cloud.api.commands.UpdateAccountCmd; -import com.cloud.api.commands.UpdateResourceLimitCmd; import com.cloud.api.commands.UpdateResourceCountCmd; +import com.cloud.api.commands.UpdateResourceLimitCmd; import com.cloud.api.commands.UpdateUserCmd; -import com.cloud.configuration.ResourceLimit; import com.cloud.configuration.ResourceCount; +import com.cloud.configuration.ResourceLimit; import com.cloud.domain.Domain; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ResourceUnavailableException; @@ -197,4 +198,6 @@ public interface AccountService { Set getDomainChildrenIds(String parentDomainPath); + Domain createDomain(CreateDomainCmd cmd); + } diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in index 6fdc6ec5251..9d432f8d103 100755 --- a/client/tomcatconf/components.xml.in +++ b/client/tomcatconf/components.xml.in @@ -122,5 +122,8 @@ + + + diff --git a/core/src/com/cloud/alert/AlertManager.java b/core/src/com/cloud/alert/AlertManager.java index 051b57faece..715b723a370 100644 --- a/core/src/com/cloud/alert/AlertManager.java +++ b/core/src/com/cloud/alert/AlertManager.java @@ -43,7 +43,8 @@ public interface AlertManager extends Manager { public static final short ALERT_TYPE_SSVM = 18; public static final short ALERT_TYPE_USAGE_SERVER_RESULT = 19; // Usage job result public static final short ALERT_TYPE_STORAGE_DELETE = 20; - public static final short ALERT_TYPE_USAGE_SANITY_RESULT = 21; + public static final short ALERT_TYPE_UPDATE_RESOURCE_COUNT = 21; // Generated when we fail to update the resource count + public static final short ALERT_TYPE_USAGE_SANITY_RESULT = 22; void clearAlert(short alertType, long dataCenterId, long podId); diff --git a/core/src/com/cloud/configuration/ResourceCountVO.java b/core/src/com/cloud/configuration/ResourceCountVO.java index 99cc29ac8a3..449c5292bc2 100644 --- a/core/src/com/cloud/configuration/ResourceCountVO.java +++ b/core/src/com/cloud/configuration/ResourceCountVO.java @@ -97,4 +97,9 @@ public class ResourceCountVO implements ResourceCount { public void setCount(long count) { this.count = count; } + + @Override + public String toString() { + return new StringBuilder("REsourceCount[").append("-").append(id).append("-").append(type).append("-").append(accountId).append("-").append(domainId).append("]").toString(); + } } diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 193acbbb21f..5c23260f1a4 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -266,7 +266,7 @@ public class ApiDBUtils { return -1; } - return _accountMgr.findCorrectResourceLimit(account, type); + return _accountMgr.findCorrectResourceLimit(account.getAccountId(), type); } public static AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId) { diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 0ba3dbb0c8d..0d51fde2e54 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -117,7 +117,6 @@ import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.test.IPRangeConfig; import com.cloud.user.Account; import com.cloud.user.AccountManager; -import com.cloud.user.AccountVO; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; @@ -2050,7 +2049,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (forVirtualNetwork) { if (account != null) { // verify resource limits - long ipResourceLimit = _accountMgr.findCorrectResourceLimit((AccountVO) account, ResourceType.public_ip); + long ipResourceLimit = _accountMgr.findCorrectResourceLimit(account.getId(), ResourceType.public_ip); long accountIpRange = NetUtils.ip2Long(endIP) - NetUtils.ip2Long(startIP) + 1; if (s_logger.isDebugEnabled()) { s_logger.debug(" IPResourceLimit " + ipResourceLimit + " accountIpRange " + accountIpRange); diff --git a/server/src/com/cloud/configuration/dao/ResourceCountDao.java b/server/src/com/cloud/configuration/dao/ResourceCountDao.java index fe6197487ec..482ef2aaf8c 100644 --- a/server/src/com/cloud/configuration/dao/ResourceCountDao.java +++ b/server/src/com/cloud/configuration/dao/ResourceCountDao.java @@ -18,8 +18,11 @@ package com.cloud.configuration.dao; -import com.cloud.configuration.ResourceCountVO; +import java.util.Set; + import com.cloud.configuration.ResourceCount.ResourceType; +import com.cloud.configuration.ResourceCountVO; +import com.cloud.configuration.ResourceLimit.OwnerType; import com.cloud.utils.db.GenericDao; public interface ResourceCountDao extends GenericDao { @@ -55,15 +58,6 @@ public interface ResourceCountDao extends GenericDao { * @param the count of resources in use for the given type and domain */ public void setDomainCount(long domainId, ResourceType type, long count); - - /** - * Update the count of resources in use for the given account and given resource type - * @param accountId the id of the account to update resource count - * @param type the type of resource (e.g. user_vm, public_ip, volume) - * @param increment whether the change is adding or subtracting from the current count - * @param delta the number of resources being added/released - */ - public void updateAccountCount(long accountId, ResourceType type, boolean increment, long delta); /** * Update the count of resources in use for the given domain and given resource type @@ -73,4 +67,17 @@ public interface ResourceCountDao extends GenericDao { * @param delta the number of resources being added/released */ public void updateDomainCount(long domainId, ResourceType type, boolean increment, long delta); + + boolean updateById(long id, boolean increment, long delta); + + ResourceCountVO findByDomainIdAndType(long domainId, ResourceType type); + + ResourceCountVO findByAccountIdAndType(long accountId, ResourceType type); + + Set listAllRowsToUpdateForAccount(long accountId, long domainId, ResourceType type); + + Set listRowsToUpdateForDomain(long domainId, ResourceType type); + + void createResourceCounts(long ownerId, OwnerType ownerType); + } diff --git a/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java b/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java index 812a10b3f83..c31bdaa90df 100644 --- a/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java +++ b/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java @@ -18,18 +18,29 @@ package com.cloud.configuration.dao; +import java.util.HashSet; +import java.util.Set; + import javax.ejb.Local; import com.cloud.configuration.ResourceCount.ResourceType; +import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceCountVO; +import com.cloud.configuration.ResourceLimit; +import com.cloud.domain.dao.DomainDaoImpl; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; @Local(value={ResourceCountDao.class}) public class ResourceCountDaoImpl extends GenericDaoBase implements ResourceCountDao { private SearchBuilder IdTypeSearch; private SearchBuilder DomainIdTypeSearch; + + protected final DomainDaoImpl _domainDao = ComponentLocator.inject(DomainDaoImpl.class); public ResourceCountDaoImpl() { IdTypeSearch = createSearchBuilder(); @@ -43,11 +54,8 @@ public class ResourceCountDaoImpl extends GenericDaoBase DomainIdTypeSearch.done(); } - private ResourceCountVO findByAccountIdAndType(long accountId, ResourceType type) { - if (type == null) { - return null; - } - + @Override + public ResourceCountVO findByAccountIdAndType(long accountId, ResourceType type) { SearchCriteria sc = IdTypeSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("type", type); @@ -55,11 +63,8 @@ public class ResourceCountDaoImpl extends GenericDaoBase return findOneIncludingRemovedBy(sc); } - private ResourceCountVO findByDomainIdAndType(long domainId, ResourceType type) { - if (type == null) { - return null; - } - + @Override + public ResourceCountVO findByDomainIdAndType(long domainId, ResourceType type) { SearchCriteria sc = DomainIdTypeSearch.create(); sc.setParameters("domainId", domainId); sc.setParameters("type", type); @@ -70,59 +75,29 @@ public class ResourceCountDaoImpl extends GenericDaoBase @Override public long getAccountCount(long accountId, ResourceType type) { ResourceCountVO resourceCountVO = findByAccountIdAndType(accountId, type); - return (resourceCountVO != null) ? resourceCountVO.getCount() : 0; + return resourceCountVO.getCount(); } @Override public long getDomainCount(long domainId, ResourceType type) { ResourceCountVO resourceCountVO = findByDomainIdAndType(domainId, type); - return (resourceCountVO != null) ? resourceCountVO.getCount() : 0; + return resourceCountVO.getCount(); } @Override public void setAccountCount(long accountId, ResourceType type, long count) { ResourceCountVO resourceCountVO = findByAccountIdAndType(accountId, type); - if (resourceCountVO == null) { - if (count != 0) { - resourceCountVO = new ResourceCountVO(accountId, null, type, count); - persist(resourceCountVO); - } - } else { - if (count != resourceCountVO.getCount()) { - resourceCountVO.setCount(count); - update(resourceCountVO.getId(), resourceCountVO); - } + if (count != resourceCountVO.getCount()) { + resourceCountVO.setCount(count); + update(resourceCountVO.getId(), resourceCountVO); } } @Override public void setDomainCount(long domainId, ResourceType type, long count) { ResourceCountVO resourceCountVO = findByDomainIdAndType(domainId, type); - if (resourceCountVO == null) { - if (count != 0) { - resourceCountVO = new ResourceCountVO(null, domainId, type, count); - persist(resourceCountVO); - } - } else { - if (count != resourceCountVO.getCount()) { - resourceCountVO.setCount(count); - update(resourceCountVO.getId(), resourceCountVO); - } - } - } - - @Override - public void updateAccountCount(long accountId, ResourceType type, boolean increment, long delta) { - delta = increment ? delta : delta * -1; - - ResourceCountVO resourceCountVO = findByAccountIdAndType(accountId, type); - - if (resourceCountVO == null) { - resourceCountVO = new ResourceCountVO(accountId, null, type, 0); - resourceCountVO.setCount(resourceCountVO.getCount() + delta); - persist(resourceCountVO); - } else { - resourceCountVO.setCount(resourceCountVO.getCount() + delta); + if (count != resourceCountVO.getCount()) { + resourceCountVO.setCount(count); update(resourceCountVO.getId(), resourceCountVO); } } @@ -132,13 +107,63 @@ public class ResourceCountDaoImpl extends GenericDaoBase delta = increment ? delta : delta * -1; ResourceCountVO resourceCountVO = findByDomainIdAndType(domainId, type); - if (resourceCountVO == null) { - resourceCountVO = new ResourceCountVO(null, domainId, type, 0); - resourceCountVO.setCount(resourceCountVO.getCount() + delta); - persist(resourceCountVO); - } else { - resourceCountVO.setCount(resourceCountVO.getCount() + delta); - update(resourceCountVO.getId(), resourceCountVO); - } + resourceCountVO.setCount(resourceCountVO.getCount() + delta); + update(resourceCountVO.getId(), resourceCountVO); } + + @Override + public boolean updateById(long id, boolean increment, long delta) { + delta = increment ? delta : delta * -1; + + ResourceCountVO resourceCountVO = findById(id); + resourceCountVO.setCount(resourceCountVO.getCount() + delta); + return update(resourceCountVO.getId(), resourceCountVO); + } + + @Override + public Set listAllRowsToUpdateForAccount(long accountId, long domainId, ResourceType type) { + Set rowIds = new HashSet(); + //Create resource count records if not exist + //1) for account + ResourceCountVO accountCountRecord = findByAccountIdAndType(accountId, type); + rowIds.add(accountCountRecord.getId()); + + //2) for domain(s) + rowIds.addAll(listRowsToUpdateForDomain(domainId, type)); + + return rowIds; + } + + @Override + public Set listRowsToUpdateForDomain(long domainId, ResourceType type) { + Set rowIds = new HashSet(); + Set domainIdsToUpdate = _domainDao.getDomainParentIds(domainId); + for (Long domainIdToUpdate : domainIdsToUpdate) { + ResourceCountVO domainCountRecord = findByDomainIdAndType(domainIdToUpdate, type); + rowIds.add(domainCountRecord.getId()); + } + return rowIds; + } + + @Override @DB + public void createResourceCounts(long ownerId, ResourceLimit.OwnerType ownerType){ + Long accountId = null; + Long domainId = null; + if (ownerType == ResourceLimit.OwnerType.Account) { + accountId = ownerId; + } else if (ownerType == ResourceLimit.OwnerType.Domain) { + domainId = ownerId; + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + ResourceType[] resourceTypes = ResourceCount.ResourceType.values(); + for (ResourceType resourceType : resourceTypes) { + ResourceCountVO resourceCountVO = new ResourceCountVO(accountId, domainId, resourceType, 0); + persist(resourceCountVO); + } + + txn.commit(); + } } \ No newline at end of file diff --git a/server/src/com/cloud/configuration/dao/ResourceLimitDao.java b/server/src/com/cloud/configuration/dao/ResourceLimitDao.java index 6f48d331c65..2addacd6b95 100644 --- a/server/src/com/cloud/configuration/dao/ResourceLimitDao.java +++ b/server/src/com/cloud/configuration/dao/ResourceLimitDao.java @@ -21,6 +21,7 @@ package com.cloud.configuration.dao; import java.util.List; import com.cloud.configuration.ResourceCount; +import com.cloud.configuration.ResourceLimit.OwnerType; import com.cloud.configuration.ResourceLimitVO; import com.cloud.utils.db.GenericDao; @@ -32,4 +33,5 @@ public interface ResourceLimitDao extends GenericDao { public List listByDomainId(Long domainId); public boolean update(Long id, Long max); public ResourceCount.ResourceType getLimitType(String type); + public ResourceLimitVO findByOwnerIdAndType(long ownerId, OwnerType ownerType, ResourceCount.ResourceType type); } diff --git a/server/src/com/cloud/configuration/dao/ResourceLimitDaoImpl.java b/server/src/com/cloud/configuration/dao/ResourceLimitDaoImpl.java index f266fadfee6..3cabd5f6acb 100644 --- a/server/src/com/cloud/configuration/dao/ResourceLimitDaoImpl.java +++ b/server/src/com/cloud/configuration/dao/ResourceLimitDaoImpl.java @@ -23,6 +23,7 @@ import java.util.List; import javax.ejb.Local; import com.cloud.configuration.ResourceCount; +import com.cloud.configuration.ResourceLimit.OwnerType; import com.cloud.configuration.ResourceLimitVO; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -99,7 +100,17 @@ public class ResourceLimitDaoImpl extends GenericDaoBase return validType; } } - return null; } + + @Override + public ResourceLimitVO findByOwnerIdAndType(long ownerId, OwnerType ownerType, ResourceCount.ResourceType type) { + if (ownerType == OwnerType.Account) { + return findByAccountIdAndType(ownerId, type); + } else if (ownerType == OwnerType.Domain) { + return findByDomainIdAndType(ownerId, type); + } else { + return null; + } + } } diff --git a/server/src/com/cloud/domain/DomainVO.java b/server/src/com/cloud/domain/DomainVO.java index 59163cc4910..76e9cbf53ee 100644 --- a/server/src/com/cloud/domain/DomainVO.java +++ b/server/src/com/cloud/domain/DomainVO.java @@ -67,7 +67,12 @@ public class DomainVO implements Domain { @Column(name="network_domain") private String networkDomain; - public DomainVO() {} + public DomainVO() {} + + public DomainVO(long id, String name, long owner, Long parentId, String networkDomain) { + this(name, owner, parentId, networkDomain); + this.id = id; + } public DomainVO(String name, long owner, Long parentId, String networkDomain) { this.parent = parentId; diff --git a/server/src/com/cloud/domain/dao/DomainDao.java b/server/src/com/cloud/domain/dao/DomainDao.java index 5ccd860d512..ca41ef0e49d 100644 --- a/server/src/com/cloud/domain/dao/DomainDao.java +++ b/server/src/com/cloud/domain/dao/DomainDao.java @@ -19,6 +19,7 @@ package com.cloud.domain.dao; import java.util.List; +import java.util.Set; import com.cloud.domain.DomainVO; import com.cloud.utils.db.GenericDao; @@ -30,5 +31,6 @@ public interface DomainDao extends GenericDao { DomainVO findImmediateChildForParent(Long parentId); List findImmediateChildrenForParent(Long parentId); List findAllChildren(String path, Long parentId); - List findInactiveDomains(); + List findInactiveDomains(); + Set getDomainParentIds(long domainId); } diff --git a/server/src/com/cloud/domain/dao/DomainDaoImpl.java b/server/src/com/cloud/domain/dao/DomainDaoImpl.java index 51ef94de4e9..15e7f4ba096 100644 --- a/server/src/com/cloud/domain/dao/DomainDaoImpl.java +++ b/server/src/com/cloud/domain/dao/DomainDaoImpl.java @@ -21,7 +21,9 @@ package com.cloud.domain.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.ejb.Local; @@ -255,5 +257,19 @@ public class DomainDaoImpl extends GenericDaoBase implements Dom SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("state", Domain.State.Inactive); return listBy(sc); + } + + @Override + public Set getDomainParentIds(long domainId) { + Set parentDomains = new HashSet(); + Domain domain = findById(domainId); + parentDomains.add(domain.getId()); + + while (domain.getParent() != null) { + domain = findById(domain.getParent()); + parentDomains.add(domain.getId()); + } + + return parentDomains; } } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 7064721dd21..218fff7f9cc 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -2234,9 +2234,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian disconnectedRouters.add(router); } - //If rules fail to apply on one domR, no need to proceed with the rest + //If ip fails to apply on one domR, no need to proceed with the rest if (!result) { - throw new ResourceUnavailableException("Unable to apply firewall rules on router ", VirtualRouter.class, router.getId()); + throw new ResourceUnavailableException("Unable to associate ip addresses on router ", VirtualRouter.class, router.getId()); } } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) { diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index cf81a1f97fd..939622cad38 100644 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -47,7 +47,9 @@ import org.apache.log4j.Logger; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationVO; +import com.cloud.configuration.ResourceLimit.OwnerType; import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.configuration.dao.ResourceCountDao; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; @@ -55,6 +57,8 @@ import com.cloud.dc.VlanVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.VlanDao; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; import com.cloud.exception.InternalErrorException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network.GuestIpType; @@ -78,7 +82,11 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.test.IPRangeConfig; import com.cloud.user.Account; +import com.cloud.user.AccountVO; import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; import com.cloud.utils.PasswordGenerator; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.component.ComponentLocator; @@ -101,6 +109,10 @@ public class ConfigurationServerImpl implements ConfigurationServer { private final NetworkDao _networkDao; private final VlanDao _vlanDao; private String _domainSuffix; + private final ResourceCountDao _resourceCountDao; + private final AccountDao _accountDao; + private final UserDao _userDao; + private final DomainDao _domainDao; public ConfigurationServerImpl() { @@ -114,14 +126,15 @@ public class ConfigurationServerImpl implements ConfigurationServer { _dataCenterDao = locator.getDao(DataCenterDao.class); _networkDao = locator.getDao(NetworkDao.class); _vlanDao = locator.getDao(VlanDao.class); + _resourceCountDao = locator.getDao(ResourceCountDao.class); + _accountDao = locator.getDao(AccountDao.class); + _userDao = locator.getDao(UserDao.class); + _domainDao = locator.getDao(DomainDao.class); } @Override @DB public void persistDefaultValues() throws InternalErrorException { - - // Create system user and admin user - saveUser(); - + // Get init String init = _configDao.getValue("init"); @@ -131,6 +144,14 @@ public class ConfigurationServerImpl implements ConfigurationServer { if (init == null || init.equals("false")) { s_logger.debug("ConfigurationServer is saving default values to the database."); + Transaction txn = Transaction.currentTxn(); + txn.start(); + // Create ROOT domain + saveRootDomain(); + + // Create system user and admin user + saveUser(); + // Save default Configuration Table values List categories = Config.getCategories(); for (String category : categories) { @@ -199,11 +220,9 @@ public class ConfigurationServerImpl implements ConfigurationServer { //Create default networks createDefaultNetworks(); - //Create userIpAddress ranges - //Update existing vlans with networkId - Transaction txn = Transaction.currentTxn(); + List vlans = _vlanDao.listAll(); if (vlans != null && !vlans.isEmpty()) { @@ -218,14 +237,17 @@ public class ConfigurationServerImpl implements ConfigurationServer { String startIp = range[0]; String endIp = range[1]; - txn.start(); + IPRangeConfig config = new IPRangeConfig(); long startIPLong = NetUtils.ip2Long(startIp); long endIPLong = NetUtils.ip2Long(endIp); config.savePublicIPRange(txn, startIPLong, endIPLong, vlan.getDataCenterId(), vlan.getId(), vlan.getNetworkId()); - txn.commit(); } } + + // Set init to true + _configDao.update("init", "true"); + txn.commit(); } // keystore for SSL/TLS connection @@ -240,8 +262,6 @@ public class ConfigurationServerImpl implements ConfigurationServer { // Update the cloud identifier updateCloudIdentifier(); - // Set init to true - _configDao.update("init", "true"); } @@ -273,27 +293,24 @@ public class ConfigurationServerImpl implements ConfigurationServer { // insert system account String insertSql = "INSERT INTO `cloud`.`account` (id, account_name, type, domain_id) VALUES (1, 'system', '1', '1')"; Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - } - // insert system user - insertSql = "INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, lastname, created) VALUES (1, 'system', '', 1, 'system', 'cloud', now())"; - txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - } - - // insert admin user - long id = 2; - String username = "admin"; - String firstname = "admin"; - String lastname = "cloud"; - String password = "password"; + txn.start(); + //Create system/admin accounts + AccountVO systemAccount = new AccountVO(1); + systemAccount.setAccountName("system"); + systemAccount.setType(Account.ACCOUNT_TYPE_ADMIN);; + systemAccount.setDomainId(1); + systemAccount.setState(Account.State.enabled); + _accountDao.persist(systemAccount); + + AccountVO adminAccount = new AccountVO(1); + adminAccount.setAccountName("admin"); + adminAccount.setType(Account.ACCOUNT_TYPE_ADMIN);; + adminAccount.setDomainId(1); + adminAccount.setState(Account.State.enabled); + _accountDao.persist(adminAccount); + + //Create system/admin users MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); @@ -301,6 +318,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { return; } + String password = "password"; md5.reset(); BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); String pwStr = pwInt.toString(16); @@ -310,29 +328,36 @@ public class ConfigurationServerImpl implements ConfigurationServer { sb.append('0'); // make sure the MD5 password is 32 digits long } sb.append(pwStr); - - // create an account for the admin user first - insertSql = "INSERT INTO `cloud`.`account` (id, account_name, type, domain_id) VALUES (" + id + ", '" + username + "', '1', '1')"; - txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - } - - // now insert the user - insertSql = "INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, lastname, created) " + - "VALUES (" + id + ",'" + username + "','" + sb.toString() + "', 2, '" + firstname + "','" + lastname + "',now())"; + password = sb.toString(); - - txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - } + UserVO systemUser = new UserVO(1); + systemUser.setUsername("system"); + systemUser.setPassword(""); + systemUser.setAccountId(1); + systemUser.setFirstname("system"); + systemUser.setLastname("system"); + systemUser.setState(Account.State.enabled); + _userDao.persist(systemUser); - + + UserVO adminUser = new UserVO(2); + adminUser.setUsername("admin"); + adminUser.setPassword(password); + adminUser.setAccountId(2); + adminUser.setFirstname("admin"); + adminUser.setLastname("cloud"); + adminUser.setState(Account.State.enabled); + _userDao.persist(adminUser); + + + //create resource counts + try { + _resourceCountDao.createResourceCounts(1, OwnerType.Account); + _resourceCountDao.createResourceCounts(2, OwnerType.Account); + } catch (Exception ex) { + // if exception happens, it might mean that resource counts are already created by another management server being started in the cluster + s_logger.warn("Failed to create initial resource counts for system/admin accounts"); + } try { String tableName = "security_group"; @@ -358,7 +383,6 @@ public class ConfigurationServerImpl implements ConfigurationServer { "VALUES ('default', 'Default Security Group', 2, 1, 'admin')"; } - txn = Transaction.currentTxn(); try { stmt = txn.prepareAutoCloseStatement(insertSql); stmt.executeUpdate(); @@ -369,6 +393,8 @@ public class ConfigurationServerImpl implements ConfigurationServer { rs.close(); } catch (Exception ex) { s_logger.warn("Failed to create default security group for default admin account due to ", ex); + } finally { + txn.commit(); } } @@ -902,7 +928,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { } } - + private void updateVlanWithNetworkId(VlanVO vlan) { long zoneId = vlan.getDataCenterId(); long networkId = 0L; @@ -939,5 +965,19 @@ public class ConfigurationServerImpl implements ConfigurationServer { } return networks.get(0).getId(); } + + @DB + public void saveRootDomain() { + Transaction txn = Transaction.currentTxn(); + txn.start(); + + DomainVO domain = new DomainVO(1, "ROOT", 2, null, null); + domain.setPath("/"); + domain.setLevel(0); + _domainDao.persist(domain); + _resourceCountDao.createResourceCounts(1, OwnerType.Domain); + + txn.commit(); + } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 6dde9968277..b9f681daf34 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -65,7 +65,6 @@ import com.cloud.alert.AlertVO; import com.cloud.alert.dao.AlertDao; import com.cloud.api.ApiConstants; import com.cloud.api.ApiDBUtils; -import com.cloud.api.commands.CreateDomainCmd; import com.cloud.api.commands.CreateSSHKeyPairCmd; import com.cloud.api.commands.DeleteDomainCmd; import com.cloud.api.commands.DeleteSSHKeyPairCmd; @@ -2968,59 +2967,6 @@ public class ManagementServerImpl implements ManagementServer { return _domainDao.search(sc, searchFilter); } - @Override - @ActionEvent(eventType = EventTypes.EVENT_DOMAIN_CREATE, eventDescription = "creating Domain") - public DomainVO createDomain(CreateDomainCmd cmd) { - String name = cmd.getDomainName(); - Long parentId = cmd.getParentDomainId(); - Long ownerId = UserContext.current().getCaller().getId(); - Account caller = UserContext.current().getCaller(); - String networkDomain = cmd.getNetworkDomain(); - - if (ownerId == null) { - ownerId = Long.valueOf(1); - } - - if (parentId == null) { - parentId = Long.valueOf(DomainVO.ROOT_DOMAIN); - } - - DomainVO parentDomain = _domainDao.findById(parentId); - if (parentDomain == null) { - throw new InvalidParameterValueException("Unable to create domain " + name + ", parent domain " + parentId + " not found."); - } - - if (parentDomain.getState().equals(Domain.State.Inactive)) { - throw new CloudRuntimeException("The domain cannot be created as the parent domain " + parentDomain.getName() + " is being deleted"); - } - - _accountMgr.checkAccess(caller, parentDomain); - - if (networkDomain != null) { - if (!NetUtils.verifyDomainName(networkDomain)) { - throw new InvalidParameterValueException( - "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); - } - } - - SearchCriteria sc = _domainDao.createSearchCriteria(); - sc.addAnd("name", SearchCriteria.Op.EQ, name); - sc.addAnd("parent", SearchCriteria.Op.EQ, parentId); - List domains = _domainDao.search(sc, null); - if ((domains == null) || domains.isEmpty()) { - DomainVO domain = new DomainVO(name, ownerId, parentId, networkDomain); - try { - return _domainDao.create(domain); - } catch (IllegalArgumentException ex) { - s_logger.warn("Failed to create domain ", ex); - throw ex; - } - } else { - throw new InvalidParameterValueException("Domain with name " + name + " already exists for the parent id=" + parentId); - } - } - @Override @ActionEvent(eventType = EventTypes.EVENT_DOMAIN_DELETE, eventDescription = "deleting Domain", async = true) public boolean deleteDomain(DeleteDomainCmd cmd) { diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 804a94431b8..ddd2ca0cc9b 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -1068,7 +1068,7 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma } // Verify that max doesn't exceed domain and account snapshot limits - long accountLimit = _accountMgr.findCorrectResourceLimit(owner, ResourceType.snapshot); + long accountLimit = _accountMgr.findCorrectResourceLimit(owner.getId(), ResourceType.snapshot); long domainLimit = _accountMgr.findCorrectResourceLimit(domain, ResourceType.snapshot); int max = cmd.getMaxSnaps().intValue(); if (owner.getType() != Account.ACCOUNT_TYPE_ADMIN && ((accountLimit != -1 && max > accountLimit) || (domainLimit != -1 && max > domainLimit))) { diff --git a/server/src/com/cloud/test/DatabaseConfig.java b/server/src/com/cloud/test/DatabaseConfig.java index 8458d49302f..dda0fc526a3 100755 --- a/server/src/com/cloud/test/DatabaseConfig.java +++ b/server/src/com/cloud/test/DatabaseConfig.java @@ -20,10 +20,7 @@ package com.cloud.test; import java.io.File; import java.io.IOException; -import java.math.BigInteger; import java.net.URISyntaxException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -410,7 +407,6 @@ public class DatabaseConfig { // Save default values for configuration fields saveVMTemplate(); - saveRootDomain(); saveDefaultConfiguations(); txn.commit(); @@ -439,8 +435,6 @@ public class DatabaseConfig { saveServiceOffering(); } else if ("diskOffering".equals(_currentObjectName)) { saveDiskOffering(); - } else if ("user".equals(_currentObjectName)) { - saveUser(); } else if ("configuration".equals(_currentObjectName)) { saveConfiguration(); } else if ("storagePool".equals(_currentObjectName)) { @@ -965,80 +959,6 @@ public class DatabaseConfig { */ } - @DB - protected void saveUser() { - // insert system account - String insertSql = "INSERT INTO `cloud`.`account` (id, account_name, type, domain_id) VALUES (1, 'system', '1', '1')"; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error creating system account", ex); - } - - // insert system user - insertSql = "INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, lastname, created) VALUES (1, 'system', '', 1, 'system', 'cloud', now())"; - txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error creating system user", ex); - } - - // insert admin user - long id = Long.parseLong(_currentObjectParams.get("id")); - String username = _currentObjectParams.get("username"); - String firstname = _currentObjectParams.get("firstname"); - String lastname = _currentObjectParams.get("lastname"); - String password = _currentObjectParams.get("password"); - String email = _currentObjectParams.get("email"); - - if (email == null || email.equals("")) { - printError("An email address for each user is required."); - } - - MessageDigest md5 = null; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - s_logger.error("error saving user", e); - return; - } - md5.reset(); - BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); - String pwStr = pwInt.toString(16); - int padding = 32 - pwStr.length(); - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < padding; i++) { - sb.append('0'); // make sure the MD5 password is 32 digits long - } - sb.append(pwStr); - - // create an account for the admin user first - insertSql = "INSERT INTO `cloud`.`account` (id, account_name, type, domain_id) VALUES (" + id + ", '" + username + "', '1', '1')"; - txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error creating account", ex); - } - - // now insert the user - insertSql = "INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, lastname, email, created) " + - "VALUES (" + id + ",'" + username + "','" + sb.toString() + "', 2, '" + firstname + "','" + lastname + "','" + email + "',now())"; - - txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error creating user", ex); - } - } - private void saveDefaultConfiguations() { for (String name : s_defaultConfigurationValues.keySet()) { String value = s_defaultConfigurationValues.get(name); @@ -1126,42 +1046,6 @@ public class DatabaseConfig { return true; } - @DB - protected void saveRootDomain() { - String insertSql = "insert into `cloud`.`domain` (id, name, parent, owner, path, level) values (1, 'ROOT', NULL, 2, '/', 0)"; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error creating ROOT domain", ex); - } - - /* - String updateSql = "update account set domain_id = 1 where id = 2"; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareStatement(updateSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error updating admin user", ex); - } finally { - txn.close(); - } - - updateSql = "update account set domain_id = 1 where id = 1"; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareStatement(updateSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error updating system user", ex); - } finally { - txn.close(); - } - */ - } - class DbConfigXMLHandler extends DefaultHandler { private DatabaseConfig _parent = null; diff --git a/server/src/com/cloud/upgrade/dao/Upgrade2211to2212.java b/server/src/com/cloud/upgrade/dao/Upgrade2211to2212.java index 81c6cc9baa3..a10244370a9 100644 --- a/server/src/com/cloud/upgrade/dao/Upgrade2211to2212.java +++ b/server/src/com/cloud/upgrade/dao/Upgrade2211to2212.java @@ -19,9 +19,16 @@ package com.cloud.upgrade.dao; import java.io.File; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import org.apache.log4j.Logger; +import com.cloud.configuration.ResourceCount; +import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; @@ -55,11 +62,78 @@ public class Upgrade2211to2212 implements DbUpgrade { @Override public void performDataMigration(Connection conn) { + createResourceCount(conn); } @Override public File[] getCleanupScripts() { return null; } + + + private void createResourceCount(Connection conn) { + s_logger.debug("Creating missing resource_count records as a part of 2.2.11-2.2.12 upgrade"); + try { + + //Get all non removed accounts + List accounts = new ArrayList(); + PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM account"); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + accounts.add(rs.getLong(1)); + } + rs.close(); + + + //get all non removed domains + List domains = new ArrayList(); + pstmt = conn.prepareStatement("SELECT id FROM domain"); + rs = pstmt.executeQuery(); + while (rs.next()) { + domains.add(rs.getLong(1)); + } + rs.close(); + + for (Long accountId : accounts) { + ResourceType[] resourceTypes = ResourceCount.ResourceType.values(); + for (ResourceType resourceType : resourceTypes) { + pstmt = conn.prepareStatement("SELECT * FROM resource_count WHERE type=? and account_id=?"); + pstmt.setString(1, resourceType.toString()); + pstmt.setLong(2, accountId); + rs = pstmt.executeQuery(); + if (!rs.next()) { + s_logger.debug("Inserting resource_count record of type " + resourceType + " for account id=" + accountId); + pstmt = conn.prepareStatement("INSERT INTO resource_count (account_id, domain_id, type, count) VALUES (?, null, ?, 0)"); + pstmt.setLong(1, accountId); + pstmt.setString(2, resourceType.toString()); + pstmt.executeUpdate(); + } + rs.close(); + } + pstmt.close(); + } + + for (Long domainId : domains) { + ResourceType[] resourceTypes = ResourceCount.ResourceType.values(); + for (ResourceType resourceType : resourceTypes) { + pstmt = conn.prepareStatement("SELECT * FROM resource_count WHERE type=? and domain_id=?"); + pstmt.setString(1, resourceType.toString()); + pstmt.setLong(2, domainId); + rs = pstmt.executeQuery(); + if (!rs.next()) { + s_logger.debug("Inserting resource_count record of type " + resourceType + " for domain id=" + domainId); + pstmt = conn.prepareStatement("INSERT INTO resource_count (account_id, domain_id, type, count) VALUES (null, ?, ?, 0)"); + pstmt.setLong(1, domainId); + pstmt.setString(2, resourceType.toString()); + pstmt.executeUpdate(); + } + rs.close(); + } + pstmt.close(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to create default security groups for existing accounts due to", e); + } + } } diff --git a/server/src/com/cloud/user/AccountManager.java b/server/src/com/cloud/user/AccountManager.java index 07de515fee8..4f9b062119f 100755 --- a/server/src/com/cloud/user/AccountManager.java +++ b/server/src/com/cloud/user/AccountManager.java @@ -49,11 +49,11 @@ public interface AccountManager extends AccountService { /** * Finds the resource limit for a specified account and type. If the account has an infinite limit, will check * the account's parent domain, and if that limit is also infinite, will return the ROOT domain's limit. - * @param account + * @param accountId * @param type * @return resource limit */ - public long findCorrectResourceLimit(AccountVO account, ResourceType type); + public long findCorrectResourceLimit(long accountId, ResourceType type); /** * Finds the resource limit for a specified domain and type. If the domain has an infinite limit, will check diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 10260f0e898..d56dbb3181e 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -38,8 +38,10 @@ import org.apache.log4j.Logger; import com.cloud.acl.ControlledEntity; import com.cloud.acl.SecurityChecker; import com.cloud.acl.SecurityChecker.AccessType; +import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; import com.cloud.api.commands.CreateAccountCmd; +import com.cloud.api.commands.CreateDomainCmd; import com.cloud.api.commands.CreateUserCmd; import com.cloud.api.commands.DeleteAccountCmd; import com.cloud.api.commands.DeleteUserCmd; @@ -57,6 +59,7 @@ import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.ResourceCountVO; +import com.cloud.configuration.ResourceLimit; import com.cloud.configuration.ResourceLimitVO; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceCountDao; @@ -68,7 +71,6 @@ import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; -import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InvalidParameterValueException; @@ -183,8 +185,6 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag @Inject private VirtualMachineManager _itMgr; @Inject - private UsageEventDao _usageEventDao; - @Inject private RemoteAccessVpnDao _remoteAccessVpnDao; @Inject private RemoteAccessVpnService _remoteAccessVpnMgr; @@ -192,10 +192,12 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag private VpnUserDao _vpnUser; @Inject private DataCenterDao _dcDao; + @Inject + private AlertManager _alertMgr; private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker")); - - private final GlobalLock m_resourceCountLock = GlobalLock.getInternLock("resource.count"); + + protected SearchBuilder ResourceCountSearch; UserVO _systemUser; AccountVO _systemAccount; @@ -223,6 +225,12 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag String value = configs.get(Config.AccountCleanupInterval.key()); _cleanupInterval = NumbersUtil.parseInt(value, 60 * 60 * 24); // 1 hour. + + ResourceCountSearch = _resourceCountDao.createSearchBuilder(); + ResourceCountSearch.and("id", ResourceCountSearch.entity().getId(), SearchCriteria.Op.IN); + ResourceCountSearch.and("accountId", ResourceCountSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + ResourceCountSearch.and("domainId", ResourceCountSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + ResourceCountSearch.done(); return true; } @@ -252,57 +260,27 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag public void incrementResourceCount(long accountId, ResourceType type, Long... delta) { long numToIncrement = (delta.length == 0) ? 1 : delta[0].longValue(); - if (m_resourceCountLock.lock(120)) { // 2 minutes - try { - _resourceCountDao.updateAccountCount(accountId, type, true, numToIncrement); - - // on a per-domain basis, increment the count - // FIXME: can this increment be done on the database side in a custom update statement? - Account account = _accountDao.findByIdIncludingRemoved(accountId); - Long domainId = account.getDomainId(); - while (domainId != null) { - _resourceCountDao.updateDomainCount(domainId, type, true, numToIncrement); - DomainVO domain = _domainDao.findById(domainId); - domainId = domain.getParent(); - } - } finally { - m_resourceCountLock.unlock(); - } + if (!updateResourceCount(accountId, type, true, numToIncrement)) { + //we should fail the operation (resource creation) when failed to update the resource count + throw new CloudRuntimeException("Failed to increment resource count of type " + type + " for account id=" + accountId); } } @Override public void decrementResourceCount(long accountId, ResourceType type, Long... delta) { long numToDecrement = (delta.length == 0) ? 1 : delta[0].longValue(); - - if (m_resourceCountLock.lock(120)) { // 2 minutes - try { - assert ((_resourceCountDao.getAccountCount(accountId, type) - numToDecrement) >= 0) : "Resource counts can not be negative. Check where we skipped increment."; - _resourceCountDao.updateAccountCount(accountId, type, false, numToDecrement); - - // on a per-domain basis, decrement the count - // FIXME: can this decrement be done on the database side in a custom update statement? - Account account = _accountDao.findByIdIncludingRemoved(accountId); // find all accounts, even removed accounts - // if this happens to be for an account - // that's being deleted - Long domainId = account.getDomainId(); - while (domainId != null) { - assert ((_resourceCountDao.getDomainCount(domainId, type) - numToDecrement) >= 0) : "Resource counts can not be negative. Check where we skipped increment."; - _resourceCountDao.updateDomainCount(domainId, type, false, numToDecrement); - DomainVO domain = _domainDao.findByIdIncludingRemoved(domainId); - domainId = domain.getParent(); - } - } finally { - m_resourceCountLock.unlock(); - } + + if (!updateResourceCount(accountId, type, false, numToDecrement)) { + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, "Failed to decrement resource count of type " + type + " for account id=" + accountId, + "Failed to decrement resource count of type " + type + " for account id=" + accountId + "; use updateResourceCount API to recalculate/fix the problem"); } } @Override - public long findCorrectResourceLimit(AccountVO account, ResourceType type) { + public long findCorrectResourceLimit(long accountId, ResourceType type) { long max = -1; - ResourceLimitVO limit = _resourceLimitDao.findByAccountIdAndType(account.getId(), type); + ResourceLimitVO limit = _resourceLimitDao.findByAccountIdAndType(accountId, type); // Check if limit is configured for account if (limit != null) { @@ -361,46 +339,49 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag return max; } - @Override + @Override @DB public boolean resourceLimitExceeded(Account account, ResourceType type, long... count) { long numResources = ((count.length == 0) ? 1 : count[0]); // Don't place any limits on system or admin accounts - long accountType = account.getType(); - if (accountType == Account.ACCOUNT_TYPE_ADMIN || accountType == Account.ACCOUNT_ID_SYSTEM) { + if (isAdmin(account.getType())) { return false; } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + try { + //Lock all rows first so nobody else can read it + Set rowIdsToLock = _resourceCountDao.listAllRowsToUpdateForAccount(account.getId(), account.getDomainId(), type); + SearchCriteria sc = ResourceCountSearch.create(); + sc.setParameters("id", rowIdsToLock.toArray()); + _resourceCountDao.lockRows(sc, null, true); - if (m_resourceCountLock.lock(120)) { // 2 minutes - try { - // Check account limits - AccountVO accountVo = _accountDao.findById(account.getAccountId()); - long accountLimit = findCorrectResourceLimit(accountVo, type); - long potentialCount = _resourceCountDao.getAccountCount(account.getId(), type) + numResources; - if (accountLimit != -1 && potentialCount > accountLimit) { - return true; - } - - // check all domains in the account's domain hierarchy - Long domainId = account.getDomainId(); - while (domainId != null) { - ResourceLimitVO domainLimit = _resourceLimitDao.findByDomainIdAndType(domainId, type); - if (domainLimit != null) { - long domainCount = _resourceCountDao.getDomainCount(domainId, type); - if ((domainCount + numResources) > domainLimit.getMax().longValue()) { - return true; - } - } - DomainVO domain = _domainDao.findById(domainId); - domainId = domain.getParent(); - } - return false; - } finally { - m_resourceCountLock.unlock(); + // Check account limits + long accountLimit = findCorrectResourceLimit(account.getId(), type); + long potentialCount = _resourceCountDao.getAccountCount(account.getId(), type) + numResources; + if (potentialCount > accountLimit) { + return true; } - } - return true; + // check all domains in the account's domain hierarchy + Long domainId = account.getDomainId(); + while (domainId != null) { + ResourceLimitVO domainLimit = _resourceLimitDao.findByDomainIdAndType(domainId, type); + if (domainLimit != null) { + long domainCount = _resourceCountDao.getDomainCount(domainId, type); + if ((domainCount + numResources) > domainLimit.getMax().longValue()) { + return true; + } + } + DomainVO domain = _domainDao.findById(domainId); + domainId = domain.getParent(); + } + + return false; + } finally { + txn.commit(); + } } @Override @@ -487,7 +468,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } } else { AccountVO account = _accountDao.findById(accountId); - limits.add(new ResourceLimitVO(null, accountId, type, findCorrectResourceLimit(account, type))); + limits.add(new ResourceLimitVO(null, accountId, type, findCorrectResourceLimit(account.getId(), type))); } } else if (domainId != null) { if (type == null) { @@ -745,69 +726,80 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } } - @Override + @Override @DB public long updateAccountResourceCount(long accountId, ResourceType type) { Long count=null; - // this lock guards against the updates to user_vm, volume, snapshot, public _ip and template table - // as any resource creation precedes with the resourceLimitExceeded check which needs this lock too - if (m_resourceCountLock.lock(120)) { // 2 minutes - try { - switch (type) { - case user_vm: - count = _userVmDao.countAllocatedVMsForAccount(accountId); - break; - case volume: - count = _volumeDao.countAllocatedVolumesForAccount(accountId); - long virtualRouterCount = _vmDao.countAllocatedVirtualRoutersForAccount(accountId); - count = count - virtualRouterCount; // don't count the volumes of virtual router - break; - case snapshot: - count = _snapshotDao.countSnapshotsForAccount(accountId); - break; - case public_ip: - count = _ipAddressDao.countAllocatedIPsForAccount(accountId); - break; - case template: - count = _vmTemplateDao.countTemplatesForAccount(accountId); - break; - } - _resourceCountDao.setAccountCount(accountId, type, (count == null) ? 0 : count.longValue()); - } catch (Exception e) { - throw new CloudRuntimeException("Failed to update resource count for account with Id" + accountId); - } finally { - m_resourceCountLock.unlock(); + Transaction txn = Transaction.currentTxn(); + txn.start(); + try { + // this lock guards against the updates to user_vm, volume, snapshot, public _ip and template table + // as any resource creation precedes with the resourceLimitExceeded check which needs this lock too + SearchCriteria sc = ResourceCountSearch.create(); + sc.setParameters("accountId", accountId); + _resourceCountDao.lockRows(sc, null, true); + + switch (type) { + case user_vm: + count = _userVmDao.countAllocatedVMsForAccount(accountId); + break; + case volume: + count = _volumeDao.countAllocatedVolumesForAccount(accountId); + long virtualRouterCount = _vmDao.countAllocatedVirtualRoutersForAccount(accountId); + count = count - virtualRouterCount; // don't count the volumes of virtual router + break; + case snapshot: + count = _snapshotDao.countSnapshotsForAccount(accountId); + break; + case public_ip: + count = _ipAddressDao.countAllocatedIPsForAccount(accountId); + break; + case template: + count = _vmTemplateDao.countTemplatesForAccount(accountId); + break; } + _resourceCountDao.setAccountCount(accountId, type, (count == null) ? 0 : count.longValue()); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to update resource count for account with Id" + accountId); + } finally { + txn.commit(); } return (count==null)?0:count.longValue(); } - @Override + @Override @DB public long updateDomainResourceCount(long domainId, ResourceType type) { long count=0; - if (m_resourceCountLock.lock(120)) { // 2 minutes - try { - List domainChildren = _domainDao.findImmediateChildrenForParent(domainId); - // for each child domain update the resource count - for (DomainVO domainChild : domainChildren) { - long domainCount = updateDomainResourceCount(domainChild.getId(), type); - count = count + domainCount; // add the child domain count to parent domain count - } + Transaction txn = Transaction.currentTxn(); + txn.start(); + + try { + //Lock all rows first so nobody else can read it + Set rowIdsToLock = _resourceCountDao.listRowsToUpdateForDomain(domainId, type); + SearchCriteria sc = ResourceCountSearch.create(); + sc.setParameters("id", rowIdsToLock.toArray()); + _resourceCountDao.lockRows(sc, null, true); + + List domainChildren = _domainDao.findImmediateChildrenForParent(domainId); + // for each child domain update the resource count + for (DomainVO domainChild : domainChildren) { + long domainCount = updateDomainResourceCount(domainChild.getId(), type); + count = count + domainCount; // add the child domain count to parent domain count + } - List accounts = _accountDao.findActiveAccountsForDomain(domainId); - for (AccountVO account : accounts) { - long accountCount = updateAccountResourceCount(account.getId(), type); - count = count + accountCount; // add account's resource count to parent domain count - } + List accounts = _accountDao.findActiveAccountsForDomain(domainId); + for (AccountVO account : accounts) { + long accountCount = updateAccountResourceCount(account.getId(), type); + count = count + accountCount; // add account's resource count to parent domain count + } - _resourceCountDao.setDomainCount(domainId, type, count); - } catch (Exception e) { - throw new CloudRuntimeException("Failed to update resource count for domain with Id " + domainId); - } finally { - m_resourceCountLock.unlock(); - } + _resourceCountDao.setDomainCount(domainId, type, count); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to update resource count for domain with Id " + domainId); + } finally { + txn.commit(); } return count; @@ -1274,7 +1266,11 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag + "and the hyphen ('-'); can't start or end with \"-\""); } } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + //Create account itself if (accountId == null) { if ((userType < Account.ACCOUNT_TYPE_NORMAL) || (userType > Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)) { throw new InvalidParameterValueException("Invalid account type " + userType + " given; unable to create user"); @@ -1330,10 +1326,15 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag s_logger.debug("Creating user: " + username + ", account: " + accountName + " (id:" + accountId + "), domain: " + domainId + " timezone:" + timezone); } - Transaction txn = Transaction.currentTxn(); - txn.start(); + //Create resource count records for the account + _resourceCountDao.createResourceCounts(accountId, ResourceLimit.OwnerType.Account); + + //Create a user UserVO dbUser = _userDao.persist(user); + + //Create default security group _networkGroupMgr.createDefaultSecurityGroup(accountId); + txn.commit(); if (!user.getPassword().equals(dbUser.getPassword())) { @@ -2034,16 +2035,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag @Override public Set getDomainParentIds(long domainId) { - Set parentDomains = new HashSet(); - Domain domain = _domainDao.findById(domainId); - parentDomains.add(domain.getId()); - - while (domain.getParent() != null) { - domain = _domainDao.findById(domain.getParent()); - parentDomains.add(domain.getId()); - } - - return parentDomains; + return _domainDao.getDomainParentIds(domainId); } @Override @@ -2060,4 +2052,94 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag return childDomains; } + + @DB + public boolean updateResourceCount(long accountId, ResourceType type, boolean increment, long delta) { + boolean result = true; + try { + Transaction txn = Transaction.currentTxn(); + txn.start(); + + Set rowsToLock = _resourceCountDao.listAllRowsToUpdateForAccount(accountId, getAccount(accountId).getDomainId(), type); + + //Lock rows first + SearchCriteria sc = ResourceCountSearch.create(); + sc.setParameters("id", rowsToLock.toArray()); + List rowsToUpdate = _resourceCountDao.lockRows(sc, null, true); + + for (ResourceCountVO rowToUpdate : rowsToUpdate) { + if (!_resourceCountDao.updateById(rowToUpdate.getId(), increment, delta)) { + s_logger.trace("Unable to update resource count for the row " + rowToUpdate); + result = false; + } + } + + txn.commit(); + } catch (Exception ex) { + s_logger.error("Failed to update resource count for account id=" + accountId); + result = false; + } + return result; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_DOMAIN_CREATE, eventDescription = "creating Domain") + @DB + public Domain createDomain(CreateDomainCmd cmd) { + String name = cmd.getDomainName(); + Long parentId = cmd.getParentDomainId(); + Long ownerId = UserContext.current().getCaller().getId(); + Account caller = UserContext.current().getCaller(); + String networkDomain = cmd.getNetworkDomain(); + + if (ownerId == null) { + ownerId = Long.valueOf(1); + } + + if (parentId == null) { + parentId = Long.valueOf(DomainVO.ROOT_DOMAIN); + } + + DomainVO parentDomain = _domainDao.findById(parentId); + if (parentDomain == null) { + throw new InvalidParameterValueException("Unable to create domain " + name + ", parent domain " + parentId + " not found."); + } + + if (parentDomain.getState().equals(Domain.State.Inactive)) { + throw new CloudRuntimeException("The domain cannot be created as the parent domain " + parentDomain.getName() + " is being deleted"); + } + + checkAccess(caller, parentDomain); + + if (networkDomain != null) { + if (!NetUtils.verifyDomainName(networkDomain)) { + throw new InvalidParameterValueException( + "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); + } + } + + SearchCriteria sc = _domainDao.createSearchCriteria(); + sc.addAnd("name", SearchCriteria.Op.EQ, name); + sc.addAnd("parent", SearchCriteria.Op.EQ, parentId); + List domains = _domainDao.search(sc, null); + if ((domains == null) || domains.isEmpty()) { + DomainVO domain = new DomainVO(name, ownerId, parentId, networkDomain); + try { + Transaction txn = Transaction.currentTxn(); + txn.start(); + + domain = _domainDao.create(domain); + _resourceCountDao.createResourceCounts(domain.getId(), ResourceLimit.OwnerType.Domain); + + txn.commit(); + return domain; + } catch (IllegalArgumentException ex) { + s_logger.warn("Failed to create domain ", ex); + throw ex; + } + } else { + throw new InvalidParameterValueException("Domain with name " + name + " already exists for the parent id=" + parentId); + } + } } diff --git a/server/test/com/cloud/agent/MockAgentManagerImpl.java b/server/test/com/cloud/agent/MockAgentManagerImpl.java index 452789e069c..dc715e24bb2 100644 --- a/server/test/com/cloud/agent/MockAgentManagerImpl.java +++ b/server/test/com/cloud/agent/MockAgentManagerImpl.java @@ -61,7 +61,6 @@ public class MockAgentManagerImpl implements AgentManager { return null; } - @Override public Answer send(Long hostId, Command cmd) throws AgentUnavailableException, OperationTimedoutException { // TODO Auto-generated method stub @@ -200,7 +199,6 @@ public class MockAgentManagerImpl implements AgentManager { return false; } - @Override public boolean isHostNativeHAEnabled(long hostId) { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java index 5d7a33d3e7f..73d857d7553 100644 --- a/server/test/com/cloud/user/MockAccountManagerImpl.java +++ b/server/test/com/cloud/user/MockAccountManagerImpl.java @@ -10,6 +10,7 @@ import javax.naming.ConfigurationException; import com.cloud.acl.ControlledEntity; import com.cloud.acl.SecurityChecker.AccessType; import com.cloud.api.commands.CreateAccountCmd; +import com.cloud.api.commands.CreateDomainCmd; import com.cloud.api.commands.CreateUserCmd; import com.cloud.api.commands.DeleteAccountCmd; import com.cloud.api.commands.DeleteUserCmd; @@ -221,7 +222,7 @@ public class MockAccountManagerImpl implements Manager, AccountManager { } @Override - public long findCorrectResourceLimit(AccountVO account, ResourceType type) { + public long findCorrectResourceLimit(long accountId, ResourceType type) { // TODO Auto-generated method stub return 0; } @@ -338,5 +339,10 @@ public class MockAccountManagerImpl implements Manager, AccountManager { // TODO Auto-generated method stub } + + @Override + public Domain createDomain(CreateDomainCmd cmd) { + return null; + } } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index c66d25c68ea..65257a90f22 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1076,7 +1076,9 @@ CREATE TABLE `cloud`.`resource_count` ( PRIMARY KEY (`id`), CONSTRAINT `fk_resource_count__account_id` FOREIGN KEY `fk_resource_count__account_id`(`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_resource_count__domain_id` FOREIGN KEY `fk_resource_count__domain_id`(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, - INDEX `i_resource_count__type`(`type`) + INDEX `i_resource_count__type`(`type`), + UNIQUE `i_resource_count__type_accountId`(`type`, `account_id`), + UNIQUE `i_resource_count__type_domaintId`(`type`, `domain_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`op_host_capacity` ( diff --git a/setup/db/db/schema-2211to2212.sql b/setup/db/db/schema-2211to2212.sql index 2083b4e2456..3e552fc3e07 100644 --- a/setup/db/db/schema-2211to2212.sql +++ b/setup/db/db/schema-2211to2212.sql @@ -54,3 +54,7 @@ ALTER TABLE `cloud_usage`.`usage_port_forwarding` ADD INDEX `i_usage_port_forwar ALTER TABLE `cloud_usage`.`usage_network_offering` ADD INDEX `i_usage_network_offering__account_id`(`account_id`); ALTER TABLE `cloud_usage`.`usage_network_offering` ADD INDEX `i_usage_network_offering__created`(`created`); ALTER TABLE `cloud_usage`.`usage_network_offering` ADD INDEX `i_usage_network_offering__deleted`(`deleted`); + +ALTER TABLE `cloud`.`resource_count` ADD UNIQUE `i_resource_count__type_accountId`(`type`, `account_id`); +ALTER TABLE `cloud`.`resource_count` ADD UNIQUE `i_resource_count__type_domaintId`(`type`, `domain_id`); +