Skip to content

Commit 2e62674

Browse files
author
zhong.zhou
committed
<feature>[storage]: support ZBS volume encryption conversion
Route ZBS conversion through a dedicated backend and KVM handler. Tests: cbok zsv groovy_test ZbsVolumeEncryptionCase Resolves: ZSV-12664 Change-Id: Ibd30f74dd17cb4dc817e57c713b0f32915487e7c
1 parent 27aafe6 commit 2e62674

17 files changed

Lines changed: 1366 additions & 4 deletions

File tree

conf/springConfigXml/VolumeManager.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
<bean id="VolumeSnapshotEncryptionHelper" class="org.zstack.storage.encrypt.VolumeSnapshotEncryptionHelper"/>
105105

106106
<bean id="ZbsVolumeEncryptionMaterialFactory" class="org.zstack.storage.encrypt.ZbsVolumeEncryptionMaterialFactory"/>
107+
<bean id="ZbsVolumeEncryptionConverter" class="org.zstack.storage.encrypt.ZbsVolumeEncryptionConverter"/>
107108
<bean id="ZbsVolumeEncryptionKvmCaller" class="org.zstack.storage.encrypt.ZbsVolumeEncryptionKvmCaller"/>
108109
<bean id="ZbsVolumeEncryptionCleanup" class="org.zstack.storage.encrypt.ZbsVolumeEncryptionCleanup"/>
109110
<bean id="ZbsEncryptedEmptyVolumeCreator" class="org.zstack.storage.encrypt.ZbsEncryptedEmptyVolumeCreator"/>

