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

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

commit 91f10143fd84998aad14efa4c250ad81e54c50ca
Author: Harikrishna Patnala <[email protected]>
AuthorDate: Tue Jan 30 12:57:48 2024 +0530

    Introduced a new API checkVolumeAndRepair that allows users or admins to 
check and repair if any leaks observed. Currently this is supported only for KVM
---
 api/src/main/java/com/cloud/event/EventTypes.java  |   1 +
 .../java/com/cloud/storage/VolumeApiService.java   |   4 +
 .../org/apache/cloudstack/api/ApiConstants.java    |   5 +
 .../admin/volume/RecoverVolumeCmdByAdmin.java      |   2 +-
 .../user/volume/CheckVolumeAndRepairCmd.java       | 116 +++++++++++++++
 .../cloudstack/api/response/VolumeResponse.java    |  25 ++++
 .../api/storage/CheckVolumeAndRepairAnswer.java    |  76 ++++++++++
 .../api/storage/CheckVolumeAndRepairCommand.java   |  77 ++++++++++
 .../subsystem/api/storage/VolumeService.java       |   2 +
 .../com/cloud/vm/VmWorkCheckAndRepairVolume.java   |  41 ++++++
 .../storage/volume/VolumeServiceImpl.java          |  30 ++++
 .../LibvirtCheckVolumeAndRepairCommandWrapper.java | 116 +++++++++++++++
 .../org/apache/cloudstack/utils/qemu/QemuImg.java  |  39 +++++
 .../kvm/storage/LibvirtStoragePoolTest.java        |   3 +
 .../com/cloud/server/ManagementServerImpl.java     |   2 +
 .../cloud/storage/CheckAndRepairVolumePayload.java |  58 ++++++++
 .../com/cloud/storage/VolumeApiServiceImpl.java    | 162 +++++++++++++++++++++
 .../src/main/java/com/cloud/utils/StringUtils.java |  22 +++
 18 files changed, 780 insertions(+), 1 deletion(-)

