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 @@ -196,7 +196,20 @@ private void handle(ReportHostCapacityMessage msg) {

// U-A (NB-30): physical fields written here under PESSIMISTIC_WRITE lock; available*
// fields are the sole responsibility of psCapacityUpdater.recalculate() below.
String serverUuid = HostCapacityUpdater.resolveServerUuidOrThrow(msg.getHostUuid());
String serverUuid = HostCapacityUpdater.resolveServerUuid(msg.getHostUuid());
if (serverUuid == null) {
// Non-unified-hardware host (ESX/vCenter etc.): no KVM_HOST PhysicalServerRoleVO and no
// PhysicalServerVO. The HostCapacityVO MERGE VIEW keys these via COALESCE(serverUuid,
// hostUuid), so report into a PhysicalServerCapacityVO row keyed by hostUuid; available*
// fields are set inline since recalculate() (which needs a PhysicalServerVO) is skipped.
// A KVM host missing its role is a FlowChain timing bug — keep failing loud (NB-24).
if (HostCapacityUpdater.isKvmHost(msg.getHostUuid())) {
HostCapacityUpdater.resolveServerUuidOrThrow(msg.getHostUuid());
}
handleReportForNonUnifiedHost(msg, totalCpu, availPhysMem);
bus.reply(msg, new MessageReply());
return;
}
PhysicalServerCapacityVO vo = dbf.getEntityManager()
.find(PhysicalServerCapacityVO.class, serverUuid, javax.persistence.LockModeType.PESSIMISTIC_WRITE);
if (vo == null) {
Expand Down Expand Up @@ -234,6 +247,47 @@ private boolean needUpdateCapacity(PhysicalServerCapacityVO vo, ReportHostCapaci
|| vo.getCpuSockets() != msg.getCpuSockets() || vo.getCpuCoreNum() != msg.getCpuCoreNum();
}

private void handleReportForNonUnifiedHost(ReportHostCapacityMessage msg, long totalCpu, long availPhysMem) {
long availCpu = totalCpu - msg.getUsedCpu();
availCpu = availCpu > 0 ? availCpu : 0;

PhysicalServerCapacityVO vo = dbf.getEntityManager()
.find(PhysicalServerCapacityVO.class, msg.getHostUuid(), javax.persistence.LockModeType.PESSIMISTIC_WRITE);
if (vo == null) {
vo = new PhysicalServerCapacityVO();
vo.setUuid(msg.getHostUuid());
vo.setTotalCpu(totalCpu);
vo.setAvailableCpu(availCpu);
vo.setTotalMemory(msg.getTotalMemory());
vo.setAvailableMemory(availPhysMem);
vo.setTotalPhysicalMemory(msg.getTotalMemory());
vo.setAvailablePhysicalMemory(availPhysMem);
vo.setCpuNum(msg.getCpuNum());
vo.setCpuSockets(msg.getCpuSockets());
vo.setCpuCoreNum(msg.getCpuCoreNum());
dbf.getEntityManager().persist(vo);
} else if (needUpdateNonUnifiedCapacity(vo, msg, totalCpu, availCpu, availPhysMem)) {
vo.setCpuNum(msg.getCpuNum());
vo.setTotalCpu(totalCpu);
vo.setAvailableCpu(availCpu);
vo.setTotalPhysicalMemory(msg.getTotalMemory());
vo.setAvailablePhysicalMemory(availPhysMem);
vo.setTotalMemory(msg.getTotalMemory());
vo.setAvailableMemory(availPhysMem);
vo.setCpuSockets(msg.getCpuSockets());
vo.setCpuCoreNum(msg.getCpuCoreNum());
dbf.getEntityManager().merge(vo);
}
}

private boolean needUpdateNonUnifiedCapacity(PhysicalServerCapacityVO vo, ReportHostCapacityMessage msg, long totalCpu, long availCpu, long availPhysMem) {
return vo.getCpuNum() != msg.getCpuNum() || vo.getTotalCpu() != totalCpu
|| vo.getAvailableCpu() != availCpu || vo.getTotalPhysicalMemory() != msg.getTotalMemory()
|| vo.getAvailablePhysicalMemory() != availPhysMem || vo.getTotalMemory() != msg.getTotalMemory()
|| vo.getAvailableMemory() != availPhysMem
|| vo.getCpuSockets() != msg.getCpuSockets() || vo.getCpuCoreNum() != msg.getCpuCoreNum();
}

private void handle(final AllocateHostMsg msg) {
if (HostAllocatorGlobalConfig.HOST_ALLOCATOR_ALLOW_CONCURRENT.value(Boolean.class)) {
thdf.chainSubmit(new ChainTask(msg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import org.zstack.core.db.Q;
import org.zstack.header.allocator.HostCapacityVO;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.host.HostVO;
import org.zstack.header.host.HostVO_;
import org.zstack.header.server.PhysicalServerCapacityVO;
import org.zstack.header.server.PhysicalServerRoleVO;
import org.zstack.header.server.PhysicalServerRoleVO_;
import org.zstack.header.server.ServerRoleType;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;

Expand Down Expand Up @@ -70,29 +73,54 @@ public HostCapacityUpdater(String hostUuid) {
/**
* Resolve PhysicalServer UUID from a KVM host UUID via PhysicalServerRoleVO mapping.
*
* <p>Throws {@link CloudRuntimeException} when no KVM_HOST role mapping is found (NB-24,
* 2026-04-22). Previous NB-22 "log null + boolean" silent-drop was reverted — fail-loud
* surfaces FlowChain timing bugs / orphan windows instead of masking them as silent capacity
* update losses. The existing "host deleted naturally" semantic is still carried by
* <p>Throws {@link CloudRuntimeException} when a KVM host has no KVM_HOST role mapping (NB-24,
* 2026-04-22) — a FlowChain timing bug. Non-KVM hosts (ESX/vCenter etc.) legitimately have no
* role mapping and return {@code hostUuid} (their PhysicalServerCapacityVO is keyed by hostUuid
* via the COALESCE(serverUuid, hostUuid) view). Previous NB-22 "log null + boolean" silent-drop
* was reverted — fail-loud surfaces FlowChain timing bugs / orphan windows instead of masking
* them as silent capacity update losses. The existing "host deleted naturally" semantic is still carried by
* {@link #lockCapacity()} returning {@code false} when the capacity row itself is absent.
*
* <p>NB-30: Phase 2 lock key invariant. All PESSIMISTIC_WRITE paths on PhysicalServerCapacityVO
* use {@code serverUuid} as the single lock key; callers MUST NOT mix {@code hostUuid} and
* {@code serverUuid}.
*/
public static String resolveServerUuidOrThrow(String hostUuid) {
String serverUuid = Q.New(PhysicalServerRoleVO.class)
String serverUuid = resolveServerUuid(hostUuid);
if (serverUuid == null) {
// Non-unified-hardware hosts (ESX/vCenter etc.) legitimately have no KVM_HOST role;
// their PhysicalServerCapacityVO is keyed by hostUuid via COALESCE(serverUuid, hostUuid).
// A KVM host missing its role is a FlowChain timing bug — keep failing loud (NB-24).
if (isKvmHost(hostUuid)) {
throw new CloudRuntimeException(String.format(
"cannot resolve PhysicalServer UUID for host[uuid:%s]: no KVM_HOST "
+ "PhysicalServerRoleVO found. FlowChain timing bug or orphan "
+ "PhysicalServerVO — capacity PRD NB-24.", hostUuid));
}
return hostUuid;
}
return serverUuid;
}

/**
* Non-throwing variant of {@link #resolveServerUuidOrThrow(String)}: returns the KVM_HOST
* {@code serverUuid} mapping or {@code null} when none exists. Callers decide fail-loud vs
* fallback; non-unified-hardware hosts (ESX/vCenter etc.) legitimately have no role mapping.
*/
public static String resolveServerUuid(String hostUuid) {
return Q.New(PhysicalServerRoleVO.class)
.eq(PhysicalServerRoleVO_.roleUuid, hostUuid)
.eq(PhysicalServerRoleVO_.roleType, ServerRoleType.KVM_HOST.toString())
.select(PhysicalServerRoleVO_.serverUuid)
.findValue();
if (serverUuid == null) {
throw new CloudRuntimeException(String.format(
"cannot resolve PhysicalServer UUID for host[uuid:%s]: no KVM_HOST "
+ "PhysicalServerRoleVO found. FlowChain timing bug or orphan "
+ "PhysicalServerVO — capacity PRD NB-24.", hostUuid));
}
return serverUuid;
}

public static boolean isKvmHost(String hostUuid) {
String hypervisorType = Q.New(HostVO.class)
.eq(HostVO_.uuid, hostUuid)
.select(HostVO_.hypervisorType)
.findValue();
return VmInstanceConstant.KVM_HYPERVISOR_TYPE.equals(hypervisorType);
}

private void logDeletedHost() {
Expand Down
7 changes: 0 additions & 7 deletions conf/db/upgrade/V5.5.18__schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,6 @@ CREATE TABLE IF NOT EXISTS `PhysicalServerHardwareInfoVO` (
`cpuCores` INT DEFAULT NULL,
`cpuArchitecture` VARCHAR(255) DEFAULT NULL,
`totalMemoryBytes` BIGINT DEFAULT NULL,
`memoryModuleCount` INT DEFAULT NULL,
`totalDiskBytes` BIGINT DEFAULT NULL,
`diskCount` INT DEFAULT NULL,
`nicCount` INT DEFAULT NULL,
`gpuCount` INT DEFAULT NULL,
`healthStatus` VARCHAR(255) DEFAULT NULL,
`discoverSource` VARCHAR(255) DEFAULT NULL,
`lastDiscoverDate` TIMESTAMP NULL DEFAULT NULL,
`lastOpDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`createDate` TIMESTAMP NOT NULL DEFAULT '2000-01-01 00:00:00',
Expand Down
7 changes: 7 additions & 0 deletions conf/springConfigXml/PhysicalServerManager.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,11 @@

<bean id="physicalServerPathTwoOrchestrator"
class="org.zstack.server.PhysicalServerPathTwoOrchestrator" />

<bean id="PhysicalServerKvmIdentityBackfillExtension"
class="org.zstack.server.PhysicalServerKvmIdentityBackfillExtension">
<zstack:plugin>
<zstack:extension interface="org.zstack.header.host.HostAddExtensionPoint" />
</zstack:plugin>
</bean>
</beans>
2 changes: 1 addition & 1 deletion conf/springConfigXml/encrypt.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@
</zstack:plugin>
</bean>

<bean id="PasswordConvert" class="org.zstack.core.convert.PasswordConverter" />
<bean id="PasswordConvert" class="org.zstack.header.core.convert.PasswordConverter" />
<bean id="SpecialDataConverter" class="org.zstack.core.convert.SpecialDataConverter" />
</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;
import org.zstack.core.encrypt.EncryptFacade;
import org.zstack.header.core.convert.EncryptFacade;
import org.zstack.core.encrypt.EncryptGlobalConfig;
import org.zstack.header.core.encrypt.PasswordEncryptType;
import org.zstack.utils.Utils;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
import org.zstack.core.Platform;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.config.*;
import org.zstack.core.convert.PasswordConverter;
import org.zstack.header.core.convert.PasswordConverter;
import org.zstack.core.convert.SpecialDataConverter;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.core.db.SQL;
import org.zstack.core.db.SQLBatch;
import org.zstack.header.Component;
import org.zstack.header.core.encrypt.*;
import org.zstack.header.core.convert.EncryptFacade;
import org.zstack.header.errorcode.ErrorableValue;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.exception.CloudRuntimeException;
Expand Down Expand Up @@ -76,6 +77,12 @@ public ErrorableValue<String> decrypt(String data, String algType) {
return encryptDriver.decrypt(data, algType);
}

@Override
public boolean isEncryptionDisabled() {
return PasswordEncryptType.None.toString()
.equals(EncryptGlobalConfig.ENABLE_PASSWORD_ENCRYPT.value(String.class));
}

private String getQuerySql(List<CovertSubClass> covertSubClasses, String className, String fieldName, String uuid) {
List<String> whereSqlList = covertSubClasses.stream()
.filter(subClass -> subClass.classSimpleName().equals(className) && !subClass.columnName().isEmpty())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package org.zstack.core.encrypt;
package org.zstack.header.core.convert;

import org.zstack.header.core.encrypt.EncryptEntityState;
import org.zstack.header.core.encrypt.EncryptedFieldBundle;
import org.zstack.header.errorcode.ErrorableValue;

import java.util.List;

/**
* Created by kayo on 2018/9/7.
*/
public interface EncryptFacade {
String encrypt(String decryptString);

Expand All @@ -23,4 +20,6 @@ public interface EncryptFacade {
List<EncryptedFieldBundle> getIntegrityEncryptionBundle();

List<EncryptedFieldBundle> getConfidentialityEncryptionBundle();
}

boolean isEncryptionDisabled();
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package org.zstack.core.convert;
package org.zstack.header.core.convert;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;
import org.zstack.core.encrypt.EncryptFacade;
import org.zstack.core.encrypt.EncryptGlobalConfig;
import org.zstack.header.core.encrypt.PasswordEncryptType;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;

Expand All @@ -32,7 +29,7 @@ public void initEncryptFacade(EncryptFacade encryptFacade){

@Override
public String convertToDatabaseColumn(String attribute) {
if (PasswordEncryptType.None.toString().equals(EncryptGlobalConfig.ENABLE_PASSWORD_ENCRYPT.value(String.class))) {
if (encryptFacade == null || encryptFacade.isEncryptionDisabled()) {
return attribute;
}
if (StringUtils.isEmpty(attribute)) {
Expand All @@ -43,7 +40,7 @@ public String convertToDatabaseColumn(String attribute) {

@Override
public String convertToEntityAttribute(String dbData) {
if (PasswordEncryptType.None.toString().equals(EncryptGlobalConfig.ENABLE_PASSWORD_ENCRYPT.value(String.class))) {
if (encryptFacade == null || encryptFacade.isEncryptionDisabled()) {
return dbData;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.zstack.header.server;

import org.zstack.header.core.convert.PasswordConverter;
import org.zstack.header.vo.ForeignKey;
import org.zstack.header.vo.ForeignKey.ReferenceOption;
import org.zstack.header.vo.ResourceVO;
Expand Down Expand Up @@ -70,6 +71,7 @@ public class PhysicalServerAO extends ResourceVO {

@EncryptColumn
@NoLogging
@Convert(converter = PasswordConverter.class)
@Column
private String oobPassword;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,5 @@ interface HardwareInfoCarrier {
void setCpuCores(Integer v);
void setCpuArchitecture(String v);
void setTotalMemoryBytes(Long v);
void setMemoryModuleCount(Integer v);
void setTotalDiskBytes(Long v);
void setDiskCount(Integer v);
void setNicCount(Integer v);
void setGpuCount(Integer v);
void setHealthStatus(String v);
}
}
Loading