mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	Display VM snapshot tags on usage records (#3560)
* Refactor usage helper tables to include VM snapshot id * Fix resource type and resource id while listing usage records * Add defensive checks
This commit is contained in:
		
							parent
							
								
									7ac9f00eee
								
							
						
					
					
						commit
						bfc08715cc
					
				| @ -25,6 +25,7 @@ import java.util.Map; | |||||||
| import javax.annotation.PostConstruct; | import javax.annotation.PostConstruct; | ||||||
| import javax.inject.Inject; | import javax.inject.Inject; | ||||||
| 
 | 
 | ||||||
|  | import org.apache.commons.collections.MapUtils; | ||||||
| import org.apache.log4j.Logger; | import org.apache.log4j.Logger; | ||||||
| import org.springframework.beans.factory.NoSuchBeanDefinitionException; | import org.springframework.beans.factory.NoSuchBeanDefinitionException; | ||||||
| 
 | 
 | ||||||
| @ -69,6 +70,12 @@ public class UsageEventUtils { | |||||||
|         s_configDao = configDao; |         s_configDao = configDao; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, | ||||||
|  |                                          Long size, String entityType, String entityUUID, Map<String, String> details) { | ||||||
|  |         saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, details); | ||||||
|  |         publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, |     public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, | ||||||
|         Long size, String entityType, String entityUUID) { |         Long size, String entityType, String entityUUID) { | ||||||
|         saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size); |         saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size); | ||||||
| @ -84,6 +91,12 @@ public class UsageEventUtils { | |||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, | ||||||
|  |                                          Long size, Long virtualSize, String entityType, String entityUUID, Map<String, String> details) { | ||||||
|  |         saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, virtualSize, details); | ||||||
|  |         publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, |     public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, | ||||||
|         Long size, Long virtualSize, String entityType, String entityUUID) { |         Long size, Long virtualSize, String entityType, String entityUUID) { | ||||||
|         saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, virtualSize); |         saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, virtualSize); | ||||||
| @ -142,11 +155,28 @@ public class UsageEventUtils { | |||||||
|         s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size)); |         s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, Long size, Map<String, String> details) { | ||||||
|  |         UsageEventVO usageEventVO = new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size); | ||||||
|  |         s_usageEventDao.persist(usageEventVO); | ||||||
|  |         if (MapUtils.isNotEmpty(details)) { | ||||||
|  |             s_usageEventDao.saveDetails(usageEventVO.getId(), details); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, Long size, |     public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, Long size, | ||||||
|         Long virtualSize) { |         Long virtualSize) { | ||||||
|         s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, virtualSize)); |         s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, virtualSize)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, Long size, | ||||||
|  |                                       Long virtualSize, Map<String, String> details) { | ||||||
|  |         UsageEventVO usageEventVO = new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size, virtualSize); | ||||||
|  |         s_usageEventDao.persist(usageEventVO); | ||||||
|  |         if (MapUtils.isNotEmpty(details)) { | ||||||
|  |             s_usageEventDao.saveDetails(usageEventVO.getId(), details); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName) { |     public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName) { | ||||||
|         s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName)); |         s_usageEventDao.persist(new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName)); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ import com.cloud.utils.db.GenericDao; | |||||||
| @Table(name = "usage_event") | @Table(name = "usage_event") | ||||||
| public class UsageEventVO implements UsageEvent { | public class UsageEventVO implements UsageEvent { | ||||||
|     public enum DynamicParameters { |     public enum DynamicParameters { | ||||||
|         cpuSpeed, cpuNumber, memory |         cpuSpeed, cpuNumber, memory, vmSnapshotId | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     @Id |     @Id | ||||||
|  | |||||||
| @ -66,6 +66,9 @@ public class UsageSnapshotOnPrimaryVO implements InternalIdentity { | |||||||
|     @Column(name = "virtualsize") |     @Column(name = "virtualsize") | ||||||
|     private Long virtualSize; |     private Long virtualSize; | ||||||
| 
 | 
 | ||||||
|  |     @Column(name = "vm_snapshot_id") | ||||||
|  |     private Long vmSnapshotId; | ||||||
|  | 
 | ||||||
|     protected UsageSnapshotOnPrimaryVO() { |     protected UsageSnapshotOnPrimaryVO() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -136,10 +139,18 @@ public class UsageSnapshotOnPrimaryVO implements InternalIdentity { | |||||||
|         return this.id; |         return this.id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public Long getVmSnapshotId() { | ||||||
|  |         return vmSnapshotId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setVmSnapshotId(Long vmSnapshotId) { | ||||||
|  |         this.vmSnapshotId = vmSnapshotId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         return "UsageSnapshotOnPrimaryVO [id=" + id + ", zoneId=" + zoneId + ", accountId=" + accountId + ", domainId=" + domainId + ", vmId=" + vmId + ", name=" + name |         return "UsageSnapshotOnPrimaryVO [id=" + id + ", zoneId=" + zoneId + ", accountId=" + accountId + ", domainId=" + domainId + ", vmId=" + vmId + ", name=" + name | ||||||
|                 + ", snapshotType=" + snapshotType + ", physicalSize=" + physicalSize + ", created=" + created + ", deleted=" + deleted + ", virtualSize=" + virtualSize + "]"; |                 + ", snapshotType=" + snapshotType + ", physicalSize=" + physicalSize + ", created=" + created + ", deleted=" + deleted + ", virtualSize=" + virtualSize + ", vmSnapshotId=" + vmSnapshotId + "]"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -60,6 +60,9 @@ public class UsageVMSnapshotVO implements InternalIdentity { | |||||||
|     @Temporal(value = TemporalType.TIMESTAMP) |     @Temporal(value = TemporalType.TIMESTAMP) | ||||||
|     private Date processed; |     private Date processed; | ||||||
| 
 | 
 | ||||||
|  |     @Column(name = "vm_snapshot_id") | ||||||
|  |     private Long vmSnapshotId; | ||||||
|  | 
 | ||||||
|     protected UsageVMSnapshotVO() { |     protected UsageVMSnapshotVO() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -120,10 +123,18 @@ public class UsageVMSnapshotVO implements InternalIdentity { | |||||||
|         return this.id; |         return this.id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public Long getVmSnapshotId() { | ||||||
|  |         return vmSnapshotId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setVmSnapshotId(Long vmSnapshotId) { | ||||||
|  |         this.vmSnapshotId = vmSnapshotId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         return "UsageVMSnapshotVO [id=" + id + ", zoneId=" + zoneId + ", accountId=" + accountId + ", domainId=" + domainId + ", vmId=" + vmId + ", diskOfferingId=" |         return "UsageVMSnapshotVO [id=" + id + ", zoneId=" + zoneId + ", accountId=" + accountId + ", domainId=" + domainId + ", vmId=" + vmId + ", diskOfferingId=" | ||||||
|                 + diskOfferingId + ", size=" + size + ", created=" + created + ", processed=" + processed + "]"; |                 + diskOfferingId + ", size=" + size + ", created=" + created + ", processed=" + processed + ", vmSnapshotId=" + vmSnapshotId + "]"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ import java.util.List; | |||||||
| import java.util.TimeZone; | import java.util.TimeZone; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | import org.apache.commons.lang.StringUtils; | ||||||
| import org.apache.log4j.Logger; | import org.apache.log4j.Logger; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| @ -36,12 +37,12 @@ import com.cloud.utils.db.TransactionLegacy; | |||||||
| @Component | @Component | ||||||
| public class UsageVMSnapshotDaoImpl extends GenericDaoBase<UsageVMSnapshotVO, Long> implements UsageVMSnapshotDao { | public class UsageVMSnapshotDaoImpl extends GenericDaoBase<UsageVMSnapshotVO, Long> implements UsageVMSnapshotDao { | ||||||
|     public static final Logger s_logger = Logger.getLogger(UsageVMSnapshotDaoImpl.class.getName()); |     public static final Logger s_logger = Logger.getLogger(UsageVMSnapshotDaoImpl.class.getName()); | ||||||
|     protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, disk_offering_id, size, created, processed " |     protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, disk_offering_id, size, created, processed, vm_snapshot_id " | ||||||
|         + " FROM usage_vmsnapshot" + " WHERE account_id = ? " + " AND ( (created BETWEEN ? AND ?) OR " |         + " FROM usage_vmsnapshot" + " WHERE account_id = ? " + " AND ( (created BETWEEN ? AND ?) OR " | ||||||
|         + "      (created < ? AND processed is NULL) ) ORDER BY created asc"; |         + "      (created < ? AND processed is NULL) ) ORDER BY created asc"; | ||||||
|     protected static final String UPDATE_DELETED = "UPDATE usage_vmsnapshot SET processed = ? WHERE account_id = ? AND id = ? and vm_id = ?  and created = ?"; |     protected static final String UPDATE_DELETED = "UPDATE usage_vmsnapshot SET processed = ? WHERE account_id = ? AND id = ? and vm_id = ?  and created = ?"; | ||||||
| 
 | 
 | ||||||
|     protected static final String PREVIOUS_QUERY = "SELECT id, zone_id, account_id, domain_id, vm_id, disk_offering_id,size, created, processed " |     protected static final String PREVIOUS_QUERY = "SELECT id, zone_id, account_id, domain_id, vm_id, disk_offering_id,size, created, processed, vm_snapshot_id " | ||||||
|         + "FROM usage_vmsnapshot " + "WHERE account_id = ? AND id = ? AND vm_id = ? AND created < ? AND processed IS NULL " + "ORDER BY created desc limit 1"; |         + "FROM usage_vmsnapshot " + "WHERE account_id = ? AND id = ? AND vm_id = ? AND created < ? AND processed IS NULL " + "ORDER BY created desc limit 1"; | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @ -99,6 +100,8 @@ public class UsageVMSnapshotDaoImpl extends GenericDaoBase<UsageVMSnapshotVO, Lo | |||||||
|                 Date processDate = null; |                 Date processDate = null; | ||||||
|                 String createdTS = rs.getString(8); |                 String createdTS = rs.getString(8); | ||||||
|                 String processed = rs.getString(9); |                 String processed = rs.getString(9); | ||||||
|  |                 String snapId = rs.getString(10); | ||||||
|  |                 Long vmSnapshotId = StringUtils.isNotBlank(snapId) ? Long.valueOf(snapId) : null; | ||||||
| 
 | 
 | ||||||
|                 if (createdTS != null) { |                 if (createdTS != null) { | ||||||
|                     createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); |                     createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); | ||||||
| @ -106,7 +109,9 @@ public class UsageVMSnapshotDaoImpl extends GenericDaoBase<UsageVMSnapshotVO, Lo | |||||||
|                 if (processed != null) { |                 if (processed != null) { | ||||||
|                     processDate = DateUtil.parseDateString(s_gmtTimeZone, processed); |                     processDate = DateUtil.parseDateString(s_gmtTimeZone, processed); | ||||||
|                 } |                 } | ||||||
|                 usageRecords.add(new UsageVMSnapshotVO(vId, zoneId, acctId, dId, vmId, doId, size, createdDate, processDate)); |                 UsageVMSnapshotVO usageVMSnapshotVO = new UsageVMSnapshotVO(vId, zoneId, acctId, dId, vmId, doId, size, createdDate, processDate); | ||||||
|  |                 usageVMSnapshotVO.setVmSnapshotId(vmSnapshotId); | ||||||
|  |                 usageRecords.add(usageVMSnapshotVO); | ||||||
|             } |             } | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             txn.rollback(); |             txn.rollback(); | ||||||
| @ -150,6 +155,8 @@ public class UsageVMSnapshotDaoImpl extends GenericDaoBase<UsageVMSnapshotVO, Lo | |||||||
|                 Date processDate = null; |                 Date processDate = null; | ||||||
|                 String createdTS = rs.getString(8); |                 String createdTS = rs.getString(8); | ||||||
|                 String processed = rs.getString(9); |                 String processed = rs.getString(9); | ||||||
|  |                 String snapId = rs.getString(10); | ||||||
|  |                 Long vmSnapshotId = StringUtils.isNotBlank(snapId) ? Long.valueOf(snapId) : null; | ||||||
| 
 | 
 | ||||||
|                 if (createdTS != null) { |                 if (createdTS != null) { | ||||||
|                     createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); |                     createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); | ||||||
| @ -157,7 +164,9 @@ public class UsageVMSnapshotDaoImpl extends GenericDaoBase<UsageVMSnapshotVO, Lo | |||||||
|                 if (processed != null) { |                 if (processed != null) { | ||||||
|                     processDate = DateUtil.parseDateString(s_gmtTimeZone, processed); |                     processDate = DateUtil.parseDateString(s_gmtTimeZone, processed); | ||||||
|                 } |                 } | ||||||
|                 usageRecords.add(new UsageVMSnapshotVO(vId, zoneId, acctId, dId, vmId, doId, size, createdDate, processDate)); |                 UsageVMSnapshotVO usageVMSnapshotVO = new UsageVMSnapshotVO(vId, zoneId, acctId, dId, vmId, doId, size, createdDate, processDate); | ||||||
|  |                 usageVMSnapshotVO.setVmSnapshotId(vmSnapshotId); | ||||||
|  |                 usageRecords.add(usageVMSnapshotVO); | ||||||
|             } |             } | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             txn.rollback(); |             txn.rollback(); | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ import java.util.List; | |||||||
| import java.util.TimeZone; | import java.util.TimeZone; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | import org.apache.commons.lang.StringUtils; | ||||||
| import org.apache.log4j.Logger; | import org.apache.log4j.Logger; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| @ -36,7 +37,7 @@ import com.cloud.utils.db.TransactionLegacy; | |||||||
| @Component | @Component | ||||||
| public class UsageVMSnapshotOnPrimaryDaoImpl extends GenericDaoBase<UsageSnapshotOnPrimaryVO, Long> implements UsageVMSnapshotOnPrimaryDao { | public class UsageVMSnapshotOnPrimaryDaoImpl extends GenericDaoBase<UsageSnapshotOnPrimaryVO, Long> implements UsageVMSnapshotOnPrimaryDao { | ||||||
|     public static final Logger s_logger = Logger.getLogger(UsageVMSnapshotOnPrimaryDaoImpl.class.getName()); |     public static final Logger s_logger = Logger.getLogger(UsageVMSnapshotOnPrimaryDaoImpl.class.getName()); | ||||||
|     protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, name, type, physicalsize, virtualsize, created, deleted " |     protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, name, type, physicalsize, virtualsize, created, deleted, vm_snapshot_id " | ||||||
|         + " FROM usage_snapshot_on_primary" + " WHERE account_id = ? " + " AND ( (created < ? AND deleted is NULL)" |         + " FROM usage_snapshot_on_primary" + " WHERE account_id = ? " + " AND ( (created < ? AND deleted is NULL)" | ||||||
|         + "     OR ( deleted BETWEEN ? AND ?)) ORDER BY created asc"; |         + "     OR ( deleted BETWEEN ? AND ?)) ORDER BY created asc"; | ||||||
|     protected static final String UPDATE_DELETED = "UPDATE usage_snapshot_on_primary SET deleted = ? WHERE account_id = ? AND id = ? and vm_id = ?  and created = ?"; |     protected static final String UPDATE_DELETED = "UPDATE usage_snapshot_on_primary SET deleted = ? WHERE account_id = ? AND id = ? and vm_id = ?  and created = ?"; | ||||||
| @ -95,6 +96,8 @@ public class UsageVMSnapshotOnPrimaryDaoImpl extends GenericDaoBase<UsageSnapsho | |||||||
|                 Date deleteDate = null; |                 Date deleteDate = null; | ||||||
|                 String createdTS = rs.getString(10); |                 String createdTS = rs.getString(10); | ||||||
|                 String deleted = rs.getString(11); |                 String deleted = rs.getString(11); | ||||||
|  |                 String snapId = rs.getString(12); | ||||||
|  |                 Long vmSnapshotId = StringUtils.isNotBlank(snapId) ? Long.valueOf(snapId) : null; | ||||||
| 
 | 
 | ||||||
|                 if (createdTS != null) { |                 if (createdTS != null) { | ||||||
|                     createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); |                     createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); | ||||||
| @ -102,7 +105,9 @@ public class UsageVMSnapshotOnPrimaryDaoImpl extends GenericDaoBase<UsageSnapsho | |||||||
|                 if (deleted != null) { |                 if (deleted != null) { | ||||||
|                     deleteDate = DateUtil.parseDateString(s_gmtTimeZone, deleted); |                     deleteDate = DateUtil.parseDateString(s_gmtTimeZone, deleted); | ||||||
|                 } |                 } | ||||||
|                 usageRecords.add(new UsageSnapshotOnPrimaryVO(vId, zoneId, acctId, dId, vmId, name, type, virtaulSize, physicalSize, createdDate, deleteDate)); |                 UsageSnapshotOnPrimaryVO usageSnapshotOnPrimaryVO = new UsageSnapshotOnPrimaryVO(vId, zoneId, acctId, dId, vmId, name, type, virtaulSize, physicalSize, createdDate, deleteDate); | ||||||
|  |                 usageSnapshotOnPrimaryVO.setVmSnapshotId(vmSnapshotId); | ||||||
|  |                 usageRecords.add(usageSnapshotOnPrimaryVO); | ||||||
|             } |             } | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             txn.rollback(); |             txn.rollback(); | ||||||
|  | |||||||
| @ -531,3 +531,7 @@ CREATE TABLE `cloud`.`template_ovf_properties` ( | |||||||
|   CONSTRAINT `fk_template_ovf_properties__template_id` FOREIGN KEY (`template_id`) REFERENCES `vm_template`(`id`) |   CONSTRAINT `fk_template_ovf_properties__template_id` FOREIGN KEY (`template_id`) REFERENCES `vm_template`(`id`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ||||||
| 
 | 
 | ||||||
|  | -- Add VM snapshot ID on usage helper tables | ||||||
|  | ALTER TABLE `cloud_usage`.`usage_vmsnapshot` ADD COLUMN `vm_snapshot_id` BIGINT(20) NULL DEFAULT NULL AFTER `processed`; | ||||||
|  | ALTER TABLE `cloud_usage`.`usage_snapshot_on_primary` ADD COLUMN `vm_snapshot_id` BIGINT(20) NULL DEFAULT NULL AFTER `deleted`; | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -18,12 +18,14 @@ | |||||||
|  */ |  */ | ||||||
| package org.apache.cloudstack.storage.vmsnapshot; | package org.apache.cloudstack.storage.vmsnapshot; | ||||||
| 
 | 
 | ||||||
|  | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
| import javax.inject.Inject; | import javax.inject.Inject; | ||||||
| import javax.naming.ConfigurationException; | import javax.naming.ConfigurationException; | ||||||
| 
 | 
 | ||||||
|  | import com.cloud.event.UsageEventVO; | ||||||
| import org.apache.log4j.Logger; | import org.apache.log4j.Logger; | ||||||
| 
 | 
 | ||||||
| import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; | import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; | ||||||
| @ -337,14 +339,22 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot | |||||||
|                 offeringId = offering.getId(); |                 offeringId = offering.getId(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         Map<String, String> details = new HashMap<>(); | ||||||
|  |         if (vmSnapshot != null) { | ||||||
|  |             details.put(UsageEventVO.DynamicParameters.vmSnapshotId.name(), String.valueOf(vmSnapshot.getId())); | ||||||
|  |         } | ||||||
|         UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(), vmSnapshot.getName(), offeringId, volume.getId(), // save volume's id into templateId field |         UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(), vmSnapshot.getName(), offeringId, volume.getId(), // save volume's id into templateId field | ||||||
|                 volumeTo.getSize(), VMSnapshot.class.getName(), vmSnapshot.getUuid()); |                 volumeTo.getSize(), VMSnapshot.class.getName(), vmSnapshot.getUuid(), details); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm, Long vmSnapSize, Long virtualSize) { |     private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm, Long vmSnapSize, Long virtualSize) { | ||||||
|         try { |         try { | ||||||
|  |             Map<String, String> details = new HashMap<>(); | ||||||
|  |             if (vmSnapshot != null) { | ||||||
|  |                 details.put(UsageEventVO.DynamicParameters.vmSnapshotId.name(), String.valueOf(vmSnapshot.getId())); | ||||||
|  |             } | ||||||
|             UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(), vmSnapshot.getName(), 0L, 0L, vmSnapSize, virtualSize, |             UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(), vmSnapshot.getName(), 0L, 0L, vmSnapSize, virtualSize, | ||||||
|                     VMSnapshot.class.getName(), vmSnapshot.getUuid()); |                     VMSnapshot.class.getName(), vmSnapshot.getUuid(), details); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             s_logger.error("Failed to publis usage event " + type, e); |             s_logger.error("Failed to publis usage event " + type, e); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -31,6 +31,8 @@ import java.util.stream.Collectors; | |||||||
| 
 | 
 | ||||||
| import javax.inject.Inject; | import javax.inject.Inject; | ||||||
| 
 | 
 | ||||||
|  | import com.cloud.vm.snapshot.VMSnapshotVO; | ||||||
|  | import com.cloud.vm.snapshot.dao.VMSnapshotDao; | ||||||
| import org.apache.cloudstack.acl.ControlledEntity; | import org.apache.cloudstack.acl.ControlledEntity; | ||||||
| import org.apache.cloudstack.acl.ControlledEntity.ACLType; | import org.apache.cloudstack.acl.ControlledEntity.ACLType; | ||||||
| import org.apache.cloudstack.affinity.AffinityGroup; | import org.apache.cloudstack.affinity.AffinityGroup; | ||||||
| @ -372,6 +374,8 @@ public class ApiResponseHelper implements ResponseGenerator { | |||||||
|     private IPAddressDao userIpAddressDao; |     private IPAddressDao userIpAddressDao; | ||||||
|     @Inject |     @Inject | ||||||
|     NetworkDetailsDao networkDetailsDao; |     NetworkDetailsDao networkDetailsDao; | ||||||
|  |     @Inject | ||||||
|  |     private VMSnapshotDao vmSnapshotDao; | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public UserResponse createUserResponse(User user) { |     public UserResponse createUserResponse(User user) { | ||||||
| @ -3643,24 +3647,35 @@ public class ApiResponseHelper implements ResponseGenerator { | |||||||
|                 usageRecResponse.setDescription(builder.toString()); |                 usageRecResponse.setDescription(builder.toString()); | ||||||
|             } |             } | ||||||
|         } else if (usageRecord.getUsageType() == UsageTypes.VM_SNAPSHOT) { |         } else if (usageRecord.getUsageType() == UsageTypes.VM_SNAPSHOT) { | ||||||
|             resourceType = ResourceObjectType.UserVm; |             resourceType = ResourceObjectType.VMSnapshot; | ||||||
|             if (vmInstance != null) { |             VMSnapshotVO vmSnapshotVO = null; | ||||||
|                 resourceId = vmInstance.getId(); |             if (usageRecord.getUsageId() != null) { | ||||||
|                 usageRecResponse.setResourceName(vmInstance.getInstanceName()); |                 vmSnapshotVO = vmSnapshotDao.findByIdIncludingRemoved(usageRecord.getUsageId()); | ||||||
|                 usageRecResponse.setUsageId(vmInstance.getUuid()); |                 if (vmSnapshotVO != null) { | ||||||
|  |                     resourceId = vmSnapshotVO.getId(); | ||||||
|  |                     usageRecResponse.setResourceName(vmSnapshotVO.getDisplayName()); | ||||||
|  |                     usageRecResponse.setUsageId(vmSnapshotVO.getUuid()); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             usageRecResponse.setSize(usageRecord.getSize()); |             usageRecResponse.setSize(usageRecord.getSize()); | ||||||
|             if (usageRecord.getOfferingId() != null) { |             if (usageRecord.getOfferingId() != null) { | ||||||
|                 usageRecResponse.setOfferingId(usageRecord.getOfferingId().toString()); |                 usageRecResponse.setOfferingId(usageRecord.getOfferingId().toString()); | ||||||
|             } |             } | ||||||
|             if (!oldFormat) { |             if (!oldFormat) { | ||||||
|                 VolumeVO volume = _entityMgr.findByIdIncludingRemoved(VolumeVO.class, usageRecord.getUsageId().toString()); |                 VolumeVO volume = null; | ||||||
|  |                 if (vmSnapshotVO == null && usageRecord.getUsageId() != null) { | ||||||
|  |                      volume = _entityMgr.findByIdIncludingRemoved(VolumeVO.class, usageRecord.getUsageId().toString()); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 DiskOfferingVO diskOff = null; |                 DiskOfferingVO diskOff = null; | ||||||
|                 if (usageRecord.getOfferingId() != null) { |                 if (usageRecord.getOfferingId() != null) { | ||||||
|                     diskOff = _entityMgr.findByIdIncludingRemoved(DiskOfferingVO.class, usageRecord.getOfferingId()); |                     diskOff = _entityMgr.findByIdIncludingRemoved(DiskOfferingVO.class, usageRecord.getOfferingId()); | ||||||
|                 } |                 } | ||||||
|                 final StringBuilder builder = new StringBuilder(); |                 final StringBuilder builder = new StringBuilder(); | ||||||
|                 builder.append("VMSnapshot usage"); |                 builder.append("VMSnapshot usage"); | ||||||
|  |                 if (vmSnapshotVO != null) { | ||||||
|  |                     builder.append(" Id: ").append(vmSnapshotVO.getId()).append(" (").append(vmSnapshotVO.getUuid()).append(") "); | ||||||
|  |                 } | ||||||
|                 if (vmInstance != null) { |                 if (vmInstance != null) { | ||||||
|                     builder.append(" for VM ").append(vmInstance.getHostName()).append(" (").append(vmInstance.getUuid()).append(")"); |                     builder.append(" for VM ").append(vmInstance.getHostName()).append(" (").append(vmInstance.getUuid()).append(")"); | ||||||
|                 } |                 } | ||||||
| @ -3684,10 +3699,23 @@ public class ApiResponseHelper implements ResponseGenerator { | |||||||
|                 usageRecResponse.setDescription(builder.toString()); |                 usageRecResponse.setDescription(builder.toString()); | ||||||
|             } |             } | ||||||
|         } else if (usageRecord.getUsageType() == UsageTypes.VM_SNAPSHOT_ON_PRIMARY) { |         } else if (usageRecord.getUsageType() == UsageTypes.VM_SNAPSHOT_ON_PRIMARY) { | ||||||
|  |             resourceType = ResourceObjectType.VMSnapshot; | ||||||
|  |             VMSnapshotVO vmSnapshotVO = null; | ||||||
|  |             if (usageRecord.getUsageId() != null) { | ||||||
|  |                 vmSnapshotVO = vmSnapshotDao.findByIdIncludingRemoved(usageRecord.getUsageId()); | ||||||
|  |                 if (vmSnapshotVO != null) { | ||||||
|  |                     resourceId = vmSnapshotVO.getId(); | ||||||
|  |                     usageRecResponse.setResourceName(vmSnapshotVO.getDisplayName()); | ||||||
|  |                     usageRecResponse.setUsageId(vmSnapshotVO.getUuid()); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|             usageRecResponse.setSize(usageRecord.getVirtualSize()); |             usageRecResponse.setSize(usageRecord.getVirtualSize()); | ||||||
|             if (!oldFormat) { |             if (!oldFormat) { | ||||||
|                 final StringBuilder builder = new StringBuilder(); |                 final StringBuilder builder = new StringBuilder(); | ||||||
|                 builder.append("VMSnapshot on primary storage usage"); |                 builder.append("VMSnapshot on primary storage usage"); | ||||||
|  |                 if (vmSnapshotVO != null) { | ||||||
|  |                     builder.append(" Id: ").append(vmSnapshotVO.getId()).append(" (").append(vmSnapshotVO.getUuid()).append(") "); | ||||||
|  |                 } | ||||||
|                 if (vmInstance != null) { |                 if (vmInstance != null) { | ||||||
|                     builder.append(" for VM ").append(vmInstance.getHostName()).append(" (").append(vmInstance.getUuid()).append(") ") |                     builder.append(" for VM ").append(vmInstance.getHostName()).append(" (").append(vmInstance.getUuid()).append(") ") | ||||||
|                             .append("with size ").append(usageRecord.getVirtualSize()); |                             .append("with size ").append(usageRecord.getVirtualSize()); | ||||||
|  | |||||||
| @ -1827,7 +1827,15 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna | |||||||
|         Date created = event.getCreateDate(); |         Date created = event.getCreateDate(); | ||||||
|         Account acct = _accountDao.findByIdIncludingRemoved(event.getAccountId()); |         Account acct = _accountDao.findByIdIncludingRemoved(event.getAccountId()); | ||||||
|         Long domainId = acct.getDomainId(); |         Long domainId = acct.getDomainId(); | ||||||
|  | 
 | ||||||
|  |         UsageEventDetailsVO detailVO = _usageEventDetailsDao.findDetail(event.getId(), UsageEventVO.DynamicParameters.vmSnapshotId.name()); | ||||||
|  |         Long vmSnapshotId = null; | ||||||
|  |         if (detailVO != null) { | ||||||
|  |             String snapId = detailVO.getValue(); | ||||||
|  |              vmSnapshotId = Long.valueOf(snapId); | ||||||
|  |         } | ||||||
|         UsageVMSnapshotVO vsVO = new UsageVMSnapshotVO(volumeId, zoneId, accountId, domainId, vmId, offeringId, size, created, null); |         UsageVMSnapshotVO vsVO = new UsageVMSnapshotVO(volumeId, zoneId, accountId, domainId, vmId, offeringId, size, created, null); | ||||||
|  |         vsVO.setVmSnapshotId(vmSnapshotId); | ||||||
|         _usageVMSnapshotDao.persist(vsVO); |         _usageVMSnapshotDao.persist(vsVO); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -1842,7 +1850,15 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna | |||||||
|             Date created = event.getCreateDate(); |             Date created = event.getCreateDate(); | ||||||
|             Account acct = _accountDao.findByIdIncludingRemoved(event.getAccountId()); |             Account acct = _accountDao.findByIdIncludingRemoved(event.getAccountId()); | ||||||
|             Long domainId = acct.getDomainId(); |             Long domainId = acct.getDomainId(); | ||||||
|  | 
 | ||||||
|  |             UsageEventDetailsVO detailVO = _usageEventDetailsDao.findDetail(event.getId(), UsageEventVO.DynamicParameters.vmSnapshotId.name()); | ||||||
|  |             Long vmSnapshotId = null; | ||||||
|  |             if (detailVO != null) { | ||||||
|  |                 String snapId = detailVO.getValue(); | ||||||
|  |                 vmSnapshotId = Long.valueOf(snapId); | ||||||
|  |             } | ||||||
|             UsageSnapshotOnPrimaryVO vsVO = new UsageSnapshotOnPrimaryVO(vmId, zoneId, accountId, domainId, vmId, name, 0, virtualsize, physicalsize, created, null); |             UsageSnapshotOnPrimaryVO vsVO = new UsageSnapshotOnPrimaryVO(vmId, zoneId, accountId, domainId, vmId, name, 0, virtualsize, physicalsize, created, null); | ||||||
|  |             vsVO.setVmSnapshotId(vmSnapshotId); | ||||||
|             if (s_logger.isDebugEnabled()) { |             if (s_logger.isDebugEnabled()) { | ||||||
|                 s_logger.debug("createSnapshotOnPrimaryEvent UsageSnapshotOnPrimaryVO " + vsVO); |                 s_logger.debug("createSnapshotOnPrimaryEvent UsageSnapshotOnPrimaryVO " + vsVO); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -94,14 +94,14 @@ public class VMSanpshotOnPrimaryParser { | |||||||
|             } |             } | ||||||
|             long duration = (endDateEffective.getTime() - created.getTime()) + 1; |             long duration = (endDateEffective.getTime() - created.getTime()) + 1; | ||||||
|             createUsageRecord(UsageTypes.VM_SNAPSHOT_ON_PRIMARY, duration, created, endDateEffective, account, usageRec.getId(), usageRec.getName(), usageRec.getZoneId(), |             createUsageRecord(UsageTypes.VM_SNAPSHOT_ON_PRIMARY, duration, created, endDateEffective, account, usageRec.getId(), usageRec.getName(), usageRec.getZoneId(), | ||||||
|                     usageRec.getVirtualSize(), usageRec.getPhysicalSize()); |                     usageRec.getVirtualSize(), usageRec.getPhysicalSize(), usageRec.getVmSnapshotId()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static void createUsageRecord(int usageType, long runningTime, Date startDate, Date endDate, AccountVO account, long vmId, String name, long zoneId, long virtualSize, |     private static void createUsageRecord(int usageType, long runningTime, Date startDate, Date endDate, AccountVO account, long vmId, String name, long zoneId, long virtualSize, | ||||||
|             long physicalSize) { |                                           long physicalSize, Long vmSnapshotId) { | ||||||
|         // Our smallest increment is hourly for now |         // Our smallest increment is hourly for now | ||||||
|         if (s_logger.isDebugEnabled()) { |         if (s_logger.isDebugEnabled()) { | ||||||
|             s_logger.debug("Total running time " + runningTime + "ms"); |             s_logger.debug("Total running time " + runningTime + "ms"); | ||||||
| @ -113,16 +113,16 @@ public class VMSanpshotOnPrimaryParser { | |||||||
|         String usageDisplay = dFormat.format(usage); |         String usageDisplay = dFormat.format(usage); | ||||||
| 
 | 
 | ||||||
|         if (s_logger.isDebugEnabled()) { |         if (s_logger.isDebugEnabled()) { | ||||||
|             s_logger.debug("Creating VMSnapshot On Primary usage record for vm: " + vmId + ", usage: " + usageDisplay + ", startDate: " + startDate + ", endDate: " + endDate |             s_logger.debug("Creating VMSnapshot Id: " + vmSnapshotId + " On Primary usage record for vm: " + vmId + ", usage: " + usageDisplay + ", startDate: " + startDate + ", endDate: " + endDate | ||||||
|                     + ", for account: " + account.getId()); |                     + ", for account: " + account.getId()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Create the usage record |         // Create the usage record | ||||||
|         String usageDesc = "VMSnapshot On Primary Usage: " + "VM Id: " + vmId; |         String usageDesc = "VMSnapshot Id: " + vmSnapshotId + " On Primary Usage: VM Id: " + vmId; | ||||||
|         usageDesc += " Size: " + virtualSize; |         usageDesc += " Size: " + virtualSize; | ||||||
| 
 | 
 | ||||||
|         UsageVO usageRecord = new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", usageType, new Double(usage), vmId, name, null, null, |         UsageVO usageRecord = new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", usageType, new Double(usage), vmId, name, null, null, | ||||||
|                 vmId, physicalSize, virtualSize, startDate, endDate); |                 vmSnapshotId, physicalSize, virtualSize, startDate, endDate); | ||||||
|         s_usageDao.persist(usageRecord); |         s_usageDao.persist(usageRecord); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -94,7 +94,7 @@ public class VMSnapshotUsageParser { | |||||||
|             long duration = (createDate.getTime() - previousCreated.getTime()) + 1; |             long duration = (createDate.getTime() - previousCreated.getTime()) + 1; | ||||||
| 
 | 
 | ||||||
|             createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, previousCreated, createDate, account, volId, zoneId, previousEvent.getDiskOfferingId(), vmId, |             createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, previousCreated, createDate, account, volId, zoneId, previousEvent.getDiskOfferingId(), vmId, | ||||||
|                 previousEvent.getSize()); |                 previousEvent.getSize(), usageRec.getVmSnapshotId()); | ||||||
|             previousEvent.setProcessed(new Date()); |             previousEvent.setProcessed(new Date()); | ||||||
|             s_usageVMSnapshotDao.update(previousEvent); |             s_usageVMSnapshotDao.update(previousEvent); | ||||||
| 
 | 
 | ||||||
| @ -113,14 +113,14 @@ public class VMSnapshotUsageParser { | |||||||
|             } |             } | ||||||
|             long duration = (endDate.getTime() - created.getTime()) + 1; |             long duration = (endDate.getTime() - created.getTime()) + 1; | ||||||
|             createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, created, endDate, account, usageRec.getId(), usageRec.getZoneId(), usageRec.getDiskOfferingId(), |             createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, created, endDate, account, usageRec.getId(), usageRec.getZoneId(), usageRec.getDiskOfferingId(), | ||||||
|                 usageRec.getVmId(), usageRec.getSize()); |                 usageRec.getVmId(), usageRec.getSize(), usageRec.getVmSnapshotId()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static void createUsageRecord(int type, long runningTime, Date startDate, Date endDate, AccountVO account, long volId, long zoneId, Long doId, Long vmId, |     private static void createUsageRecord(int type, long runningTime, Date startDate, Date endDate, AccountVO account, long volId, long zoneId, Long doId, Long vmId, | ||||||
|         long size) { |                                           long size, Long vmSnapshotId) { | ||||||
|         // Our smallest increment is hourly for now |         // Our smallest increment is hourly for now | ||||||
|         if (s_logger.isDebugEnabled()) { |         if (s_logger.isDebugEnabled()) { | ||||||
|             s_logger.debug("Total running time " + runningTime + "ms"); |             s_logger.debug("Total running time " + runningTime + "ms"); | ||||||
| @ -132,12 +132,12 @@ public class VMSnapshotUsageParser { | |||||||
|         String usageDisplay = dFormat.format(usage); |         String usageDisplay = dFormat.format(usage); | ||||||
| 
 | 
 | ||||||
|         if (s_logger.isDebugEnabled()) { |         if (s_logger.isDebugEnabled()) { | ||||||
|             s_logger.debug("Creating VMSnapshot Volume usage record for vol: " + volId + ", usage: " + usageDisplay + ", startDate: " + startDate + ", endDate: " + |             s_logger.debug("Creating VMSnapshot Id:" + vmSnapshotId + " Volume usage record for vol: " + volId + ", usage: " + usageDisplay + ", startDate: " + startDate + ", endDate: " + | ||||||
|                 endDate + ", for account: " + account.getId()); |                 endDate + ", for account: " + account.getId()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Create the usage record |         // Create the usage record | ||||||
|         String usageDesc = "VMSnapshot Usage: " + "VM Id: " + vmId + " Volume Id: " + volId + " "; |         String usageDesc = "VMSnapshot Id: " + vmSnapshotId + " Usage: " + "VM Id: " + vmId + " Volume Id: " + volId + " "; | ||||||
| 
 | 
 | ||||||
|         if (doId != null) { |         if (doId != null) { | ||||||
|             usageDesc += " DiskOffering: " + doId; |             usageDesc += " DiskOffering: " + doId; | ||||||
| @ -146,7 +146,7 @@ public class VMSnapshotUsageParser { | |||||||
|         usageDesc += " Size: " + size; |         usageDesc += " Size: " + size; | ||||||
| 
 | 
 | ||||||
|         UsageVO usageRecord = |         UsageVO usageRecord = | ||||||
|             new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", type, new Double(usage), vmId, null, doId, null, volId, size, |             new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", type, new Double(usage), vmId, null, doId, null, vmSnapshotId, size, | ||||||
|                 startDate, endDate); |                 startDate, endDate); | ||||||
|         s_usageDao.persist(usageRecord); |         s_usageDao.persist(usageRecord); | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user