Skip to content

Commit dbe0a33

Browse files
authored
Merge pull request #784 from jschoiRR/europa-2026
[Mold API Euroap] 복제 기능중 RBD, SharedMountPoint 형식의 두가지 기본스토리지에서 vm스냅샷 기능이 유지 되도록 수정
2 parents 235496a + a839f83 commit dbe0a33

10 files changed

Lines changed: 88 additions & 43 deletions

File tree

agent/src/main/java/com/cloud/agent/Agent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@
7878
import com.cloud.agent.api.MaintainCommand;
7979
import com.cloud.agent.api.MigrateAgentConnectionAnswer;
8080
import com.cloud.agent.api.MigrateAgentConnectionCommand;
81-
import com.cloud.agent.api.PingAnswer;
8281
import com.cloud.agent.api.NetworkUsageCommand;
82+
import com.cloud.agent.api.PingAnswer;
8383
import com.cloud.agent.api.PingCommand;
8484
import com.cloud.agent.api.ReadyCommand;
8585
import com.cloud.agent.api.ShutdownCommand;

server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Comparator;
2424
import java.util.Date;
2525
import java.util.HashMap;
26+
import java.util.LinkedHashSet;
2627
import java.util.List;
2728
import java.util.Map;
2829
import java.util.Set;
@@ -187,6 +188,9 @@
187188

