diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java index 76b93909b8c1..9d64ab841214 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -44,9 +44,7 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { private Long physicalSize = (long) 0; private long accountId; - public SnapshotObjectTO() { - } public SnapshotObjectTO(SnapshotInfo snapshot) { diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index 7602a142f885..df7e476cc204 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -358,13 +358,16 @@ public void processEvent(ObjectInDataStoreStateMachine.Event event, Answer answe if (answer instanceof CreateObjectAnswer) { SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CreateObjectAnswer)answer).getData(); snapshotStore.setInstallPath(snapshotTO.getPath()); + if (snapshotTO.getPhysicalSize() != null && snapshotTO.getPhysicalSize() > 0L) { + snapshotStore.setPhysicalSize(snapshotTO.getPhysicalSize()); + } snapshotStoreDao.update(snapshotStore.getId(), snapshotStore); } else if (answer instanceof CopyCmdAnswer) { SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CopyCmdAnswer)answer).getNewData(); snapshotStore.setInstallPath(snapshotTO.getPath()); if (snapshotTO.getPhysicalSize() != null) { // For S3 delta snapshot, physical size is currently not set - snapshotStore.setPhysicalSize(snapshotTO.getPhysicalSize()); + snapshotStore.setPhysicalSize(snapshotTO.getPhysicalSize()); } if (snapshotTO.getParentSnapshotPath() == null) { snapshotStore.setParentSnapshotId(0L); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index b66a838a3a52..de5dca66f47d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1836,7 +1836,7 @@ protected static boolean isInterface(final String fname) { for (final String ifNamePattern : ifNamePatterns) { commonPattern.append("|(").append(ifNamePattern).append(".*)"); } - if(fname.matches(commonPattern.toString())) { + if (fname.matches(commonPattern.toString())) { return true; } return false; @@ -2128,11 +2128,10 @@ private String getBroadcastUriFromBridge(final String brName) { final Pattern pattern = Pattern.compile("(\\D+)(\\d+)(\\D*)(\\d*)(\\D*)(\\d*)"); final Matcher matcher = pattern.matcher(pif); LOGGER.debug("getting broadcast uri for pif " + pif + " and bridge " + brName); - if(matcher.find()) { + if (matcher.find()) { if (brName.startsWith("brvx")){ return BroadcastDomainType.Vxlan.toUri(matcher.group(2)).toString(); - } - else{ + } else { if (!matcher.group(6).isEmpty()) { return BroadcastDomainType.Vlan.toUri(matcher.group(6)).toString(); } else if (!matcher.group(4).isEmpty()) { @@ -3331,7 +3330,7 @@ public int compare(final DiskTO arg0, final DiskTO arg1) { } else if (volume.getType() == Volume.Type.DATADISK) { final KVMPhysicalDisk physicalDisk = storagePoolManager.getPhysicalDisk(store.getPoolType(), store.getUuid(), data.getPath()); final KVMStoragePool pool = physicalDisk.getPool(); - if(StoragePoolType.RBD.equals(pool.getType())) { + if (StoragePoolType.RBD.equals(pool.getType())) { final int devId = volume.getDiskSeq().intValue(); final String device = mapRbdDevice(physicalDisk); if (device != null) { @@ -4778,7 +4777,7 @@ protected long getMemoryFreeInKBs(Domain dm) throws LibvirtException { } for (int i = 0; i < memoryStats.length; i++) { - if(memoryStats[i].getTag() == UNUSEDMEMORY) { + if (memoryStats[i].getTag() == UNUSEDMEMORY) { freeMemory = memoryStats[i].getValue(); break; } @@ -5244,12 +5243,12 @@ public HypervisorType getHypervisorType(){ return hypervisorType; } - public String mapRbdDevice(final KVMPhysicalDisk disk){ + public String mapRbdDevice(final KVMPhysicalDisk disk) { final KVMStoragePool pool = disk.getPool(); //Check if rbd image is already mapped final String[] splitPoolImage = disk.getPath().split("/"); String device = Script.runSimpleBashScript("rbd showmapped | grep \""+splitPoolImage[0]+"[ ]*"+splitPoolImage[1]+"\" | grep -o \"[^ ]*[ ]*$\""); - if(device == null) { + if (device == null) { //If not mapped, map and return mapped device Script.runSimpleBashScript("rbd map " + disk.getPath() + " --id " + pool.getAuthUserName()); device = Script.runSimpleBashScript("rbd showmapped | grep \""+splitPoolImage[0]+"[ ]*"+splitPoolImage[1]+"\" | grep -o \"[^ ]*[ ]*$\""); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 33bd41ee6ba3..a7a9cc4b1339 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -43,6 +43,9 @@ import javax.naming.ConfigurationException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.cloudstack.agent.directdownload.DirectDownloadAnswer; import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand; import org.apache.cloudstack.direct.download.DirectDownloadHelper; @@ -305,7 +308,7 @@ public Answer copyTemplateToPrimaryStorage(final CopyCommand cmd) { newTemplate.setPath(primaryVol.getName()); newTemplate.setSize(primaryVol.getSize()); - if(List.of( + if (List.of( StoragePoolType.RBD, StoragePoolType.PowerFlex, StoragePoolType.Linstor, @@ -696,7 +699,7 @@ public Answer createTemplateFromVolume(final CopyCommand cmd) { templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator"); - try(FileOutputStream templFo = new FileOutputStream(templateProp);){ + try (FileOutputStream templFo = new FileOutputStream(templateProp);) { templFo.write(templateContent.getBytes()); templFo.flush(); } catch (final IOException e) { @@ -761,11 +764,9 @@ private Answer createTemplateFromVolumeOrSnapshot(CopyCommand cmd) { if (srcData instanceof VolumeObjectTO) { isVolume = true; - } - else if (srcData instanceof SnapshotObjectTO) { + } else if (srcData instanceof SnapshotObjectTO) { isVolume = false; - } - else { + } else { return new CopyCmdAnswer("unsupported object type"); } @@ -831,8 +832,7 @@ else if (srcData instanceof SnapshotObjectTO) { if (isVolume) { templateContent += "volume.name=" + dateFormat.format(date) + System.getProperty("line.separator"); - } - else { + } else { templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator"); } @@ -870,8 +870,7 @@ else if (srcData instanceof SnapshotObjectTO) { } catch (Exception ex) { if (isVolume) { logger.debug("Failed to create template from volume: ", ex); - } - else { + } else { logger.debug("Failed to create template from snapshot: ", ex); } @@ -1034,7 +1033,7 @@ public Answer backupSnapshot(final CopyCommand cmd) { q.convert(srcFile, destFile); final File snapFile = new File(snapshotFile); - if(snapFile.exists()) { + if (snapFile.exists()) { size = snapFile.length(); } @@ -1067,7 +1066,7 @@ public Answer backupSnapshot(final CopyCommand cmd) { return new CopyCmdAnswer(result); } final File snapFile = new File(snapshotDestPath + "/" + descName); - if(snapFile.exists()){ + if (snapFile.exists()) { size = snapFile.length(); } } @@ -1404,7 +1403,7 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean if (resource.getHypervisorType() == Hypervisor.HypervisorType.LXC) { final String device = resource.mapRbdDevice(attachingDisk); if (device != null) { - logger.debug("RBD device on host is: "+device); + logger.debug("RBD device on host is: " + device); attachingDisk.setPath(device); } } @@ -1441,11 +1440,11 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean } diskdef.setSerial(serial); if (attachingPool.getType() == StoragePoolType.RBD) { - if(resource.getHypervisorType() == Hypervisor.HypervisorType.LXC){ + if (resource.getHypervisorType() == Hypervisor.HypervisorType.LXC) { // For LXC, map image to host and then attach to Vm final String device = resource.mapRbdDevice(attachingDisk); if (device != null) { - logger.debug("RBD device on host is: "+device); + logger.debug("RBD device on host is: " + device); diskdef.defBlockBasedDisk(device, devId, busT); } else { throw new InternalErrorException("Error while mapping disk "+attachingDisk.getPath()+" on host"); @@ -1515,7 +1514,7 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean if ((iopsWriteRateMaxLength != null) && (iopsWriteRateMaxLength > 0)) { diskdef.setIopsWriteRateMaxLength(iopsWriteRateMaxLength); } - if(cacheMode != null) { + if (cacheMode != null) { diskdef.setCacheMode(DiskDef.DiskCacheMode.valueOf(cacheMode.toUpperCase())); } @@ -1675,7 +1674,7 @@ public Answer createVolume(final CreateObjectCommand cmd) { } final VolumeObjectTO newVol = new VolumeObjectTO(); - if(vol != null) { + if (vol != null) { newVol.setPath(vol.getName()); if (vol.getQemuEncryptFormat() != null) { newVol.setEncryptFormat(vol.getQemuEncryptFormat().toString()); @@ -1778,6 +1777,7 @@ public Answer createSnapshot(final CreateObjectCommand cmd) { String diskPath = disk.getPath(); String snapshotPath = diskPath + File.separator + snapshotName; + Long snapshotSize = null; if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING && !primaryPool.isExternalSnapshot()) { validateAvailableSizeOnPoolToTakeVolumeSnapshot(primaryPool, disk); @@ -1838,6 +1838,11 @@ public Answer createSnapshot(final CreateObjectCommand cmd) { logger.debug("Attempting to create RBD snapshot " + disk.getName() + "@" + snapshotName); image.snapCreate(snapshotName); + long rbdSnapshotSize = getRbdSnapshotSize(primaryPool.getSourceDir(), disk.getName(), snapshotName, primaryPool.getSourceHost(), primaryPool.getAuthUserName(), primaryPool.getAuthSecret()); + if (rbdSnapshotSize > 0) { + snapshotSize = rbdSnapshotSize; + } + rbd.close(image); r.ioCtxDestroy(io); } catch (final Exception e) { @@ -1861,8 +1866,11 @@ public Answer createSnapshot(final CreateObjectCommand cmd) { } final SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); - newSnapshot.setPath(snapshotPath); + if (snapshotSize != null) { + newSnapshot.setPhysicalSize(snapshotSize); + } + return new CreateObjectAnswer(newSnapshot); } catch (CloudRuntimeException | LibvirtException | IOException ex) { String errorMsg = String.format("Failed take snapshot for volume [%s], in VM [%s], due to [%s].", volume, vmName, ex.getMessage()); @@ -1873,6 +1881,31 @@ public Answer createSnapshot(final CreateObjectCommand cmd) { } } + private long getRbdSnapshotSize(String poolPath, String diskName, String snapshotName, String rbdMonitor, String authUser, String authSecret) { + logger.debug("Get RBD snapshot size for {}/{}@{}", poolPath, diskName, snapshotName); + //cmd: rbd du /@ --format json --mon-host --id --key 2>/dev/null + String snapshotDetailsInJson = Script.runSimpleBashScript(String.format("rbd du %s/%s@%s --format json --mon-host %s --id %s --key %s 2>/dev/null", poolPath, diskName, snapshotName, rbdMonitor, authUser, authSecret)); + if (StringUtils.isNotBlank(snapshotDetailsInJson)) { + ObjectMapper mapper = new ObjectMapper(); + try { + JsonNode root = mapper.readTree(snapshotDetailsInJson); + for (JsonNode image : root.path("images")) { + if (snapshotName.equals(image.path("snapshot").asText())) { + long usedSizeInBytes = image.path("used_size").asLong(); + logger.debug("RBD snapshot {}/{}@{} used size in bytes: {}", poolPath, diskName, snapshotName, usedSizeInBytes); + return usedSizeInBytes; + } + } + } catch (JsonProcessingException e) { + logger.error("Unable to get the RBD snapshot size, RBD snapshot cmd output: {}", snapshotDetailsInJson, e); + } + } else { + logger.warn("Failed to get RBD snapshot size for {}/{}@{} - no output for RBD snapshot cmd", poolPath, diskName, snapshotName); + } + + return 0; + } + protected void deleteFullVmSnapshotAfterConvertingItToExternalDiskSnapshot(Domain vm, String snapshotName, VolumeObjectTO volume, String vmName) throws LibvirtException { logger.debug(String.format("Deleting full Instance Snapshot [%s] of Instance [%s] as we already converted it to an external disk Snapshot of the volume [%s].", snapshotName, vmName, volume));