fix CLOUDSTACK-3591 add usage recording for VM snapshots

This commit is contained in:
Mice Xia 2013-07-29 17:05:44 +08:00
parent 53feae08de
commit acd2396660
16 changed files with 777 additions and 54 deletions

View File

@ -41,6 +41,7 @@ public class VolumeTO implements InternalIdentity {
private Long bytesWriteRate;
private Long iopsReadRate;
private Long iopsWriteRate;
private Long chainSize;
public VolumeTO(long id, Volume.Type type, StoragePoolType poolType, String poolUuid, String name, String mountPoint, String path, long size, String chainInfo) {
this.id = id;
@ -77,6 +78,7 @@ public class VolumeTO implements InternalIdentity {
this.storagePoolUuid = pool.getUuid();
this.mountPoint = volume.getFolder();
this.chainInfo = volume.getChainInfo();
this.chainSize = volume.getVmSnapshotChainSize();
if (volume.getDeviceId() != null)
this.deviceId = volume.getDeviceId();
}
@ -170,4 +172,11 @@ public class VolumeTO implements InternalIdentity {
return iopsWriteRate;
}
public Long getChainSize() {
return chainSize;
}
public void setChainSize(Long chainSize) {
this.chainSize = chainSize;
}
}

View File

@ -184,4 +184,5 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
*/
void setReservationId(String reserv);
Storage.ImageFormat getFormat();
Long getVmSnapshotChainSize();
}

View File