diff --git a/api/src/main/java/com/cloud/event/EventTypes.java 
b/api/src/main/java/com/cloud/event/EventTypes.java
index 67fed5500ee..41817525df2 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -303,6 +303,7 @@ public class EventTypes {
     public static final String EVENT_VOLUME_CREATE = "VOLUME.CREATE";
     public static final String EVENT_VOLUME_DELETE = "VOLUME.DELETE";
     public static final String EVENT_VOLUME_ATTACH = "VOLUME.ATTACH";
+    public static final String EVENT_VOLUME_CHECK = "VOLUME.CHECK";
     public static final String EVENT_VOLUME_DETACH = "VOLUME.DETACH";
     public static final String EVENT_VOLUME_EXTRACT = "VOLUME.EXTRACT";
     public static final String EVENT_VOLUME_UPLOAD = "VOLUME.UPLOAD";
diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java 
b/api/src/main/java/com/cloud/storage/VolumeApiService.java
index 8d5f7892f10..652d4ab3cc8 100644
--- a/api/src/main/java/com/cloud/storage/VolumeApiService.java
+++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java
@@ -22,9 +22,11 @@ import java.net.MalformedURLException;
 import java.util.List;
 import java.util.Map;
 
+import com.cloud.utils.Pair;
 import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
 import 
org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.CheckVolumeAndRepairCmd;
 import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd;
@@ -178,4 +180,6 @@ public interface VolumeApiService {
     void publishVolumeCreationUsageEvent(Volume volume);
 
     boolean stateTransitTo(Volume vol, Volume.Event event) throws 
NoTransitionException;
+
+    Pair<String, String> checkAndRepairVolume(CheckVolumeAndRepairCmd cmd) 
throws ResourceAllocationException;
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index db0c5ce494c..e3616900a8b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -379,6 +379,8 @@ public class ApiConstants {
     public static final String RECEIVED_BYTES = "receivedbytes";
     public static final String RECONNECT = "reconnect";
     public static final String RECOVER = "recover";
+
+    public static final String REPAIR = "repair";
     public static final String REQUIRES_HVM = "requireshvm";
     public static final String RESOURCE_NAME = "resourcename";
     public static final String RESOURCE_TYPE = "resourcetype";
@@ -502,6 +504,9 @@ public class ApiConstants {
     public static final String IS_VOLATILE = "isvolatile";
     public static final String VOLUME_ID = "volumeid";
     public static final String VOLUMES = "volumes";
+    public static final String VOLUME_CHECK_RESULT = "volumecheckresult";
+    public static final String VOLUME_REPAIR_RESULT = "volumerepairresult";
+
     public static final String ZONE = "zone";
     public static final String ZONE_ID = "zoneid";
     public static final String ZONE_NAME = "zonename";
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java
index f51aeec9719..92c62fa8662 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/RecoverVolumeCmdByAdmin.java
@@ -30,7 +30,7 @@ import org.apache.cloudstack.context.CallContext;
 
 import com.cloud.storage.Volume;
 
-@APICommand(name = "recoverVolume", description = "Recovers a Destroy 
volume.", responseObject = VolumeResponse.class,  responseView = 
ResponseView.Full, entityType = {Volume.class},
+@APICommand(name = "recoverVolume", description = "Recovers a Destroy volume, 
curr", responseObject = VolumeResponse.class,  responseView = 
ResponseView.Full, entityType = {Volume.class},
             since = "4.14.0",
             authorized = {RoleType.Admin},
             requestHasSensitiveInfo = false,
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckVolumeAndRepairCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckVolumeAndRepairCmd.java
new file mode 100644
index 00000000000..268ccdcc0a4
--- /dev/null
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckVolumeAndRepairCmd.java
@@ -0,0 +1,116 @@
+// 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 org.apache.cloudstack.api.command.user.volume;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiCommandResourceType;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ResponseObject.ResponseView;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.VolumeResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.log4j.Logger;
+
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.storage.Volume;
+import com.cloud.user.Account;
+import com.cloud.utils.Pair;
+import com.cloud.utils.StringUtils;
+
+@APICommand(name = "checkVolumeAndRepair", description = "Check the volume and 
repair if needed, this is currently supported for KVM only", responseObject = 
VolumeResponse.class, entityType = {Volume.class},
+        since = "4.18.1",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, 
RoleType.DomainAdmin, RoleType.User},
+        requestHasSensitiveInfo = false,
+        responseHasSensitiveInfo = true)
+public class CheckVolumeAndRepairCmd extends BaseCmd {
+    public static final Logger s_logger = 
Logger.getLogger(CheckVolumeAndRepairCmd.class.getName());
+
+    private static final String s_name = "checkvolumeandrepairresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = 
VolumeResponse.class, required = true, description = "The ID of the volume")
+    private Long id;
+
+    @Parameter(name = ApiConstants.REPAIR, type = CommandType.BOOLEAN, 
required = false, description = "true if the volume has leaks and repair the 
volume")
+    private Boolean repair;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public boolean getRepair() {
+        return repair == null ? false : repair;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        Volume volume = _entityMgr.findById(Volume.class, getId());
+        if (volume != null) {
+            return volume.getAccountId();
+        }
+
+        return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent 
this command to SYSTEM so ERROR events are tracked
+    }
+
+    @Override
+    public Long getApiResourceId() {
+        return id;
+    }
+
+    @Override
+    public ApiCommandResourceType getApiResourceType() {
+        return ApiCommandResourceType.Volume;
+    }
+
+    @Override
+    public void execute() throws ResourceAllocationException {
+        CallContext.current().setEventDetails("Volume Id: " + getId());
+        Pair<String, String> result = 
_volumeService.checkAndRepairVolume(this);
+        Volume volume = _responseGenerator.findVolumeById(getId());
+        if (result != null) {
+            VolumeResponse response = 
_responseGenerator.createVolumeResponse(ResponseView.Full, volume);
+            
response.setVolumeCheckResult(StringUtils.parseJsonToMap(result.first()));
+            if (getRepair()) {
+                
response.setVolumeRepairResult(StringUtils.parseJsonToMap(result.second()));
+            }
+            response.setResponseName(getCommandName());
+            setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed 
to check volume and repair");
+        }
+    }
+}
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java
index 00a1eabc40b..785284ad46f 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java
@@ -18,6 +18,7 @@ package org.apache.cloudstack.api.response;
 
 import java.util.Date;
 import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.cloudstack.acl.RoleType;
@@ -288,6 +289,14 @@ public class VolumeResponse extends 
BaseResponseWithTagInformation implements Co
     @Param(description = "volume uuid that is given by virtualisation provider 
(only for VMware)")
     private String externalUuid;
 
+    @SerializedName(ApiConstants.VOLUME_CHECK_RESULT)
+    @Param(description = "details for the volume check result")
+    private Map<String, String> volumeCheckResult;
+
+    @SerializedName(ApiConstants.VOLUME_REPAIR_RESULT)
+    @Param(description = "details for the volume repair result")
+    private Map<String, String> volumeRepairResult;
+
     public String getPath() {
         return path;
     }
@@ -817,4 +826,20 @@ public class VolumeResponse extends 
BaseResponseWithTagInformation implements Co
     public void setExternalUuid(String externalUuid) {
         this.externalUuid = externalUuid;
     }
+
+    public Map<String, String> getVolumeCheckResult() {
+        return volumeCheckResult;
+    }
+
+    public void setVolumeCheckResult(Map<String, String> volumeCheckResult) {
+        this.volumeCheckResult = volumeCheckResult;
+    }
+
+    public Map<String, String> getVolumeRepairResult() {
+        return volumeRepairResult;
+    }
+
+    public void setVolumeRepairResult(Map<String, String> volumeRepairResult) {
+        this.volumeRepairResult = volumeRepairResult;
+    }
 }
diff --git 
a/core/src/main/java/com/cloud/agent/api/storage/CheckVolumeAndRepairAnswer.java
 
b/core/src/main/java/com/cloud/agent/api/storage/CheckVolumeAndRepairAnswer.java
new file mode 100644
index 00000000000..526e8933406
--- /dev/null
+++ 
b/core/src/main/java/com/cloud/agent/api/storage/CheckVolumeAndRepairAnswer.java
@@ -0,0 +1,76 @@
+//
+// 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.agent.api.storage;
+
+import com.cloud.agent.api.Answer;
+
+public class CheckVolumeAndRepairAnswer extends Answer {
+    private long leaks;
+    private boolean repaired;
+    private long leaksFixed;
+    private String volumeCheckExecutionResult;
+    private String volumeRepairedExecutionResult;
+
+    protected CheckVolumeAndRepairAnswer() {
+        super();
+    }
+
+    public CheckVolumeAndRepairAnswer(CheckVolumeAndRepairCommand cmd, boolean 
result, String details, long leaks,
+                                      boolean repaired, long leaksFixed, 
String volumeCheckExecutionResult, String volumeRepairedExecutionResult) {
+        super(cmd, result, details);
+        this.leaks = leaks;
+        this.repaired = repaired;
+        this.leaksFixed = leaksFixed;
+        this.volumeCheckExecutionResult = volumeCheckExecutionResult;
+        this.volumeRepairedExecutionResult = volumeRepairedExecutionResult;
+    }
+
+    public CheckVolumeAndRepairAnswer(CheckVolumeAndRepairCommand cmd, boolean 
result, String details) {
+        super(cmd, result, details);
+    }
+
+    public long getLeaks() {
+        return leaks;
+    }
+
+    public boolean isRepaired() {
+        return repaired;
+    }
+
+    public long getLeaksFixed() {
+        return leaksFixed;
+    }
+
+    public String getVolumeCheckExecutionResult() {
+        return volumeCheckExecutionResult;
+    }
+
+    public String getVolumeRepairedExecutionResult() {
+        return volumeRepairedExecutionResult;
+    }
+
+    public void setVolumeCheckExecutionResult(String 
volumeCheckExecutionResult) {
+        this.volumeCheckExecutionResult = volumeCheckExecutionResult;
+    }
+
+    public void setVolumeRepairedExecutionResult(String 
volumeRepairedExecutionResult) {
+        this.volumeRepairedExecutionResult = volumeRepairedExecutionResult;
+    }
+}
diff --git 
a/core/src/main/java/com/cloud/agent/api/storage/CheckVolumeAndRepairCommand.java
 
b/core/src/main/java/com/cloud/agent/api/storage/CheckVolumeAndRepairCommand.java
new file mode 100644
index 00000000000..040e6ca8608
--- /dev/null
+++ 
b/core/src/main/java/com/cloud/agent/api/storage/CheckVolumeAndRepairCommand.java
@@ -0,0 +1,77 @@
+//
+// 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.agent.api.storage;
+
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.LogLevel;
+import com.cloud.agent.api.to.StorageFilerTO;
+
+import java.util.Arrays;
+
+public class CheckVolumeAndRepairCommand extends Command {
+    private String path;
+    private StorageFilerTO pool;
+    private boolean repair;
+    @LogLevel(LogLevel.Log4jLevel.Off)
+    private byte[] passphrase;
+    private String encryptFormat;
+
+    public CheckVolumeAndRepairCommand(String path, StorageFilerTO pool, 
boolean repair, byte[] passphrase, String encryptFormat) {
+        this.path = path;
+        this.pool = pool;
+        this.repair = repair;
+        this.passphrase = passphrase;
+        this.encryptFormat = encryptFormat;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getPoolUuid() {
+        return pool.getUuid();
+    }
+
+    public StorageFilerTO getPool() {
+        return pool;
+    }
+
+    public boolean needRepair() {
+        return repair;
+    }
+
+    public String getEncryptFormat() { return encryptFormat; }
+
+    public byte[] getPassphrase() { return passphrase; }
+
+    public void clearPassphrase() {
+        if (this.passphrase != null) {
+            Arrays.fill(this.passphrase, (byte) 0);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+}
diff --git 
a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
 
b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
index 50aee83f497..4350e264125 100644
--- 
a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
+++ 
b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
@@ -115,4 +115,6 @@ public interface VolumeService {
       VolumeInfo sourceVolume, VolumeInfo destinationVolume, boolean 
retryExpungeVolumeAsync);
 
     void moveVolumeOnSecondaryStorageToAnotherAccount(Volume volume, Account 
sourceAccount, Account destAccount);
+
+    Pair<String, String> checkAndRepairVolume(VolumeInfo volume);
 }
diff --git 
a/engine/components-api/src/main/java/com/cloud/vm/VmWorkCheckAndRepairVolume.java
 
b/engine/components-api/src/main/java/com/cloud/vm/VmWorkCheckAndRepairVolume.java
new file mode 100644
index 00000000000..f4053dd1741
--- /dev/null
+++ 
b/engine/components-api/src/main/java/com/cloud/vm/VmWorkCheckAndRepairVolume.java
@@ -0,0 +1,41 @@
+// 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.vm;
+
+public class VmWorkCheckAndRepairVolume extends VmWork {
+
+    private static final long serialVersionUID = 341816293003023823L;
+
+    private Long volumeId;
+
+    private boolean repair;
+
+    public VmWorkCheckAndRepairVolume(long userId, long accountId, long vmId, 
String handlerName,
+                                    Long volumeId, boolean repair) {
+        super(userId, accountId, vmId, handlerName);
+        this.repair = repair;
+        this.volumeId = volumeId;
+    }
+
+    public Long getVolumeId() {
+        return volumeId;
+    }
+
+    public boolean needRepair() {
+        return repair;
+    }
+}
diff --git 
a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
 
b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
index 8a3cd39ecbf..8c839a9b4d4 100644
--- 
a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
+++ 
b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
@@ -2760,6 +2760,36 @@ public class VolumeServiceImpl implements VolumeService {
         return snapshot;
     }
 
+    @Override
+    public Pair<String, String> checkAndRepairVolume(VolumeInfo volume) {
+        Long poolId = volume.getPoolId();
+        StoragePool pool = _storageMgr.getStoragePool(poolId);
+        CheckAndRepairVolumePayload payload = (CheckAndRepairVolumePayload) 
volume.getpayload();
+        CheckVolumeAndRepairCommand command = new 
CheckVolumeAndRepairCommand(volume.getPath(), new StorageFilerTO(pool), 
payload.isRepair(),
+                 volume.getPassphrase(), volume.getEncryptFormat());
+
+        try {
+            CheckVolumeAndRepairAnswer answer = (CheckVolumeAndRepairAnswer) 
_storageMgr.sendToPool(pool, null, command);
+            if (answer != null && answer.getResult()) {
+                s_logger.debug("Check volume response result: " + 
answer.getDetails());
+                
payload.setVolumeCheckExecutionResult(answer.getVolumeCheckExecutionResult());
+                if (payload.isRepair()) {
+                    
payload.setVolumeRepairedExecutionResult(answer.getVolumeRepairedExecutionResult());
+                }
+                return new Pair<>(answer.getVolumeCheckExecutionResult(), 
answer.getVolumeRepairedExecutionResult());
+            } else {
+                s_logger.debug("Failed to check and repair the volume with 
error " + answer.getDetails());
+            }
+
+        } catch (Exception e) {
+            s_logger.debug("sending check and repair volume command failed", 
e);
+        } finally {
+            command.clearPassphrase();
+        }
+
+        return null;
+    }
+
     // For managed storage on Xen and VMware, we need to potentially make 
space for hypervisor snapshots.
     // The disk offering can collect this information and pass it on to the 
volume that's about to be created.
     // Ex. if you want a 10 GB CloudStack volume to reside on managed storage 
on Xen, this leads to an SR
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeAndRepairCommandWrapper.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeAndRepairCommandWrapper.java
new file mode 100644
index 00000000000..553de2953c1
--- /dev/null
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeAndRepairCommandWrapper.java
@@ -0,0 +1,116 @@
+//
+// 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.hypervisor.kvm.resource.wrapper;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.storage.CheckVolumeAndRepairCommand;
+import com.cloud.agent.api.storage.CheckVolumeAndRepairAnswer;
+import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
+import com.cloud.storage.Storage;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.utils.cryptsetup.KeyFile;
+import org.apache.cloudstack.utils.qemu.QemuImageOptions;
+import org.apache.cloudstack.utils.qemu.QemuImg;
+import org.apache.cloudstack.utils.qemu.QemuImgException;
+import org.apache.cloudstack.utils.qemu.QemuImgFile;
+import org.apache.cloudstack.utils.qemu.QemuObject;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.log4j.Logger;
+import org.libvirt.LibvirtException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@ResourceWrapper(handles =  CheckVolumeAndRepairCommand.class)
+public final class LibvirtCheckVolumeAndRepairCommandWrapper extends 
CommandWrapper<CheckVolumeAndRepairCommand, Answer, LibvirtComputingResource> {
+
+    private static final Logger s_logger = 
Logger.getLogger(LibvirtCheckVolumeAndRepairCommandWrapper.class);
+
+    @Override
+    public Answer execute(CheckVolumeAndRepairCommand command, 
LibvirtComputingResource serverResource) {
+        final String volumeId = command.getPath();
+        final boolean repair = command.needRepair();
+        final StorageFilerTO spool = command.getPool();
+
+        final KVMStoragePoolManager storagePoolMgr = 
serverResource.getStoragePoolMgr();
+        KVMStoragePool pool = storagePoolMgr.getStoragePool(spool.getType(), 
spool.getUuid());
+
+        if (spool.getType().equals(Storage.StoragePoolType.PowerFlex)) {
+            pool.connectPhysicalDisk(volumeId, null);
+        }
+
+        final KVMPhysicalDisk vol = pool.getPhysicalDisk(volumeId);
+        QemuObject.EncryptFormat encryptFormat = 
QemuObject.EncryptFormat.enumValue(command.getEncryptFormat());
+        try {
+            String checkVolumeResult = checkVolumeAndRepair(vol, false, 
encryptFormat, command.getPassphrase(), serverResource);
+            s_logger.info(String.format("Check Volume result is %s", 
checkVolumeResult));
+            CheckVolumeAndRepairAnswer answer = new 
CheckVolumeAndRepairAnswer(command, true, checkVolumeResult);
+            answer.setVolumeCheckExecutionResult(checkVolumeResult);
+
+            if (repair) {
+                String repairVolumeResult = checkVolumeAndRepair(vol, true, 
encryptFormat, command.getPassphrase(), serverResource);
+                String finalResult = (checkVolumeResult != null ? 
checkVolumeResult.concat(",") : "") + repairVolumeResult;
+                s_logger.info(String.format("Repair Volume result for the 
volume %s is %s", vol.getName(), repairVolumeResult));
+
+                answer = new CheckVolumeAndRepairAnswer(command, true, 
finalResult);
+                answer.setVolumeRepairedExecutionResult(repairVolumeResult);
+                answer.setVolumeCheckExecutionResult(checkVolumeResult);
+            }
+            return answer;
+        } catch (Exception e) {
+            return new CheckVolumeAndRepairAnswer(command, false, 
e.toString());
+        }
+    }
+
+    private String checkVolumeAndRepair(final KVMPhysicalDisk vol, final 
boolean repair, final QemuObject.EncryptFormat encryptFormat, byte[] 
passphrase, final LibvirtComputingResource libvirtComputingResource) throws 
CloudRuntimeException {
+        List<QemuObject> passphraseObjects = new ArrayList<>();
+        QemuImageOptions imgOptions = null;
+        if (ArrayUtils.isEmpty(passphrase)) {
+            passphrase = null;
+        }
+        try (KeyFile keyFile = new KeyFile(passphrase)) {
+            if (passphrase != null) {
+                passphraseObjects.add(
+                        QemuObject.prepareSecretForQemuImg(vol.getFormat(), 
encryptFormat, keyFile.toString(), "sec0", null)
+                );
+                imgOptions = new QemuImageOptions(vol.getFormat(), 
vol.getPath(),"sec0");
+            }
+            QemuImg q = new QemuImg(libvirtComputingResource.getCmdsTimeout());
+            QemuImgFile file = new QemuImgFile(vol.getPath());
+            return q.checkAndRepair(file, imgOptions, passphraseObjects, 
repair);
+        } catch (QemuImgException | LibvirtException ex) {
+            throw new CloudRuntimeException("Failed to run qemu-img for check 
volume", ex);
+        } catch (IOException ex) {
+            throw new CloudRuntimeException("Failed to create keyfile for 
encrypted volume for check volume operation", ex);
+        } finally {
+            if (passphrase != null) {
+                Arrays.fill(passphrase, (byte) 0);
+            }
+        }
+    }
+}
diff --git 
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java
 
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java
index 1cd63b9b566..9a71a596420 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java
@@ -812,4 +812,43 @@ public class QemuImg {
         Pattern pattern = 
Pattern.compile("Supported\\sformats:[a-zA-Z0-9-_\\s]*?\\b" + format + "\\b", 
CASE_INSENSITIVE);
         return pattern.matcher(text).find();
     }
+
+    /**
+     * check for any leaks for an image and repair.
+     *
+     * @param imageOptions
+     *         Qemu style image options to be used in the checking process.
+     * @param qemuObjects
+     *         Qemu style options (e.g. for passing secrets).
+     * @param repair
+     *         Boolean option whether to repair any leaks
+     */
+    public String checkAndRepair(final QemuImgFile file, final 
QemuImageOptions imageOptions, final List<QemuObject> qemuObjects, final 
boolean repair) throws QemuImgException {
+        final Script s = new Script(_qemuImgPath);
+        s.add("check");
+        s.add(file.getFileName());
+
+        for (QemuObject o : qemuObjects) {
+            s.add(o.toCommandFlag());
+        }
+
+        if (imageOptions != null) {
+            s.add(imageOptions.toCommandFlag());
+        }
+
+        s.add("--output=json");
+
+        if (repair) {
+            s.add("-r");
+            s.add("leaks");
+        }
+
+        OutputInterpreter.AllLinesParser parser = new 
OutputInterpreter.AllLinesParser();
+        final String result = s.execute(parser);
+        if (result != null) {
+            throw new QemuImgException(result);
+        }
+
+        return (parser.getLines() != null) ? parser.getLines().trim() : null;
+    }
 }
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePoolTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePoolTest.java
index b2c58fd9b96..88d4daa2dab 100644
--- 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePoolTest.java
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePoolTest.java
@@ -87,6 +87,9 @@ public class LibvirtStoragePoolTest extends TestCase {
         StoragePool storage = Mockito.mock(StoragePool.class);
 
         LibvirtStoragePool nfsPool = new LibvirtStoragePool(uuid, name, 
StoragePoolType.NetworkFilesystem, adapter, storage);
+        if (nfsPool.getType() != StoragePoolType.NetworkFilesystem) {
+            System.out.println("tested");
+        }
         assertFalse(nfsPool.isExternalSnapshot());
 
         LibvirtStoragePool rbdPool = new LibvirtStoragePool(uuid, name, 
StoragePoolType.RBD, adapter, storage);
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java 
b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index a73ba9b092c..56df8d501ee 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -553,6 +553,7 @@ import 
org.apache.cloudstack.api.command.user.volume.AddResourceDetailCmd;
 import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
 import 
org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.CheckVolumeAndRepairCmd;
 import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.DestroyVolumeCmd;
@@ -3706,6 +3707,7 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
         cmdList.add(ListVMGroupsCmd.class);
         cmdList.add(UpdateVMGroupCmd.class);
         cmdList.add(AttachVolumeCmd.class);
+        cmdList.add(CheckVolumeAndRepairCmd.class);
         cmdList.add(CreateVolumeCmd.class);
         cmdList.add(DeleteVolumeCmd.class);
         cmdList.add(UpdateVolumeCmd.class);
diff --git 
a/server/src/main/java/com/cloud/storage/CheckAndRepairVolumePayload.java 
b/server/src/main/java/com/cloud/storage/CheckAndRepairVolumePayload.java
new file mode 100644
index 00000000000..16a1531b1d6
--- /dev/null
+++ b/server/src/main/java/com/cloud/storage/CheckAndRepairVolumePayload.java
@@ -0,0 +1,58 @@
+// 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.storage;
+
+public class CheckAndRepairVolumePayload {
+
+    public final boolean repair;
+    public String result;
+    private String volumeCheckExecutionResult;
+    private String volumeRepairedExecutionResult;
+
+    public CheckAndRepairVolumePayload(boolean repair) {
+        this.repair = repair;
+    }
+
+    public boolean isRepair() {
+        return repair;
+    }
+
+    public String getResult() {
+        return result;
+    }
+
+    public void setResult(String result) {
+        this.result = result;
+    }
+
+    public String getVolumeCheckExecutionResult() {
+        return volumeCheckExecutionResult;
+    }
+
+    public String getVolumeRepairedExecutionResult() {
+        return volumeRepairedExecutionResult;
+    }
+
+    public void setVolumeCheckExecutionResult(String 
volumeCheckExecutionResult) {
+        this.volumeCheckExecutionResult = volumeCheckExecutionResult;
+    }
+
+    public void setVolumeRepairedExecutionResult(String 
volumeRepairedExecutionResult) {
+        this.volumeRepairedExecutionResult = volumeRepairedExecutionResult;
+    }
+}
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java 
b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 2a0821c5c0a..a82cc1f1bd8 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -42,6 +42,7 @@ import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
 import 
org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.CheckVolumeAndRepairCmd;
 import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd;
@@ -217,6 +218,7 @@ import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.VmWork;
 import com.cloud.vm.VmWorkAttachVolume;
+import com.cloud.vm.VmWorkCheckAndRepairVolume;
 import com.cloud.vm.VmWorkConstants;
 import com.cloud.vm.VmWorkDetachVolume;
 import com.cloud.vm.VmWorkExtractVolume;
@@ -1817,7 +1819,142 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         s_logger.debug(String.format("Volume [%s] has been successfully 
recovered, thus a new usage event %s has been published.", volume.getUuid(), 
EventTypes.EVENT_VOLUME_CREATE));
     }
 
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CHECK, eventDescription = 
"checking volume and repair if needed", async = true)
+    public Pair<String, String> checkAndRepairVolume(CheckVolumeAndRepairCmd 
cmd) throws ResourceAllocationException {
+        Account caller = CallContext.current().getCallingAccount();
+
+        // Verify input parameters
+        long volumeId = cmd.getId();
+        boolean repair = cmd.getRepair();
+        final VolumeVO volume = _volsDao.findById(volumeId);
+
+        _accountMgr.checkAccess(caller, null, true, volume);
+
+        Long vmId = volume.getInstanceId();
+        UserVmVO vm = null;
+        if (vmId != null) {
+            vm = _userVmDao.findById(vmId);
+            if (vm == null) {
+                throw new InvalidParameterValueException(String.format("VM not 
found, please check the VM to which this volume %d is attached", volumeId));
+            }
+            if (vm.getState() != State.Stopped) {
+                throw new InvalidParameterValueException(String.format("VM to 
which the volume %d is attached should be in stopped state", volumeId));
+            }
+        }
+
+        if (volume.getState() != Volume.State.Ready) {
+            throw new InvalidParameterValueException(String.format("VolumeId: 
%d is not in Ready state", volumeId));
+        }
+
+        HypervisorType hypervisorType = 
_volsDao.getHypervisorType(volume.getId());
+        if (!HypervisorType.KVM.equals(hypervisorType)) {
+            throw new InvalidParameterValueException(String.format("Check and 
Repair volumes is supported only for KVM hypervisor"));
+        }
+
+        if (vm != null) {
+            _accountMgr.checkAccess(caller, null, true, vm);
+            // serialize VM operation
+            AsyncJobExecutionContext jobContext = 
AsyncJobExecutionContext.getCurrentExecutionContext();
+            if 
(jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
+                // avoid re-entrance
+
+                VmWorkJobVO placeHolder = null;
+                placeHolder = createPlaceHolderWork(vm.getId());
+                try {
+                    Pair<String, String> result = 
orchestrateCheckVolumeAndRepair(volumeId, repair);
+                    return result;
+                } finally {
+                    _workJobDao.expunge(placeHolder.getId());
+                }
+            } else {
+                Outcome<Pair> outcome = 
checkVolumeAndRepairThroughJobQueue(vm.getId(), volumeId, repair);
+
+                try {
+                    outcome.get();
+                } catch (InterruptedException e) {
+                    throw new RuntimeException("Operation is interrupted", e);
+                } catch (java.util.concurrent.ExecutionException e) {
+                    throw new RuntimeException("Execution exception--", e);
+                }
+
+                Object jobResult = 
_jobMgr.unmarshallResultObject(outcome.getJob());
+                if (jobResult != null) {
+                    if (jobResult instanceof ConcurrentOperationException) {
+                        throw (ConcurrentOperationException)jobResult;
+                    } else if (jobResult instanceof 
ResourceAllocationException) {
+                        throw (ResourceAllocationException)jobResult;
+                    } else if (jobResult instanceof Throwable) {
+                        throw new RuntimeException("Unexpected exception", 
(Throwable)jobResult);
+                    }
+                }
+
+                // retrieve the entity url from job result
+                if (jobResult != null && jobResult instanceof Pair) {
+                    return (Pair<String, String>) jobResult;
+                }
+
+                return null;
+            }
+        } else {
+            CheckAndRepairVolumePayload payload = new 
CheckAndRepairVolumePayload(repair);
+            VolumeInfo volumeInfo = volFactory.getVolume(volumeId);
+            volumeInfo.addPayload(payload);
 
+            Pair<String, String> result = 
volService.checkAndRepairVolume(volumeInfo);
+            return result;
+        }
+    }
+
+    private Pair<String, String> orchestrateCheckVolumeAndRepair(Long 
volumeId, boolean repair) {
+
+        VolumeInfo volume = volFactory.getVolume(volumeId);
+
+        if (volume == null) {
+            throw new InvalidParameterValueException("Checking volume and 
repairing failed due to volume:" + volumeId + " doesn't exist");
+        }
+
+        if (volume.getState() != Volume.State.Ready) {
+            throw new InvalidParameterValueException("VolumeId: " + volumeId + 
" is not in " + Volume.State.Ready + " state but " + volume.getState() + ". 
Cannot check and repair the volume.");
+        }
+
+        CheckAndRepairVolumePayload payload = new 
CheckAndRepairVolumePayload(repair);
+        volume.addPayload(payload);
+
+        return volService.checkAndRepairVolume(volume);
+    }
+
+    public Outcome<Pair> checkVolumeAndRepairThroughJobQueue(final Long vmId, 
final Long volumeId, boolean repair) {
+
+        final CallContext context = CallContext.current();
+        final User callingUser = context.getCallingUser();
+        final Account callingAccount = context.getCallingAccount();
+
+        final VMInstanceVO vm = _vmInstanceDao.findById(vmId);
+
+        VmWorkJobVO workJob = new VmWorkJobVO(context.getContextId());
+
+        workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
+        workJob.setCmd(VmWorkCheckAndRepairVolume.class.getName());
+
+        workJob.setAccountId(callingAccount.getId());
+        workJob.setUserId(callingUser.getId());
+        workJob.setStep(VmWorkJobVO.Step.Starting);
+        workJob.setVmType(VirtualMachine.Type.Instance);
+        workJob.setVmInstanceId(vm.getId());
+        workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
+
+        // save work context info (there are some duplications)
+        VmWorkCheckAndRepairVolume workInfo = new 
VmWorkCheckAndRepairVolume(callingUser.getId(), callingAccount.getId(), 
vm.getId(),
+                VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, repair);
+        workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
+
+        _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, 
vm.getId());
+
+        
AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
+
+        return new VmJobCheckAndRepairVolumeOutcome(workJob);
+    }
 
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CHANGE_DISK_OFFERING, 
eventDescription = "Changing disk offering of a volume")
@@ -4596,6 +4733,24 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         }
     }
 
+    public class VmJobCheckAndRepairVolumeOutcome extends OutcomeImpl<Pair> {
+
+        public VmJobCheckAndRepairVolumeOutcome(final AsyncJob job) {
+            super(Pair.class, job, VmJobCheckInterval.value(), new Predicate() 
{
+                @Override
+                public boolean checkCondition() {
+                    AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, 
job.getId());
+                    assert (jobVo != null);
+                    if (jobVo == null || jobVo.getStatus() != 
JobInfo.Status.IN_PROGRESS) {
+                        return true;
+                    }
+
+                    return false;
+                }
+            }, AsyncJob.Topics.JOB_STATE);
+        }
+    }
+
     public Outcome<Volume> attachVolumeToVmThroughJobQueue(final Long vmId, 
final Long volumeId, final Long deviceId) {
 
         final CallContext context = CallContext.current();
@@ -4833,6 +4988,13 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, 
_jobMgr.marshallResultObject(work.getSnapshotId()));
     }
 
+    @ReflectionUse
+    private Pair<JobInfo.Status, String> 
orchestrateCheckVolumeAndRepair(VmWorkCheckAndRepairVolume work) throws 
Exception {
+        Account account = _accountDao.findById(work.getAccountId());
+        Pair<String, String> result = 
orchestrateCheckVolumeAndRepair(work.getVolumeId(), work.needRepair());
+        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, 
_jobMgr.marshallResultObject(result));
+    }
+
     @Override
     public Pair<JobInfo.Status, String> handleVmWorkJob(VmWork work) throws 
Exception {
         return _jobHandlerProxy.handleVmWorkJob(work);
diff --git a/utils/src/main/java/com/cloud/utils/StringUtils.java 
b/utils/src/main/java/com/cloud/utils/StringUtils.java
index 9e197a8a94b..ac9e6b09329 100644
--- a/utils/src/main/java/com/cloud/utils/StringUtils.java
+++ b/utils/src/main/java/com/cloud/utils/StringUtils.java
@@ -19,6 +19,10 @@
 
 package com.cloud.utils;
 
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -282,4 +286,22 @@ public class StringUtils {
         final String value = keyValuePair.substring(index + 1);
         return new Pair<>(key.trim(), value.trim());
     }
+
+    public static Map<String, String> parseJsonToMap(String jsonString) {
+        ObjectMapper objectMapper = new ObjectMapper();
+        Map<String, String> mapResult = new HashMap<>();
+
+        if (org.apache.commons.lang3.StringUtils.isNotEmpty(jsonString)) {
+            try {
+                JsonNode jsonNode = objectMapper.readTree(jsonString);
+                jsonNode.fields().forEachRemaining(entry -> {
+                    mapResult.put(entry.getKey(), entry.getValue().asText());
+                });
+            } catch (Exception e) {
+                throw new CloudRuntimeException("Error while parsing json to 
convert it to map " + e.getMessage());
+            }
+        }
+
+        return mapResult;
+    }
 }


Reply via email to