188189
@Component
189190
public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implements SnapshotManager, SnapshotApiService, Configurable {
191+
192+
private static final String KVM_STORAGE_SNAPSHOT_DETAIL = "kvmStorageSnapshot";
193+
private static final String KVM_FILE_BASED_STORAGE_SNAPSHOT_DETAIL = "kvmFileBasedStorageSnapshot";
190194
@Inject
191195
VMTemplateDao _templateDao;
192196
@Inject
@@ -712,8 +716,8 @@ public Snapshot backupSnapshotFromVmSnapshot(Long snapshotId, Long vmId, Long vo
712716

713717
private void updateSnapshotInfo(Long volumeId, Long vmSnapshotId, VMSnapshotVO vmSnapshot, SnapshotVO snapshot,
714718
SnapshotDataStoreVO snapshotOnPrimaryStore, StoragePoolVO storagePool) {
715-
if ((storagePool.getPoolType() == StoragePoolType.NetworkFilesystem || storagePool.getPoolType() == StoragePoolType.Filesystem) && vmSnapshot.getType() == VMSnapshot.Type.Disk) {
716-
List<VMSnapshotDetailsVO> vmSnapshotDetails = vmSnapshotDetailsDao.findDetails(vmSnapshotId, "kvmStorageSnapshot");
719+
if (isFileBasedKvmPrimaryPool(storagePool.getPoolType()) && vmSnapshot.getType() == VMSnapshot.Type.Disk) {
720+
List<VMSnapshotDetailsVO> vmSnapshotDetails = getVmSnapshotVolumeDetails(vmSnapshotId);
717721
for (VMSnapshotDetailsVO vmSnapshotDetailsVO : vmSnapshotDetails) {
718722
SnapshotInfo sInfo = snapshotDataFactory.getSnapshot(Long.parseLong(vmSnapshotDetailsVO.getValue()), storagePool.getId(), DataStoreRole.Primary);
719723
if (sInfo.getVolumeId() == volumeId) {
@@ -731,6 +735,30 @@ private void updateSnapshotInfo(Long volumeId, Long vmSnapshotId, VMSnapshotVO v
731735
}
732736
}
733737

738+
private boolean isFileBasedKvmPrimaryPool(StoragePoolType poolType) {
739+
return poolType == StoragePoolType.NetworkFilesystem
740+
|| poolType == StoragePoolType.Filesystem
741+
|| poolType == StoragePoolType.SharedMountPoint;
742+
}
743+
744+
private List<VMSnapshotDetailsVO> getVmSnapshotVolumeDetails(Long vmSnapshotId) {
745+
List<VMSnapshotDetailsVO> details = new ArrayList<>();
746+
Set<String> uniqueSnapshotIds = new LinkedHashSet<>();
747+
for (String detailName : List.of(KVM_STORAGE_SNAPSHOT_DETAIL, KVM_FILE_BASED_STORAGE_SNAPSHOT_DETAIL)) {
748+
List<VMSnapshotDetailsVO> found = vmSnapshotDetailsDao.findDetails(vmSnapshotId, detailName);
749+
if (CollectionUtils.isEmpty(found)) {
750+
continue;
751+
}
752+
for (VMSnapshotDetailsVO detail : found) {
753+
if (detail == null || !uniqueSnapshotIds.add(detail.getValue())) {
754+
continue;
755+
}
756+
details.add(detail);
757+
}
758+
}
759+
return details;
760+
}
761+
734762
@Override
735763
public SnapshotVO getParentSnapshot(VolumeInfo volume) {
736764
long preId = _snapshotDao.getLastSnapshot(volume.getId(), DataStoreRole.Primary);

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
443443

444444
private static final String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso";
445445
private static final String KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME = "kvm-default-vm-import-dummy-template";
446+
private static final String KVM_STORAGE_SNAPSHOT_DETAIL = "kvmStorageSnapshot";
447+
private static final String KVM_FILE_BASED_STORAGE_SNAPSHOT_DETAIL = "kvmFileBasedStorageSnapshot";
446448

447449
@Inject
448450
private EntityManager _entityMgr;
@@ -10731,7 +10733,10 @@ public Optional<UserVm> cloneVirtualMachine(CloneVMCmd cmd) throws ResourceAlloc
1073110733
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vm snapshot: " + e.getMessage(), e);
1073210734
}
1073310735

10734-
List<VMSnapshotDetailsVO> listSnapshots = vmSnapshotDetailsDao.findDetails(vmSnapshot.getId(), "kvmFileBasedStorageSnapshot");
10736+
List<VMSnapshotDetailsVO> listSnapshots = getVmSnapshotVolumeDetails(vmSnapshot.getId());
10737+
if (CollectionUtils.isEmpty(listSnapshots)) {
10738+
throw new CloudRuntimeException("Could not find volume snapshots mapped to VM snapshot");
10739+
}
1073510740

1073610741
Integer countOfCloneVM = cmd.getCount();
1073710742
for (int cnt = 1; cnt <= countOfCloneVM; cnt++) {
@@ -10850,6 +10855,24 @@ public Optional<UserVm> cloneVirtualMachine(CloneVMCmd cmd) throws ResourceAlloc
1085010855
return null;
1085110856
}
1085210857

10858+
private List<VMSnapshotDetailsVO> getVmSnapshotVolumeDetails(Long vmSnapshotId) {
10859+
List<VMSnapshotDetailsVO> details = new ArrayList<>();
10860+
Set<String> uniqueSnapshotIds = new LinkedHashSet<>();
10861+
for (String detailName : List.of(KVM_STORAGE_SNAPSHOT_DETAIL, KVM_FILE_BASED_STORAGE_SNAPSHOT_DETAIL)) {
10862+
List<VMSnapshotDetailsVO> found = vmSnapshotDetailsDao.findDetails(vmSnapshotId, detailName);
10863+
if (CollectionUtils.isEmpty(found)) {
10864+
continue;
10865+
}
10866+
for (VMSnapshotDetailsVO detail : found) {
10867+
if (detail == null || !uniqueSnapshotIds.add(detail.getValue())) {
10868+
continue;
10869+
}
10870+
details.add(detail);
10871+
}
10872+
}
10873+
return details;
10874+
}
10875+
1085310876
public UserVm createCloneVM(CloneVMCmd cmd, Long rootVolumeId) throws ConcurrentOperationException, ResourceAllocationException, InsufficientCapacityException, ResourceUnavailableException {
1085410877
//network configurations and check, then create the template
1085510878
UserVm curVm = cmd.getTargetVM();

server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
210210

211211
private static final List<Storage.StoragePoolType> forceConvertToPoolAllowedTypes =
212212
Arrays.asList(Storage.StoragePoolType.NetworkFilesystem, Storage.StoragePoolType.Filesystem,
213-
Storage.StoragePoolType.SharedMountPoint);
213+
Storage.StoragePoolType.SharedMountPoint, Storage.StoragePoolType.RBD);
214214

215215
ConfigKey<Boolean> ConvertVmwareInstanceToKvmExtraParamsAllowed = new ConfigKey<>(Boolean.class,
216216
"convert.vmware.instance.to.kvm.extra.params.allowed",

services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Map;
2828
import java.util.Map.Entry;
2929

30+
import io.netty.util.IllegalReferenceCountException;
3031
import org.apache.cloudstack.storage.template.UploadEntity;
3132
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
3233
import org.apache.commons.lang3.StringUtils;
@@ -61,7 +62,6 @@
6162
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
6263
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
6364
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
64-
import io.netty.util.IllegalReferenceCountException;
6565
import io.netty.util.CharsetUtil;
6666

6767
public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObject> {
@@ -85,7 +85,7 @@ enum UploadHeader {
8585
SIGNATURE("x-signature"),
8686
METADATA("x-metadata"),
8787
EXPIRES("x-expires"),
88-
HOST("x-host"),
88+
HOST("x-forwarded-host"),
8989
CONTENT_LENGTH("content-length");
9090

9191
private final String name;
@@ -127,7 +127,9 @@ public HttpUploadServerHandler(NfsSecondaryStorageResource storageResource) {
127127

128128
@Override
129129
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
130-
destroyDecoder();
130+
if (decoder != null) {
131+
decoder.cleanFiles();
132+
}
131133
requestProcessed = false;
132134
}
133135

ui/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@
103103
"eslint-plugin-vue": "^7.0.0",
104104
"less": "^3.0.4",
105105
"less-loader": "^5.0.0",
106-
"nan": "2.18.0",
107-
"node-gyp": "10.0.1",
108106
"sass": "^1.49.9",
109107
"sass-loader": "^8.0.2",
110108
"uglifyjs-webpack-plugin": "^2.2.0",
111109
"vue-jest": "^5.0.0-0",
112110
"vue-svg-loader": "^0.17.0-beta.2",
113-
"webpack": "^4.46.0"
111+
"webpack": "^4.46.0",
112+
"node-gyp": "10.0.1",
113+
"nan": "2.18.0"
114114
},
115115
"resolutions": {
116116
"nan": "2.18.0"

ui/public/locales/ko_KR.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@
404404
"label.all.available.data": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c \ubaa8\ub4e0 \ub370\uc774\ud130",
405405
"label.all.ipv6": "\ubaa8\ub4e0 IPv6",
406406
"label.all.zone": "\ubaa8\ub4e0 Zones",
407-
"label.allocated": "\ud560\ub2f9\ub428",
407+
"label.allocated": "\ud560\ub2f9",
408408
"label.allocatedonly": "\ud560\ub2f9",
409409
"label.allocationstate": "\ud560\ub2f9 \uc0c1\ud0dc",
410410
"label.allow": "\ud5c8\uc6a9",
@@ -2367,7 +2367,7 @@
23672367
"label.snapshot": "\uc2a4\ub0c5\uc0f7",
23682368
"label.snapshot.name": "\uc2a4\ub0c5\uc0f7 \uc774\ub984",
23692369
"label.snapshotlimit": "\uc2a4\ub0c5\uc0f7 \uc81c\ud55c",
2370-
"label.snapshotmemory": "\uc2a4\ub0c5\uc0f7 \uba54\ubaa8\ub9ac",
2370+
"label.snapshotmemory": "\uba54\ubaa8\ub9ac \uc2a4\ub0c5\uc0f7",
23712371
"label.snapshotpolicy": "\uc2a4\ub0c5\uc0f7 \uc815\ucc45",
23722372
"label.snapshotpolicies": "\uc2a4\ub0c5\uc0f7 \uc815\ucc45",
23732373
"label.snapshots": "\uc2a4\ub0c5\uc0f7",
@@ -2725,7 +2725,7 @@
27252725
"label.use.kubectl.access.cluster": "\ud074\ub7ec\uc2a4\ud130\uc5d0 \uc561\uc138\uc2a4\ud558\uae30\uc704\ud55c <code> <b> kubectl </ b> </ code> \ubc0f <code> <b> kubeconfig </ b> </ code> \ud30c\uc77c",
27262726
"label.use.local.timezone": "\ub85c\uceec \uc2dc\uac04\ub300 \uc0ac\uc6a9",
27272727
"label.use.router.ip.resolver": "\uac00\uc0c1 \ub77c\uc6b0\ud130 IP\ub97c Resolver\ub85c \uc0ac\uc6a9",
2728-
"label.used": "\uc0ac\uc6a9\ub428",
2728+
"label.used": "\uc0ac\uc6a9",
27292729
"label.usehttps": "HTTPS \uc0ac\uc6a9",
27302730
"label.usenewdiskoffering": "\ub514\uc2a4\ud06c \uc624\ud37c\ub9c1\uc744 \ubcc0\uacbd\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?",
27312731
"label.user": "\uc0ac\uc6a9\uc790",
@@ -4315,6 +4315,7 @@
43154315
"GB*Month": "GB * \uc6d4",
43164316
"IP*Month": "IP * \uc6d4\uac04",
43174317
"Policy*Month": "\uc815\ucc45 * \uc6d4",
4318+
43184319
"label.desktop.service": "\ub370\uc2a4\ud06c\ud1b1",
43194320
"title.desktop.cluster": "\ud074\ub7ec\uc2a4\ud130",
43204321
"title.desktop.controller": "\ucee8\ud2b8\ub864\ub7ec \ud15c\ud50c\ub9bf",
@@ -4861,6 +4862,8 @@
48614862
"backup.schedule.delete": "\ubc31\uc5c5 \uc77c\uc815 \uc0ad\uc81c",
48624863
"backup.usage.metric": "\ubc31\uc5c5 \uc0ac\uc6a9\ub7c9 \uba54\ud2b8\ub9ad",
48634864
"backup.offering.edit": "\ubc31\uc5c5 \uc624\ud37c\ub9c1 \ud3b8\uc9d1",
4865+
"backup.repository.add": "\ubc31\uc5c5 \uc800\uc7a5\uc18c \ucd94\uac00",
4866+
"backup.repository.update": "\ubc31\uc5c5 \uc800\uc7a5\uc18c \ud3b8\uc9d1",
48644867
"physical.nvpcontroller.add": "\ubb3c\ub9ac NVP\ucee8\ud2b8\ub864\ub7ec \ucd94\uac00",
48654868
"physical.nvpcontroller.delete": "\ubb3c\ub9ac NVP\ucee8\ud2b8\ub864\ub7ec \uc0ad\uc81c",
48664869
"physical.nvpcontroller.configure": "\ubb3c\ub9ac NVP\ucee8\ud2b8\ub864\ub7ec \uad6c\uc131",

ui/src/components/page/GlobalLayout.vue

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,24 @@
1313
<div class="banner-spacer" :style="{ height: combinedBannerHeight + 'px' }" aria-hidden="true"></div>
1414

1515
<a-layout class="layout" :class="[device]">
16-
<a-affix style="z-index: 200" :offsetTop="this.$store.getters.maintenanceInitiated || this.$store.getters.shutdownTriggered ? 25 : 0">
17-
<div class="sticky-sidebar">
18-
<template v-if="isSideMenu()">
19-
<a-drawer
20-
v-if="isMobile()"
21-
:wrapClassName="'drawer-sider ' + navTheme"
22-
:closable="false"
23-
:visible="collapsed"
24-
placement="left"
25-
@close="() => (collapsed = false)"
26-
>
27-
<side-menu
28-
:menus="menus"
29-
:theme="navTheme"
30-
:collapsed="false"
31-
:collapsible="true"
32-
mode="inline"
33-
:style="{ paddingBottom: isSidebarVisible ? '300px' : '0' }"
34-
@menuSelect="menuSelect"
35-
/>
36-
</a-drawer>
37-
16+
<div class="sticky-sidebar">
17+
<template v-if="isSideMenu()">
18+
<a-drawer
19+
v-if="isMobile()"
20+
:wrapClassName="'drawer-sider ' + navTheme"
21+
:closable="false"
22+
:visible="collapsed"
23+
placement="left"
24+
@close="() => (collapsed = false)"
25+
>
3826
<side-menu
39-
v-else
40-
mode="inline"
4127
:menus="menus"
4228
:theme="navTheme"
43-
:collapsed="collapsed"
29+
:collapsed="false"
4430
:collapsible="true"
31+
mode="inline"
4532
:style="{ paddingBottom: isSidebarVisible ? '300px' : '0' }"
33+
@menuSelect="menuSelect"
4634
/>
4735
</a-drawer>
4836

@@ -106,6 +94,11 @@
10694
</a-button>
10795
</div>
10896
</template>
97+
<template #drawer>
98+
<setting :visible="showSetting" />
99+
</template>
100+
</drawer>
101+
</div>
109102

110103
<event-sidebar
111104
:isVisible="isSidebarVisible"
@@ -119,7 +112,6 @@
119112
>
120113
<div class="sticky-header">
121114
<global-header
122-
:style="this.$store.getters.maintenanceInitiated || this.$store.getters.shutdownTriggered ? 'margin-top: 25px;' : null"
123115
:mode="layoutMode"
124116
:menus="menus"
125117
:theme="navTheme"

ui/src/views/compute/EditVM.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@
100100
<a-form-item ref="securitygroupids" name="securitygroupids" :label="$t('label.security.groups')" v-if="securityGroupsEnabled">
101101
<a-select
102102
mode="multiple"
103-
:placeholder="$t('label.select.security.groups')"
104103
v-model:value="form.securitygroupids"
105104
showSearch
106105
optionFilterProp="label"

ui/src/views/tools/ImportUnmanagedInstance.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,8 +800,6 @@ export default {
800800
return meta
801801
},
802802
getMinCpu () {
803-
console.log('this.resource.cpunumber :>> ', this.resource.cpunumber)
804-
console.log('this.computeOffering.serviceofferingdetails.mincpunumber :>> ', this.computeOffering.serviceofferingdetails.mincpunumber)
805803
if (this.isVmRunning) {
806804
return this.resource.cpunumber
807805
}

0 commit comments

Comments
 (0)