@ -40,6 +40,7 @@ public class UsageTypes {
public static final int VM_DISK_IO_WRITE = 22;
public static final int VM_DISK_BYTES_READ = 23;
public static final int VM_DISK_BYTES_WRITE = 24;
public static final int VM_SNAPSHOT = 25;
public static List<UsageTypeResponse> listUsageTypes(){
List<UsageTypeResponse> responseList = new ArrayList<UsageTypeResponse>();
@ -61,6 +62,7 @@ public class UsageTypes {
responseList.add(new UsageTypeResponse(VM_DISK_IO_WRITE, "VM Disk usage(I/O Write)"));
responseList.add(new UsageTypeResponse(VM_DISK_BYTES_READ, "VM Disk usage(Bytes Read)"));
responseList.add(new UsageTypeResponse(VM_DISK_BYTES_WRITE, "VM Disk usage(Bytes Write)"));
responseList.add(new UsageTypeResponse(VM_SNAPSHOT, "VM Snapshot storage usage"));
return responseList;
}
}

View File

@ -150,6 +150,9 @@ public class VolumeVO implements Volume {
@Column(name = "iscsi_name")
private String _iScsiName;
@Column(name = "vm_snapshot_chain_size")
private Long vmSnapshotChainSize;
@Transient
// @Column(name="reservation")
String reservationId;
@ -550,4 +553,12 @@ public class VolumeVO implements Volume {
public void setFormat(Storage.ImageFormat format) {
this.format = format;
}
public void setVmSnapshotChainSize(Long vmSnapshotChainSize){
this.vmSnapshotChainSize = vmSnapshotChainSize;
}
public Long getVmSnapshotChainSize(){
return this.vmSnapshotChainSize;
}
}

View File

@ -0,0 +1,122 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.usage;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.cloudstack.api.InternalIdentity;
@Entity
@Table(name="usage_vmsnapshot")
public class UsageVMSnapshotVO implements InternalIdentity {
@Column(name="id") // volumeId
private long id;
@Column(name="zone_id")
private long zoneId;
@Column(name="account_id")
private long accountId;
@Column(name="domain_id")
private long domainId;
@Column(name="vm_id")
private long vmId;
@Column(name="disk_offering_id")
private Long diskOfferingId;
@Column(name="size")
private long size;
@Column(name="created")
@Temporal(value=TemporalType.TIMESTAMP)
private Date created = null;
@Column(name="processed")
@Temporal(value=TemporalType.TIMESTAMP)
private Date processed;
protected UsageVMSnapshotVO() {
}
public UsageVMSnapshotVO(long id, long zoneId, long accountId, long domainId,
long vmId, Long diskOfferingId, long size, Date created, Date processed) {
this.zoneId = zoneId;
this.accountId = accountId;
this.domainId = domainId;
this.diskOfferingId = diskOfferingId;
this.id = id;
this.size = size;
this.created = created;
this.vmId = vmId;
this.processed = processed;
}
public long getZoneId() {
return zoneId;
}
public long getAccountId() {
return accountId;
}
public long getDomainId() {
return domainId;
}
public Long getDiskOfferingId() {
return diskOfferingId;
}
public long getSize() {
return size;
}
public Date getProcessed() {
return processed;
}
public void setProcessed(Date processed) {
this.processed = processed;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public long getVmId() {
return vmId;
}
public long getId(){
return this.id;
}
}

View File

@ -0,0 +1,29 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.usage.dao;
import java.util.Date;
import java.util.List;
import com.cloud.usage.UsageVMSnapshotVO;
import com.cloud.utils.db.GenericDao;
public interface UsageVMSnapshotDao extends GenericDao<UsageVMSnapshotVO, Long> {
public void update(UsageVMSnapshotVO usage);
public List<UsageVMSnapshotVO> getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate);
UsageVMSnapshotVO getPreviousUsageRecord(UsageVMSnapshotVO rec);
}

View File

@ -0,0 +1,167 @@
package com.cloud.usage.dao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.ejb.Local;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.usage.UsageVMSnapshotVO;
import com.cloud.utils.DateUtil;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.Transaction;
@Component
@Local(value={UsageVMSnapshotDao.class})
public class UsageVMSnapshotDaoImpl extends GenericDaoBase<UsageVMSnapshotVO, Long> implements UsageVMSnapshotDao{
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 " +
" FROM usage_vmsnapshot" +
" WHERE account_id = ? " +
" AND ( (created BETWEEN ? AND ?) OR " +
" (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 PREVIOUS_QUERY =
"SELECT id, zone_id, account_id, domain_id, vm_id, disk_offering_id,size, created, processed " +
"FROM usage_vmsnapshot " +
"WHERE account_id = ? AND id = ? AND vm_id = ? AND created < ? AND processed IS NULL " +
"ORDER BY created desc limit 1";
public void update(UsageVMSnapshotVO usage) {
Transaction txn = Transaction.open(Transaction.USAGE_DB);
PreparedStatement pstmt = null;
try {
txn.start();
pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED);
pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getProcessed()));
pstmt.setLong(2, usage.getAccountId());
pstmt.setLong(3, usage.getId());
pstmt.setLong(4, usage.getVmId());
pstmt.setString(5, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getCreated()));
pstmt.executeUpdate();
txn.commit();
} catch (Exception e) {
txn.rollback();
s_logger.warn("Error updating UsageVMSnapshotVO", e);
} finally {
txn.close();
}
}
public List<UsageVMSnapshotVO> getUsageRecords(Long accountId, Long domainId,
Date startDate, Date endDate) {
List<UsageVMSnapshotVO> usageRecords = new ArrayList<UsageVMSnapshotVO>();
String sql = GET_USAGE_RECORDS_BY_ACCOUNT;
Transaction txn = Transaction.open(Transaction.USAGE_DB);
PreparedStatement pstmt = null;
try {
int i = 1;
pstmt = txn.prepareAutoCloseStatement(sql);
pstmt.setLong(i++, accountId);
pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate));
pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate));
pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate));
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
//id, zone_id, account_id, domain_iVMSnapshotVOd, vm_id, disk_offering_id, size, created, processed
Long vId = Long.valueOf(rs.getLong(1));
Long zoneId = Long.valueOf(rs.getLong(2));
Long acctId = Long.valueOf(rs.getLong(3));
Long dId = Long.valueOf(rs.getLong(4));
Long vmId = Long.valueOf(rs.getLong(5));
Long doId = Long.valueOf(rs.getLong(6));
if(doId == 0){
doId = null;
}
Long size = Long.valueOf(rs.getLong(7));
Date createdDate = null;
Date processDate = null;
String createdTS = rs.getString(8);
String processed = rs.getString(9);
if (createdTS != null) {
createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS);
}
if (processed != null) {
processDate = DateUtil.parseDateString(s_gmtTimeZone, processed);
}
usageRecords.add(new UsageVMSnapshotVO(vId, zoneId, acctId, dId, vmId,
doId, size, createdDate, processDate));
}
} catch (Exception e) {
txn.rollback();
s_logger.warn("Error getting usage records", e);
} finally {
txn.close();
}
return usageRecords;
}
@Override
public UsageVMSnapshotVO getPreviousUsageRecord(UsageVMSnapshotVO rec) {
List<UsageVMSnapshotVO> usageRecords = new ArrayList<UsageVMSnapshotVO>();
String sql = PREVIOUS_QUERY;
Transaction txn = Transaction.open(Transaction.USAGE_DB);
PreparedStatement pstmt = null;
try {
int i = 1;
pstmt = txn.prepareAutoCloseStatement(sql);
pstmt.setLong(i++, rec.getAccountId());
pstmt.setLong(i++, rec.getId());
pstmt.setLong(i++, rec.getVmId());
pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), rec.getCreated()));
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
//id, zone_id, account_id, domain_iVMSnapshotVOd, vm_id, disk_offering_id, size, created, processed
Long vId = Long.valueOf(rs.getLong(1));
Long zoneId = Long.valueOf(rs.getLong(2));
Long acctId = Long.valueOf(rs.getLong(3));
Long dId = Long.valueOf(rs.getLong(4));
Long vmId = Long.valueOf(rs.getLong(5));
Long doId = Long.valueOf(rs.getLong(6));
if(doId == 0){
doId = null;
}
Long size = Long.valueOf(rs.getLong(7));
Date createdDate = null;
Date processDate = null;
String createdTS = rs.getString(8);
String processed = rs.getString(9);
if (createdTS != null) {
createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS);
}
if (processed != null) {
processDate = DateUtil.parseDateString(s_gmtTimeZone, processed);
}
usageRecords.add(new UsageVMSnapshotVO(vId, zoneId, acctId, dId, vmId,
doId, size, createdDate, processDate));
}
} catch (Exception e) {
txn.rollback();
s_logger.warn("Error getting usage records", e);
} finally {
txn.close();
}
if(usageRecords.size() > 0)
return usageRecords.get(0);
return null;
}
}

