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 .collections .CollectionUtils ;
38+ import org .apache .commons .lang .BooleanUtils ;
3739
3840import com .cloud .configuration .Resource .ResourceOwnerType ;
3941import com .cloud .configuration .ResourceLimit ;
6365import com .cloud .utils .component .ManagerBase ;
6466import com .cloud .utils .db .DB ;
6567import com .cloud .utils .db .Filter ;
68+ import com .cloud .utils .db .GlobalLock ;
6669import com .cloud .utils .db .SearchBuilder ;
6770import com .cloud .utils .db .SearchCriteria ;
6871import com .cloud .utils .db .Transaction ;
@@ -109,6 +112,14 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom
109112 @ Inject
110113 MessageBus _messageBus ;
111114
115+ protected GlobalLock getGlobalLock (String name ) {
116+ return GlobalLock .getInternLock (name );
117+ }
118+
119+ protected Account getCaller () {
120+ return CallContext .current ().getCallingAccount ();
121+ }
122+
112123 @ Override
113124 public Domain getDomain (long domainId ) {
114125 return _domainDao .findById (domainId );
@@ -151,7 +162,7 @@ public boolean isChildDomain(Long parentId, Long childId) {
151162 @ Override
152163 @ ActionEvent (eventType = EventTypes .EVENT_DOMAIN_CREATE , eventDescription = "creating Domain" )
153164 public Domain createDomain (String name , Long parentId , String networkDomain , String domainUUID ) {
154- Account caller = CallContext . current (). getCallingAccount ();
165+ Account caller = getCaller ();
155166
156167 if (parentId == null ) {
157168 parentId = Long .valueOf (Domain .ROOT_DOMAIN );
@@ -256,7 +267,7 @@ public List<? extends Domain> findInactiveDomains() {
256267 @ Override
257268 @ ActionEvent (eventType = EventTypes .EVENT_DOMAIN_DELETE , eventDescription = "deleting Domain" , async = true )
258269 public boolean deleteDomain (long domainId , Boolean cleanup ) {
259- Account caller = CallContext . current (). getCallingAccount ();
270+ Account caller = getCaller ();
260271
261272 DomainVO domain = _domainDao .findById (domainId );
262273
@@ -273,82 +284,146 @@ public boolean deleteDomain(long domainId, Boolean cleanup) {
273284
274285 @ Override
275286 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 ;
287+ GlobalLock lock = getGlobalLock ("AccountCleanup" );
288+ if (lock == null ) {
289+ s_logger .debug ("Couldn't get the global lock" );
290+ return false ;
291+ }
292+
293+ if (!lock .lock (30 )) {
294+ s_logger .debug ("Couldn't lock the db" );
295+ return false ;
296+ }
282297
283298 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 ())) {
306- rollBackState = true ;
307- 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" );
310- e .addProxyObject (domain .getUuid (), "domainId" );
311- throw e ;
312- }
313- _messageBus .publish (_name , MESSAGE_REMOVE_DOMAIN_EVENT , PublishScope .LOCAL , domain );
299+ // mark domain as inactive
300+ s_logger .debug ("Marking domain id=" + domain .getId () + " as " + Domain .State .Inactive + " before actually deleting it" );
301+ domain .setState (Domain .State .Inactive );
302+ _domainDao .update (domain .getId (), domain );
303+
304+ try {
305+ long ownerId = domain .getAccountId ();
306+ if (BooleanUtils .toBoolean (cleanup )) {
307+ tryCleanupDomain (domain , ownerId );
314308 } 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- }
309+ removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources (domain );
310+ }
324311
325- CloudRuntimeException e = new CloudRuntimeException ("Can't delete the domain yet because it has " + msg );
326- e .addProxyObject (domain .getUuid (), "domainId" );
327- throw e ;
312+ cleanupDomainOfferings (domain .getId ());
313+ CallContext .current ().putContextParameter (Domain .class , domain .getUuid ());
314+ return true ;
315+ } catch (Exception ex ) {
316+ s_logger .error ("Exception deleting domain with id " + domain .getId (), ex );
317+ if (ex instanceof CloudRuntimeException ) {
318+ rollbackDomainState (domain );
319+ throw (CloudRuntimeException )ex ;
328320 }
321+ else
322+ return false ;
329323 }
324+ }
325+ finally {
326+ lock .unlock ();
327+ }
328+ }
330329
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- }
330+ /**
331+ * Roll back domain state to Active
332+ * @param domain domain
333+ */
334+ protected void rollbackDomainState (DomainVO domain ) {
335+ s_logger .debug ("Changing domain id=" + domain .getId () + " state back to " + Domain .State .Active +
336+ " because it can't be removed due to resources referencing to it" );
337+ domain .setState (Domain .State .Active );
338+ _domainDao .update (domain .getId (), domain );
339+ }
340+
341+ /**
342+ * Try cleaning up domain. If it couldn't throws CloudRuntimeException
343+ * @param domain domain
344+ * @param ownerId owner id
345+ * @throws ConcurrentOperationException
346+ * @throws ResourceUnavailableException
347+ * @throws CloudRuntimeException when cleanupDomain
348+ */
349+ protected void tryCleanupDomain (DomainVO domain , long ownerId ) throws ConcurrentOperationException , ResourceUnavailableException , CloudRuntimeException {
350+ if (!cleanupDomain (domain .getId (), ownerId )) {
351+ CloudRuntimeException e =
352+ new CloudRuntimeException ("Failed to clean up domain resources and sub domains, delete failed on domain " + domain .getName () + " (id: " +
353+ domain .getId () + ")." );
354+ e .addProxyObject (domain .getUuid (), "domainId" );
355+ throw e ;
348356 }
349357 }
350358
351- private void cleanupDomainOfferings (Long domainId ) {
359+ /**
360+ * First check domain resources before removing domain. There are 2 cases:
361+ * <ol>
362+ * <li>Domain doesn't have accounts for cleanup, non-removed networks, or dedicated resources</li>
363+ * <ul><li>Delete domain</li></ul>
364+ * <li>Domain has one of the following: accounts set for cleanup, non-removed networks, dedicated resources</li>
365+ * <ul><li>Dont' delete domain</li><li>Fail operation</li></ul>
366+ * </ol>
367+ * @param domain domain to remove
368+ * @throws CloudRuntimeException when case 2 or when domain cannot be deleted on case 1
369+ */
370+ protected void removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources (DomainVO domain ) {
371+ boolean hasDedicatedResources = false ;
372+ List <Long > networkIds = _networkDomainDao .listNetworkIdsByDomain (domain .getId ());
373+ List <AccountVO > accountsForCleanup = _accountDao .findCleanupsForRemovedAccounts (domain .getId ());
374+ List <DedicatedResourceVO > dedicatedResources = _dedicatedDao .listByDomainId (domain .getId ());
375+ if (CollectionUtils .isNotEmpty (dedicatedResources )) {
376+ s_logger .error ("There are dedicated resources for the domain " + domain .getId ());
377+ hasDedicatedResources = true ;
378+ }
379+ if (accountsForCleanup .isEmpty () && networkIds .isEmpty () && !hasDedicatedResources ) {
380+ publishRemoveEventsAndRemoveDomain (domain );
381+ } else {
382+ failRemoveOperation (domain , accountsForCleanup , networkIds , hasDedicatedResources );
383+ }
384+ }
385+
386+ /**
387+ * Fail domain remove operation including proper message
388+ * @param domain domain
389+ * @param accountsForCleanup domain accounts for cleanup
390+ * @param networkIds domain network ids
391+ * @param hasDedicatedResources indicates if domain has dedicated resources
392+ * @throws CloudRuntimeException including descriptive message indicating the reason for failure
393+ */
394+ protected void failRemoveOperation (DomainVO domain , List <AccountVO > accountsForCleanup , List <Long > networkIds , boolean hasDedicatedResources ) {
395+ String msg = null ;
396+ if (!accountsForCleanup .isEmpty ()) {
397+ msg = accountsForCleanup .size () + " accounts to cleanup" ;
398+ } else if (!networkIds .isEmpty ()) {
399+ msg = networkIds .size () + " non-removed networks" ;
400+ } else if (hasDedicatedResources ) {
401+ msg = "dedicated resources." ;
402+ }
403+
404+ CloudRuntimeException e = new CloudRuntimeException ("Can't delete the domain yet because it has " + msg );
405+ e .addProxyObject (domain .getUuid (), "domainId" );
406+ throw e ;
407+ }
408+
409+ /**
410+ * Publish pre-remove and remove domain events and remove domain
411+ * @param domain domain to remove
412+ * @throws CloudRuntimeException when domain cannot be removed
413+ */
414+ protected void publishRemoveEventsAndRemoveDomain (DomainVO domain ) {
415+ _messageBus .publish (_name , MESSAGE_PRE_REMOVE_DOMAIN_EVENT , PublishScope .LOCAL , domain );
416+ if (!_domainDao .remove (domain .getId ())) {
417+ CloudRuntimeException e =
418+ new CloudRuntimeException ("Delete failed on domain " + domain .getName () + " (id: " + domain .getId () +
419+ "); Please make sure all users and sub domains have been removed from the domain before deleting" );
420+ e .addProxyObject (domain .getUuid (), "domainId" );
421+ throw e ;
422+ }
423+ _messageBus .publish (_name , MESSAGE_REMOVE_DOMAIN_EVENT , PublishScope .LOCAL , domain );
424+ }
425+
426+ protected void cleanupDomainOfferings (Long domainId ) {
352427 // delete the service and disk offerings associated with this domain
353428 List <DiskOfferingVO > diskOfferingsForThisDomain = _diskOfferingDao .listByDomainId (domainId );
354429 for (DiskOfferingVO diskOffering : diskOfferingsForThisDomain ) {
@@ -361,7 +436,7 @@ private void cleanupDomainOfferings(Long domainId) {
361436 }
362437 }
363438
364- private boolean cleanupDomain (Long domainId , Long ownerId ) throws ConcurrentOperationException , ResourceUnavailableException {
439+ protected boolean cleanupDomain (Long domainId , Long ownerId ) throws ConcurrentOperationException , ResourceUnavailableException {
365440 s_logger .debug ("Cleaning up domain id=" + domainId );
366441 boolean success = true ;
367442 DomainVO domainHandle = _domainDao .findById (domainId );
@@ -399,15 +474,15 @@ private boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOper
399474 for (AccountVO account : accounts ) {
400475 if (account .getType () != Account .ACCOUNT_TYPE_PROJECT ) {
401476 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 ());
477+ boolean deleteAccount = _accountMgr .deleteAccount (account , CallContext .current ().getCallingUserId (), getCaller ());
403478 if (!deleteAccount ) {
404479 s_logger .warn ("Failed to cleanup account id=" + account .getId () + " as a part of domain cleanup" );
405480 }
406481 success = (success && deleteAccount );
407482 } else {
408483 ProjectVO project = _projectDao .findByProjectAccountId (account .getId ());
409484 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 );
485+ boolean deleteProject = _projectMgr .deleteProject (getCaller (), CallContext .current ().getCallingUserId (), project );
411486 if (!deleteProject ) {
412487 s_logger .warn ("Failed to cleanup project " + project + " as a part of domain cleanup" );
413488 }
@@ -470,7 +545,7 @@ private boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOper
470545
471546 @ Override
472547 public Pair <List <? extends Domain >, Integer > searchForDomains (ListDomainsCmd cmd ) {
473- Account caller = CallContext . current (). getCallingAccount ();
548+ Account caller = getCaller ();
474549 Long domainId = cmd .getId ();
475550 boolean listAll = cmd .listAll ();
476551 boolean isRecursive = false ;
@@ -542,7 +617,7 @@ public Pair<List<? extends Domain>, Integer> searchForDomainChildren(ListDomainC
542617 boolean listAll = cmd .listAll ();
543618 String path = null ;
544619
545- Account caller = CallContext . current (). getCallingAccount ();
620+ Account caller = getCaller ();
546621 if (domainId != null ) {
547622 _accountMgr .checkAccess (caller , getDomain (domainId ));
548623 } else {
@@ -612,7 +687,7 @@ public DomainVO updateDomain(UpdateDomainCmd cmd) {
612687 }
613688
614689 // check permissions
615- Account caller = CallContext . current (). getCallingAccount ();
690+ Account caller = getCaller ();
616691 _accountMgr .checkAccess (caller , domain );
617692
618693 // domain name is unique in the cloud
0 commit comments