Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4aa9008
<fix>[conf]: bump version to 5.4.10
liang-hanyu Jan 20, 2026
784c703
Merge branch '5.4.10' into '5.4.10'
Jan 20, 2026
1176758
<fix>[search]: fix zql field query total
Jun 24, 2026
4ef0c8f
<fix>[kvm]: force virtio driver for windows vhostuser nic
Jan 15, 2026
ada4787
<fix>[query]: limit API query concurrency
May 14, 2026
08177ca
Merge branch 'codex/ZSTAC-86417-5.4.10' into '5.4.10'
Jul 1, 2026
f25df0a
<fix>[gpu]: add gpu normalized model schema
AlanJager Jul 2, 2026
b5d630f
<fix>[search]: stabilize paginated query order
AlanJager Jul 2, 2026
dc86975
<fix>[kvm]: skip known systemd timeout on reconnect
May 11, 2026
e6d3189
<fix>[identity]: restrict APIQueryAccountMsg to admin-only in RBAC
gladtoseeu Jul 2, 2026
7e46fa5
<fix>[mn]: synchronize hash ring operations
AlanJager Jul 3, 2026
d2b599d
<fix>[externalStorage]: prevent split-brain on deactivate fail
PandaWuu Jul 3, 2026
b0ed121
<fix>[kvm]: defer orphaned skip tracing cleanup
PandaWuu Jul 3, 2026
7c4765f
Merge branch 'fix/5.4.10/ZSTAC-86350' into '5.4.10'
Jul 3, 2026
dadf250
<fix>[kvm]: disable arm vm pmu by default
gladtoseeu Jul 2, 2026
7bd28ad
Merge branch 'fix-86349' into '5.4.10'
Jul 3, 2026
42a035d
<fix>[loadBalancer]: block SLB deletion during grayscale upgrade
AlanJager Feb 13, 2026
3079c28
<fix>[virtualrouter]: configure dnsmasq query limits
ruansteve Jun 4, 2026
c9fbad0
<fix>[sugonSdn]: fix port sync plugin declaration
ruansteve May 21, 2026
43e3374
<fix>[sugonSdn]: sync tf ports after ping
May 21, 2026
31c9dbb
<fix>[sugonSdn]: adapt ping sync backport
Jun 30, 2026
16667ce
<fix>[sugonSdn]: address review comments
Jul 1, 2026
57209ea
<fix>[sdnController]: put sdn host crud in seperate queue
ruansteve Dec 22, 2025
f977724
<fix>[virtualRouterProvider]: change vrouter ip_local_port_range
ruansteve Jan 20, 2026
ab7f88e
Merge branch 'shixin-ZSTAC-86394' into '5.4.10'
Jul 4, 2026
e0476a9
Merge branch 'shixin-ZSTAC-86484@@2' into '5.4.10'
Jul 4, 2026
f5f2924
Merge branch 'shixin-ZSTAC-86501' into '5.4.10'
Jul 5, 2026
30e5a2a
Merge branch 'shixin-ZSTAC-86489@@3' into '5.4.10'
Jul 5, 2026
88f0030
Merge branch 'shixin-ZSTAC-86445' into '5.4.10'
Jul 5, 2026
d981c92
Merge branch 'fix/5.4.10/ZSTAC-86338@@3' into '5.4.10'
Jul 5, 2026
e5f4059
Merge branch 'shixin-ZSTAC-86470' into '5.4.10'
Jul 6, 2026
9e83867
Merge branch 'fix/ZSTAC-86475-5.4.10' into '5.4.10'
Jul 6, 2026
d997b64
Merge branch 'fix/ZSTAC-86476-5.4.10@@2' into '5.4.10'
Jul 6, 2026
adf11eb
<fix>[sdk]: update mdev candidates sdk
AlanJager Jul 6, 2026
7122b0c
Merge branch 'bugfix/ZSTAC-84636@@2' into '5.4.10'
Jul 6, 2026
473ad46
Merge branch 'bugfix/ZSTAC-86444-5.4.10' into '5.4.10'
Jul 6, 2026
e7037c6
Merge branch 'bugfix/ZSTAC-86294-5.4.10' into '5.4.10'
Jul 6, 2026
f3fd7fa
Merge branch 'backport/5.4.10/ZSTAC-86486-batch@@3' into '5.4.10'
Jul 6, 2026
a2071a3
Merge branch 'fix/ZSTAC-86497-5.4.10@@2' into '5.4.10'
Jul 7, 2026
fc5e381
<fix>[ha]: stabilize suspect host pre-fence flow
huhu0316 May 18, 2026
d991155
<fix>[ha]: create pre-fence tag in core
huhu0316 May 20, 2026
118e53d
<fix>[ha]: require peer for pre-fence tag
huhu0316 May 28, 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
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
MAJOR=5
MINOR=4
UPDATE=7
UPDATE=10
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public class VmGlobalConfig {
@GlobalConfigValidation(validValues = {"true", "false"})
@BindResourceConfig(value = {VmInstanceVO.class})
public static GlobalConfig VM_PORT_OFF = new GlobalConfig(CATEGORY, "vmPortOff");
@GlobalConfigDef(defaultValue = "false", type = Boolean.class, description = "enable PMU for VM, disabled by default on aarch64 to avoid Kunpeng-920 kernel panic")
@GlobalConfigValidation(validValues = {"true", "false"})
@BindResourceConfig(value = {VmInstanceVO.class, ClusterVO.class})
public static GlobalConfig VM_PMU = new GlobalConfig(CATEGORY, "vm.pmu");
@GlobalConfigValidation(validValues = {"true", "false"})
@BindResourceConfig(value = {VmInstanceVO.class, ClusterVO.class})
public static GlobalConfig EMULATE_HYPERV = new GlobalConfig(CATEGORY, "emulateHyperV");
Expand Down
27 changes: 24 additions & 3 deletions compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -1004,16 +1004,38 @@ public void done(ErrorCodeList errorCodeList) {

logger.debug(String.format("HaStartVmJudger[%s] says the VM[uuid:%s, name:%s] is qualified for HA start, now we are starting it",
judger.getClass(), self.getUuid(), self.getName()));
String hostUuid = self.getHostUuid();
String suspectHostUuid = StringUtils.trimToNull(hostUuid);
String peerHostUuid = StringUtils.trimToNull(msg.getAccessiblePeerHostUuid());
UpdateQuery sql = SQL.New(VmInstanceVO.class)
.eq(VmInstanceVO_.uuid, self.getUuid())
.set(VmInstanceVO_.state, VmInstanceState.Stopped)
.set(VmInstanceVO_.hostUuid, null);

if (self.getHostUuid() != null) {
sql.set(VmInstanceVO_.lastHostUuid, self.getHostUuid());
if (hostUuid != null) {
sql.set(VmInstanceVO_.lastHostUuid, hostUuid);
}

sql.update();
refreshVO();
if (suspectHostUuid == null) {
logger.debug(String.format("HA-start vm[%s]: skip creating pre-fence tag because suspect host is absent",
self.getUuid()));
} else if (peerHostUuid == null) {
logger.debug(String.format("HA-start vm[%s]: skip creating pre-fence tag because peer host is absent",
self.getUuid()));
} else {
Map<String, String> tokens = new HashMap<>();
tokens.put(VmSystemTags.HA_PRE_FENCE_SUSPECT_HOST_UUID_TOKEN, suspectHostUuid);
tokens.put(VmSystemTags.HA_PRE_FENCE_ACCESSIBLE_PEER_HOST_UUID_TOKEN, peerHostUuid);

SystemTagCreator creator = VmSystemTags.HA_PRE_FENCE_PENDING.newSystemTagCreator(self.getUuid());
creator.inherent = true;
creator.recreate = true;
creator.ignoreIfExisting = true;
creator.setTagByTokens(tokens);
creator.create();
}

startVm(msg, new Completion(msg, chain) {
@Override
Expand Down Expand Up @@ -8984,4 +9006,3 @@ public void run(MessageReply reply) {
});
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.asyncbatch.While;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.workflow.FlowChainBuilder;
import org.zstack.header.core.Completion;
import org.zstack.header.core.WhileDoneCompletion;
import org.zstack.header.core.workflow.Flow;
import org.zstack.header.core.workflow.FlowChain;
import org.zstack.header.core.workflow.FlowDoneHandler;
import org.zstack.header.core.workflow.FlowErrorHandler;
import org.zstack.header.core.workflow.FlowRollback;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.errorcode.ErrorCodeList;
import org.zstack.header.host.HostConstant;
import org.zstack.header.message.MessageReply;
import org.zstack.header.vm.*;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;

import java.util.List;
import java.util.Map;
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class VmStartOnHypervisorFlow implements Flow {
Expand All @@ -26,34 +35,68 @@ public class VmStartOnHypervisorFlow implements Flow {
@Autowired
private PluginRegistry pluginRgty;

private final List<VmBeforeStartOnHypervisorExtensionPoint> exts = pluginRgty.getExtensionList(VmBeforeStartOnHypervisorExtensionPoint.class);;

private void fireExtensions(VmInstanceSpec spec) {
for (VmBeforeStartOnHypervisorExtensionPoint ext : exts) {
ext.beforeStartVmOnHypervisor(spec);
}
}

@Override
public void run(final FlowTrigger chain, final Map data) {
final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
FlowChain fchain = FlowChainBuilder.newSimpleFlowChain();
fchain.setName(String.format("vm-start-on-hypervisor-vm-%s", spec.getVmInventory().getUuid()));
fchain.then(new NoRollbackFlow() {
@Override
public void run(FlowTrigger trigger, Map d) {
new While<>(pluginRgty.getExtensionList(VmBeforeStartOnHypervisorExtensionPoint.class))
.each((ext, comp) -> ext.beforeStartVmOnHypervisor(spec, new Completion(comp) {
@Override
public void success() {
comp.done();
}

fireExtensions(spec);

StartVmOnHypervisorMsg msg = new StartVmOnHypervisorMsg();
msg.setVmSpec(spec);
bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, spec.getDestHost().getUuid());
bus.send(msg, new CloudBusCallBack(chain) {
@Override
public void fail(ErrorCode errorCode) {
comp.addError(errorCode);
comp.done();
}
})).run(new WhileDoneCompletion(trigger) {
@Override
public void done(ErrorCodeList errorCodeList) {
if (errorCodeList.getCauses().size() > 0) {
trigger.fail(errorCodeList.getCauses().get(0));
} else {
trigger.next();
}
}
});
}
});
fchain.then(new NoRollbackFlow() {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
data.put(VmStartOnHypervisorFlow.class.getName(), true);
chain.next();
} else {
chain.fail(reply.getError());
}
public void run(FlowTrigger trigger, Map d) {
StartVmOnHypervisorMsg msg = new StartVmOnHypervisorMsg();
msg.setVmSpec(spec);
bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, spec.getDestHost().getUuid());
bus.send(msg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
data.put(VmStartOnHypervisorFlow.class.getName(), true);
trigger.next();
} else {
trigger.fail(reply.getError());
}
}
});
}
});
fchain.done(new FlowDoneHandler(chain) {
@Override
public void handle(Map d) {
chain.next();
}
}).error(new FlowErrorHandler(chain) {
@Override
public void handle(ErrorCode errCode, Map d) {
chain.fail(errCode);
}
}).start();
}

@Override
Expand Down
7 changes: 7 additions & 0 deletions compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,11 @@ public String desensitizeTag(SystemTag systemTag, String tag) {
public static PatternedSystemTag VM_STATE_PAUSED_AFTER_MIGRATE = new PatternedSystemTag(("vmPausedAfterMigrate"), VmInstanceVO.class);

public static PatternedSystemTag VM_MEMORY_ACCESS_MODE_SHARED = new PatternedSystemTag(("vmMemoryAccessModeShared"), VmInstanceVO.class);

public static String HA_PRE_FENCE_SUSPECT_HOST_UUID_TOKEN = "suspectHostUuid";
public static String HA_PRE_FENCE_ACCESSIBLE_PEER_HOST_UUID_TOKEN = "accessiblePeerHostUuid";
public static PatternedSystemTag HA_PRE_FENCE_PENDING =
new PatternedSystemTag(String.format("haPreFencePending::{%s}::{%s}",
HA_PRE_FENCE_SUSPECT_HOST_UUID_TOKEN,
HA_PRE_FENCE_ACCESSIBLE_PEER_HOST_UUID_TOKEN), VmInstanceVO.class);
}
3 changes: 3 additions & 0 deletions conf/db/upgrade/V5.4.10__schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- ZSTAC-86476: Add normalizedModelName column for GPU spec dedup
CALL ADD_COLUMN('GpuDeviceSpecVO', 'normalizedModelName', 'VARCHAR(255)', 1, NULL);
CALL CREATE_INDEX('GpuDeviceSpecVO', 'idx_gpu_spec_normalized_model', 'normalizedModelName');
14 changes: 14 additions & 0 deletions conf/globalConfig/virutalRouter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@
<category>virtualRouter</category>
<type>java.lang.Integer</type>
</config>
<config>
<name>dnsmasq.dnsForwardMax</name>
<description>The maximum number of concurrent DNS queries forwarded by dnsmasq on a virtual router.</description>
<defaultValue>1000</defaultValue>
<category>virtualRouter</category>
<type>java.lang.Integer</type>
</config>
<config>
<name>dnsmasq.cacheSize</name>
<description>The DNS cache size used by dnsmasq on a virtual router.</description>
<defaultValue>10000</defaultValue>
<category>virtualRouter</category>
<type>java.lang.Integer</type>
</config>
<config>
<name>ping.interval</name>
<description>The interval management nodes ping the virtual router agents running virtual router VMs, in seconds</description>
Expand Down
6 changes: 1 addition & 5 deletions conf/springConfigXml/sugonSdnController.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,7 @@
</zstack:plugin>
</bean>

<bean id="TfZstackPortSync" class="org.zstack.sugonSdnController.network.TfZstackPortSync">
<zstack:plugin>
<zstack:extension interface="org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint" />
</zstack:plugin>
</bean>
<bean id="TfZstackPortSync" class="org.zstack.sugonSdnController.network.TfZstackPortSync" />

<bean id="SugonApiInterceptor" class="org.zstack.sugonSdnController.header.SugonApiInterceptor">
<zstack:plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,27 @@ public class ResourceDestinationMakerImpl implements ManagementNodeChangeListene
private DatabaseFacade dbf;

@Override
public void nodeJoin(ManagementNodeInventory inv) {
public synchronized void nodeJoin(ManagementNodeInventory inv) {
nodeHash.add(inv.getUuid());
nodes.put(inv.getUuid(), new NodeInfo(inv));
}

@Override
public void nodeLeft(ManagementNodeInventory inv) {
public synchronized void nodeLeft(ManagementNodeInventory inv) {
String nodeId = inv.getUuid();
nodeHash.remove(nodeId);
nodes.remove(nodeId);
}

@Override
public void iAmDead(ManagementNodeInventory inv) {
public synchronized void iAmDead(ManagementNodeInventory inv) {
String nodeId = inv.getUuid();
nodeHash.remove(nodeId);
nodes.remove(nodeId);
}

@Override
public void iJoin(ManagementNodeInventory inv) {
public synchronized void iJoin(ManagementNodeInventory inv) {
List<ManagementNodeVO> lst = Q.New(ManagementNodeVO.class).list();
lst.forEach((ManagementNodeVO node) -> {
nodeHash.add(node.getUuid());
Expand All @@ -56,7 +56,7 @@ public void iJoin(ManagementNodeInventory inv) {
}

@Override
public String makeDestination(String resourceUuid) {
public synchronized String makeDestination(String resourceUuid) {
String nodeUuid = nodeHash.get(resourceUuid);
if (nodeUuid == null) {
throw new CloudRuntimeException("Cannot find any available management node to send message");
Expand All @@ -66,18 +66,18 @@ public String makeDestination(String resourceUuid) {
}

@Override
public boolean isManagedByUs(String resourceUuid) {
public synchronized boolean isManagedByUs(String resourceUuid) {
String nodeUuid = makeDestination(resourceUuid);
return nodeUuid.equals(Platform.getManagementServerId());
}

@Override
public Collection<String> getManagementNodesInHashRing() {
return nodeHash.getNodes();
public synchronized Collection<String> getManagementNodesInHashRing() {
return new ArrayList<>(nodeHash.getNodes());
}

@Override
public NodeInfo getNodeInfo(String nodeUuid) {
public synchronized NodeInfo getNodeInfo(String nodeUuid) {
NodeInfo info = nodes.get(nodeUuid);
if (info == null) {
ManagementNodeVO vo = dbf.findByUuid(nodeUuid, ManagementNodeVO.class);
Expand All @@ -93,17 +93,17 @@ public NodeInfo getNodeInfo(String nodeUuid) {
}

@Override
public Collection<NodeInfo> getAllNodeInfo() {
return nodes.values();
public synchronized Collection<NodeInfo> getAllNodeInfo() {
return new ArrayList<>(nodes.values());
}

@Override
public int getManagementNodeCount() {
return nodes.values().size();
public synchronized int getManagementNodeCount() {
return nodes.size();
}


public boolean isNodeInCircle(String nodeId) {
public synchronized boolean isNodeInCircle(String nodeId) {
return nodeHash.hasNode(nodeId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public void permissions() {
.name("identity")
.adminOnlyAPIs(
APICreateAccountMsg.class,
APIQueryAccountMsg.class,
APIShareResourceMsg.class,
APIRevokeResourceSharingMsg.class,
APIUpdateQuotaMsg.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public interface PrimaryStorageNodeSvc {

void deactivate(String installPath, String protocol, ActiveVolumeClient client, Completion comp);

void blacklist(String installPath, String protocol, HostInventory h, Completion comp);
void blacklist(String installPath, String protocol, HostInventory h);

String getActivePath(BaseVolumeInfo v, HostInventory h, boolean shareable);
BaseVolumeInfo getActiveVolumeInfo(String activePath, HostInventory h, boolean shareable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class HaStartVmInstanceMsg extends NeedReplyMessage implements VmInstance
private String vmInstanceUuid;
private String judgerClassName;
private List<String> softAvoidHostUuids;
private String accessiblePeerHostUuid;
private String haReason;

public String getJudgerClassName() {
Expand All @@ -30,6 +31,14 @@ public void setSoftAvoidHostUuids(List<String> softAvoidHostUuids) {
this.softAvoidHostUuids = softAvoidHostUuids;
}

public String getAccessiblePeerHostUuid() {
return accessiblePeerHostUuid;
}

public void setAccessiblePeerHostUuid(String accessiblePeerHostUuid) {
this.accessiblePeerHostUuid = accessiblePeerHostUuid;
}

@Override
public String getVmInstanceUuid() {
return vmInstanceUuid;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package org.zstack.header.vm;

import org.zstack.header.core.Completion;

/**
*/
public interface VmBeforeStartOnHypervisorExtensionPoint {
void beforeStartVmOnHypervisor(VmInstanceSpec spec);
default void beforeStartVmOnHypervisor(VmInstanceSpec spec) {}

default void beforeStartVmOnHypervisor(VmInstanceSpec spec, Completion completion) {
beforeStartVmOnHypervisor(spec);
completion.success();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -614,14 +614,13 @@ public void cleanActiveRecord(ImageCacheInventory cache) {
}

@Override
public void blacklist(String installPath, String protocol, HostInventory h, Completion comp) {
public void blacklist(String installPath, String protocol, HostInventory h) {
logger.debug(String.format("blacklisting volume[path: %s, protocol:%s] on host[uuid:%s, ip:%s]",
installPath, protocol, h.getUuid(), h.getManagementIp()));

UssGatewayModule uss = getUssGateway(VolumeProtocol.valueOf(protocol), h.getManagementIp());
VolumeModule exponVol = apiHelper.getVolume(getVolIdFromPath(installPath));
apiHelper.addVolumePathToBlacklist(buildExponVolumeBoundPath(uss, exponVol.getVolumeName()));
comp.success();
}

@Override
Expand Down
Loading