header/src/main/java/org/zstack/header/storage/addon/primary/CreateVolumeSpec.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class CreateVolumeSpec {
1010
private boolean encrypted;
1111
private String hostUuid;
1212
private String encryptedDek;
13+
private String encryptionKeyResourceType;
14+
private String encryptionKeyResourceUuid;
1315

1416
private String allocatedUrl;
1517
private boolean dryRun;
@@ -85,4 +87,20 @@ public String getEncryptedDek() {
8587
public void setEncryptedDek(String encryptedDek) {
8688
this.encryptedDek = encryptedDek;
8789
}
90+
91+
public String getEncryptionKeyResourceType() {
92+
return encryptionKeyResourceType;
93+
}
94+
95+
public void setEncryptionKeyResourceType(String encryptionKeyResourceType) {
96+
this.encryptionKeyResourceType = encryptionKeyResourceType;
97+
}
98+
99+
public String getEncryptionKeyResourceUuid() {
100+
return encryptionKeyResourceUuid;
101+
}
102+
103+
public void setEncryptionKeyResourceUuid(String encryptionKeyResourceUuid) {
104+
this.encryptionKeyResourceUuid = encryptionKeyResourceUuid;
105+
}
88106
}

header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import org.zstack.header.storage.addon.*;
77
import org.zstack.header.storage.primary.EncryptVolumeBitsOnPrimaryStorageMsg;
88
import org.zstack.header.storage.primary.EncryptVolumeBitsOnPrimaryStorageReply;
9+
import org.zstack.header.storage.primary.ConvertVolumeEncryptionOnPrimaryStorageMsg;
10+
import org.zstack.header.storage.primary.ConvertVolumeEncryptionOnPrimaryStorageReply;
911
import org.zstack.header.storage.snapshot.VolumeSnapshotStats;
1012
import org.zstack.header.volume.VolumeInventory;
1113
import org.zstack.header.volume.VolumeProtocol;
@@ -34,6 +36,8 @@ public interface PrimaryStorageControllerSvc {
3436
void cloneVolume(String srcInstallPath, CreateVolumeSpec dst, ReturnValueCompletion<VolumeStats>comp);
3537
void copyVolume(String srcInstallPath, CreateVolumeSpec dst, ReturnValueCompletion<VolumeStats>comp);
3638
void encryptVolumeBits(EncryptVolumeBitsOnPrimaryStorageMsg msg, ReturnValueCompletion<EncryptVolumeBitsOnPrimaryStorageReply> comp);
39+
void convertVolumeEncryption(ConvertVolumeEncryptionOnPrimaryStorageMsg msg,
40+
ReturnValueCompletion<ConvertVolumeEncryptionOnPrimaryStorageReply> completion);
3741
void flattenVolume(String installPath, ReturnValueCompletion<VolumeStats>comp);
3842

3943
// support uri or path

header/src/main/java/org/zstack/header/storage/addon/primary/ZbsVolumeEncryptionBackend.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,12 @@ public interface ZbsVolumeEncryptionBackend {
2121

2222
void checkNoSnapshots(String installPath, Completion completion);
2323

24+
void validateConversionPaths(String sourceInstallPath, String targetInstallPath);
25+
26+
void createConversionTarget(String targetInstallPath, long virtualSize, boolean targetEncrypted,
27+
ReturnValueCompletion<String> completion);
28+
29+
void deleteConversionTarget(String targetInstallPath, Completion completion);
30+
2431
void stats(String installPath, ReturnValueCompletion<VolumeStats> completion);
2532
}

header/src/main/java/org/zstack/header/storage/addon/primary/ZbsVolumeEncryptionExtensionPoint.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import org.zstack.header.core.ReturnValueCompletion;
44
import org.zstack.header.storage.primary.EncryptVolumeBitsOnPrimaryStorageMsg;
55
import org.zstack.header.storage.primary.EncryptVolumeBitsOnPrimaryStorageReply;
6+
import org.zstack.header.storage.primary.ConvertVolumeEncryptionOnPrimaryStorageMsg;
7+
import org.zstack.header.storage.primary.ConvertVolumeEncryptionOnPrimaryStorageReply;
68
import org.zstack.header.storage.snapshot.VolumeSnapshotInventory;
79
import org.zstack.header.volume.VolumeInventory;
810
import org.zstack.header.volume.VolumeStats;
@@ -23,6 +25,9 @@ void copyEncryptedVolumeFromSnapshot(ZbsVolumeEncryptionBackend backend, String
2325
void encryptVolumeBits(ZbsVolumeEncryptionBackend backend, EncryptVolumeBitsOnPrimaryStorageMsg msg,
2426
ReturnValueCompletion<EncryptVolumeBitsOnPrimaryStorageReply> completion);
2527

28+
void convertVolumeEncryption(ZbsVolumeEncryptionBackend backend, ConvertVolumeEncryptionOnPrimaryStorageMsg msg,
29+
ReturnValueCompletion<ConvertVolumeEncryptionOnPrimaryStorageReply> completion);
30+
2631
void resizeEncryptedVolume(String primaryStorageUuid, VolumeInventory volume, long size,
2732
ReturnValueCompletion<VolumeStats> completion);
2833

plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import org.zstack.header.storage.addon.primary.*;
3838
import org.zstack.header.storage.primary.EncryptVolumeBitsOnPrimaryStorageMsg;
3939
import org.zstack.header.storage.primary.EncryptVolumeBitsOnPrimaryStorageReply;
40+
import org.zstack.header.storage.primary.ConvertVolumeEncryptionOnPrimaryStorageMsg;
41+
import org.zstack.header.storage.primary.ConvertVolumeEncryptionOnPrimaryStorageReply;
4042
import org.zstack.header.storage.primary.ImageCacheInventory;
4143
import org.zstack.header.storage.primary.VolumeSnapshotCapability;
4244
import org.zstack.header.storage.snapshot.VolumeSnapshotStats;
@@ -1102,6 +1104,13 @@ public void encryptVolumeBits(EncryptVolumeBitsOnPrimaryStorageMsg msg, ReturnVa
11021104
comp.fail(new ErrorCode(SysErrors.UNIMPLEMENTED_OPERATION_ERROR.toString(), "unimplemented operation", details));
11031105
}
11041106

1107+
@Override
1108+
public void convertVolumeEncryption(ConvertVolumeEncryptionOnPrimaryStorageMsg msg,
1109+
ReturnValueCompletion<ConvertVolumeEncryptionOnPrimaryStorageReply> completion) {
1110+
String details = String.format("primary storage controller[%s] does not support volume encryption conversion", getIdentity());
1111+
completion.fail(new ErrorCode(SysErrors.UNIMPLEMENTED_OPERATION_ERROR.toString(), "unimplemented operation", details));
1112+
}
1113+
11051114
@Override
11061115
public void flattenVolume(String installPath, ReturnValueCompletion<VolumeStats> comp) {
11071116
// TODO flatten snapshot

plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,16 @@ public void encryptVolumeBits(EncryptVolumeBitsOnPrimaryStorageMsg msg, ReturnVa
789789
}
790790
}
791791

792+
@Override
793+
public void convertVolumeEncryption(ConvertVolumeEncryptionOnPrimaryStorageMsg msg,
794+
ReturnValueCompletion<ConvertVolumeEncryptionOnPrimaryStorageReply> completion) {
795+
try {
796+
getVolumeEncryptionExtension().convertVolumeEncryption(encryptionBackend(), msg, completion);
797+
} catch (OperationFailureException e) {
798+
completion.fail(e.getErrorCode());
799+
}
800+
}
801+
792802
@Override
793803
public void flattenVolume(String installPath, ReturnValueCompletion<VolumeStats> comp) {
794804
FlattenVolumeCmd cmd = new FlattenVolumeCmd();

plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsVolumeEncryptionBackendImpl.java

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.zstack.header.storage.addon.primary.ZbsVolumeEncryptionBackend;
1414
import org.zstack.header.volume.VolumeConstant;
1515
import org.zstack.header.volume.VolumeStats;
16+
import org.zstack.utils.Utils;
17+
import org.zstack.utils.logging.CLogger;
1618

1719
import java.util.UUID;
1820

@@ -22,6 +24,7 @@
2224
import static org.zstack.storage.zbs.ZbsHelper.getSizeUnit;
2325

2426
class ZbsVolumeEncryptionBackendImpl implements ZbsVolumeEncryptionBackend {
27+
private static final CLogger logger = Utils.getLogger(ZbsVolumeEncryptionBackendImpl.class);
2528
private static final String QUERY_SNAPSHOT_PATH = "/zbs/primarystorage/snapshot/query";
2629
private static final long LUKS_PAYLOAD_OFFSET = 8L * 1024 * 1024;
2730

@@ -178,6 +181,90 @@ public void fail(ErrorCode errorCode) {
178181
});
179182
}
180183

184+
@Override
185+
public void validateConversionPaths(String sourceInstallPath, String targetInstallPath) {
186+
CbdPath source = parseConversionCbdPath(sourceInstallPath);
187+
CbdPath target = parseConversionCbdPath(targetInstallPath);
188+
if (!source.physicalPool.equals(target.physicalPool) || !source.logicalPool.equals(target.logicalPool)) {
189+
throw new OperationFailureException(operr(
190+
"ZBS volume encryption conversion requires source[%s] and target[%s] in the same physical and logical pool",
191+
sourceInstallPath, targetInstallPath));
192+
}
193+
if (source.volume.equals(target.volume)) {
194+
throw new OperationFailureException(operr(
195+
"ZBS volume encryption conversion requires different source and target volumes, but both are[%s]",
196+
sourceInstallPath));
197+
}
198+
}
199+
200+
@Override
201+
public void createConversionTarget(String targetInstallPath, long virtualSize, boolean targetEncrypted,
202+
ReturnValueCompletion<String> completion) {
203+
CbdPath target;
204+
try {
205+
target = parseConversionCbdPath(targetInstallPath);
206+
} catch (OperationFailureException e) {
207+
completion.fail(e.getErrorCode());
208+
return;
209+
}
210+
211+
ZbsStorageController.CreateVolumeCmd cmd = new ZbsStorageController.CreateVolumeCmd();
212+
cmd.setLogicalPool(target.logicalPool);
213+
cmd.setVolume(target.volume);
214+
cmd.setUnit(getSizeUnit(addonInfo.getClusterInfo().getVersion()));
215+
long allocatedSize = targetEncrypted ? luksBackingSize(virtualSize) : virtualSize;
216+
cmd.setSize(alignSizeTo(allocatedSize, cmd.getUnit()));
217+
cmd.setSkipIfExisting(false);
218+
219+
controller.httpCall(ZbsStorageController.CREATE_VOLUME_PATH, cmd, ZbsStorageController.CreateVolumeRsp.class,
220+
new ReturnValueCompletion<ZbsStorageController.CreateVolumeRsp>(completion) {
221+
@Override
222+
public void success(ZbsStorageController.CreateVolumeRsp returnValue) {
223+
String createdInstallPath = returnValue.getInstallPath();
224+
if (targetInstallPath.equals(createdInstallPath)) {
225+
completion.success(createdInstallPath);
226+
return;
227+
}
228+
229+
ErrorCode error = operr(
230+
"ZBS volume encryption conversion target[%s] was created at unexpected path[%s]",
231+
targetInstallPath, createdInstallPath);
232+
String cleanupInstallPath = targetInstallPath;
233+
try {
234+
deleteConversionTarget(cleanupInstallPath, new Completion(completion) {
235+
@Override
236+
public void success() {
237+
completion.fail(error);
238+
}
239+
240+
@Override
241+
public void fail(ErrorCode cleanupError) {
242+
logger.warn(String.format(
243+
"failed to cleanup unexpected ZBS conversion target[installPath:%s] after create path mismatch: %s",
244+
cleanupInstallPath, cleanupError));
245+
completion.fail(error);
246+
}
247+
});
248+
} catch (Exception e) {
249+
logger.warn(String.format(
250+
"failed to cleanup unexpected ZBS conversion target[installPath:%s] after create path mismatch: %s",
251+
cleanupInstallPath, e.getMessage()));
252+
completion.fail(error);
253+
}
254+
}
255+
256+
@Override
257+
public void fail(ErrorCode errorCode) {
258+
completion.fail(errorCode);
259+
}
260+
});
261+
}
262+
263+
@Override
264+
public void deleteConversionTarget(String targetInstallPath, Completion completion) {
265+
controller.doDeleteVolume(targetInstallPath, true, completion);
266+
}
267+
181268
@Override
182269
public void stats(String installPath, ReturnValueCompletion<VolumeStats> completion) {
183270
controller.stats(installPath, completion);
@@ -207,6 +294,25 @@ private CbdPath parseActiveCbdPath(String installPath) {
207294
return path;
208295
}
209296

297+
private CbdPath parseConversionCbdPath(String installPath) {
298+
if (StringUtils.isBlank(installPath) || !installPath.startsWith("cbd:") || installPath.contains("@")) {
299+
throw new OperationFailureException(operr(
300+
"ZBS volume encryption conversion requires an active CBD path, but got[%s]", installPath));
301+
}
302+
303+
String[] parts = installPath.substring("cbd:".length()).split("/", -1);
304+
if (parts.length != 3 || StringUtils.isBlank(parts[0]) || StringUtils.isBlank(parts[1]) ||
305+
StringUtils.isBlank(parts[2])) {
306+
throw new OperationFailureException(operr("invalid ZBS CBD path[%s]", installPath));
307+
}
308+
309+
CbdPath path = new CbdPath();
310+
path.physicalPool = parts[0];
311+
path.logicalPool = parts[1];
312+
path.volume = parts[2];
313+
return path;
314+
}
315+
210316
private long luksBackingSize(long virtualSize) {
211317
return virtualSize + LUKS_PAYLOAD_OFFSET;
212318
}

storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,24 @@ public void fail(ErrorCode errorCode) {
460460
});
461461
}
462462

463+
@Override
464+
protected void handle(ConvertVolumeEncryptionOnPrimaryStorageMsg msg) {
465+
ConvertVolumeEncryptionOnPrimaryStorageReply reply = new ConvertVolumeEncryptionOnPrimaryStorageReply();
466+
controller.convertVolumeEncryption(msg,
467+
new ReturnValueCompletion<ConvertVolumeEncryptionOnPrimaryStorageReply>(msg) {
468+
@Override
469+
public void success(ConvertVolumeEncryptionOnPrimaryStorageReply returnValue) {
470+
bus.reply(msg, returnValue);
471+
}
472+
473+
@Override
474+
public void fail(ErrorCode errorCode) {
475+
reply.setError(errorCode);
476+
bus.reply(msg, reply);
477+
}
478+
});
479+
}
480+
463481
protected void handle(final SetTrashExpirationTimeMsg msg) {
464482
controller.setTrashExpireTime(msg.getExpirationTime(), new Completion(msg) {
465483
@Override

storage/src/main/java/org/zstack/storage/encrypt/ZbsEncryptedEmptyVolumeCreator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ private void createOnPreparedBacking(String primaryStorageUuid, String installPa
5757
ReturnValueCompletion<VolumeStats> completion) {
5858
ZbsVolumeEncryptionMaterial material;
5959
try {
60-
material = materialFactory.prepareVolumeEncryption(primaryStorageUuid, spec.getUuid(),
60+
material = materialFactory.prepareVolumeEncryption(primaryStorageUuid, spec,
6161
String.format("create encrypted volume[uuid:%s]", spec.getUuid()));
6262
} catch (OperationFailureException e) {
6363
completion.fail(e.getErrorCode());

0 commit comments

Comments
 (0)