3434import org .apache .cloudstack .framework .messagebus .MessageBus ;
3535import org .apache .cloudstack .framework .messagebus .PublishScope ;
3636import org .apache .cloudstack .region .RegionManager ;
37+ import org .apache .commons .lang .BooleanUtils ;
3738
3839import com .cloud .configuration .Resource .ResourceOwnerType ;
3940import com .cloud .configuration .ResourceLimit ;
6364import com .cloud .utils .component .ManagerBase ;
6465import com .cloud .utils .db .DB ;
6566import com .cloud .utils .db .Filter ;
67+ import com .cloud .utils .db .GlobalLock ;
6668import com .cloud .utils .db .SearchBuilder ;
6769import com .cloud .utils .db .SearchCriteria ;
6870import com .cloud .utils .db .Transaction ;
@@ -109,6 +111,20 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom
109111 @ Inject
110112 MessageBus _messageBus ;
111113
114+ static boolean rollBackState = false ;
115+
116+ protected GlobalLock getGlobalLock (String name ) {
117+ return GlobalLock .getInternLock (name );
118+ }
119+
120+ protected boolean getRollBackState () {
121+ return rollBackState ;
122+ }
123+
124+ protected Account getCaller () {
125+ return CallContext .current ().getCallingAccount ();
126+ }
127+
112128 @ Override
113129 public Domain getDomain (long domainId ) {
114130 return _domainDao .findById (domainId );
@@ -151,7 +167,7 @@ public boolean isChildDomain(Long parentId, Long childId) {
151167 @ Override
152168 @ ActionEvent (eventType = EventTypes .EVENT_DOMAIN_CREATE , eventDescription = "creating Domain" )
153169 public Domain createDomain (String name , Long parentId , String networkDomain , String domainUUID ) {
154- Account caller = CallContext . current (). getCallingAccount ();
170+ Account caller = getCaller ();
155171
156172 if (parentId == null ) {
157173 parentId = Long .valueOf (Domain .ROOT_DOMAIN );
@@ -256,7 +272,7 @@ public List<? extends Domain> findInactiveDomains() {
256272 @ Override
257273 @ ActionEvent (eventType = EventTypes .EVENT_DOMAIN_DELETE , eventDescription = "deleting Domain" , async = true )
258274 public boolean deleteDomain (long domainId , Boolean cleanup ) {
259- Account caller = CallContext . current (). getCallingAccount ();
275+ Account caller = getCaller ();
260276
261277 DomainVO domain = _domainDao .findById (domainId );
262278
@@ -273,82 +289,133 @@ public boolean deleteDomain(long domainId, Boolean cleanup) {
273289
274290 @ Override
275291 public boolean deleteDomain (DomainVO domain , Boolean cleanup ) {
276- // mark domain as inactive
277- s_logger .debug ("Marking domain id=" + domain .getId () + " as " + Domain .State .Inactive + " before actually deleting it" );
278- domain .setState (Domain .State .Inactive );
279- _domainDao .update (domain .getId (), domain );
280- boolean rollBackState = false ;
281- boolean hasDedicatedResources = false ;
292+ GlobalLock lock = getGlobalLock ("AccountCleanup" );
293+ if (lock == null ) {
294+ s_logger .debug ("Couldn't get the global lock" );
295+ return false ;
296+ }
297+
298+ if (!lock .lock (30 )) {
299+ s_logger .debug ("Couldn't lock the db" );
300+ return false ;
301+ }
282302
283303 try {
284- long ownerId = domain .getAccountId ();
285- if ((cleanup != null ) && cleanup .booleanValue ()) {
286- if (!cleanupDomain (domain .getId (), ownerId )) {
287- rollBackState = true ;
288- CloudRuntimeException e =
289- new CloudRuntimeException ("Failed to clean up domain resources and sub domains, delete failed on domain " + domain .getName () + " (id: " +
290- domain .getId () + ")." );
291- e .addProxyObject (domain .getUuid (), "domainId" );
292- throw e ;
293- }
294- } else {
295- //don't delete the domain if there are accounts set for cleanup, or non-removed networks exist, or domain has dedicated resources
296- List <Long > networkIds = _networkDomainDao .listNetworkIdsByDomain (domain .getId ());
297- List <AccountVO > accountsForCleanup = _accountDao .findCleanupsForRemovedAccounts (domain .getId ());
298- List <DedicatedResourceVO > dedicatedResources = _dedicatedDao .listByDomainId (domain .getId ());
299- if (dedicatedResources != null && !dedicatedResources .isEmpty ()) {
300- s_logger .error ("There are dedicated resources for the domain " + domain .getId ());
301- hasDedicatedResources = true ;
302- }
303- if (accountsForCleanup .isEmpty () && networkIds .isEmpty () && !hasDedicatedResources ) {
304- _messageBus .publish (_name , MESSAGE_PRE_REMOVE_DOMAIN_EVENT , PublishScope .LOCAL , domain );
305- if (!_domainDao .remove (domain .getId ())) {
304+ // mark domain as inactive
305+ s_logger .debug ("Marking domain id=" + domain .getId () + " as " + Domain .State .Inactive + " before actually deleting it" );
306+ domain .setState (Domain .State .Inactive );
307+ _domainDao .update (domain .getId (), domain );
308+
309+ try {
310+ long ownerId = domain .getAccountId ();
311+ if (BooleanUtils .toBoolean (cleanup )) {
312+ if (!cleanupDomain (domain .getId (), ownerId )) {
306313 rollBackState = true ;
307314 CloudRuntimeException e =
308- new CloudRuntimeException ("Delete failed on domain " + domain .getName () + " (id: " + domain . getId () +
309- "); Please make sure all users and sub domains have been removed from the domain before deleting " );
315+ new CloudRuntimeException ("Failed to clean up domain resources and sub domains, delete failed on domain " + domain .getName () + " (id: " +
316+ domain . getId () + "). " );
310317 e .addProxyObject (domain .getUuid (), "domainId" );
311318 throw e ;
312319 }
313- _messageBus .publish (_name , MESSAGE_REMOVE_DOMAIN_EVENT , PublishScope .LOCAL , domain );
314320 } else {
315- rollBackState = true ;
316- String msg = null ;
317- if (!accountsForCleanup .isEmpty ()) {
318- msg = accountsForCleanup .size () + " accounts to cleanup" ;
319- } else if (!networkIds .isEmpty ()) {
320- msg = networkIds .size () + " non-removed networks" ;
321- } else if (hasDedicatedResources ) {
322- msg = "dedicated resources." ;
323- }
321+ checkDomainAccountsNetworksAndResourcesBeforeRemoving (domain );
322+ }
324323
325- CloudRuntimeException e = new CloudRuntimeException ("Can't delete the domain yet because it has " + msg );
326- e .addProxyObject (domain .getUuid (), "domainId" );
327- throw e ;
324+ cleanupDomainOfferings (domain .getId ());
325+ CallContext .current ().putContextParameter (Domain .class , domain .getUuid ());
326+ return true ;
327+ } catch (Exception ex ) {
328+ s_logger .error ("Exception deleting domain with id " + domain .getId (), ex );
329+ if (ex instanceof CloudRuntimeException )
330+ throw (CloudRuntimeException )ex ;
331+ else
332+ return false ;
333+ } finally {
334+ //when success is false
335+ if (rollBackState ) {
336+ s_logger .debug ("Changing domain id=" + domain .getId () + " state back to " + Domain .State .Active +
337+ " because it can't be removed due to resources referencing to it" );
338+ domain .setState (Domain .State .Active );
339+ _domainDao .update (domain .getId (), domain );
328340 }
329341 }
342+ }
343+ finally {
344+ lock .unlock ();
345+ rollBackState = false ;
346+ }
347+ }
330348
331- cleanupDomainOfferings (domain .getId ());
332- CallContext .current ().putContextParameter (Domain .class , domain .getUuid ());
333- return true ;
334- } catch (Exception ex ) {
335- s_logger .error ("Exception deleting domain with id " + domain .getId (), ex );
336- if (ex instanceof CloudRuntimeException )
337- throw (CloudRuntimeException )ex ;
338- else
339- return false ;
340- } finally {
341- //when success is false
342- if (rollBackState ) {
343- s_logger .debug ("Changing domain id=" + domain .getId () + " state back to " + Domain .State .Active +
344- " because it can't be removed due to resources referencing to it" );
345- domain .setState (Domain .State .Active );
346- _domainDao .update (domain .getId (), domain );
347- }
349+ /**
350+ * Check domain resources before removing domain. There are 2 cases:
351+ * <ol>
352+ * <li>Domain doesn't have accounts for cleanup, non-removed networks, or dedicated resources</li>
353+ * <ul><li>Delete domain</li></ul>
354+ * <li>Domain has one of the following: accounts set for cleanup, non-removed networks, dedicated resources</li>
355+ * <ul><li>Dont' delete domain</li><li>Fail operation</li></ul>
356+ * </ol>
357+ * @param domain domain to remove
358+ * @throws CloudRuntimeException when case 2 or when domain cannot be deleted on case 1
359+ */
360+ protected void checkDomainAccountsNetworksAndResourcesBeforeRemoving (DomainVO domain ) {
361+ boolean hasDedicatedResources = false ;
362+ List <Long > networkIds = _networkDomainDao .listNetworkIdsByDomain (domain .getId ());
363+ List <AccountVO > accountsForCleanup = _accountDao .findCleanupsForRemovedAccounts (domain .getId ());
364+ List <DedicatedResourceVO > dedicatedResources = _dedicatedDao .listByDomainId (domain .getId ());
365+ if (dedicatedResources != null && !dedicatedResources .isEmpty ()) {
366+ s_logger .error ("There are dedicated resources for the domain " + domain .getId ());
367+ hasDedicatedResources = true ;
368+ }
369+ if (accountsForCleanup .isEmpty () && networkIds .isEmpty () && !hasDedicatedResources ) {
370+ publishRemoveEventsAndRemoveDomain (domain );
371+ } else {
372+ failRemoveOperation (domain , accountsForCleanup , networkIds , hasDedicatedResources );
348373 }
349374 }
350375
351- private void cleanupDomainOfferings (Long domainId ) {
376+ /**
377+ * Enable rollback state and fail domain remove operation including proper message
378+ * @param domain domain
379+ * @param accountsForCleanup domain accounts for cleanup
380+ * @param networkIds domain network ids
381+ * @param hasDedicatedResources indicates if domain has dedicated resources
382+ * @throws CloudRuntimeException including descriptive message indicating the reason for failure
383+ */
384+ protected void failRemoveOperation (DomainVO domain , List <AccountVO > accountsForCleanup , List <Long > networkIds , boolean hasDedicatedResources ) {
385+ rollBackState = true ;
386+ String msg = null ;
387+ if (!accountsForCleanup .isEmpty ()) {
388+ msg = accountsForCleanup .size () + " accounts to cleanup" ;
389+ } else if (!networkIds .isEmpty ()) {
390+ msg = networkIds .size () + " non-removed networks" ;
391+ } else if (hasDedicatedResources ) {
392+ msg = "dedicated resources." ;
393+ }
394+
395+ CloudRuntimeException e = new CloudRuntimeException ("Can't delete the domain yet because it has " + msg );
396+ e .addProxyObject (domain .getUuid (), "domainId" );
397+ throw e ;
398+ }
399+
400+ /**
401+ * Publish pre-remove and remove domain events and remove domain
402+ * @param domain domain to remove
403+ * @throws CloudRuntimeException when domain cannot be removed
404+ */
405+ protected void publishRemoveEventsAndRemoveDomain (DomainVO domain ) {
406+ _messageBus .publish (_name , MESSAGE_PRE_REMOVE_DOMAIN_EVENT , PublishScope .LOCAL , domain );
407+ if (!_domainDao .remove (domain .getId ())) {
408+ rollBackState = true ;
409+ CloudRuntimeException e =
410+ new CloudRuntimeException ("Delete failed on domain " + domain .getName () + " (id: " + domain .getId () +
411+ "); Please make sure all users and sub domains have been removed from the domain before deleting" );
412+ e .addProxyObject (domain .getUuid (), "domainId" );
413+ throw e ;
414+ }
415+ _messageBus .publish (_name , MESSAGE_REMOVE_DOMAIN_EVENT , PublishScope .LOCAL , domain );
416+ }
417+
418+ protected void cleanupDomainOfferings (Long domainId ) {
352419 // delete the service and disk offerings associated with this domain
353420 List <DiskOfferingVO > diskOfferingsForThisDomain = _diskOfferingDao .listByDomainId (domainId );
354421 for (DiskOfferingVO diskOffering : diskOfferingsForThisDomain ) {
@@ -361,7 +428,7 @@ private void cleanupDomainOfferings(Long domainId) {
361428 }
362429 }
363430
364- private boolean cleanupDomain (Long domainId , Long ownerId ) throws ConcurrentOperationException , ResourceUnavailableException {
431+ protected boolean cleanupDomain (Long domainId , Long ownerId ) throws ConcurrentOperationException , ResourceUnavailableException {
365432 s_logger .debug ("Cleaning up domain id=" + domainId );
366433 boolean success = true ;
367434 DomainVO domainHandle = _domainDao .findById (domainId );
@@ -399,15 +466,15 @@ private boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOper
399466 for (AccountVO account : accounts ) {
400467 if (account .getType () != Account .ACCOUNT_TYPE_PROJECT ) {
401468 s_logger .debug ("Deleting account " + account + " as a part of domain id=" + domainId + " cleanup" );
402- boolean deleteAccount = _accountMgr .deleteAccount (account , CallContext .current ().getCallingUserId (), CallContext . current (). getCallingAccount ());
469+ boolean deleteAccount = _accountMgr .deleteAccount (account , CallContext .current ().getCallingUserId (), getCaller ());
403470 if (!deleteAccount ) {
404471 s_logger .warn ("Failed to cleanup account id=" + account .getId () + " as a part of domain cleanup" );
405472 }
406473 success = (success && deleteAccount );
407474 } else {
408475 ProjectVO project = _projectDao .findByProjectAccountId (account .getId ());
409476 s_logger .debug ("Deleting project " + project + " as a part of domain id=" + domainId + " cleanup" );
410- boolean deleteProject = _projectMgr .deleteProject (CallContext . current (). getCallingAccount (), CallContext .current ().getCallingUserId (), project );
477+ boolean deleteProject = _projectMgr .deleteProject (getCaller (), CallContext .current ().getCallingUserId (), project );
411478 if (!deleteProject ) {
412479 s_logger .warn ("Failed to cleanup project " + project + " as a part of domain cleanup" );
413480 }
@@ -470,7 +537,7 @@ private boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOper
470537
471538 @ Override
472539 public Pair <List <? extends Domain >, Integer > searchForDomains (ListDomainsCmd cmd ) {
473- Account caller = CallContext . current (). getCallingAccount ();
540+ Account caller = getCaller ();
474541 Long domainId = cmd .getId ();
475542 boolean listAll = cmd .listAll ();
476543 boolean isRecursive = false ;
@@ -542,7 +609,7 @@ public Pair<List<? extends Domain>, Integer> searchForDomainChildren(ListDomainC
542609 boolean listAll = cmd .listAll ();
543610 String path = null ;
544611
545- Account caller = CallContext . current (). getCallingAccount ();
612+ Account caller = getCaller ();
546613 if (domainId != null ) {
547614 _accountMgr .checkAccess (caller , getDomain (domainId ));
548615 } else {
@@ -612,7 +679,7 @@ public DomainVO updateDomain(UpdateDomainCmd cmd) {
612679 }
613680
614681 // check permissions
615- Account caller = CallContext . current (). getCallingAccount ();
682+ Account caller = getCaller ();
616683 _accountMgr .checkAccess (caller , domain );
617684
618685 // domain name is unique in the cloud
0 commit comments