Merge remote-tracking branch 'origin/4.15'

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2021-02-19 15:30:21 +05:30
commit 6634382f69
11 changed files with 135 additions and 59 deletions

View File

@ -21,11 +21,11 @@ import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;
import com.cloud.hypervisor.Hypervisor;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponseWithTagInformation;
import org.apache.cloudstack.api.EntityReference;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.serializer.Param;
import com.cloud.vm.snapshot.VMSnapshot;
import com.google.gson.annotations.SerializedName;
@ -57,9 +57,17 @@ public class VMSnapshotResponse extends BaseResponseWithTagInformation implement
@Param(description = "the Zone ID of the vm snapshot")
private String zoneId;
@SerializedName(ApiConstants.ZONE_NAME)
@Param(description = "the Zone name of the vm snapshot", since = "4.15.1")
private String zoneName;
@SerializedName(ApiConstants.VIRTUAL_MACHINE_ID)
@Param(description = "the vm ID of the vm snapshot")
private String virtualMachineid;
private String virtualMachineId;
@SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME)
@Param(description = "the vm name of the vm snapshot", since = "4.15.1")
private String virtualMachineName;
@SerializedName("parent")
@Param(description = "the parent ID of the vm snapshot")
@ -154,12 +162,28 @@ public class VMSnapshotResponse extends BaseResponseWithTagInformation implement
this.zoneId = zoneId;
}
public String getVirtualMachineid() {
return virtualMachineid;
public String getZoneName() {
return zoneName;
}
public void setVirtualMachineid(String virtualMachineid) {
this.virtualMachineid = virtualMachineid;
public void setZoneName(String zoneName) {
this.zoneName = zoneName;
}
public String getVirtualMachineId() {
return virtualMachineId;
}
public void setVirtualMachineId(String virtualMachineId) {
this.virtualMachineId = virtualMachineId;
}
public String getVirtualMachineName() {
return virtualMachineName;
}
public void setVirtualMachineName(String virtualMachineName) {
this.virtualMachineName = virtualMachineName;
}
public void setName(String name) {

View File

@ -56,6 +56,9 @@ import org.apache.cloudstack.utils.hypervisor.HypervisorUtils;
import org.apache.cloudstack.utils.linux.CPUStat;
import org.apache.cloudstack.utils.linux.KVMHostInfo;
import org.apache.cloudstack.utils.linux.MemStat;
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.QemuImg.PhysicalDiskFormat;
import org.apache.cloudstack.utils.security.KeyStoreUtils;
import org.apache.commons.collections.MapUtils;
@ -2528,6 +2531,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
volPath = physicalDisk.getPath();
}
if (volume.getType() != Volume.Type.ISO
&& physicalDisk != null && physicalDisk.getFormat() == PhysicalDiskFormat.QCOW2
&& (pool.getType() == StoragePoolType.NetworkFilesystem
|| pool.getType() == StoragePoolType.SharedMountPoint
|| pool.getType() == StoragePoolType.Filesystem
|| pool.getType() == StoragePoolType.Gluster)) {
setBackingFileFormat(physicalDisk.getPath());
}
// check for disk activity, if detected we should exit because vm is running elsewhere
if (_diskActivityCheckEnabled && physicalDisk != null && physicalDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
s_logger.debug("Checking physical disk file at path " + volPath + " for disk activity to ensure vm is not running elsewhere");
@ -4265,4 +4277,25 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
cmd.setTopology(numCoresPerSocket, vcpus / numCoresPerSocket);
}
}
public void setBackingFileFormat(String volPath) {
final int timeout = 0;
QemuImgFile file = new QemuImgFile(volPath);
QemuImg qemu = new QemuImg(timeout);
try{
Map<String, String> info = qemu.info(file);
String backingFilePath = info.get(new String("backing_file"));
String backingFileFormat = info.get(new String("backing_file_format"));
if (org.apache.commons.lang.StringUtils.isEmpty(backingFileFormat)) {
s_logger.info("Setting backing file format of " + volPath);
QemuImgFile backingFile = new QemuImgFile(backingFilePath);
Map<String, String> backingFileinfo = qemu.info(backingFile);
String backingFileFmt = backingFileinfo.get(new String("file_format"));
qemu.rebase(file, backingFile, backingFileFmt, false);
}
} catch (QemuImgException e) {
s_logger.error("Failed to set backing file format of " + volPath + " due to : " + e.getMessage());
}
}
}

