diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java index 0245f228b89..548f4d67b23 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; @@ -54,10 +55,16 @@ public class DeleteBackupScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, - required = true, description = "ID of the VM") private Long vmId; + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = BackupScheduleResponse.class, + description = "ID of the schedule", + since = "4.20.1") + private Long id; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +73,9 @@ public class DeleteBackupScheduleCmd extends BaseCmd { return vmId; } + public Long getId() { return id; } + + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -73,7 +83,7 @@ public class DeleteBackupScheduleCmd extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.deleteBackupSchedule(getVmId()); + boolean result = backupManager.deleteBackupSchedule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java index 3847176608c..0db51f04034 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java @@ -57,10 +57,6 @@ public class BackupRepositoryResponse extends BaseResponse { @Param(description = "backup type") private String type; - @SerializedName(ApiConstants.MOUNT_OPTIONS) - @Param(description = "mount options for the backup repository") - private String mountOptions; - @SerializedName(ApiConstants.CAPACITY_BYTES) @Param(description = "capacity of the backup repository") private Long capacityBytes; @@ -112,14 +108,6 @@ public class BackupRepositoryResponse extends BaseResponse { this.address = address; } - public String getMountOptions() { - return mountOptions; - } - - public void setMountOptions(String mountOptions) { - this.mountOptions = mountOptions; - } - public String getProviderName() { return providerName; } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index cbd4b7e0596..eebad3af067 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -23,6 +23,7 @@ import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd; import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd; import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd; +import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; import org.apache.cloudstack.framework.config.ConfigKey; @@ -192,10 +193,10 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer /** * Deletes VM backup schedule for a VM - * @param vmId + * @param cmd * @return */ - boolean deleteBackupSchedule(Long vmId); + boolean deleteBackupSchedule(DeleteBackupScheduleCmd cmd); /** * Creates backup of a VM diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java index 765602e42d0..ca56446631c 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java @@ -1473,7 +1473,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl } if (!BooleanUtils.toBoolean(EnableKVMAutoEnableDisable.valueIn(host.getClusterId()))) { logger.debug("{} is disabled for the cluster {}, cannot process the health check result " + - "received for the host {}", EnableKVMAutoEnableDisable.key(), host.getClusterId(), host); + "received for {}", EnableKVMAutoEnableDisable.key(), host.getClusterId(), host); return; } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 7b231d02cb0..472fe148a5d 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -6071,6 +6071,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public Map getDiskOfferingSuitabilityForVm(long vmId, List diskOfferingIds) { VMInstanceVO vm = _vmDao.findById(vmId); + if (userVmDetailsDao.findDetail(vm.getId(), VmDetailConstants.DEPLOY_VM) != null) { + return new HashMap<>(); + } VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); Pair clusterAndHost = findClusterAndHostIdForVm(vm, false); Long clusterId = clusterAndHost.first(); diff --git a/packaging/el8/cloud.spec b/packaging/el8/cloud.spec index 244f4431a3b..c9bea72f5e8 100644 --- a/packaging/el8/cloud.spec +++ b/packaging/el8/cloud.spec @@ -114,6 +114,7 @@ Requires: iproute Requires: ipset Requires: perl Requires: rsync +Requires: cifs-utils Requires: (python3-libvirt or python3-libvirt-python) Requires: (qemu-img or qemu-tools) Requires: qemu-kvm diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index ba37c899169..1e3b5aaa8e5 100644 --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@ -219,6 +219,7 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co restoreCommand.setBackupPath(backup.getExternalId()); restoreCommand.setBackupRepoType(backupRepository.getType()); restoreCommand.setBackupRepoAddress(backupRepository.getAddress()); + restoreCommand.setMountOptions(backupRepository.getMountOptions()); restoreCommand.setVmName(vm.getName()); restoreCommand.setVolumePaths(getVolumePaths(volumes)); restoreCommand.setVmExists(vm.getRemoved() == null); @@ -287,6 +288,7 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co restoreCommand.setVmName(vmNameAndState.first()); restoreCommand.setVolumePaths(Collections.singletonList(String.format("%s/%s", dataStore.getLocalPath(), volumeUUID))); restoreCommand.setDiskType(volume.getVolumeType().name().toLowerCase(Locale.ROOT)); + restoreCommand.setMountOptions(backupRepository.getMountOptions()); restoreCommand.setVmExists(null); restoreCommand.setVmState(vmNameAndState.second()); restoreCommand.setRestoreVolumeUUID(volumeUuid); @@ -372,8 +374,12 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co Long vmBackupSize = 0L; Long vmBackupProtectedSize = 0L; for (final Backup backup: backupDao.listByVmId(null, vm.getId())) { - vmBackupSize += backup.getSize(); - vmBackupProtectedSize += backup.getProtectedSize(); + if (Objects.nonNull(backup.getSize())) { + vmBackupSize += backup.getSize(); + } + if (Objects.nonNull(backup.getProtectedSize())) { + vmBackupProtectedSize += backup.getProtectedSize(); + } } Backup.Metric vmBackupMetric = new Backup.Metric(vmBackupSize,vmBackupProtectedSize); LOG.debug("Metrics for VM {} is [backup size: {}, data size: {}].", vm, vmBackupMetric.getBackupSize(), vmBackupMetric.getDataSize()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java index 0f00a6e29bd..e050cb4e85d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java @@ -27,6 +27,7 @@ import com.cloud.agent.api.GetVmIpAddressCommand; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; +import com.cloud.utils.Pair; import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.Script; @@ -34,6 +35,26 @@ import com.cloud.utils.script.Script; public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper { + static String virsh_path = null; + static String virt_win_reg_path = null; + static String grep_path = null; + static String awk_path = null; + static String sed_path = null; + static String virt_ls_path = null; + static String virt_cat_path = null; + static String tail_path = null; + + static void init() { + virt_ls_path = Script.getExecutableAbsolutePath("virt-ls"); + virt_cat_path = Script.getExecutableAbsolutePath("virt-cat"); + virt_win_reg_path = Script.getExecutableAbsolutePath("virt-win-reg"); + tail_path = Script.getExecutableAbsolutePath("tail"); + grep_path = Script.getExecutableAbsolutePath("grep"); + awk_path = Script.getExecutableAbsolutePath("awk"); + sed_path = Script.getExecutableAbsolutePath("sed"); + virsh_path = Script.getExecutableAbsolutePath("virsh"); + } + @Override public Answer execute(final GetVmIpAddressCommand command, final LibvirtComputingResource libvirtComputingResource) { String ip = null; @@ -42,65 +63,113 @@ public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper commands = new ArrayList<>(); - final String virt_ls_path = Script.getExecutableAbsolutePath("virt-ls"); - final String virt_cat_path = Script.getExecutableAbsolutePath("virt-cat"); - final String virt_win_reg_path = Script.getExecutableAbsolutePath("virt-win-reg"); - final String tail_path = Script.getExecutableAbsolutePath("tail"); - final String grep_path = Script.getExecutableAbsolutePath("grep"); - final String awk_path = Script.getExecutableAbsolutePath("awk"); - final String sed_path = Script.getExecutableAbsolutePath("sed"); - if(!command.isWindows()) { - //List all dhcp lease files inside guestVm - commands.add(new String[]{virt_ls_path, sanitizedVmName, "/var/lib/dhclient/"}); - commands.add(new String[]{grep_path, ".*\\*.leases"}); - String leasesList = Script.executePipedCommands(commands, 0).second(); - if(leasesList != null) { - String[] leasesFiles = leasesList.split("\n"); - for(String leaseFile : leasesFiles){ - //Read from each dhclient lease file inside guest Vm using virt-cat libguestfs utility - commands = new ArrayList<>(); - commands.add(new String[]{virt_cat_path, sanitizedVmName, "/var/lib/dhclient/" + leaseFile}); - commands.add(new String[]{tail_path, "-16"}); - commands.add(new String[]{grep_path, "fixed-address"}); - commands.add(new String[]{awk_path, "{print $2}"}); - commands.add(new String[]{sed_path, "-e", "s/;//"}); - String ipAddr = Script.executePipedCommands(commands, 0).second(); - // Check if the IP belongs to the network - if((ipAddr != null) && NetUtils.isIpWithInCidrRange(ipAddr, networkCidr)) { - ip = ipAddr; - break; - } - logger.debug("GetVmIp: "+ vmName + " Ip: "+ipAddr+" does not belong to network "+networkCidr); - } - } - } else { - // For windows, read from guest Vm registry using virt-win-reg libguestfs ulitiy. Registry Path: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Tcpip\Parameters\Interfaces\\DhcpIPAddress - commands = new ArrayList<>(); - commands.add(new String[]{virt_win_reg_path, "--unsafe-printable-strings", sanitizedVmName, "HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces"}); - commands.add(new String[]{grep_path, "DhcpIPAddress"}); - commands.add(new String[]{awk_path, "-F", ":", "{print $2}"}); - commands.add(new String[]{sed_path, "-e", "s/^\"//", "-e", "s/\"$//"}); - String ipList = Script.executePipedCommands(commands, 0).second(); - if(ipList != null) { - logger.debug("GetVmIp: "+ vmName + "Ips: "+ipList); - String[] ips = ipList.split("\n"); - for (String ipAddr : ips){ - // Check if the IP belongs to the network - if((ipAddr != null) && NetUtils.isIpWithInCidrRange(ipAddr, networkCidr)){ - ip = ipAddr; - break; - } - logger.debug("GetVmIp: "+ vmName + " Ip: "+ipAddr+" does not belong to network "+networkCidr); - } + + ip = ipFromDomIf(sanitizedVmName, networkCidr); + + if (ip == null) { + if(!command.isWindows()) { + ip = ipFromDhcpLeaseFile(sanitizedVmName, networkCidr); + } else { + ip = ipFromWindowsRegistry(sanitizedVmName, networkCidr); } } + if(ip != null){ result = true; logger.debug("GetVmIp: "+ vmName + " Found Ip: "+ip); + } else { + logger.warn("GetVmIp: "+ vmName + " IP not found."); } + return new Answer(command, result, ip); } + + private String ipFromDomIf(String sanitizedVmName, String networkCidr) { + String ip = null; + List commands = new ArrayList<>(); + commands.add(new String[]{virsh_path, "domifaddr", sanitizedVmName, "--source", "agent"}); + Pair response = executePipedCommands(commands, 0); + if (response != null) { + String output = response.second(); + String[] lines = output.split("\n"); + for (String line : lines) { + if (line.contains("ipv4")) { + String[] parts = line.split(" "); + String[] ipParts = parts[parts.length-1].split("/"); + if (ipParts.length > 1) { + if (NetUtils.isIpWithInCidrRange(ipParts[0], networkCidr)) { + ip = ipParts[0]; + break; + } + } + } + } + } else { + logger.error("ipFromDomIf: Command execution failed for VM: " + sanitizedVmName); + } + return ip; + } + + private String ipFromDhcpLeaseFile(String sanitizedVmName, String networkCidr) { + String ip = null; + List commands = new ArrayList<>(); + commands.add(new String[]{virt_ls_path, sanitizedVmName, "/var/lib/dhclient/"}); + commands.add(new String[]{grep_path, ".*\\*.leases"}); + Pair response = executePipedCommands(commands, 0); + + if(response != null && response.second() != null) { + String leasesList = response.second(); + String[] leasesFiles = leasesList.split("\n"); + for(String leaseFile : leasesFiles){ + commands = new ArrayList<>(); + commands.add(new String[]{virt_cat_path, sanitizedVmName, "/var/lib/dhclient/" + leaseFile}); + commands.add(new String[]{tail_path, "-16"}); + commands.add(new String[]{grep_path, "fixed-address"}); + commands.add(new String[]{awk_path, "{print $2}"}); + commands.add(new String[]{sed_path, "-e", "s/;//"}); + String ipAddr = executePipedCommands(commands, 0).second(); + if((ipAddr != null) && NetUtils.isIpWithInCidrRange(ipAddr, networkCidr)) { + ip = ipAddr; + break; + } + logger.debug("GetVmIp: "+ sanitizedVmName + " Ip: "+ipAddr+" does not belong to network "+networkCidr); + } + } else { + logger.error("ipFromDhcpLeaseFile: Command execution failed for VM: " + sanitizedVmName); + } + return ip; + } + + private String ipFromWindowsRegistry(String sanitizedVmName, String networkCidr) { + String ip = null; + List commands = new ArrayList<>(); + commands.add(new String[]{virt_win_reg_path, "--unsafe-printable-strings", sanitizedVmName, "HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces"}); + commands.add(new String[]{grep_path, "DhcpIPAddress"}); + commands.add(new String[]{awk_path, "-F", ":", "{print $2}"}); + commands.add(new String[]{sed_path, "-e", "s/^\"//", "-e", "s/\"$//"}); + Pair pair = executePipedCommands(commands, 0); + if(pair != null && pair.second() != null) { + String ipList = pair.second(); + ipList = ipList.replaceAll("\"", ""); + logger.debug("GetVmIp: "+ sanitizedVmName + "Ips: "+ipList); + String[] ips = ipList.split("\n"); + for (String ipAddr : ips){ + if((ipAddr != null) && NetUtils.isIpWithInCidrRange(ipAddr, networkCidr)){ + ip = ipAddr; + break; + } + logger.debug("GetVmIp: "+ sanitizedVmName + " Ip: "+ipAddr+" does not belong to network "+networkCidr); + } + } else { + logger.error("ipFromWindowsRegistry: Command execution failed for VM: " + sanitizedVmName); + } + return ip; + } + + static Pair executePipedCommands(List commands, long timeout) { + return Script.executePipedCommands(commands, timeout); + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java index 23ead355096..49b67194356 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java @@ -67,7 +67,7 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper(vmName, command.getVmState())); + new Pair<>(vmName, command.getVmState()), mountOptions); } else if (Boolean.TRUE.equals(vmExists)) { restoreVolumesOfExistingVM(volumePaths, backupPath, backupRepoType, backupRepoAddress, mountOptions); } else { @@ -80,7 +80,7 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper volumePaths, String backupPath, String backupRepoType, String backupRepoAddress, String mountOptions) { String diskType = "root"; - String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType); + String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType, mountOptions); try { for (int idx = 0; idx < volumePaths.size(); idx++) { String volumePath = volumePaths.get(idx); @@ -101,7 +101,7 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper volumePaths, String vmName, String backupPath, String backupRepoType, String backupRepoAddress, String mountOptions) { - String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType); + String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType, mountOptions); String diskType = "root"; try { for (int i = 0; i < volumePaths.size(); i++) { @@ -121,8 +121,8 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper vmNameAndState) { - String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType); + String diskType, String volumeUUID, Pair vmNameAndState, String mountOptions) { + String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType, mountOptions); Pair bkpPathAndVolUuid; try { bkpPathAndVolUuid = getBackupPath(mountDirectory, volumePath, backupPath, diskType, volumeUUID); @@ -145,12 +145,22 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper scriptMock = mockStatic(Script.class); + + when(getVmIpAddressCommand.getVmName()).thenReturn("validVmName"); + when(getVmIpAddressCommand.getVmNetworkCidr()).thenReturn("192.168.0.0/24"); + when(getVmIpAddressCommand.isWindows()).thenReturn(false); + when(Script.executePipedCommands(anyList(), anyLong())).thenReturn(new Pair<>(0, VIRSH_DOMIF_OUTPUT)); + + Answer answer = commandWrapper.execute(getVmIpAddressCommand, libvirtComputingResource); + + try { + assertTrue(answer.getResult()); + assertEquals("192.168.0.10", answer.getDetails()); + } finally { + scriptMock.close(); + } + } + + @Test + public void testExecuteWithInvalidVmName() { + LibvirtComputingResource libvirtComputingResource = mock(LibvirtComputingResource.class); + GetVmIpAddressCommand getVmIpAddressCommand = mock(GetVmIpAddressCommand.class); + LibvirtGetVmIpAddressCommandWrapper commandWrapper = new LibvirtGetVmIpAddressCommandWrapper(); + MockedStatic + + diff --git a/ui/src/views/infra/ConfigureHostOOBM.vue b/ui/src/views/infra/ConfigureHostOOBM.vue new file mode 100644 index 00000000000..d80ac68fc06 --- /dev/null +++ b/ui/src/views/infra/ConfigureHostOOBM.vue @@ -0,0 +1,172 @@ +// 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. + + + + + + diff --git a/ui/src/views/infra/HostEnableDisable.vue b/ui/src/views/infra/HostEnableDisable.vue index bc71aa27080..84310a0051f 100644 --- a/ui/src/views/infra/HostEnableDisable.vue +++ b/ui/src/views/infra/HostEnableDisable.vue @@ -28,15 +28,15 @@ > -
+
{ - if (json.listconfigurationsresponse.configuration[0]) { - this.enableKVMAutoEnableDisableSetting = json.listconfigurationsresponse.configuration[0].value + if (json.listconfigurationsresponse.configuration?.[0]) { + this.kvmAutoEnableDisableSetting = json?.listconfigurationsresponse?.configuration?.[0]?.value || false } }) }, diff --git a/ui/src/views/infra/HostInfo.vue b/ui/src/views/infra/HostInfo.vue index 1a4e9ee9f44..259445154a0 100644 --- a/ui/src/views/infra/HostInfo.vue +++ b/ui/src/views/infra/HostInfo.vue @@ -86,14 +86,48 @@
- -
- {{ $t('label.powerstate') }} + +
- {{ host.outofbandmanagement.powerstate }} + {{ $t('label.oobm.username') }} +
+ {{ host.outofbandmanagement.username }} +
-
-
+ + +
+ {{ $t('label.oobm.powerstate') }} +
+ {{ host.outofbandmanagement.powerstate }} +
+
+
+ +
+ {{ $t('label.oobm.driver') }} +
+ {{ host.outofbandmanagement.driver }} +
+
+
+ +
+ {{ $t('label.oobm.address') }} +
+ {{ host.outofbandmanagement.address }} +
+
+
+ +
+ {{ $t('label.oobm.port') }} +
+ {{ host.outofbandmanagement.port }} +
+
+
+
{{ $t('label.haenable') }} diff --git a/ui/src/views/offering/AddDiskOffering.vue b/ui/src/views/offering/AddDiskOffering.vue index 5cb1ff8bde9..f4e4d49c3dc 100644 --- a/ui/src/views/offering/AddDiskOffering.vue +++ b/ui/src/views/offering/AddDiskOffering.vue @@ -491,7 +491,6 @@ export default { const formRaw = toRaw(this.form) const values = this.handleRemoveFields(formRaw) var params = { - isMirrored: false, name: values.name, displaytext: values.displaytext, storageType: values.storagetype, diff --git a/ui/src/views/storage/CreateTemplate.vue b/ui/src/views/storage/CreateTemplate.vue index 13ce75777fb..65941d39a9d 100644 --- a/ui/src/views/storage/CreateTemplate.vue +++ b/ui/src/views/storage/CreateTemplate.vue @@ -43,7 +43,7 @@ v-model:value="form.displaytext" :placeholder="apiParams.displaytext.description" /> - + @@ -130,41 +130,40 @@ - - - - - - - {{ $t('label.passwordenabled') }} - - - - - {{ $t('label.isdynamicallyscalable') }} - - - - - {{ $t('label.requireshvm') }} - - - - - {{ $t('label.isfeatured') }} - - - - - {{ $t('label.ispublic') }} - - - - + + + + + + + + + + + + + + + + + + + +
@@ -234,7 +233,9 @@ export default { }, fetchData () { this.fetchOsTypes() - this.fetchSnapshotZones() + if (this.resource.intervaltype) { + this.fetchSnapshotZones() + } if ('listDomains' in this.$store.getters.apis) { this.fetchDomains() } @@ -300,21 +301,24 @@ export default { this.handleDomainChange(null) }) }, - handleDomainChange (domain) { + async handleDomainChange (domain) { this.domainid = domain this.form.account = null this.account = null if ('listAccounts' in this.$store.getters.apis) { - this.fetchAccounts() + await this.fetchAccounts() } }, fetchAccounts () { - api('listAccounts', { - domainid: this.domainid - }).then(response => { - this.accounts = response.listaccountsresponse.account || [] - }).catch(error => { - this.$notifyError(error) + return new Promise((resolve, reject) => { + api('listAccounts', { + domainid: this.domainid + }).then(response => { + this.accounts = response?.listaccountsresponse?.account || [] + resolve(this.accounts) + }).catch(error => { + this.$notifyError(error) + }) }) }, handleAccountChange (acc) { @@ -329,17 +333,22 @@ export default { this.formRef.value.validate().then(() => { const formRaw = toRaw(this.form) const values = this.handleRemoveFields(formRaw) - values.snapshotid = this.resource.id - if (values.groupenabled) { - const input = values.groupenabled - for (const index in input) { - const name = input[index] - values[name] = true + const params = {} + if (this.resource.intervaltype) { + params.snapshotid = this.resource.id + } else { + params.volumeid = this.resource.id + } + + for (const key in values) { + const input = values[key] + if (input === undefined) { + continue } - delete values.groupenabled + params[key] = input } this.loading = true - api('createTemplate', values).then(response => { + api('createTemplate', params).then(response => { this.$pollJob({ jobId: response.createtemplateresponse.jobid, title: this.$t('message.success.create.template'),