View File

@ -613,4 +613,9 @@ public class VolumeObject implements VolumeInfo {
}
return true;
}
@Override
public Long getVmSnapshotChainSize() {
return this.volumeVO.getVmSnapshotChainSize();
}
}

View File

@ -51,14 +51,18 @@ import com.cloud.agent.api.RevertToVMSnapshotCommand;
import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.mo.SnapshotDescriptor;
import com.cloud.hypervisor.vmware.mo.SnapshotDescriptor.SnapshotInfo;
import com.cloud.hypervisor.vmware.mo.TaskMO;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.cloud.storage.JavaStorageLayer;
import com.cloud.storage.Volume;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.StorageLayer;
import com.cloud.storage.template.VmdkProcessor;
@ -69,6 +73,10 @@ import com.cloud.utils.Ternary;
import com.cloud.utils.script.Script;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;
import com.vmware.vim25.FileInfo;
import com.vmware.vim25.FileQueryFlags;
import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.TaskEvent;
import com.vmware.vim25.TaskInfo;
@ -1205,6 +1213,55 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
return "snapshots/" + accountId + "/" + volumeId;
}
private long getVMSnapshotChainSize(VmwareContext context, VmwareHypervisorHost hyperHost,
String fileName, String poolUuid, String exceptFileName)
throws Exception{
long size = 0;
ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, poolUuid);
DatastoreMO dsMo = new DatastoreMO(context, morDs);
HostDatastoreBrowserMO browserMo = dsMo.getHostDatastoreBrowserMO();
String datastorePath = "[" + dsMo.getName() + "]";
HostDatastoreBrowserSearchSpec searchSpec = new HostDatastoreBrowserSearchSpec();
FileQueryFlags fqf = new FileQueryFlags();
fqf.setFileSize(true);
fqf.setFileOwner(true);
fqf.setModification(true);
searchSpec.setDetails(fqf);
searchSpec.setSearchCaseInsensitive(false);
searchSpec.getMatchPattern().add(fileName);
ArrayList<HostDatastoreBrowserSearchResults> results = browserMo.
searchDatastoreSubFolders(datastorePath, searchSpec);
for(HostDatastoreBrowserSearchResults result : results){
if (result != null) {
List<FileInfo> info = result.getFile();
for (FileInfo fi : info) {
if(exceptFileName != null && fi.getPath().contains(exceptFileName))
continue;
else
size = size + fi.getFileSize();
}
}
}
return size;
}
private String extractSnapshotBaseFileName(String input) {
if(input == null)
return null;
String result = input;
if (result.endsWith(".vmdk")){ // get rid of vmdk file extension
result = result.substring(0, result.length() - (".vmdk").length());
}
if(result.split("-").length == 1) // e.g 4da6dcbd412c47b59f96c7ff6dbd7216.vmdk
return result;
if(result.split("-").length > 2) // e.g ROOT-5-4.vmdk, ROOT-5-4-000001.vmdk
return result.split("-")[0] + "-" + result.split("-")[1];
if(result.split("-").length == 2) // e.g 4da6dcbd412c47b59f96c7ff6dbd7216-000001.vmdk
return result.split("-")[0];
else
return result;
}
@Override
public CreateVMSnapshotAnswer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd) {
List<VolumeTO> volumeTOs = cmd.getVolumeTOs();
@ -1246,24 +1303,30 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
// find VM disk file path after creating snapshot
VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
for (int i = 0; i < vdisks.length; i ++){
@SuppressWarnings("deprecation")
List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false);
for(Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
String vmdkName = fileItem.first().split(" ")[1];
if ( vmdkName.endsWith(".vmdk")){
if (vmdkName.endsWith(".vmdk")){
vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
}
String[] s = vmdkName.split("-");
mapNewDisk.put(s[0], vmdkName);
String baseName = extractSnapshotBaseFileName(vmdkName);
mapNewDisk.put(baseName, vmdkName);
}
}
// update volume path using maps
for (VolumeTO volumeTO : volumeTOs) {
String parentUUID = volumeTO.getPath();
String[] s = parentUUID.split("-");
String key = s[0];
volumeTO.setPath(mapNewDisk.get(key));
String baseName = extractSnapshotBaseFileName(volumeTO.getPath());
String newPath = mapNewDisk.get(baseName);
// get volume's chain size for this VM snapshot, exclude current volume vdisk
long size = getVMSnapshotChainSize(context,hyperHost,baseName + "*.vmdk",
volumeTO.getPoolUuid(), newPath);
if(volumeTO.getType()== Volume.Type.ROOT){
// add memory snapshot size
size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",volumeTO.getPoolUuid(),null);
}
volumeTO.setChainSize(size);
volumeTO.setPath(newPath);
}
return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs);
}
@ -1318,16 +1381,21 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
if (vmdkName.endsWith(".vmdk")) {
vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
}
String[] s = vmdkName.split("-");
mapNewDisk.put(s[0], vmdkName);
String baseName = extractSnapshotBaseFileName(vmdkName);
mapNewDisk.put(baseName, vmdkName);
}
}
for (VolumeTO volumeTo : listVolumeTo) {
String key = null;
String parentUUID = volumeTo.getPath();
String[] s = parentUUID.split("-");
key = s[0];
volumeTo.setPath(mapNewDisk.get(key));
String baseName = extractSnapshotBaseFileName(volumeTo.getPath());
String newPath = mapNewDisk.get(baseName);
long size = getVMSnapshotChainSize(context,hyperHost,
baseName + "*.vmdk", volumeTo.getPoolUuid(), newPath);
if(volumeTo.getType()== Volume.Type.ROOT){
// add memory snapshot size
size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",volumeTo.getPoolUuid(),null);
}
volumeTo.setChainSize(size);
volumeTo.setPath(newPath);
}
return new DeleteVMSnapshotAnswer(cmd, listVolumeTo);
}

