This is an automated email from the ASF dual-hosted git repository. dahn pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit f06ac512fa9bd699d154ce11d963bf72ce1c7456 Merge: da1c7cebf94 7aba434dc42 Author: Daan Hoogland <[email protected]> AuthorDate: Tue Dec 16 11:50:58 2025 +0100 merge forward 4.22 to main .../java/com/cloud/server/ManagementService.java | 16 + .../network/CreateManagementNetworkIpRangeCmd.java | 3 +- .../command/admin/vlan/CreateVlanIpRangeCmd.java | 3 +- .../cloudstack/cluster/ClusterDrsAlgorithm.java | 198 ++++---- .../cloudstack/config/ApiServiceConfiguration.java | 19 + .../config/ApiServiceConfigurationTest.java | 95 ++++ .../com/cloud/upgrade/DatabaseUpgradeChecker.java | 111 ++++- .../DatabaseUpgradeCheckerDoUpgradesTest.java | 173 +++++++ .../cloud/upgrade/DatabaseUpgradeCheckerTest.java | 92 +++- .../org/apache/cloudstack/cluster/Balanced.java | 25 +- .../apache/cloudstack/cluster/BalancedTest.java | 61 ++- .../org/apache/cloudstack/cluster/Condensed.java | 26 +- .../apache/cloudstack/cluster/CondensedTest.java | 61 ++- .../cluster/KubernetesClusterManagerImpl.java | 69 ++- .../KubernetesClusterActionWorker.java | 2 +- .../com/cloud/network/as/AutoScaleManagerImpl.java | 5 +- .../network/lb/LoadBalancingRulesManagerImpl.java | 8 +- .../com/cloud/server/ManagementServerImpl.java | 133 +++++- .../cloud/storage/StoragePoolAutomationImpl.java | 505 ++++++++++---------- .../java/com/cloud/user/AccountManagerImpl.java | 6 + .../cloudstack/cluster/ClusterDrsServiceImpl.java | 288 ++++++++++-- .../com/cloud/server/ManagementServerImplTest.java | 234 +++++----- .../com/cloud/user/AccountManagerImplTest.java | 16 + .../cluster/ClusterDrsServiceImplTest.java | 506 ++++++++++++++++++++- .../maint/test_primary_storage_nfsmountopts_kvm.py | 2 +- ui/public/locales/en.json | 4 + ui/src/views/iam/EditAccount.vue | 6 +- ui/src/views/infra/network/IpRangesTabPublic.vue | 26 +- ui/src/views/network/NicsTab.vue | 65 +-- ui/src/views/network/VpnDetails.vue | 250 +++++----- 30 files changed, 2233 insertions(+), 775 deletions(-) diff --cc engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java index 004a5f9dd65,296f80f4b5e..0e784d961b3 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java @@@ -459,39 -460,77 +462,77 @@@ public class DatabaseUpgradeChecker imp throw new CloudRuntimeException("Unable to acquire lock to check for database integrity."); } - try { - initializeDatabaseEncryptors(); - - final CloudStackVersion dbVersion = CloudStackVersion.parse(_dao.getCurrentVersion()); - final String currentVersionValue = this.getClass().getPackage().getImplementationVersion(); + doUpgrades(lock); + } finally { + lock.releaseRef(); + } + } - if (StringUtils.isBlank(currentVersionValue)) { - return; + boolean isStandalone() throws CloudRuntimeException { + return Transaction.execute(new TransactionCallback<>() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + String sql = "SELECT COUNT(*) FROM `cloud`.`mshost` WHERE `state` = 'UP'"; + try (Connection conn = TransactionLegacy.getStandaloneConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql); + ResultSet rs = pstmt.executeQuery()) { + if (rs.next()) { + int count = rs.getInt(1); + return count == 0; + } + } catch (SQLException e) { + String errorMessage = "Unable to check if the management server is running in standalone mode."; + LOGGER.error(errorMessage, e); + return false; + } catch (NullPointerException npe) { + String errorMessage = "Unable to check if the management server is running in standalone mode. Not able to get a Database connection."; + LOGGER.error(errorMessage, npe); + return false; } + return true; + } + }); + } - String csVersion = SystemVmTemplateRegistration.parseMetadataFile(); - final CloudStackVersion sysVmVersion = CloudStackVersion.parse(csVersion); - final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue); - SystemVmTemplateRegistration.CS_MAJOR_VERSION = sysVmVersion.getMajorRelease() + "." + sysVmVersion.getMinorRelease(); - SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getPatchRelease()); + @VisibleForTesting + protected void doUpgrades(GlobalLock lock) { + try { + initializeDatabaseEncryptors(); - LOGGER.info("DB version = {} Code Version = {}", dbVersion, currentVersion); + final CloudStackVersion dbVersion = CloudStackVersion.parse(_dao.getCurrentVersion()); + final String currentVersionValue = getImplementationVersion(); - if (dbVersion.compareTo(currentVersion) > 0) { - throw new CloudRuntimeException("Database version " + dbVersion + " is higher than management software version " + currentVersionValue); - } + if (StringUtils.isBlank(currentVersionValue)) { + return; + } - if (dbVersion.compareTo(currentVersion) == 0) { - LOGGER.info("DB version and code version matches so no upgrade needed."); - return; - } + String csVersion = parseSystemVmMetadata(); + final CloudStackVersion sysVmVersion = CloudStackVersion.parse(csVersion); + final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue); - SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.valueOf(sysVmVersion.getMajorRelease()) + "." + String.valueOf(sysVmVersion.getMinorRelease()); ++ SystemVmTemplateRegistration.CS_MAJOR_VERSION = sysVmVersion.getMajorRelease() + "." + sysVmVersion.getMinorRelease(); + SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getPatchRelease()); + - LOGGER.info("DB version = " + dbVersion + " Code Version = " + currentVersion); ++ LOGGER.info("DB version = {} Code Version = {}", dbVersion, currentVersion); + + if (dbVersion.compareTo(currentVersion) > 0) { + throw new CloudRuntimeException("Database version " + dbVersion + " is higher than management software version " + currentVersionValue); + } + if (dbVersion.compareTo(currentVersion) == 0) { + LOGGER.info("DB version and code version matches so no upgrade needed."); + return; + } + + if (isStandalone()) { upgrade(dbVersion, currentVersion); - } finally { - lock.unlock(); + } else { + String errorMessage = "Database upgrade is required but the management server is running in a clustered environment. " + + "Please perform the database upgrade when the management server is not running in a clustered environment."; + LOGGER.error(errorMessage); + handleClusteredUpgradeRequired(); // allow tests to override behavior } } finally { - lock.releaseRef(); + lock.unlock(); } } diff --cc server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java index 93bc1261530,594bf3f90bb..a5192e25a63 --- a/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java +++ b/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java @@@ -235,95 -218,108 +218,108 @@@ public class StoragePoolAutomationImpl continue; } - // if the instance is of type consoleproxy, call the console - // proxy - if (vmInstance.getType().equals(VirtualMachine.Type.ConsoleProxy)) { - // call the consoleproxymanager - ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(vmInstance.getId()); - vmMgr.advanceStop(consoleProxy.getUuid(), false); - // update work status - work.setStoppedForMaintenance(true); - _storagePoolWorkDao.update(work.getId(), work); + switch (vmInstance.getType()) { + case ConsoleProxy: + case SecondaryStorageVm: + case DomainRouter: + handleVmStart(work, vmInstance); + break; + case User: + handleUserVmStart(work, vmInstance); + break; + } + } catch (Exception e) { + logger.debug("Failed start vm", e); + throw new CloudRuntimeException(e.toString()); + } + } + } - if (restart) { + private void handleStopVmForMigration(StoragePoolWorkVO work, VMInstanceVO vmInstance) throws ResourceUnavailableException, OperationTimedoutException { + vmMgr.advanceStop(vmInstance.getUuid(), false); + // update work status + work.setStoppedForMaintenance(true); + _storagePoolWorkDao.update(work.getId(), work); + } - vmMgr.advanceStart(consoleProxy.getUuid(), null, null); - // update work status - work.setStartedAfterMaintenance(true); - _storagePoolWorkDao.update(work.getId(), work); - } - } + private void handleVmMigration(boolean restart, StoragePoolWorkVO work, VMInstanceVO vmInstance) throws ResourceUnavailableException, OperationTimedoutException, InsufficientCapacityException { + handleStopVmForMigration(work, vmInstance); - // if the instance is of type uservm, call the user vm manager - if (vmInstance.getType() == VirtualMachine.Type.User) { - UserVmVO userVm = userVmDao.findById(vmInstance.getId()); - vmMgr.advanceStop(userVm.getUuid(), false); - // update work status - work.setStoppedForMaintenance(true); - _storagePoolWorkDao.update(work.getId(), work); - } + if (restart) { + handleVmStart(work, vmInstance); + } + } - // if the instance is of type secondary storage vm, call the - // secondary storage vm manager - if (vmInstance.getType().equals(VirtualMachine.Type.SecondaryStorageVm)) { - SecondaryStorageVmVO secStrgVm = _secStrgDao.findById(vmInstance.getId()); - vmMgr.advanceStop(secStrgVm.getUuid(), false); - // update work status - work.setStoppedForMaintenance(true); - _storagePoolWorkDao.update(work.getId(), work); + private void handleVmStart(StoragePoolWorkVO work, VMInstanceVO vmInstance) throws InsufficientCapacityException, ResourceUnavailableException, OperationTimedoutException { + vmMgr.advanceStart(vmInstance.getUuid(), null, null); + // update work queue + work.setStartedAfterMaintenance(true); + _storagePoolWorkDao.update(work.getId(), work); + } - if (restart) { - vmMgr.advanceStart(secStrgVm.getUuid(), null, null); - // update work status - work.setStartedAfterMaintenance(true); - _storagePoolWorkDao.update(work.getId(), work); - } - } + private void enqueueMigrationsForVolumes(List<VolumeVO> allVolumes, StoragePoolVO pool) { + for (VolumeVO volume : allVolumes) { + VMInstanceVO vmInstance = vmDao.findById(volume.getInstanceId()); - // if the instance is of type domain router vm, call the network - // manager - if (vmInstance.getType().equals(VirtualMachine.Type.DomainRouter)) { - DomainRouterVO domR = _domrDao.findById(vmInstance.getId()); - vmMgr.advanceStop(domR.getUuid(), false); - // update work status - work.setStoppedForMaintenance(true); - _storagePoolWorkDao.update(work.getId(), work); + if (vmInstance == null) { + continue; + } - if (restart) { - vmMgr.advanceStart(domR.getUuid(), null, null); - // update work status - work.setStartedAfterMaintenance(true); - _storagePoolWorkDao.update(work.getId(), work); + // enqueue sp work + if (vmInstance.getState().equals(State.Running) || vmInstance.getState().equals(State.Starting) || vmInstance.getState().equals(State.Stopping)) { + + try { + StoragePoolWorkVO work = new StoragePoolWorkVO(vmInstance.getId(), pool.getId(), false, false, server.getId()); + _storagePoolWorkDao.persist(work); + } catch (Exception e) { + if (logger.isDebugEnabled()) { - logger.debug("Work record already exists, re-using by re-setting values"); ++ logger.debug("Work record already exists, reusing by re-setting values"); } + StoragePoolWorkVO work = _storagePoolWorkDao.findByPoolIdAndVmId(pool.getId(), vmInstance.getId()); + work.setStartedAfterMaintenance(false); + work.setStoppedForMaintenance(false); + work.setManagementServerId(server.getId()); + _storagePoolWorkDao.update(work.getId(), work); } } - } catch (Exception e) { - logger.error("Exception in enabling primary storage maintenance:", e); - pool.setStatus(StoragePoolStatus.ErrorInMaintenance); - primaryDataStoreDao.update(pool.getId(), pool); - throw new CloudRuntimeException(e.getMessage()); } - return true; } - @Override - public boolean cancelMaintain(DataStore store) { - return cancelMaintain(store, null); + private void removeHeartbeatForHostsFromPool(List<HostVO> hosts, StoragePool storagePool) { + // remove heartbeat + for (HostVO host : hosts) { + ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(false, storagePool); + final Answer answer = agentMgr.easySend(host.getId(), cmd); + if (answer == null || !answer.getResult()) { + if (logger.isDebugEnabled()) { + logger.debug("ModifyStoragePool false failed due to {}", ((answer == null) ? "answer null" : answer.getDetails())); + } + } else { + reportSucceededModifyStorePool(storagePool, (ModifyStoragePoolAnswer) answer, host, false); + } + } } - @Override - public boolean cancelMaintain(DataStore store, Map<String,String> details) { - // Change the storage state back to up - Long userId = CallContext.current().getCallingUserId(); - User user = _userDao.findById(userId); - Account account = CallContext.current().getCallingAccount(); - StoragePoolVO poolVO = primaryDataStoreDao.findById(store.getId()); - StoragePool pool = (StoragePool)store; + private void reportSucceededModifyStorePool(StoragePool storagePool, ModifyStoragePoolAnswer answer, HostVO host, boolean add) { + if (logger.isDebugEnabled()) { + logger.debug("ModifyStoragePool succeeded for {}", add ? "adding" : "removing"); + } + if (storagePool.getPoolType() == Storage.StoragePoolType.DatastoreCluster) { + logger.debug("Started synchronising datastore cluster storage pool {} with vCenter", storagePool); + storageManager.syncDatastoreClusterStoragePool(storagePool.getId(), answer.getDatastoreClusterChildren(), host.getId()); + } + } - //Handeling the Zone wide and cluster wide primay storage - List<HostVO> hosts = new ArrayList<HostVO>(); - // if the storage scope is ZONE wide, then get all the hosts for which hypervisor ZWSP created to send Modifystoragepoolcommand - if (poolVO.getScope().equals(ScopeType.ZONE)) { + /** + * Handling the Zone wide and cluster wide primary storage + * if the storage scope is ZONE wide, then get all the hosts for which hypervisor ZoneWideStoragePools created to send ModifyStoragePoolCommand + * TODO: if it's zone wide, this code will list a lot of hosts in the zone, which may cause performance/OOM issue. + * @param pool pool to check for connected hosts + * @return a list of connected hosts + */ + private List<HostVO> getHostsForStoragePool(StoragePoolVO pool) { + List<HostVO> hosts; + if (pool.getScope().equals(ScopeType.ZONE)) { if (HypervisorType.Any.equals(pool.getHypervisor())) { hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZone(pool.getDataCenterId()); }
