diff --git a/.gitconfig/hooks/pre-push b/.gitconfig/hooks/pre-push new file mode 100755 index 0000000000..d629072f7b --- /dev/null +++ b/.gitconfig/hooks/pre-push @@ -0,0 +1,82 @@ +#!/bin/bash +# Pre-push hook: validate remote, branch name, and commit before pushing +# Prevents common mistakes: +# - Pushing to fork instead of upstream (or vice versa) +# - Branch name not matching ZSTAC-XXXXX pattern for fix branches +# - Pushing wrong commits + +RED='\033[0;31m' +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +NC='\033[0m' + +REMOTE="$1" +URL="$2" + +BRANCH=$(git branch --show-current) + +# Skip validation for non-fix branches (master, release branches, etc.) +if [[ ! "$BRANCH" =~ ^fix/ ]] && [[ ! "$BRANCH" =~ ^feature/ ]]; then + exit 0 +fi + +ERRORS=0 + +# --- Check 1: Branch name format --- +if [[ "$BRANCH" =~ ^fix/ ]]; then + if [[ ! "$BRANCH" =~ ^fix/ZSTAC-[0-9]+ ]]; then + echo -e "${RED}[pre-push] Branch name '$BRANCH' does not match fix/ZSTAC-XXXXX pattern${NC}" + echo -e "${YELLOW} Hint: Use 'ZSTAC' not 'ZSTACK'. Example: fix/ZSTAC-82099${NC}" + ERRORS=$((ERRORS + 1)) + fi +fi + +# --- Check 2: Remote URL sanity --- +REMOTE_URL=$(git remote get-url "$REMOTE" 2>/dev/null) +if [ -z "$REMOTE_URL" ]; then + echo -e "${RED}[pre-push] Cannot resolve remote '$REMOTE'${NC}" + ERRORS=$((ERRORS + 1)) +fi + +# Warn if pushing to upstream (zstackio) directly — usually want to push to fork (origin) +if echo "$REMOTE_URL" | grep -q "zstackio/" && [ "$REMOTE" = "upstream" ]; then + echo -e "${YELLOW}[pre-push] WARNING: Pushing directly to upstream (zstackio). Are you sure?${NC}" + echo -e "${YELLOW} Remote URL: $REMOTE_URL${NC}" + echo -e "${YELLOW} Usually you want to push to 'origin' (your fork) instead.${NC}" + # Warning only, don't block +fi + +# --- Check 3: Verify latest commit has a Change-Id --- +LAST_COMMIT_MSG=$(git log -1 --format=%B) +if ! echo "$LAST_COMMIT_MSG" | grep -q "^Change-Id: I[0-9a-f]\{40\}"; then + echo -e "${RED}[pre-push] Latest commit is missing Change-Id${NC}" + echo -e "${YELLOW} Use 'source scripts/zcommit.sh && zcommit ...' to create commits with Change-Id${NC}" + ERRORS=$((ERRORS + 1)) +fi + +# --- Check 4: Verify latest commit references a Jira ticket (for fix branches) --- +if [[ "$BRANCH" =~ ^fix/ ]]; then + if ! echo "$LAST_COMMIT_MSG" | grep -qi "ZSTAC-[0-9]\+"; then + echo -e "${RED}[pre-push] Latest commit does not reference a ZSTAC ticket${NC}" + ERRORS=$((ERRORS + 1)) + fi +fi + +# --- Check 5: Confirm repo identity (main vs premium) --- +TOPLEVEL=$(git rev-parse --show-toplevel 2>/dev/null) +REPO_NAME=$(basename "$TOPLEVEL") +if [[ "$BRANCH" =~ premium ]] && [[ "$REPO_NAME" != "premium" ]]; then + echo -e "${RED}[pre-push] Branch name suggests premium but you're in repo '$REPO_NAME'${NC}" + ERRORS=$((ERRORS + 1)) +fi + +# --- Summary --- +if [ $ERRORS -gt 0 ]; then + echo "" + echo -e "${RED}[pre-push] $ERRORS error(s) found. Push blocked.${NC}" + echo -e "${YELLOW} To bypass: git push --no-verify${NC}" + exit 1 +fi + +echo -e "${GREEN}[pre-push] All checks passed: branch=$BRANCH remote=$REMOTE${NC}" +exit 0 diff --git a/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java b/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java index d8c8be6532..80ba38486f 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java @@ -7226,6 +7226,7 @@ protected void scripts() { self.setZoneUuid(spec.getDestHost().getZoneUuid()); } }.execute()); + syncVmDevicesAddressInfo(self.getUuid()); logger.debug(String.format("vm[uuid:%s] is running ..", self.getUuid())); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.afterStartVm(inv); @@ -7495,6 +7496,9 @@ public void run(FlowTrigger trigger, Map data) { self.setHypervisorType(spec.getDestHost().getHypervisorType()); self.setRootVolumeUuid(spec.getDestRootVolume().getUuid()); }); + if (struct.getStrategy() == VmCreationStrategy.InstantStart) { + syncVmDevicesAddressInfo(self.getUuid()); + } logger.debug(String.format("vm[uuid:%s] is started ..", self.getUuid())); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.afterStartNewCreatedVm(inv); @@ -7931,6 +7935,7 @@ public void handle(Map data) { public void done() { self = changeVmStateInDb(VmInstanceStateEvent.running, () -> self.setHostUuid(originalCopy.getHostUuid())); + syncVmDevicesAddressInfo(self.getUuid()); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.afterRebootVm(inv); new StaticIpOperator().deleteIpChange(self.getUuid()); @@ -8347,6 +8352,7 @@ protected void resumeVm(final Message msg, Completion completion) { @Override public void handle(Map Data) { self = changeVmStateInDb(VmInstanceStateEvent.running); + syncVmDevicesAddressInfo(self.getUuid()); completion.success(); } }).error(new FlowErrorHandler(completion) { @@ -8467,6 +8473,28 @@ public String getName() { }); } + private void syncVmDevicesAddressInfo(String vmUuid) { + if (self.getHostUuid() == null) { + return; + } + SyncVmDeviceInfoMsg msg = new SyncVmDeviceInfoMsg(); + msg.setVmInstanceUuid(vmUuid); + msg.setHostUuid(self.getHostUuid()); + bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, msg.getHostUuid()); + bus.send(msg, new CloudBusCallBack(msg) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + logger.warn(String.format("Failed to sync vm device info for vm[uuid:%s], %s", + vmUuid, reply.getError())); + } else { + logger.debug(String.format("Sent SyncVmDeviceInfoMsg for vm[uuid:%s] on host[uuid:%s]", + vmUuid, self.getHostUuid())); + } + } + }); + } + private void deleteVmCdRom(String cdRomUuid, Completion completion) { boolean exist = dbf.isExist(cdRomUuid, VmCdRomVO.class); if (!exist) { diff --git a/conf/i18n/globalErrorCodeMapping/global-error-en_US.json b/conf/i18n/globalErrorCodeMapping/global-error-en_US.json index 32eb4c8f05..715e823d95 100644 --- a/conf/i18n/globalErrorCodeMapping/global-error-en_US.json +++ b/conf/i18n/globalErrorCodeMapping/global-error-en_US.json @@ -3374,7 +3374,7 @@ "ORG_ZSTACK_NETWORK_HUAWEI_IMASTER_10019": "delete token of SDN controller [IP:%s] failed because %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10004": "Cannot execute volume mapping to host flow due to invalid volume ID.%s", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10007": "port forwarding rule [uuid:%s] has not been attached to any virtual machine network interface, cannot detach", - "ORG_ZSTACK_MEVOCO_10088": "cannot take a snapshot for volumes[%s] when volume[uuid: %s] is not attached", + "ORG_ZSTACK_MEVOCO_10088": "cannot create snapshot for volume[uuid:%s] because it is not attached to any VM instance. Please attach the volume to a VM first. Affected volumes: %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10005": "Cannot execute map LUN to host flow due to invalid LUN type: %s", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10008": "port forwarding rule [uuid:%s] has been associated with vm nic [uuid:%s], cannot be reassigned again", "ORG_ZSTACK_MEVOCO_10087": "A Running VM[uuid:%s] has no associated Host UUID.", diff --git a/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json b/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json index 84609838dd..01960e8eb4 100644 --- a/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json +++ b/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json @@ -3374,7 +3374,7 @@ "ORG_ZSTACK_NETWORK_HUAWEI_IMASTER_10019": "删除 SDN 控制器 [IP:%s] 的令牌失败,因为 %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10004": "无法执行映射LUN到主机流程,无效的LUN ID", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10007": "端口转发规则 rule[uuid:%s] 没有绑定到任何 VM 的网卡上,无法解除绑定", - "ORG_ZSTACK_MEVOCO_10088": "无法为挂载状态以外的卷[%s]创建快照", + "ORG_ZSTACK_MEVOCO_10088": "无法为云盘[uuid:%s]创建快照,因为该云盘未挂载到任何云主机。请先将云盘挂载到云主机后再创建快照。相关云盘: %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10005": "无法执行映射LUN到主机流程,无效的LUN类型", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10008": "端口转发规则[uuid:%s]已绑定到VM网卡[uuid:%s],无法再次绑定", "ORG_ZSTACK_MEVOCO_10087": "如何一个运行中的VM[uuid:%s]没有宿主机uuid?", diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java b/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java index 7007c592ae..99ee2173b9 100755 --- a/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java +++ b/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java @@ -847,7 +847,9 @@ public void setBootMode(String bootMode) { public long getRootDiskAllocateSize() { if (rootDiskOffering == null) { - return this.getImageSpec().getInventory().getSize(); + long virtualSize = this.getImageSpec().getInventory().getSize(); + long actualSize = this.getImageSpec().getInventory().getActualSize(); + return Math.max(virtualSize, actualSize); } return rootDiskOffering.getDiskSize(); } diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceState.java b/header/src/main/java/org/zstack/header/vm/VmInstanceState.java index 8a755b52fd..49303e2325 100755 --- a/header/src/main/java/org/zstack/header/vm/VmInstanceState.java +++ b/header/src/main/java/org/zstack/header/vm/VmInstanceState.java @@ -168,6 +168,7 @@ public enum VmInstanceState { new Transaction(VmInstanceStateEvent.destroyed, VmInstanceState.Destroyed), new Transaction(VmInstanceStateEvent.destroying, VmInstanceState.Destroying), new Transaction(VmInstanceStateEvent.running, VmInstanceState.Running), + new Transaction(VmInstanceStateEvent.stopped, VmInstanceState.Stopped), new Transaction(VmInstanceStateEvent.expunging, VmInstanceState.Expunging) ); Destroyed.transactions( diff --git a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java index db06239acb..276ab367ba 100644 --- a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java +++ b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java @@ -179,7 +179,10 @@ public List getActiveClients(String installPath, String prot if (VolumeProtocol.CBD.toString().equals(protocol)) { GetVolumeClientsCmd cmd = new GetVolumeClientsCmd(); cmd.setPath(installPath); - GetVolumeClientsRsp rsp = syncHttpCall(GET_VOLUME_CLIENTS_PATH, cmd, GetVolumeClientsRsp.class); + GetVolumeClientsRsp rsp = new HttpCaller<>(GET_VOLUME_CLIENTS_PATH, cmd, GetVolumeClientsRsp.class, + null, TimeUnit.SECONDS, 30, true) + .setTryNext(true) + .syncCall(); List clients = new ArrayList<>(); if (!rsp.isSuccess()) { @@ -1411,6 +1414,11 @@ public class HttpCaller { private boolean tryNext = false; + HttpCaller setTryNext(boolean tryNext) { + this.tryNext = tryNext; + return this; + } + public HttpCaller(String path, AgentCommand cmd, Class retClass, ReturnValueCompletion callback) { this(path, cmd, retClass, callback, null, 0, false); }