Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService, PluggableService, Configurable {

String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso";
String KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME = "kvm-default-vm-import-dummy-template";
ConfigKey<Boolean> UnmanageVMPreserveNic = new ConfigKey<>("Advanced", Boolean.class, "unmanage.vm.preserve.nics", "false",
"If set to true, do not remove VM nics (and its MAC addresses) when unmanaging a VM, leaving them allocated but not reserved. " +
"If set to false, nics are removed and MAC addresses can be reassigned", true, ConfigKey.Scope.Zone);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
// under the License.
package com.cloud.upgrade.dao;

import org.apache.cloudstack.vm.UnmanagedVMsManager;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class Upgrade42200to42210 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate {

@Override
Expand All @@ -27,4 +35,46 @@ public String[] getUpgradableVersionRange() {
public String getUpgradedVersion() {
return "4.22.1.0";
}

@Override
public void performDataMigration(Connection conn) {
removeDuplicateDummyTemplates(conn);
}

private void removeDuplicateDummyTemplates(Connection conn) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private void removeDuplicateDummyTemplates(Connection conn) {
private void removeDuplicateKVMImportTemplates(Connection conn) {

List<Long> templateIds = new ArrayList<>();
try (PreparedStatement selectStmt = conn.prepareStatement(String.format("SELECT id FROM cloud.vm_template WHERE name='%s' ORDER BY id ASC", UnmanagedVMsManager.KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME))) {
ResultSet rs = selectStmt.executeQuery();
while (rs.next()) {
templateIds.add(rs.getLong(1));
}

if (templateIds.size() <= 1) {
return;
}

Long firstTemplateId = templateIds.get(0);

String updateTemplateSql = "UPDATE cloud.vm_instance SET vm_template_id = ? WHERE vm_template_id = ?";
String deleteTemplateSql = "DELETE FROM cloud.vm_template WHERE id = ?";

try (PreparedStatement updateTemplateStmt = conn.prepareStatement(updateTemplateSql);
PreparedStatement deleteTemplateStmt = conn.prepareStatement(deleteTemplateSql)) {
for (int i = 1; i < templateIds.size(); i++) {
Long duplicateTemplateId = templateIds.get(i);

// Update VM references
updateTemplateStmt.setLong(1, firstTemplateId);
updateTemplateStmt.setLong(2, duplicateTemplateId);
updateTemplateStmt.executeUpdate();

// Delete duplicate dummy template
deleteTemplateStmt.setLong(1, duplicateTemplateId);
deleteTemplateStmt.executeUpdate();
}
}
} catch (Exception e) {
logger.warn("Failed to clean duplicate kvm-default-vm-import-dummy-template entries", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
logger.warn("Failed to clean duplicate kvm-default-vm-import-dummy-template entries", e);
logger.warn("Failed to clean duplicate " + UnmanagedVMsManager.KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME + " entries", e);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ UPDATE `cloud`.`alert` SET type = 34 WHERE name = 'ALERT.VR.PRIVATE.IFACE.MTU';

-- Update configuration 'kvm.ssh.to.agent' description and is_dynamic fields
UPDATE `cloud`.`configuration` SET description = 'True if the management server will restart the agent service via SSH into the KVM hosts after or during maintenance operations', is_dynamic = 1 WHERE name = 'kvm.ssh.to.agent';

UPDATE `cloud`.`vm_template` SET guest_os_id = 99 WHERE name = 'kvm-default-vm-import-dummy-template';
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This UPDATE targets rows by name only. To reduce the risk of unintentionally modifying non-system templates (or future templates) that happen to share the same name, consider narrowing the predicate (e.g., also filter on type = 'SYSTEM' and format = 'ISO' if that matches the intended rows).

Suggested change
UPDATE `cloud`.`vm_template` SET guest_os_id = 99 WHERE name = 'kvm-default-vm-import-dummy-template';
UPDATE `cloud`.`vm_template`
SET guest_os_id = 99
WHERE name = 'kvm-default-vm-import-dummy-template'
AND type = 'SYSTEM'
AND format = 'ISO';

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine with name

Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description shows many duplicate rows for kvm-default-vm-import-dummy-template. This migration updates guest_os_id but does not address the existing duplicates, so the system can continue operating with ambiguous data (and findByName may pick an arbitrary row depending on query ordering). Consider adding a migration step to de-duplicate (e.g., keep a single canonical row and mark others removed/unused) to make behavior deterministic and align with the PR’s stated goal of fixing duplicates.

Suggested change
UPDATE `cloud`.`vm_template` SET guest_os_id = 99 WHERE name = 'kvm-default-vm-import-dummy-template';
-- De-duplicate 'kvm-default-vm-import-dummy-template' entries and update guest_os_id
-- Keep the template with the smallest id as the canonical one and mark others as removed
UPDATE `cloud`.`vm_template` t
JOIN (
SELECT MIN(id) AS keep_id
FROM `cloud`.`vm_template`
WHERE name = 'kvm-default-vm-import-dummy-template'
) k ON t.id != k.keep_id
SET t.removed = NOW()
WHERE t.name = 'kvm-default-vm-import-dummy-template'
AND t.removed IS NULL;
-- Update guest_os_id only for the canonical template
UPDATE `cloud`.`vm_template` t
JOIN (
SELECT MIN(id) AS keep_id
FROM `cloud`.`vm_template`
WHERE name = 'kvm-default-vm-import-dummy-template'
AND removed IS NULL
) k ON t.id = k.keep_id
SET t.guest_os_id = 99;

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;

import static org.apache.cloudstack.vm.UnmanagedVMsManagerImpl.KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME;
import static org.apache.cloudstack.vm.UnmanagedVMsManagerImpl.VM_IMPORT_DEFAULT_TEMPLATE_NAME;
import static org.apache.cloudstack.vm.UnmanagedVMsManager.KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME;
import static org.apache.cloudstack.vm.UnmanagedVMsManager.VM_IMPORT_DEFAULT_TEMPLATE_NAME;

public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
protected Logger logger = LogManager.getLogger(getClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,8 @@
import static org.apache.cloudstack.vm.ImportVmTask.Step.Importing;

public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
public static final String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso";
public static final String KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME = "kvm-default-vm-import-dummy-template";
protected Logger logger = LogManager.getLogger(UnmanagedVMsManagerImpl.class);
private static final long OTHER_LINUX_64_GUEST_OS_ID = 99;
private static final List<Hypervisor.HypervisorType> importUnmanagedInstancesSupportedHypervisors =
Arrays.asList(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM);

Expand Down Expand Up @@ -326,7 +325,7 @@ private VMTemplateVO createDefaultDummyVmImportTemplate(boolean isKVM) {
try {
template = VMTemplateVO.createSystemIso(templateDao.getNextInSequence(Long.class, "id"), templateName, templateName, true,
"", true, 64, Account.ACCOUNT_ID_SYSTEM, "",
"VM Import Default Template", false, 1);
"VM Import Default Template", false, OTHER_LINUX_64_GUEST_OS_ID);
template.setState(VirtualMachineTemplate.State.Inactive);
template = templateDao.persist(template);
if (template == null) {
Expand Down Expand Up @@ -1550,11 +1549,13 @@ private ServiceOfferingVO getServiceOfferingForImportInstance(Long serviceOfferi
protected VMTemplateVO getTemplateForImportInstance(Long templateId, Hypervisor.HypervisorType hypervisorType) {
VMTemplateVO template;
if (templateId == null) {
template = templateDao.findByName(VM_IMPORT_DEFAULT_TEMPLATE_NAME);
boolean isKVMHypervisor = Hypervisor.HypervisorType.KVM.equals(hypervisorType);
String templateName = (isKVMHypervisor) ? KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME : VM_IMPORT_DEFAULT_TEMPLATE_NAME;
template = templateDao.findByName(templateName);
if (template == null) {
template = createDefaultDummyVmImportTemplate(Hypervisor.HypervisorType.KVM == hypervisorType);
template = createDefaultDummyVmImportTemplate(isKVMHypervisor);
if (template == null) {
throw new InvalidParameterValueException(String.format("Default VM import template with unique name: %s for hypervisor: %s cannot be created. Please use templateid parameter for import", VM_IMPORT_DEFAULT_TEMPLATE_NAME, hypervisorType.toString()));
throw new InvalidParameterValueException(String.format("Default VM import template with unique name: %s for hypervisor: %s cannot be created. Please use templateid parameter for import", templateName, hypervisorType.toString()));
}
}
} else {
Expand Down
Loading