View File

@ -6713,6 +6713,60 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
private long getVMSnapshotChainSize(Connection conn, VolumeTO volumeTo, String vmName)
throws BadServerResponse, XenAPIException, XmlRpcException {
Set<VDI> allvolumeVDIs = VDI.getByNameLabel(conn, volumeTo.getName());
long size = 0;
for (VDI vdi : allvolumeVDIs) {
try {
if (vdi.getIsASnapshot(conn)
&& vdi.getSmConfig(conn).get("vhd-parent") != null) {
String parentUuid = vdi.getSmConfig(conn).get("vhd-parent");
VDI parentVDI = VDI.getByUuid(conn, parentUuid);
// add size of snapshot vdi node, usually this only contains meta data
size = size + vdi.getPhysicalUtilisation(conn);
// add size of snapshot vdi parent, this contains data
if (parentVDI != null)
size = size
+ parentVDI.getPhysicalUtilisation(conn)
.longValue();
}
} catch (Exception e) {
s_logger.debug("Exception occurs when calculate "
+ "snapshot capacity for volumes: " + e.getMessage());
continue;
}
}
if (volumeTo.getType() == Volume.Type.ROOT) {
Map<VM, VM.Record> allVMs = VM.getAllRecords(conn);
// add size of memory snapshot vdi
if (allVMs.size() > 0) {
for (VM vmr : allVMs.keySet()) {
try {
String vName = vmr.getNameLabel(conn);
if (vName != null && vName.contains(vmName)
&& vmr.getIsASnapshot(conn)) {
VDI memoryVDI = vmr.getSuspendVDI(conn);
size = size
+ memoryVDI.getParent(conn)
.getPhysicalUtilisation(conn);
size = size
+ memoryVDI.getPhysicalUtilisation(conn);
}
} catch (Exception e) {
s_logger.debug("Exception occurs when calculate "
+ "snapshot capacity for memory: "
+ e.getMessage());
continue;
}
}
}
}
return size;
}
protected Answer execute(final CreateVMSnapshotCommand cmd) {
String vmName = cmd.getVmName();
String vmSnapshotName = cmd.getTarget().getSnapshotName();
@ -6790,6 +6844,16 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
// extract VM snapshot ref from result
String ref = result.substring("<value>".length(), result.length() - "</value>".length());
vmSnapshot = Types.toVM(ref);
try {
Thread.sleep(5000);
} catch (final InterruptedException ex) {
}
// calculate used capacity for this VM snapshot
for (VolumeTO volumeTo : cmd.getVolumeTOs()){
long size = getVMSnapshotChainSize(conn,volumeTo,cmd.getVmName());
volumeTo.setChainSize(size);
}
success = true;
return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs());
@ -6907,6 +6971,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
for (VDI vdi : vdiList) {
vdi.destroy(conn);
}
try {
Thread.sleep(5000);
} catch (final InterruptedException ex) {
}
// re-calculate used capacify for this VM snapshot
for (VolumeTO volumeTo : cmd.getVolumeTOs()){
long size = getVMSnapshotChainSize(conn,volumeTo,cmd.getVmName());
volumeTo.setChainSize(size);
}
return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs());
} catch (Exception e) {
s_logger.warn("Catch Exception: " + e.getClass().toString()

View File

@ -3409,6 +3409,13 @@ public class ApiResponseHelper implements ResponseGenerator {
//Security Group Id
SecurityGroupVO sg = _entityMgr.findByIdIncludingRemoved(SecurityGroupVO.class, usageRecord.getUsageId().toString());
usageRecResponse.setUsageId(sg.getUuid());
} else if(usageRecord.getUsageType() == UsageTypes.VM_SNAPSHOT){
VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getVmInstanceId().toString());
usageRecResponse.setVmName(vm.getInstanceName());
usageRecResponse.setUsageId(vm.getUuid());
usageRecResponse.setSize(usageRecord.getSize());
if(usageRecord.getOfferingId() != null)
usageRecResponse.setOfferingId(usageRecord.getOfferingId().toString());
}
if (usageRecord.getRawUsage() != null) {

View File

@ -471,24 +471,9 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager,
for (VolumeVO volume : volumes) {
if(volume.getInstanceId() == null)
continue;
Long vmId = volume.getInstanceId();
UserVm vm = _userVMDao.findById(vmId);
if(vm == null)
continue;
ServiceOffering offering = _offeringsDao.findById(vm.getServiceOfferingId());
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
long pathCount = 0;
long memorySnapshotSize = 0;
for (VMSnapshotVO vmSnapshotVO : vmSnapshots) {
if(_vmSnapshotDao.listByParent(vmSnapshotVO.getId()).size() == 0)
pathCount++;
if(vmSnapshotVO.getType() == VMSnapshot.Type.DiskAndMemory)
memorySnapshotSize += (offering.getRamSize() * 1024L * 1024L);
}
if(pathCount <= 1)
totalSize = totalSize + memorySnapshotSize;
else
totalSize = totalSize + volume.getSize() * (pathCount - 1) + memorySnapshotSize;
Long chainSize = volume.getVmSnapshotChainSize();
if(chainSize != null)
totalSize += chainSize;
}
return totalSize;
}

View File

@ -48,6 +48,7 @@ import com.cloud.agent.api.to.VolumeTO;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
@ -59,14 +60,17 @@ import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
import com.cloud.projects.Project.ListProjectResourcesCriteria;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.StoragePool;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VolumeDao;
@ -121,6 +125,8 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
@Inject DataStoreManager dataStoreMgr;
@Inject ConfigurationDao _configDao;
@Inject HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
@Inject DiskOfferingDao _diskOfferingDao;
@Inject ServiceOfferingDao _serviceOfferingDao;
int _vmSnapshotMax;
int _wait;
StateMachine2<VMSnapshot.State, VMSnapshot.Event, VMSnapshot> _vmSnapshottateMachine ;
@ -249,7 +255,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
// check hypervisor capabilities
if(!_hypervisorCapabilitiesDao.isVmSnapshotEnabled(userVmVo.getHypervisorType(), "default"))
throw new InvalidParameterValueException("VM snapshot is not enabled for hypervisor type: " + userVmVo.getHypervisorType());
throw new InvalidParameterValueException("VM snapshot is not enabled for hypervisor type: " + userVmVo.getHypervisorType());
// parameter length check
if(vsDisplayName != null && vsDisplayName.length()>255)
@ -351,8 +357,8 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
}
protected VMSnapshot createVmSnapshotInternal(UserVmVO userVm, VMSnapshotVO vmSnapshot, Long hostId) {
try {
CreateVMSnapshotAnswer answer = null;
CreateVMSnapshotAnswer answer = null;
try {
GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
// prepare snapshotVolumeTos
@ -403,9 +409,37 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
s_logger.warn("Create vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName());
_vmSnapshotDao.remove(vmSnapshot.getId());
}
if(vmSnapshot.getState() == VMSnapshot.State.Ready && answer != null){
for (VolumeTO volumeTo : answer.getVolumeTOs()){
publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_CREATE,vmSnapshot,userVm,volumeTo);
}
}
}
}
private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm, VolumeTO volumeTo){
VolumeVO volume = _volumeDao.findById(volumeTo.getId());
Long diskOfferingId = volume.getDiskOfferingId();
Long offeringId = null;
if (diskOfferingId != null) {
DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId);
if (offering != null
&& (offering.getType() == DiskOfferingVO.Type.Disk)) {
offeringId = offering.getId();
}
}
UsageEventUtils.publishUsageEvent(
type,
vmSnapshot.getAccountId(),
userVm.getDataCenterId(),
userVm.getId(),
vmSnapshot.getName(),
offeringId,
volume.getId(), // save volume's id into templateId field
volumeTo.getChainSize(),
VMSnapshot.class.getName(), vmSnapshot.getUuid());
}
protected List<VolumeTO> getVolumeTOList(Long vmId) {
List<VolumeTO> volumeTOs = new ArrayList<VolumeTO>();
List<VolumeVO> volumeVos = _volumeDao.findByInstance(vmId);
@ -532,6 +566,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
if (volume.getPath() != null) {
VolumeVO volumeVO = _volumeDao.findById(volume.getId());
volumeVO.setPath(volume.getPath());
volumeVO.setVmSnapshotChainSize(volume.getChainSize());
_volumeDao.persist(volumeVO);
}
}
@ -590,7 +625,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
@DB
protected boolean deleteSnapshotInternal(VMSnapshotVO vmSnapshot) {
UserVmVO userVm = _userVMDao.findById(vmSnapshot.getVmId());
DeleteVMSnapshotAnswer answer = null;
try {
vmSnapshotStateTransitTo(vmSnapshot,VMSnapshot.Event.ExpungeRequested);
Long hostId = pickRunningHost(vmSnapshot.getVmId());
@ -606,7 +641,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
DeleteVMSnapshotCommand deleteSnapshotCommand = new DeleteVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs,guestOS.getDisplayName());
DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) sendToPool(hostId, deleteSnapshotCommand);
answer = (DeleteVMSnapshotAnswer) sendToPool(hostId, deleteSnapshotCommand);
if (answer != null && answer.getResult()) {
processAnswer(vmSnapshot, userVm, answer, hostId);
@ -620,6 +655,12 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
String msg = "Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage();
s_logger.error(msg , e);
throw new CloudRuntimeException(e.getMessage());
} finally{
if(answer != null && answer.getResult()){
for (VolumeTO volumeTo : answer.getVolumeTOs()){
publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_DELETE,vmSnapshot,userVm,volumeTo);
}
}
}
}
@ -672,19 +713,19 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
// start or stop VM first, if revert from stopped state to running state, or from running to stopped
if(userVm.getState() == VirtualMachine.State.Stopped && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory){
try {
vm = _itMgr.advanceStart(userVm, new HashMap<VirtualMachineProfile.Param, Object>(), callerUser, owner);
hostId = vm.getHostId();
} catch (Exception e) {
s_logger.error("Start VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage());
throw new CloudRuntimeException(e.getMessage());
}
vm = _itMgr.advanceStart(userVm, new HashMap<VirtualMachineProfile.Param, Object>(), callerUser, owner);
hostId = vm.getHostId();
} catch (Exception e) {
s_logger.error("Start VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage());
throw new CloudRuntimeException(e.getMessage());
}
}else {
if(userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk){
try {
_itMgr.advanceStop(userVm, true, callerUser, owner);
_itMgr.advanceStop(userVm, true, callerUser, owner);
} catch (Exception e) {
s_logger.error("Stop VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage());
throw new CloudRuntimeException(e.getMessage());
throw new CloudRuntimeException(e.getMessage());
}
}
hostId = pickRunningHost(userVm.getId());
@ -715,7 +756,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana
String vmInstanceName = userVm.getInstanceName();
VMSnapshotTO parent = getSnapshotWithParents(snapshot).getParent();
VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(snapshot.getId(), snapshot.getName(), snapshot.getType(),
snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent);
snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent);
GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
RevertToVMSnapshotCommand revertToSnapshotCommand = new RevertToVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs, guestOS.getDisplayName());

View File

@ -2191,3 +2191,19 @@ ALTER TABLE `cloud_usage`.`cloud_usage` ADD COLUMN `virtual_size` bigint unsigne
INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.loadbalancer.haproxy.max.conn', '4096', 'Load Balancer(haproxy) maximum number of concurrent connections(global max)');
ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `concurrent_connections` int(10) unsigned COMMENT 'Load Balancer(haproxy) maximum number of concurrent connections(global max)';
DROP TABLE IF EXISTS `cloud_usage`.`usage_vmsnapshot`;
CREATE TABLE `cloud_usage`.`usage_vmsnapshot` (
`id` bigint(20) unsigned NOT NULL,
`zone_id` bigint(20) unsigned NOT NULL,
`account_id` bigint(20) unsigned NOT NULL,
`domain_id` bigint(20) unsigned NOT NULL,
`vm_id` bigint(20) unsigned NOT NULL,
`disk_offering_id` bigint(20) unsigned,
`size` bigint(20),
`created` datetime NOT NULL,
`processed` datetime,
INDEX `i_usage_vmsnapshot` (`account_id`,`id`,`vm_id`,`created`)
) ENGINE=InnoDB CHARSET=utf8;
ALTER TABLE volumes ADD COLUMN vm_snapshot_chain_size bigint(20) unsigned;

View File

@ -53,6 +53,7 @@ import com.cloud.usage.dao.UsagePortForwardingRuleDao;
import com.cloud.usage.dao.UsageSecurityGroupDao;
import com.cloud.usage.dao.UsageStorageDao;
import com.cloud.usage.dao.UsageVMInstanceDao;
import com.cloud.usage.dao.UsageVMSnapshotDao;
import com.cloud.usage.dao.UsageVPNUserDao;
import com.cloud.usage.dao.UsageVmDiskDao;
import com.cloud.usage.dao.UsageVolumeDao;
@ -64,6 +65,7 @@ import com.cloud.usage.parser.PortForwardingUsageParser;
import com.cloud.usage.parser.SecurityGroupUsageParser;
import com.cloud.usage.parser.StorageUsageParser;
import com.cloud.usage.parser.VMInstanceUsageParser;
import com.cloud.usage.parser.VMSnapshotUsageParser;
import com.cloud.usage.parser.VPNUserUsageParser;
import com.cloud.usage.parser.VmDiskUsageParser;
import com.cloud.usage.parser.VolumeUsageParser;
@ -116,7 +118,8 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
@Inject protected AlertManager _alertMgr;
@Inject protected UsageEventDao _usageEventDao;
@Inject ConfigurationDao _configDao;
@Inject private UsageVMSnapshotDao m_usageVMSnapshotDao;
private String m_version = null;
private final Calendar m_jobExecTime = Calendar.getInstance();
private int m_aggregationDuration = 0;
@ -236,7 +239,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
}
// use the configured exec time and aggregation duration for scheduling the job
m_scheduledFuture = m_executor.scheduleAtFixedRate(this, m_jobExecTime.getTimeInMillis() - System.currentTimeMillis(), m_aggregationDuration * 60 * 1000, TimeUnit.MILLISECONDS);
m_scheduledFuture = m_executor.scheduleAtFixedRate(this, m_jobExecTime.getTimeInMillis() - System.currentTimeMillis(), m_aggregationDuration * 60 * 1000, TimeUnit.MILLISECONDS);
m_heartbeat = m_heartbeatExecutor.scheduleAtFixedRate(new Heartbeat(), /* start in 15 seconds...*/15*1000, /* check database every minute*/60*1000, TimeUnit.MILLISECONDS);
@ -857,6 +860,12 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
s_logger.debug("VPN user usage successfully parsed? " + parsed + " (for account: " + account.getAccountName() + ", id: " + account.getId() + ")");
}
}
parsed = VMSnapshotUsageParser.parse(account, currentStartDate, currentEndDate);
if (s_logger.isDebugEnabled()) {
if (!parsed) {
s_logger.debug("VM Snapshot usage successfully parsed? " + parsed + " (for account: " + account.getAccountName() + ", id: " + account.getId() + ")");
}
}
return parsed;
}
@ -884,6 +893,8 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
createVPNUserEvent(event);
} else if (isSecurityGroupEvent(eventType)) {
createSecurityGroupEvent(event);
} else if (isVmSnapshotEvent(eventType)){
createVMSnapshotEvent(event);
}
}
@ -951,7 +962,12 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
return (eventType.equals(EventTypes.EVENT_SECURITY_GROUP_ASSIGN) ||
eventType.equals(EventTypes.EVENT_SECURITY_GROUP_REMOVE));
}
private boolean isVmSnapshotEvent(String eventType){
if (eventType == null) return false;
return (eventType.equals(EventTypes.EVENT_VM_SNAPSHOT_CREATE) ||
eventType.equals(EventTypes.EVENT_VM_SNAPSHOT_DELETE));
}
private void createVMHelperEvent(UsageEventVO event) {
// One record for handling VM.START and VM.STOP
@ -1573,7 +1589,22 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
}
}
}
private void createVMSnapshotEvent(UsageEventVO event){
Long vmId = event.getResourceId();
Long volumeId = event.getTemplateId();
Long offeringId = event.getOfferingId();
Long zoneId = event.getZoneId();
Long accountId = event.getAccountId();
long size = event.getSize();
Date created = event.getCreateDate();
Account acct = m_accountDao.findByIdIncludingRemoved(event.getAccountId());
Long domainId = acct.getDomainId();
UsageVMSnapshotVO vsVO = new UsageVMSnapshotVO(volumeId,zoneId,accountId,
domainId,vmId,offeringId, size, created, null);
m_usageVMSnapshotDao.persist(vsVO);
}
private class Heartbeat implements Runnable {
public void run() {
Transaction usageTxn = Transaction.open(Transaction.USAGE_DB);

View File

@ -0,0 +1,153 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.usage.parser;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.apache.cloudstack.usage.UsageTypes;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.usage.UsageVMSnapshotVO;
import com.cloud.usage.UsageVO;
import com.cloud.usage.dao.UsageDao;
import com.cloud.usage.dao.UsageVMSnapshotDao;
import com.cloud.user.AccountVO;
@Component
public class VMSnapshotUsageParser {
public static final Logger s_logger = Logger.getLogger(VMSnapshotUsageParser.class.getName());
private static UsageDao m_usageDao;
private static UsageVMSnapshotDao m_usageVMSnapshotDao;
@Inject private UsageDao _usageDao;
@Inject private UsageVMSnapshotDao _usageVMSnapshotDao;
@PostConstruct
void init() {
m_usageDao = _usageDao;
m_usageVMSnapshotDao = _usageVMSnapshotDao;
}
public static boolean parse(AccountVO account, Date startDate, Date endDate) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Parsing all VmSnapshot volume usage events for account: " + account.getId());
}
if ((endDate == null) || endDate.after(new Date())) {
endDate = new Date();
}
List<UsageVMSnapshotVO> usageUsageVMSnapshots =
m_usageVMSnapshotDao.getUsageRecords(account.getId(), account.getDomainId(), startDate, endDate);
if(usageUsageVMSnapshots.isEmpty()){
s_logger.debug("No VM snapshot usage events for this period");
return true;
}
Map<String, UsageVMSnapshotVO> unprocessedUsage = new HashMap<String, UsageVMSnapshotVO>();
for (UsageVMSnapshotVO usageRec : usageUsageVMSnapshots) {
long zoneId = usageRec.getZoneId();
Long volId = usageRec.getId();
long vmId = usageRec.getVmId();
String key = vmId+":"+volId;
if(usageRec.getCreated().before(startDate)){
unprocessedUsage.put(key, usageRec);
continue;
}
UsageVMSnapshotVO previousEvent = m_usageVMSnapshotDao.
getPreviousUsageRecord(usageRec);
if(previousEvent == null || previousEvent.getSize() == 0){
unprocessedUsage.put(key, usageRec);
continue;
}
Date previousCreated = previousEvent.getCreated();
if (previousCreated.before(startDate)) {
previousCreated = startDate;
}
Date createDate = usageRec.getCreated();
long duration = (createDate.getTime() - previousCreated.getTime()) + 1;
createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, previousCreated, createDate,
account, volId, zoneId, previousEvent.getDiskOfferingId(),
vmId, previousEvent.getSize());
previousEvent.setProcessed(new Date());
m_usageVMSnapshotDao.update(previousEvent);
if(usageRec.getSize() == 0){
usageRec.setProcessed(new Date());
m_usageVMSnapshotDao.update(usageRec);
}
else
unprocessedUsage.put(key, usageRec);
}
for (String key : unprocessedUsage.keySet()){
UsageVMSnapshotVO usageRec = unprocessedUsage.get(key);
Date created = usageRec.getCreated();
if (created.before(startDate)) {
created = startDate;
}
long duration = (endDate.getTime() - created.getTime()) + 1;
createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, created, endDate,
account, usageRec.getId(), usageRec.getZoneId(), usageRec.getDiskOfferingId(),
usageRec.getVmId(), usageRec.getSize());
}
return true;
}
private static void createUsageRecord(int type, long runningTime, Date startDate, Date endDate, AccountVO account, long volId, long zoneId, Long doId, Long vmId, long size) {
// Our smallest increment is hourly for now
if (s_logger.isDebugEnabled()) {
s_logger.debug("Total running time " + runningTime + "ms");
}
float usage = runningTime / 1000f / 60f / 60f;
DecimalFormat dFormat = new DecimalFormat("#.######");
String usageDisplay = dFormat.format(usage);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Creating VMSnapshot Volume usage record for vol: " + volId + ", usage: " + usageDisplay + ", startDate: " + startDate + ", endDate: " + endDate + ", for account: " + account.getId());
}
// Create the usage record
String usageDesc = "VMSnapshot Usage: " + "VM Id: " + vmId + " Volume Id: "+volId+" ";
if(doId != null){
usageDesc += " DiskOffering: " +doId ;
}
usageDesc += " Size: " + size;
UsageVO usageRecord = new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", type,
new Double(usage), vmId, null, doId, null, volId, size, startDate, endDate);
m_usageDao.persist(usageRecord);
}
}