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());
              }

Reply via email to