Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private static void publishUsageEvent(String usageEventType, Long accountId, Lon
return; // no provider is configured to provide events bus, so just return
}

Account account = s_accountDao.findById(accountId);
Account account = s_accountDao.findByIdIncludingRemoved(accountId);
DataCenterVO dc = s_dcDao.findById(zoneId);

// if account has been deleted, this might be called during cleanup of resources and results in null pointer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ private void pubishOnEventBus(String event, String status, Volume vo, State oldS
eventDescription.put("id", vo.getUuid());
eventDescription.put("old-state", oldState.name());
eventDescription.put("new-state", newState.name());
eventDescription.put("status", status);

String eventDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").format(new Date());
eventDescription.put("eventDateTime", eventDate);
Expand Down
13 changes: 13 additions & 0 deletions server/src/com/cloud/user/AccountManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import com.cloud.event.ActionEventUtils;
import com.cloud.event.ActionEvents;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.CloudAuthenticationException;
import com.cloud.exception.ConcurrentOperationException;
Expand Down Expand Up @@ -160,6 +161,7 @@
import com.cloud.vm.UserVmManager;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.InstanceGroupDao;
import com.cloud.vm.dao.UserVmDao;
Expand Down Expand Up @@ -763,6 +765,17 @@ protected boolean cleanupAccount(AccountVO account, long callerUserId, Account c
s_logger.error("Unable to expunge vm: " + vm.getId());
accountCleanupNeeded = true;
}
else if (!vm.getState().equals(VirtualMachine.State.Destroyed)) {
// We have to emit the event here because the state listener is ignoring root volume deletions,
// assuming that the UserVMManager is responsible for emitting the usage event for them when
// the vm delete command is processed
List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
for (VolumeVO volume : volumes) {
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(),
Volume.class.getName(), volume.getUuid(), volume.isDisplayVolume());
}

}
}

// Mark the account's volumes as destroyed
Expand Down
171 changes: 158 additions & 13 deletions server/test/com/cloud/user/AccountManagerImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,21 @@
package com.cloud.user;

import java.lang.reflect.Field;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import com.cloud.server.auth.UserAuthenticator;
import com.cloud.utils.Pair;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker;
Expand All @@ -44,10 +42,14 @@
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.messagebus.MessageBus;
import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao;

import com.cloud.vm.snapshot.VMSnapshotManager;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.dao.ResourceCountDao;
Expand All @@ -58,6 +60,8 @@
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.UsageEventUtils;
import com.cloud.event.UsageEventVO;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.as.AutoScaleManager;
Expand All @@ -74,7 +78,9 @@
import com.cloud.projects.ProjectManager;
import com.cloud.projects.dao.ProjectAccountDao;
import com.cloud.projects.dao.ProjectDao;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeApiService;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
Expand All @@ -87,12 +93,16 @@
import com.cloud.vm.UserVmManager;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.InstanceGroupDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.vm.snapshot.VMSnapshotManager;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;

