This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.18 by this push:
     new ab0297ea9b3 VM.CREATE/VOLUME.DELETE/VOLUME.DESTROY not being emitted 
(#7760)
ab0297ea9b3 is described below

commit ab0297ea9b3bba8ceb5a04aa347ac7c238893067
Author: mprokopchuk <[email protected]>
AuthorDate: Sun Aug 6 21:48:17 2023 -0700

    VM.CREATE/VOLUME.DELETE/VOLUME.DESTROY not being emitted (#7760)
    
    VM.CREATE/VOLUME.DELETE/VOLUME.DESTROY not being emitted
    
    * Update server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
    
    Co-authored-by: dahn <[email protected]>
    
    * Update 
api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
    
    Co-authored-by: dahn <[email protected]>
    
    ---------
    
    Co-authored-by: Maxim Prokopchuk <[email protected]>
    Co-authored-by: dahn <[email protected]>
---
 .../java/com/cloud/storage/VolumeApiService.java   |  2 +
 api/src/main/java/com/cloud/vm/UserVmService.java  |  8 ++++
 .../api/command/user/vm/DeployVMCmd.java           | 50 +++++++++++++---------
 .../com/cloud/vm/VirtualMachineManagerImpl.java    | 35 +++++++++------
 .../engine/orchestration/VolumeOrchestrator.java   | 25 ++++++++---
 .../java/com/cloud/api/query/QueryManagerImpl.java |  4 ++
 .../com/cloud/storage/VolumeApiServiceImpl.java    |  6 +++
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 35 +++++++++++++--
 test/integration/smoke/test_events_resource.py     |  3 +-
 9 files changed, 123 insertions(+), 45 deletions(-)

diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java 
b/api/src/main/java/com/cloud/storage/VolumeApiService.java
index b85195dafae..e5c938b1603 100644
--- a/api/src/main/java/com/cloud/storage/VolumeApiService.java
+++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java
@@ -163,6 +163,8 @@ public interface VolumeApiService {
 
     Volume destroyVolume(long volumeId, Account caller, boolean expunge, 
boolean forceExpunge);
 
+    void destroyVolume(long volumeId);
+
     Volume recoverVolume(long volumeId);
 
     void validateCustomDiskOfferingSizeRange(Long sizeInGB);
diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java 
b/api/src/main/java/com/cloud/vm/UserVmService.java
index 258e87002da..b9fa2ee4a65 100644
--- a/api/src/main/java/com/cloud/vm/UserVmService.java
+++ b/api/src/main/java/com/cloud/vm/UserVmService.java
@@ -432,6 +432,14 @@ public interface UserVmService {
     UserVm createVirtualMachine(DeployVMCmd cmd) throws 
InsufficientCapacityException, ResourceUnavailableException, 
ConcurrentOperationException,
         StorageUnavailableException, ResourceAllocationException;
 
+    /**
+     * This API is mostly to trigger VM.CREATE event for deployVirtualMachine 
with startvm=false, because there is no code in "execute" part of VM creation.
+     * However, it can be used for additional VM customization in the future.
+     * @param vmId - Virtual Machine Id
+     * @return - Virtual Machine
+     */
+    UserVm finalizeCreateVirtualMachine(long vmId);
+
     UserVm getUserVm(long vmId);
 
     VirtualMachine getVm(long vmId);
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 78d6155d012..7aa69bd11ee 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -753,7 +753,10 @@ public class DeployVMCmd extends 
BaseAsyncCreateCustomIdCmd implements SecurityG
 
     @Override
     public String getEventDescription() {
-        return "starting Vm. Vm Id: " + getEntityUuid();
+        if(getStartVm()) {
+            return "starting Vm. Vm Id: " + getEntityUuid();
+        }
+        return "deploying Vm. Vm Id: " + getEntityUuid();
     }
 
     @Override
@@ -765,28 +768,33 @@ public class DeployVMCmd extends 
BaseAsyncCreateCustomIdCmd implements SecurityG
     public void execute() {
         UserVm result;
 
-        try {
-            CallContext.current().setEventDetails("Vm Id: " + getEntityUuid());
-            result = _userVmService.startVirtualMachine(this);
-        } catch (ResourceUnavailableException ex) {
-            s_logger.warn("Exception: ", ex);
-            throw new 
ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
-        } catch (ResourceAllocationException ex) {
-            s_logger.warn("Exception: ", ex);
-            throw new 
ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage());
-        } catch (ConcurrentOperationException ex) {
-            s_logger.warn("Exception: ", ex);
-            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
ex.getMessage());
-        } catch (InsufficientCapacityException ex) {
-            StringBuilder message = new StringBuilder(ex.getMessage());
-            if (ex instanceof InsufficientServerCapacityException) {
-                if 
(((InsufficientServerCapacityException)ex).isAffinityApplied()) {
-                    message.append(", Please check the affinity groups 
provided, there may not be sufficient capacity to follow them");
+        CallContext.current().setEventDetails("Vm Id: " + getEntityUuid());
+        if (getStartVm()) {
+            try {
+                result = _userVmService.startVirtualMachine(this);
+            } catch (ResourceUnavailableException ex) {
+                s_logger.warn("Exception: ", ex);
+                throw new 
ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
+            } catch (ResourceAllocationException ex) {
+                s_logger.warn("Exception: ", ex);
+                throw new 
ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage());
+            } catch (ConcurrentOperationException ex) {
+                s_logger.warn("Exception: ", ex);
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
ex.getMessage());
+            } catch (InsufficientCapacityException ex) {
+                StringBuilder message = new StringBuilder(ex.getMessage());
+                if (ex instanceof InsufficientServerCapacityException) {
+                    if 
(((InsufficientServerCapacityException)ex).isAffinityApplied()) {
+                        message.append(", Please check the affinity groups 
provided, there may not be sufficient capacity to follow them");
+                    }
                 }
+                s_logger.info(String.format("%s: %s", message.toString(), 
ex.getLocalizedMessage()));
+                s_logger.debug(message.toString(), ex);
+                throw new 
ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, 
message.toString());
             }
-            s_logger.info(ex);
-            s_logger.info(message.toString(), ex);
-            throw new 
ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, 
message.toString());
+        } else {
+            s_logger.info("VM " + getEntityUuid() + " already created, load 
UserVm from DB");
+            result = 
_userVmService.finalizeCreateVirtualMachine(getEntityId());
         }
 
         if (result != null) {
diff --git 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index d3ba95061b6..cf188cbf58d 100755
--- 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -500,22 +500,29 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
 
             allocateRootVolume(persistedVm, template, rootDiskOfferingInfo, 
owner, rootDiskSizeFinal);
 
-            if (dataDiskOfferings != null) {
-                for (final DiskOfferingInfo dataDiskOfferingInfo : 
dataDiskOfferings) {
-                    volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + 
persistedVm.getId(), dataDiskOfferingInfo.getDiskOffering(), 
dataDiskOfferingInfo.getSize(),
-                            dataDiskOfferingInfo.getMinIops(), 
dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, null);
+            // Create new Volume context and inject event resource type, id 
and details to generate VOLUME.CREATE event for the ROOT disk.
+            CallContext volumeContext = 
CallContext.register(CallContext.current(), ApiCommandResourceType.Volume);
+            try {
+                if (dataDiskOfferings != null) {
+                    for (final DiskOfferingInfo dataDiskOfferingInfo : 
dataDiskOfferings) {
+                        volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + 
persistedVm.getId(), dataDiskOfferingInfo.getDiskOffering(), 
dataDiskOfferingInfo.getSize(),
+                                dataDiskOfferingInfo.getMinIops(), 
dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, null);
+                    }
                 }
-            }
-            if (datadiskTemplateToDiskOfferingMap != null && 
!datadiskTemplateToDiskOfferingMap.isEmpty()) {
-                int diskNumber = 1;
-                for (Entry<Long, DiskOffering> 
dataDiskTemplateToDiskOfferingMap : 
datadiskTemplateToDiskOfferingMap.entrySet()) {
-                    DiskOffering diskOffering = 
dataDiskTemplateToDiskOfferingMap.getValue();
-                    long diskOfferingSize = diskOffering.getDiskSize() / (1024 
* 1024 * 1024);
-                    VMTemplateVO dataDiskTemplate = 
_templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey());
-                    volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + 
persistedVm.getId() + "-" + String.valueOf(diskNumber), diskOffering, 
diskOfferingSize, null, null,
-                            persistedVm, dataDiskTemplate, owner, 
Long.valueOf(diskNumber));
-                    diskNumber++;
+                if (datadiskTemplateToDiskOfferingMap != null && 
!datadiskTemplateToDiskOfferingMap.isEmpty()) {
+                    int diskNumber = 1;
+                    for (Entry<Long, DiskOffering> 
dataDiskTemplateToDiskOfferingMap : 
datadiskTemplateToDiskOfferingMap.entrySet()) {
+                        DiskOffering diskOffering = 
dataDiskTemplateToDiskOfferingMap.getValue();
+                        long diskOfferingSize = diskOffering.getDiskSize() / 
(1024 * 1024 * 1024);
+                        VMTemplateVO dataDiskTemplate = 
_templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey());
+                        volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + 
persistedVm.getId() + "-" + String.valueOf(diskNumber), diskOffering, 
diskOfferingSize, null, null,
+                                persistedVm, dataDiskTemplate, owner, 
Long.valueOf(diskNumber));
+                        diskNumber++;
+                    }
                 }
+            } finally {
+                // Remove volumeContext and pop vmContext back
+                CallContext.unregister();
             }
 
             if (s_logger.isDebugEnabled()) {
diff --git 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 2aa997e13aa..f7c8c9c70bf 100644
--- 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -819,7 +819,7 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
                 vol.getTemplateId());
     }
 
-    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription 
= "creating ROOT volume", create = true)
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription 
= "creating volume", create = true)
     @Override
     public DiskProfile allocateRawVolume(Type type, String name, DiskOffering 
offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, 
VirtualMachineTemplate template, Account owner,
                                          Long deviceId) {
@@ -1035,13 +1035,13 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
     private void updateRootDiskVolumeEventDetails(Type type, VirtualMachine 
vm, List<DiskProfile> diskProfiles) {
         CallContext callContext = CallContext.current();
         // Update only for volume type ROOT and API command resource type 
Volume
-        if (type == Type.ROOT && callContext != null && 
callContext.getEventResourceType() == ApiCommandResourceType.Volume) {
+        if ((type == Type.ROOT || type == Type.DATADISK) && callContext != 
null && callContext.getEventResourceType() == ApiCommandResourceType.Volume) {
             List<Long> volumeIds = 
diskProfiles.stream().map(DiskProfile::getVolumeId).filter(volumeId -> volumeId 
!= null).collect(Collectors.toList());
             if (!volumeIds.isEmpty()) {
                 callContext.setEventResourceId(volumeIds.get(0));
             }
             String volumeUuids = volumeIds.stream().map(volumeId -> 
this._uuidMgr.getUuid(Volume.class, volumeId)).collect(Collectors.joining(", 
"));
-            callContext.setEventDetails("Volume Id: " + volumeUuids + " Vm Id: 
" + this._uuidMgr.getUuid(VirtualMachine.class, vm.getId()));
+            callContext.setEventDetails("Volume Type: " + type + "Volume Id: " 
+ volumeUuids + " Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, 
vm.getId()));
         }
     }
 
@@ -1245,7 +1245,7 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
                         // Destroy volume if not already destroyed
                         boolean volumeAlreadyDestroyed = (vol.getState() == 
Volume.State.Destroy || vol.getState() == Volume.State.Expunged || 
vol.getState() == Volume.State.Expunging);
                         if (!volumeAlreadyDestroyed) {
-                            volService.destroyVolume(vol.getId());
+                            destroyVolumeInContext(vol);
                         } else {
                             s_logger.debug(String.format("Skipping destroy for 
the volume [%s] as it is in [%s] state.", volumeToString, 
vol.getState().toString()));
                         }
@@ -1277,6 +1277,21 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
         }
     }
 
+    private void destroyVolumeInContext(Volume volume) {
+        // Create new context and inject correct event resource type, id and 
details,
+        // otherwise VOLUME.DESTROY event will be associated with 
VirtualMachine and contain VM id and other information.
+        CallContext volumeContext = 
CallContext.register(CallContext.current(), ApiCommandResourceType.Volume);
+        volumeContext.setEventDetails("Volume Type: " + volume.getVolumeType() 
+ " Volume Id: " +  volume.getUuid() + " Vm Id: " + 
_uuidMgr.getUuid(VirtualMachine.class, volume.getInstanceId()));
+        volumeContext.setEventResourceType(ApiCommandResourceType.Volume);
+        volumeContext.setEventResourceId(volume.getId());
+        try {
+            _volumeApiService.destroyVolume(volume.getId());
+        } finally {
+            // Remove volumeContext and pop vmContext back
+            CallContext.unregister();
+        }
+    }
+
     @Override
     public void revokeAccess(DataObject dataObject, Host host, DataStore 
dataStore) {
         DataStoreDriver dataStoreDriver = dataStore != null ? 
dataStore.getDriver() : null;
@@ -2080,7 +2095,7 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
                 
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), 
ResourceType.volume, volume.isDisplay());
                 
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), 
ResourceType.primary_storage, volume.isDisplay(), new Long(volume.getSize()));
             } else {
-                volService.destroyVolume(volume.getId());
+                destroyVolumeInContext(volume);
             }
             // FIXME - All this is boiler plate code and should be done as 
