Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
9913be6
kvm: restore backups into the volume they were taken from
calvix Aug 18, 2026
4722bd4
kvm: add regression tests for backup restore volume mapping
calvix Aug 18, 2026
2e7d20e
kvm: keep create-instance-from-backup working when mapping volumes by…
calvix Aug 21, 2026
7ea1dca
Add NULL check during restoreVM operation when host is removed (#13779)
nvazquez Aug 24, 2026
5b49d6a
Errorprone static analysis tool integration and fixes of reported iss…
Pearl1594 Sep 2, 2026
7d45dcb
Migrations triggered by Maintenance (system) not reported on VM event…
abh1sar Sep 2, 2026
b09005f
UI: make serviceofferingid optional when add network offering (#13502)
weizhouapache Sep 2, 2026
3dc2305
server: fix lastpinged of hosts (#13030)
weizhouapache Sep 2, 2026
f2dec38
Merge branch '4.20' into 4.22
Sep 2, 2026
c760568
ui: label instance volume sizes as GiB (#13831)
Dogface2k Sep 2, 2026
a5954f9
fix domain cleanup when a removed account owns templates (#13630)
Damans227 Sep 2, 2026
167320e
Propagate StopAnswer error details up the call chain when stopping a …
Damans227 Sep 2, 2026
6434ff2
make sure virtual machine params exist (#12771)
DaanHoogland Sep 2, 2026
8d26cb3
Support for migration of system VMs with volumes in the GUI (#13144)
Tonitzpp Sep 2, 2026
042ec7e
UI: fix usage records end date with local timezone (#13769)
Dogface2k Sep 2, 2026
2cd8c5e
Create bridge on dest host during SSVM migration when using storage n…
Pearl1594 Sep 2, 2026
e3c7b76
Merge remote-tracking branch 'origin/4.20' into 4.22
Pearl1594 Sep 2, 2026
2b3cca6
SAML SLO does not clear session cookies on logout (#14017)
DaanHoogland Sep 4, 2026
2327e83
UI: Fix listing of resources for ASG belonging to a project (#13187)
vishesh92 Sep 7, 2026
c2c74c5
api: fix ProjectResponse.setVpcLimit assigning the network limit (#14…
nagaboinaramgopal Sep 7, 2026
ac45be4
Change 2FA Field Type From Password to Text (#13723)
daviftorres Sep 7, 2026
3a79799
Make `domainid` optional in `assignVirtualMachine` (#13213)
winterhazel Sep 7, 2026
6ad0d75
volume: reset pool_id on failed volume create for zone-wide storage (…
nikolauseppinger Sep 8, 2026
f725211
fix label key for gpudisplay (#13272)
sudo87 Sep 8, 2026
6a2e033
apply the restored volume max IOPS to the max IOPS field (#14024)
nagaboinaramgopal Sep 8, 2026
cdbda0a
engine: key removeNic work job lookup on nic uuid to fix concurrent r…
Andr0human Sep 8, 2026
bbf8c1a
server: do not NPE when a VPN provider returns no per-user result (#1…
nagaboinaramgopal Sep 8, 2026
c860e0c
kvm: fix RBD exclusive-lock leak that breaks revertSnapshot on Ceph (…
calvix Sep 8, 2026
f493d99
sharedfs: skip hypervisors without system templates (#13830)
Dogface2k Sep 8, 2026
223351c
Add VR's system offering to network listing (#12862)
erikbocks Sep 8, 2026
74e7105
ui: show selected template hypervisor in deploy form (#13813)
Dogface2k Sep 8, 2026
215ef98
Allow cleaning up of networks stuck in Implementing state (#12409)
Pearl1594 Sep 9, 2026
245bbd9
server: null-check public IP before the DNS/Source NAT guard in creat…
nagaboinaramgopal Sep 9, 2026
c7f2efa
kvm: fix restore-and-attach of a backed up volume (#14007)
abh1sar Sep 9, 2026
a9b2f33
ui: filter accessible pools on live migration with storage wizard (#1…
winterhazel Sep 9, 2026
9c8bdf3
config: persist config subgroups with their name and group id (#14050)
nagaboinaramgopal Sep 9, 2026
10d3e11
KVM: fix UEFI disk-only instance snapshot NVRAM handling (#13020)
Kunalbehbud Sep 9, 2026
4ea3d0b
Merge branch '4.22' into fix/nas-backup-restore-volume-mapping
weizhouapache Sep 9, 2026
1f761f0
Update pmd config to make it pmd 7 compatible (#13168)
vishesh92 Sep 9, 2026
6e4413d
engine: do not stall the host command queue on a failed send in Agent…
nagaboinaramgopal Sep 9, 2026
b93f603
api: fix inverted value comparison in ImageStoreDetailResponse.equals…
nagaboinaramgopal Sep 10, 2026
c467686
[ConsoleProxy] Performance fixes on concurrency and closing console s…
nvazquez Sep 10, 2026
af8691c
Routed: get vm network statistics on Routed network (#13105)
weizhouapache Sep 10, 2026
17098c1
Fix lscpu speed checking order (#12190)
guillemtp Sep 10, 2026
02da97d
Merge remote-tracking branch 'apache/4.20' into 4.22
shwstppr Sep 10, 2026
d0d49bc
ui: fix build
shwstppr Sep 10, 2026
b9a5977
fix: use auth_client_required instead of removed auth_supported for R…
waterWang Sep 10, 2026
9018034
Add basic `.editorconfig` file to help maintain consistent coding sty…
jbampton Sep 11, 2026
9030443
SharedFS: fix NPE when accessing systemvm templates for unsupported h…
abh1sar Sep 11, 2026
2e450cb
marvin: use pycryptodome instead of PyCrypt (#13594)
weizhouapache Jul 13, 2026
a01fb0b
server: copy template details to vm instance details when import VM (…
weizhouapache Sep 14, 2026
e38b7a7
kvm: apply rbd_default_data_pool when creating volumes from templates…
bhouse-nexthop Sep 14, 2026
10037c8
Nas backup: Fix mount/unmount error handling and timeout in LibvirtRe…
abh1sar Sep 15, 2026
bb8f0cb
Merge branch '4.22' into fix/nas-backup-restore-volume-mapping
calvix Sep 16, 2026
1ba78e1
kvm: adapt the restore volume mapping tests to the backup command tim…
calvix Sep 16, 2026
b0b7d11
kvm: keep the restore volume mapping tests on the rsync path
calvix Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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.
root = true

[*]
charset = utf-8
end_of_line = lf
# indent_size = 4
indent_style = space
insert_final_newline = true
# max_line_length = 120
# tab_width = 4
1 change: 1 addition & 0 deletions .github/linters/codespell.txt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ environmnet
equivalant
erro
erronous
errorprone
everthing
everytime
excute
Expand Down
14 changes: 14 additions & 0 deletions PendingReleaseNotes
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,17 @@ example.ver.1 > example.ver.2:
which can now be attached to Instances. This is to prevent the Secondary
Storage to grow to enormous sizes as Linux Distributions keep growing in
size while a stripped down Linux should fit on a 2.88MB floppy.

4.22.0.0 > 4.22.0.1:
* Disk-only instance snapshots for KVM UEFI VMs now include a sidecar copy of
the active NVRAM state so revert operations restore both disk and firmware
boot state consistently.

* UEFI disk-only instance snapshots taken before this change do not contain an
NVRAM sidecar and cannot be safely reverted. Take a new snapshot after
upgrading before relying on revert for UEFI VMs.

* Taking a disk-only instance snapshot for KVM UEFI VMs now briefly suspends
the guest while the NVRAM sidecar is copied, so that the captured firmware
state is consistent with the disk snapshot. Non-UEFI VMs are unaffected and
continue to snapshot live.
2 changes: 1 addition & 1 deletion agent/src/main/java/com/cloud/agent/mockvm/MockVmMgr.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ public void freeVncPort(int port) {
public MockVm createVmFromSpec(VirtualMachineTO vmSpec) {
String vmName = vmSpec.getName();
long ramSize = vmSpec.getMinRam();
int utilizationPercent = randSeed.nextInt() % 100;
int utilizationPercent = randSeed.nextInt(100);
MockVm vm = null;

synchronized (this) {
Expand Down
1 change: 1 addition & 0 deletions api/src/main/java/com/cloud/host/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static String[] toStrings(Host.Type... types) {
}

String HOST_UEFI_ENABLE = "host.uefi.enable";
String HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM = "host.kvm.diskonlyvmsnapshot.nvram";
String HOST_VOLUME_ENCRYPTION = "host.volume.encryption";
String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
String HOST_VDDK_SUPPORT = "host.vddk.support";
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/java/com/cloud/resource/ResourceService.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public interface ResourceService {

DataCenter getZone(Long zoneId);

List<HypervisorType> getSupportedHypervisorTypes(long zoneId, boolean forVirtualRouter, Long podId);
List<HypervisorType> getSupportedHypervisorTypes(long zoneId, boolean forSystemVm, Long podId);

boolean releaseHostReservation(Long hostId);

Expand Down
1 change: 1 addition & 0 deletions api/src/main/java/com/cloud/vm/VirtualMachineProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public static class Param {
public static final Param PreserveNics = new Param("PreserveNics");
public static final Param ConsiderLastHost = new Param("ConsiderLastHost");
public static final Param ReturnAfterVolumePrepare = new Param("ReturnAfterVolumePrepare");
public static final Param ResetPasswordOnRestore = new Param("ResetPasswordOnRestore");

private String name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ public class ApiConstants {
public static final String CURRENT_PASSWORD = "currentpassword";
public static final String SHOULD_UPDATE_PASSWORD = "update_passwd_on_host";
public static final String PASSWORD_ENABLED = "passwordenabled";
public static final String RESET_PASSWORD = "resetpassword";
public static final String SSHKEY_ENABLED = "sshkeyenabled";
public static final String PATH = "path";
public static final String PATH_READY = "pathready";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ public Boolean getAllowUserDrivenBackups() {
public void execute() {
try {
if (StringUtils.isAllEmpty(getName(), getDescription()) && getAllowUserDrivenBackups() == null) {
throw new InvalidParameterValueException(String.format("Can't update Backup Offering [id: %s] because there are no parameters to be updated, at least one of the",
"following should be informed: name, description or allowUserDrivenBackups.", id));
throw new InvalidParameterValueException(String.format("Can't update Backup Offering [id: %s] because there are no parameters to be updated," +
" at least one of the following should be passed: name, description or allowUserDrivenBackups.", id));
}

BackupOffering result = backupManager.updateBackupOffering(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public class CreateVMFromBackupCmd extends BaseDeployVMCmd {
@Parameter(name = ApiConstants.PRESERVE_IP, type = CommandType.BOOLEAN, description = "Use the same IP/MAC addresses as stored in the backup metadata. Works only if the original Instance is deleted and the IP/MAC address is available.")
private Boolean preserveIp;

@Parameter(name = ApiConstants.RESET_PASSWORD, type = CommandType.BOOLEAN,
description = "For a password enabled template, whether to generate a new password for the created Instance and return it in the response. " +
"If not specified, the zone setting `restore.vm.from.backup.reset.password` decides.", since = "4.22.1.0")
private Boolean resetPassword;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand All @@ -90,6 +95,10 @@ public boolean getPreserveIp() {
return (preserveIp != null) ? preserveIp : false;
}

public Boolean getResetPassword() {
return resetPassword;
}

@Override
public void create() {
UserVm vm;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;

@APICommand(name = "createVMSnapshot", description = "Creates Snapshot for an Instance.", responseObject = VMSnapshotResponse.class, since = "4.2.0", entityType = {VMSnapshot.class},
@APICommand(name = "createVMSnapshot", description = "Creates Snapshot for an Instance. Running KVM UEFI disk-only snapshots briefly suspend the Instance while copying NVRAM state.",
responseObject = VMSnapshotResponse.class, since = "4.2.0", entityType = {VMSnapshot.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// under the License.
package org.apache.cloudstack.api.response;

import java.util.Objects;

import com.google.gson.annotations.SerializedName;

import org.apache.cloudstack.api.BaseResponse;
Expand Down Expand Up @@ -81,7 +83,7 @@ public boolean equals(Object obj) {
return false;
} else if (!oid.equals(other.getName()))
return false;
else if (this.getValue().equals(other.getValue()))
else if (!Objects.equals(this.getValue(), other.getValue()))
return false;
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations {
@Param(description = "The ID of the service offering used by virtual router provider")
private String serviceOfferingId;

@SerializedName(ApiConstants.SERVICE_OFFERING_NAME)
@Param(description = "the name of the service offering used by virtual router provider")
private String serviceOfferingName;

@SerializedName(ApiConstants.SERVICE)
@Param(description = "The list of supported services", responseObject = ServiceResponse.class)
private List<ServiceResponse> services;
Expand Down Expand Up @@ -330,4 +334,12 @@ public String getRoutingMode() {
public void setRoutingMode(String routingMode) {
this.routingMode = routingMode;
}

public String getServiceOfferingName() {
return serviceOfferingName;
}

public void setServiceOfferingName(String serviceOfferingName) {
this.serviceOfferingName = serviceOfferingName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ public void setNetworkAvailable(String networkAvailable) {

@Override
public void setVpcLimit(String vpcLimit) {
this.vpcLimit = networkLimit;
this.vpcLimit = vpcLimit;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
public class IsolationMethodTest {
@After
public void cleanTheRegistry() {
PhysicalNetwork.IsolationMethod.registeredIsolationMethods.removeAll(PhysicalNetwork.IsolationMethod.registeredIsolationMethods);
PhysicalNetwork.IsolationMethod.registeredIsolationMethods.clear();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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.response;

import org.junit.Assert;
import org.junit.Test;

public class ImageStoreDetailResponseTest {

@Test
public void equalsIsTrueForSameNameAndValue() {
ImageStoreDetailResponse a = new ImageStoreDetailResponse("key", "value");
ImageStoreDetailResponse b = new ImageStoreDetailResponse("key", "value");
Assert.assertEquals(a, b);
Assert.assertEquals(a.hashCode(), b.hashCode());
}

@Test
public void equalsIsFalseWhenValueDiffers() {
ImageStoreDetailResponse a = new ImageStoreDetailResponse("key", "value");
ImageStoreDetailResponse c = new ImageStoreDetailResponse("key", "other");
Assert.assertNotEquals(a, c);
}

@Test
public void equalsIsFalseWhenNameDiffers() {
ImageStoreDetailResponse a = new ImageStoreDetailResponse("key", "value");
ImageStoreDetailResponse d = new ImageStoreDetailResponse("other", "value");
Assert.assertNotEquals(a, d);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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.response;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.test.util.ReflectionTestUtils;

public class ProjectResponseTest {

@Test
public void setVpcLimitStoresItsOwnParameterNotTheNetworkLimit() {
ProjectResponse response = new ProjectResponse();
response.setNetworkLimit("5");
response.setVpcLimit("10");

Assert.assertEquals("10", ReflectionTestUtils.getField(response, "vpcLimit"));
Assert.assertEquals("5", ReflectionTestUtils.getField(response, "networkLimit"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,24 @@
public class CreateDiskOnlyVmSnapshotAnswer extends Answer {

protected Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath;
private String nvramSnapshotPath;

public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath) {
this(command, success, details, mapVolumeToSnapshotSizeAndNewVolumePath, null);
}

public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath,
String nvramSnapshotPath) {
super(command, success, details);
this.mapVolumeToSnapshotSizeAndNewVolumePath = mapVolumeToSnapshotSizeAndNewVolumePath;
this.nvramSnapshotPath = nvramSnapshotPath;
}

public Map<String, Pair<Long, String>> getMapVolumeToSnapshotSizeAndNewVolumePath() {
return mapVolumeToSnapshotSizeAndNewVolumePath;
}

public String getNvramSnapshotPath() {
return nvramSnapshotPath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,30 @@
public class CreateDiskOnlyVmSnapshotCommand extends VMSnapshotBaseCommand {

protected VirtualMachine.State vmState;
private final String vmUuid;
private final boolean uefiEnabled;

public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List<VolumeObjectTO> volumeTOs, String guestOSType, VirtualMachine.State vmState) {
this(vmName, null, snapshot, volumeTOs, guestOSType, vmState, false);
}

public CreateDiskOnlyVmSnapshotCommand(String vmName, String vmUuid, VMSnapshotTO snapshot, List<VolumeObjectTO> volumeTOs, String guestOSType,
VirtualMachine.State vmState, boolean uefiEnabled) {
super(vmName, snapshot, volumeTOs, guestOSType);
this.vmUuid = vmUuid;
this.vmState = vmState;
this.uefiEnabled = uefiEnabled;
}

public VirtualMachine.State getVmState() {
return vmState;
}

public String getVmUuid() {
return vmUuid;
}

public boolean isUefiEnabled() {
return uefiEnabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,43 @@
package com.cloud.agent.api.storage;

import com.cloud.agent.api.Command;

import com.cloud.agent.api.to.DataTO;

import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;

import java.util.List;

public class DeleteDiskOnlyVmSnapshotCommand extends Command {

List<DataTO> snapshots;
private final List<DataTO> snapshots;
private final String nvramSnapshotPath;
private final PrimaryDataStoreTO primaryDataStore;

public DeleteDiskOnlyVmSnapshotCommand(List<DataTO> snapshots) {
this(snapshots, null);
}

public DeleteDiskOnlyVmSnapshotCommand(List<DataTO> snapshots, String nvramSnapshotPath) {
this(snapshots, nvramSnapshotPath, null);
}

public DeleteDiskOnlyVmSnapshotCommand(List<DataTO> snapshots, String nvramSnapshotPath, PrimaryDataStoreTO primaryDataStore) {
this.snapshots = snapshots;
this.nvramSnapshotPath = nvramSnapshotPath;
this.primaryDataStore = primaryDataStore;
}

public List<DataTO> getSnapshots() {
return snapshots;
}

public String getNvramSnapshotPath() {
return nvramSnapshotPath;
}

public PrimaryDataStoreTO getPrimaryDataStore() {
return primaryDataStore;
}

@Override
public boolean executeInSequence() {
return false;
Expand Down
Loading
Loading