View File

@ -155,6 +155,14 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
// delete the metadata of vm snapshots before migration
vmsnapshots = libvirtComputingResource.cleanVMSnapshotMetadata(dm);
// Verify Format of backing file
for (DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.DISK
&& disk.getDiskFormatType() == DiskDef.DiskFmtType.QCOW2) {
libvirtComputingResource.setBackingFileFormat(disk.getDiskPath());
}
}
Map<String, MigrateCommand.MigrateDiskInfo> mapMigrateStorage = command.getMigrateStorage();
// migrateStorage is declared as final because the replaceStorage method may mutate mapMigrateStorage, but
// migrateStorage's value should always only be associated with the initial state of mapMigrateStorage.

View File

@ -379,8 +379,29 @@ public class QemuImg {
}
/* Changes the backing file of an image */
public void rebase() throws QemuImgException {
public void rebase(final QemuImgFile file, final QemuImgFile backingFile, final String backingFileFormat, final boolean secure) throws QemuImgException {
if (backingFile == null) {
throw new QemuImgException("No backing file was passed");
}
final Script s = new Script(_qemuImgPath, timeout);
s.add("rebase");
if (! secure) {
s.add("-u");
}
s.add("-F");
if (backingFileFormat != null) {
s.add(backingFileFormat);
} else {
s.add(backingFile.getFormat().toString());
}
s.add("-b");
s.add(backingFile.getFileName());
s.add(file.getFileName());
final String result = s.execute();
if (result != null) {
throw new QemuImgException(result);
}
}
/**

View File

@ -351,6 +351,7 @@ import com.cloud.vm.dao.NicSecondaryIpVO;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import com.google.common.base.Strings;
public class ApiResponseHelper implements ResponseGenerator {
@ -622,11 +623,13 @@ public class ApiResponseHelper implements ResponseGenerator {
vmSnapshotResponse.setDisplayName(vmSnapshot.getDisplayName());
UserVm vm = ApiDBUtils.findUserVmById(vmSnapshot.getVmId());
if (vm != null) {
vmSnapshotResponse.setVirtualMachineid(vm.getUuid());
vmSnapshotResponse.setVirtualMachineId(vm.getUuid());
vmSnapshotResponse.setVirtualMachineName(Strings.isNullOrEmpty(vm.getDisplayName()) ? vm.getHostName() : vm.getDisplayName());
vmSnapshotResponse.setHypervisor(vm.getHypervisorType());
DataCenterVO datacenter = ApiDBUtils.findZoneById(vm.getDataCenterId());
if (datacenter != null) {
vmSnapshotResponse.setZoneId(datacenter.getUuid());
vmSnapshotResponse.setZoneName(datacenter.getName());
}
}
if (vmSnapshot.getParent() != null) {

View File

@ -2232,6 +2232,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
if (capabilities != null) {
liveMigrateVolume = capabilities.isStorageMotionSupported();
}
if (liveMigrateVolume && HypervisorType.KVM.equals(host.getHypervisorType())) {
throw new InvalidParameterValueException("KVM does not support volume live migration due to the limited possibility to refresh VM XML domain. " +
"Therefore, to live migrate a volume between storage pools, one must migrate the VM to a different host as well to force the VM XML domain update. " +
"Use 'migrateVirtualMachineWithVolumes' instead.");
}
}
// If vm is running, and hypervisor doesn't support live migration, then return error

View File

@ -3968,7 +3968,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// * verify that there are no duplicates
if (hostNames.contains(hostName)) {
throw new InvalidParameterValueException("The vm with hostName " + hostName + " already exists in the network domain: " + ntwkDomain.getKey() + "; network="
+ _networkModel.getNetwork(ntwkId));
+ ((_networkModel.getNetwork(ntwkId) != null) ? _networkModel.getNetwork(ntwkId).getName() : "<unknown>"));
}
}
}

View File

@ -1,30 +0,0 @@
# 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.
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
location /client/ {
# http://127.0.0.1:8080 should be replaced your CloudStack management
# Server's actual URI
proxy_pass http://127.0.0.1:8080;
}
}

41
ui/package-lock.json generated
View File

@ -4766,9 +4766,9 @@
"dev": true
},
"ant-design-vue": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.2.tgz",
"integrity": "sha512-iVskTSG62OSiptyGQkvyhoeLlLMiqKtAOTcWZ7MmsMrj38h0TCpGtqSYS4/fwq4yYgyzloYSteBo8U8TrV99RA==",
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.3.tgz",
"integrity": "sha512-OuJBZmpcy32OvdvP7iW1EqdqU6l/dta1zcQ/U0fep7EGksPviVK7ssifI5QMZFQDDsti0dYQhCEOZWUG0Y/xxQ==",
"requires": {
"@ant-design/icons": "^2.1.1",
"@ant-design/icons-vue": "^2.0.0",
@ -5402,11 +5402,23 @@
}
},
"antd-theme-webpack-plugin": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/antd-theme-webpack-plugin/-/antd-theme-webpack-plugin-1.3.7.tgz",
"integrity": "sha512-FB101smtUHtEW/SdLm88BiR0MKIKhoIGqiQdmrIwQ46zuwJ7MHxAKEUVXg+IWV3F8b71jKaQlFCPnLCe9RqL2A==",
"version": "1.3.9",
"resolved": "https://registry.npmjs.org/antd-theme-webpack-plugin/-/antd-theme-webpack-plugin-1.3.9.tgz",
"integrity": "sha512-J/xYPqW8oZrk/UZslRODdKSLmhFlpLKuqL6GMHPoKib9u0RoIeVCUAD+IT4yRwIkFBYVIX0vlahnLvGHNEeMxA==",
"requires": {
"antd-theme-generator": "^1.2.7"
"antd-theme-generator": "^1.2.8",
"webpack-sources": "^2.2.0"
},
"dependencies": {
"webpack-sources": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz",
"integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==",
"requires": {
"source-list-map": "^2.0.1",
"source-map": "^0.6.1"
}
}
}
},
"any-observable": {
@ -22641,8 +22653,7 @@
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
"integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
"dev": true
"integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw=="
},
"source-map": {
"version": "0.6.1",
@ -25239,9 +25250,9 @@
"integrity": "sha512-uKNKpFOVeWNqS2mrBZqnpLyXJo5Q+vnkex6JvpENvhXHFNBW/SJTP8vJywLuVT3DpxwXcF9N0dyIiZ4/NpTexQ=="
},
"vue-router": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz",
"integrity": "sha512-CGAKWN44RqXW06oC+u4mPgHLQQi2t6vLD/JbGRDAXm0YpMv0bgpKuU5bBd7AvMgfTz9kXVRIWKHqRwGEb8xFkA=="
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.1.tgz",
"integrity": "sha512-RRQNLT8Mzr8z7eL4p7BtKvRaTSGdCbTy2+Mm5HTJvLGYSSeG9gDzNasJPP/yOYKLy+/cLG/ftrqq5fvkFwBJEw=="
},
"vue-sfc-descriptor-to-string": {
"version": "1.0.0",
@ -25363,9 +25374,9 @@
}
},
"vuex": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.0.tgz",
"integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ=="
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
"integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw=="
},
"w3c-hr-time": {
"version": "1.0.2",

View File

@ -36,8 +36,8 @@
"@fortawesome/free-brands-svg-icons": "^5.15.2",
"@fortawesome/free-solid-svg-icons": "^5.15.2",
"@fortawesome/vue-fontawesome": "^2.0.2",
"ant-design-vue": "~1.7.2",
"antd-theme-webpack-plugin": "^1.3.7",
"ant-design-vue": "~1.7.3",
"antd-theme-webpack-plugin": "^1.3.9",
"axios": "^0.21.1",
"babel-plugin-require-context-hook": "^1.0.0",
"core-js": "^3.6.5",
@ -54,10 +54,10 @@
"vue-cropper": "0.5.6",
"vue-i18n": "^8.22.4",
"vue-ls": "^3.2.2",
"vue-router": "^3.4.9",
"vue-router": "^3.5.1",
"vue-svg-component-runtime": "^1.0.1",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.0"
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli": "^4.4.1",

View File

@ -35,7 +35,7 @@
:pagination="false"
>
<template slot="account" slot-scope="record">
<a-button @click="() => handleOpenAccountModal(record)">{{ `[${record.domain}] ${record.account}` }}</a-button>
<a-button @click="() => handleOpenAccountModal(record)">{{ `[${record.domain}] ${record.account === undefined ? '' : record.account}` }}</a-button>
</template>
<template slot="actions" slot-scope="record">
<div class="actions">