part of state transition. This shouldn't be part of orchestrator.
             // publish usage event for the volume
diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java 
b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
index 5aad6afcdbb..f1e370de33d 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -721,6 +721,10 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
         ListProjectResourcesCriteria listProjectResourcesCriteria = 
domainIdRecursiveListProject.third();
 
         Filter searchFilter = new Filter(EventJoinVO.class, "createDate", 
false, cmd.getStartIndex(), cmd.getPageSizeVal());
+        // additional order by since createdDate does not have milliseconds
+        // and two events, created within one second can be incorrectly 
ordered (for example VM.CREATE Completed before Scheduled)
+        searchFilter.addOrderBy(EventJoinVO.class, "id", false);
+
         SearchBuilder<EventJoinVO> sb = _eventJoinDao.createSearchBuilder();
         _accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, 
permittedAccounts, listProjectResourcesCriteria);
 
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java 
b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index e39fac147e7..e03942edc59 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -1728,6 +1728,12 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         return volume;
     }
 
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DESTROY, eventDescription 
= "destroying a volume")
+    public void destroyVolume(long volumeId) {
+        volService.destroyVolume(volumeId);
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_RECOVER, eventDescription 
= "recovering a volume in Destroy state")
     public Volume recoverVolume(long volumeId) {
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 7ec3450acb1..ecf60556db6 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -3272,7 +3272,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
 
         autoScaleManager.removeVmFromVmGroup(vmId);
 
-        deleteVolumesFromVm(volumesToBeDeleted, expunge);
+        deleteVolumesFromVm(vm, volumesToBeDeleted, expunge);
 
         if (getDestroyRootVolumeOnVmDestruction(vm.getDomainId())) {
             VolumeVO rootVolume = _volsDao.getInstanceRootVolume(vm.getId());
@@ -3716,6 +3716,19 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap, 
dynamicScalingEnabled, vmType, overrideDiskOfferingId);
     }
 
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = 
"deploying Vm")
+    public UserVm finalizeCreateVirtualMachine(long vmId) {
+        s_logger.info("Loading UserVm " + vmId + " from DB");
+        UserVm userVm = getUserVm(vmId);
+        if (userVm == null) {
+            s_logger.info("Loaded UserVm " + vmId + " (" + userVm.getUuid() + 
") from DB");
+        } else {
+            s_logger.warn("UserVm " + vmId + " does not exist in DB");
+        }
+        return userVm;
+    }
+
     private NetworkVO getNetworkToAddToNetworkList(VirtualMachineTemplate 
template, Account owner, HypervisorType hypervisor,
             List<HypervisorType> vpcSupportedHTypes, Long networkId) {
         NetworkVO network = _networkDao.findById(networkId);
@@ -8002,7 +8015,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             // Create new context and inject correct event resource type, id 
and details,
             // otherwise VOLUME.DETACH event will be associated with 
VirtualMachine and contain VM id and other information.
             CallContext volumeContext = 
CallContext.register(CallContext.current(), ApiCommandResourceType.Volume);
-            volumeContext.setEventDetails("Volume Id: " + 
this._uuidMgr.getUuid(Volume.class, volume.getId()) + " Vm Id: " + 
this._uuidMgr.getUuid(VirtualMachine.class, volume.getInstanceId()));
+            volumeContext.setEventDetails("Volume Type: " + 
volume.getVolumeType() + " Volume Id: " + this._uuidMgr.getUuid(Volume.class, 
volume.getId()) + " Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, 
volume.getInstanceId()));
             volumeContext.setEventResourceType(ApiCommandResourceType.Volume);
             volumeContext.setEventResourceId(volume.getId());
 