@RunWith(MockitoJUnitRunner.class)
public class AccountManagerImplTest {
Expand Down Expand Up @@ -192,6 +202,9 @@ public class AccountManagerImplTest {
@Mock
VMSnapshotDao _vmSnapshotDao;

@Mock
MockUsageEventDao _usageEventDao;

@Mock
User callingUser;
@Mock
Expand All @@ -205,6 +218,11 @@ public class AccountManagerImplTest {
@Mock
private UserAuthenticator userAuthenticator;

//Maintain a list of old fields in the usage utils class... This
//is because of weirdness of how it uses static fields and an init
//method.
private Map<String, Object> oldFields = new HashMap<>();

@Before
public void setup() throws NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
Expand Down Expand Up @@ -353,6 +371,133 @@ public void testAuthenticateUser() throws UnknownHostException {
Mockito.verify(userAuthenticator, Mockito.times(1)).authenticate("test", "fail", 1L, null);
Mockito.verify(userAuthenticator, Mockito.never()).authenticate("test", null, 1L, null);
Mockito.verify(userAuthenticator, Mockito.never()).authenticate("test", "", 1L, null);
}

public UsageEventUtils setupUsageUtils() {
_usageEventDao = new MockUsageEventDao();
UsageEventUtils utils = new UsageEventUtils();

Map<String, String> usageUtilsFields = new HashMap<String, String>();
usageUtilsFields.put("usageEventDao", "_usageEventDao");
usageUtilsFields.put("accountDao", "_accountDao");
usageUtilsFields.put("dcDao", "_dcDao");
usageUtilsFields.put("configDao", "_configDao");

for (String fieldName : usageUtilsFields.keySet()) {
try {
Field f = UsageEventUtils.class.getDeclaredField(fieldName);
f.setAccessible(true);
//Remember the old fields for cleanup later (see cleanupUsageUtils)
Field staticField = UsageEventUtils.class.getDeclaredField("s_" + fieldName);
staticField.setAccessible(true);
oldFields.put(f.getName(), staticField.get(null));
f.set(utils,
this.getClass()
.getDeclaredField(
usageUtilsFields.get(fieldName))
.get(this));
} catch (IllegalArgumentException | IllegalAccessException
| NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
try {
Method method = UsageEventUtils.class.getDeclaredMethod("init");
method.setAccessible(true);
method.invoke(utils);
} catch (SecurityException | NoSuchMethodException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return utils;
}

public void cleanupUsageUtils() {
UsageEventUtils utils = new UsageEventUtils();

for (String fieldName : oldFields.keySet()) {
try {
Field f = UsageEventUtils.class.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(utils, oldFields.get(fieldName));
} catch (IllegalArgumentException | IllegalAccessException
| NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
try {
Method method = UsageEventUtils.class.getDeclaredMethod("init");
method.setAccessible(true);
method.invoke(utils);
} catch (SecurityException | NoSuchMethodException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public List<UsageEventVO> deleteUserAccountRootVolumeUsageEvents(boolean vmDestroyedPrior) {
AccountVO account = new AccountVO();
account.setId(42l);
DomainVO domain = new DomainVO();
UserVmVO vm = Mockito.mock(UserVmVO.class);
VolumeVO vol = Mockito.mock(VolumeVO.class);
Mockito.when(_accountDao.findById(42l)).thenReturn(account);
Mockito.when(
securityChecker.checkAccess(Mockito.any(Account.class),
Mockito.any(ControlledEntity.class), Mockito.any(AccessType.class),
Mockito.anyString()))
.thenReturn(true);
Mockito.when(_accountDao.remove(42l)).thenReturn(true);
Mockito.when(_userVmDao.listByAccountId(42l)).thenReturn(
Arrays.asList(vm));
Mockito.when(_userVmDao.findByUuid(Mockito.any(String.class))).thenReturn(vm);
Mockito.when(
_vmMgr.expunge(Mockito.any(UserVmVO.class), Mockito.anyLong(),
Mockito.any(Account.class))).thenReturn(true);
Mockito.when(vm.getState()).thenReturn(
vmDestroyedPrior
? VirtualMachine.State.Destroyed
: VirtualMachine.State.Running);
Mockito.when(
_volumeDao.findByInstanceAndType(Mockito.any(Long.class),
Mockito.eq(Volume.Type.ROOT))).thenReturn(
Arrays.asList(vol));
Mockito.when(vol.getAccountId()).thenReturn((long) 1);
Mockito.when(vol.getDataCenterId()).thenReturn((long) 1);
Mockito.when(vol.getId()).thenReturn((long) 1);
Mockito.when(vol.getName()).thenReturn("root volume");
Mockito.when(vol.getUuid()).thenReturn("vol-111111");
Mockito.when(vol.isDisplayVolume()).thenReturn(true);

Mockito.when(_domainMgr.getDomain(Mockito.anyLong()))
.thenReturn(domain);

accountManager.deleteUserAccount(42);
return _usageEventDao.listAll();
}

@Test
public void destroyedVMRootVolumeUsageEvent() {
UsageEventUtils utils = setupUsageUtils();
List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(true);
Assert.assertEquals(0, emittedEvents.size());
cleanupUsageUtils();
}

@Test
public void runningVMRootVolumeUsageEvent() {
UsageEventUtils utils = setupUsageUtils();
List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(false);
Assert.assertEquals(1, emittedEvents.size());
cleanupUsageUtils();
}
}
Loading