@@ -8020,15 +8033,29 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         }
     }
 
-    private void deleteVolumesFromVm(List<VolumeVO> volumes, boolean expunge) {
+    private void deleteVolumesFromVm(UserVmVO vm, List<VolumeVO> volumes, 
boolean expunge) {
 
         for (VolumeVO volume : volumes) {
+            destroyVolumeInContext(vm, expunge, volume);
+        }
+    }
 
+    private void destroyVolumeInContext(UserVmVO vm, boolean expunge, VolumeVO 
volume) {
+        // Create new context and inject correct event resource type, id and 
details,
+        // otherwise VOLUME.DESTROY event will be associated with 
VirtualMachine and contain VM id and other information.
+        CallContext volumeContext = 
CallContext.register(CallContext.current(), ApiCommandResourceType.Volume);
+        volumeContext.setEventDetails("Volume Type: " + volume.getVolumeType() 
+ " Volume Id: " + this._uuidMgr.getUuid(Volume.class, volume.getId()) + " Vm 
Id: " + vm.getUuid());
+        volumeContext.setEventResourceType(ApiCommandResourceType.Volume);
+        volumeContext.setEventResourceId(volume.getId());
+        try {
             Volume result = _volumeService.destroyVolume(volume.getId(), 
CallContext.current().getCallingAccount(), expunge, false);
 
             if (result == null) {
-                s_logger.error("DestroyVM remove volume - failed to delete 
volume " + volume.getInstanceId() + " from instance " + volume.getId());
+                s_logger.error(String.format("DestroyVM remove volume - failed 
to delete volume %s from instance %s", volume.getId(), volume.getInstanceId()));
             }
+        } finally {
+            // Remove volumeContext and pop vmContext back
+            CallContext.unregister();
         }
     }
 
diff --git a/test/integration/smoke/test_events_resource.py 
b/test/integration/smoke/test_events_resource.py
index 4d6872af57e..660cbd37bce 100644
--- a/test/integration/smoke/test_events_resource.py
+++ b/test/integration/smoke/test_events_resource.py
@@ -16,6 +16,7 @@
 # under the License.
 """ BVT tests for Events Resource
 """
+import json
 import os
 import tempfile
 import time
@@ -184,7 +185,7 @@ class TestEventsResource(cloudstackTestCase):
         for event in events:
             if event.type.startswith("VM.") or 
(event.type.startswith("NETWORK.") and not 
event.type.startswith("NETWORK.ELEMENT")) or event.type.startswith("VOLUME.") 
or event.type.startswith("ACCOUNT.") or event.type.startswith("DOMAIN.") or 
event.type.startswith("TEMPLATE."):
                 if event.resourceid is None or event.resourcetype is None:
-                    self.debug("Failed event:: %" % event)
+                    self.debug("Failed event:: %s" % json.dumps(event, 
indent=2))
                     self.fail("resourceid or resourcetype for the event not 
found!")
                 else:
                     self.debug("Event %s at %s:: Resource Type: %s, Resource 
ID: %s" % (event.type, event.created, event.resourcetype, event.resourceid))

Reply via email to