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
42 changes: 42 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# ZStack AI Coding Assistant Instructions

## Architecture Overview
ZStack is an open-source IaaS platform for datacenter management via APIs. It uses a **plugin-based architecture** where core orchestration is extensible without impacting existing code. Key frameworks:
- **CloudBus**: Asynchronous messaging system for inter-component communication (see `core/src/main/java/org/zstack/core/cloudbus/`)
- **Workflow Engine**: Manages complex operations with rollback on failure (see `core/src/main/java/org/zstack/core/workflow/`)
- **Cascade Framework**: Propagates operations across dependent resources (see `core/src/main/java/org/zstack/core/cascade/`)
- **Plugin System**: Everything is a plugin; use `PluginRegistry` for extensions (see `core/src/main/java/org/zstack/core/plugin/`)

Major components are organized as Maven modules: `core`, `compute`, `storage`, `network`, `plugin/*`, etc.

## Development Workflow
- **Build**: Use Maven; run `./runMavenProfile premium` for enterprise features or `mvn clean install` for standard build
- **Database**: Deploy schema with `./runMavenProfile deploydb`; uses Hibernate ORM
- **Testing**: Three-tier system - unit tests in modules, integration tests in `test/`, system tests in `testlib/`
- **Debugging**: Simulator module (`simulator/`) mocks hypervisors for local testing
- **Deployment**: WAR file deployment to Tomcat; scripts in `build/` for automation

## Coding Conventions
- **Java 8** with Spring Framework 5.x, Hibernate 5.x
- Packages: `org.zstack.*`; core in `core/`, compute logic in `compute/`
- **Flows for Operations**: VM operations use `Flow` interface with rollback (e.g., `VmStartOnHypervisorFlow.java`)
- **Messages**: Async via CloudBus; extend `Message` for requests, `MessageReply` for responses
- **Extensions**: Use `PluginRegistry.getExtensionList()` for plugin hooks (e.g., `VmBeforeStartOnHypervisorExtensionPoint`)
- **Error Handling**: Use `ErrorCode` and `CloudRuntimeException`; avoid checked exceptions
- **Logging**: `CLogger` from `Utils.getLogger()`
- **Database**: JPA entities with `@Entity`; queries via `DatabaseFacade`

## Key Patterns
- **Plugin Implementation**: Create module under `plugin/`, implement `PluginDriver` interface
- **API Messages**: Extend `APIMessage` for user-facing APIs, handle in managers (e.g., `VmInstanceManagerImpl`)
- **Resource Allocation**: Use workflow chains for multi-step allocations (e.g., host, storage, network)
- **State Machines**: Built-in for resource states; use `Platform.createStateMachine()`
- **Global Config**: Use `GlobalProperty` for runtime configurations

## Integration Points
- **External Services**: RabbitMQ for CloudBus, Ansible for automation
- **Hypervisors**: KVM plugin in `plugin/kvm/`, others like Ceph, NFS
- **Networking**: NFV-based; virtual routers as appliances
- **Storage**: Primary/backup storage abstraction; plugins for different backends

Reference: `README.md` for overview, `pom.xml` for dependencies, `runMavenProfile` for dev scripts.
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
package org.zstack.vm;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

包名错误

根据文件路径 compute/src/main/java/org/zstack/compute/vm/VmGpuPciMappingService.java,包名应为 org.zstack.compute.vm,而非 org.zstack.vm

🔎 建议的修复
-package org.zstack.vm;
+package org.zstack.compute.vm;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
package org.zstack.vm;
package org.zstack.compute.vm;
🤖 Prompt for AI Agents
In compute/src/main/java/org/zstack/compute/vm/VmGpuPciMappingService.java at
line 1 the package declaration is incorrect (package org.zstack.vm); change it
to package org.zstack.compute.vm to match the file path and Java package
conventions, update any relative imports or references in the file if needed,
and rebuild to ensure no package-related compilation errors.


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zstack.core.Platform;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.header.vm.VmGpuPciMappingVO;
import org.zstack.header.vm.VmGpuPciMappingVO_;
import org.zstack.utils.logging.CLogger;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
public class VmGpuPciMappingService {
private static final CLogger logger = CLogger.getLogger(VmGpuPciMappingService.class);

@Autowired
private DatabaseFacade dbf;

// 缓存配置
private static final long CACHE_EXPIRE_MS = 5 * 60 * 1000; // 5分钟缓存过期时间

// 缓存结构:key = vmUuid + ":" + vmPciAddress, value = hostPciAddress
private final Map<String, String> mappingCache = new ConcurrentHashMap<>();

// 缓存时间戳:key = cacheKey, value = timestamp
private final Map<String, Long> cacheTimestamp = new ConcurrentHashMap<>();
Comment on lines +24 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

代码中包含中文注释,违反编码规范

根据编码规范:"代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"。所有注释应使用英文。

🔎 建议的修复示例
-    // 缓存配置
-    private static final long CACHE_EXPIRE_MS = 5 * 60 * 1000; // 5分钟缓存过期时间
+    // Cache configuration
+    private static final long CACHE_EXPIRE_MS = 5 * 60 * 1000; // Cache expires after 5 minutes

-    // 缓存结构:key = vmUuid + ":" + vmPciAddress, value = hostPciAddress
+    // Cache structure: key = vmUuid + ":" + vmPciAddress, value = hostPciAddress
     private final Map<String, String> mappingCache = new ConcurrentHashMap<>();

-    // 缓存时间戳:key = cacheKey, value = timestamp
+    // Cache timestamp: key = cacheKey, value = timestamp
     private final Map<String, Long> cacheTimestamp = new ConcurrentHashMap<>();

请将文件中所有中文注释(第 24、25、27、30、34、39、43、49、60、66、72、85、89、104、131、140、145、156、160、166、178、182、188、199、203、209、230、248、263 行等)替换为英文。

🤖 Prompt for AI Agents
In compute/src/main/java/org/zstack/compute/vm/VmGpuPciMappingService.java
around lines 24-31 (and other listed lines:
34,39,43,49,60,66,72,85,89,104,131,140,145,156,160,166,178,182,188,199,203,209,230,248,263),
replace all Chinese comments with clear, correctly spelled English comments that
preserve the original meaning (e.g., "缓存配置" -> "Cache configuration", "缓存结构:key
= vmUuid + \":\" + vmPciAddress, value = hostPciAddress" -> "Cache structure:
key = vmUuid + \":\" + vmPciAddress, value = hostPciAddress", etc.); ensure no
Chinese characters remain in comments or error messages, keep comment formatting
and locations unchanged, and run a quick grep/IDE search to verify all
occurrences are converted before committing.


/**
* 根据VM UUID和VM PCI地址获取Host PCI地址(带缓存)
*/
public String getHostPciAddress(String vmUuid, String vmPciAddress) {
String cacheKey = vmUuid + ":" + vmPciAddress;

// 检查缓存是否过期
Long timestamp = cacheTimestamp.get(cacheKey);
if (timestamp != null &&
System.currentTimeMillis() - timestamp > CACHE_EXPIRE_MS) {
// 缓存过期,清理
mappingCache.remove(cacheKey);
cacheTimestamp.remove(cacheKey);
timestamp = null;
}

// 从缓存获取或查询数据库
return mappingCache.computeIfAbsent(cacheKey, key -> {
String hostAddress = queryFromDatabase(vmUuid, vmPciAddress);
if (hostAddress != null) {
cacheTimestamp.put(cacheKey, System.currentTimeMillis());
}
return hostAddress;
});
}
Comment on lines +36 to +57
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

缺少 queryFromDatabase 方法定义

第 51 行调用了 queryFromDatabase(vmUuid, vmPciAddress) 方法,但该方法在类中未定义。这会导致编译错误。

🔎 建议添加缺失的方法
/**
 * Query mapping from database
 */
private String queryFromDatabase(String vmUuid, String vmPciAddress) {
    VmGpuPciMappingVO mapping = Q.New(VmGpuPciMappingVO.class)
        .eq(VmGpuPciMappingVO_.vmInstanceUuid, vmUuid)
        .eq(VmGpuPciMappingVO_.vmPciAddress, vmPciAddress)
        .find();
    
    return mapping != null ? mapping.getHostPciAddress() : null;
}


/**
* 批量获取Host PCI地址(带缓存优化)
*/
public Map<String, String> getHostPciAddressesBatch(List<String> cacheKeys) {
Map<String, String> result = new HashMap<>();
List<String> needQueryKeys = new ArrayList<>();

// 先从缓存获取
for (String cacheKey : cacheKeys) {
// 检查缓存是否过期
Long timestamp = cacheTimestamp.get(cacheKey);
if (timestamp != null &&
System.currentTimeMillis() - timestamp > CACHE_EXPIRE_MS) {
// 缓存过期,清理
mappingCache.remove(cacheKey);
cacheTimestamp.remove(cacheKey);
}

String cachedValue = mappingCache.get(cacheKey);
if (cachedValue != null) {
result.put(cacheKey, cachedValue);
} else {
needQueryKeys.add(cacheKey);
}
}

// 批量查询数据库中缺失的映射
if (!needQueryKeys.isEmpty()) {
Map<String, String> dbResults = batchQueryFromDatabase(needQueryKeys);

// 更新缓存和结果
long currentTime = System.currentTimeMillis();
for (Map.Entry<String, String> entry : dbResults.entrySet()) {
String cacheKey = entry.getKey();
String hostAddress = entry.getValue();

mappingCache.put(cacheKey, hostAddress);
cacheTimestamp.put(cacheKey, currentTime);
result.put(cacheKey, hostAddress);
}
}

return result;
}

/**
* 从数据库批量查询映射关系
*/
private Map<String, String> batchQueryFromDatabase(List<String> cacheKeys) {
// 解析cacheKeys为vmUuid和pciAddress
Set<String> vmUuids = cacheKeys.stream()
.map(key -> key.split(":")[0])
.collect(Collectors.toSet());

Set<String> pciAddresses = cacheKeys.stream()
.map(key -> key.split(":")[1])
.collect(Collectors.toSet());

// 批量查询数据库
List<VmGpuPciMappingVO> mappings = Q.New(VmGpuPciMappingVO.class)
.in(VmGpuPciMappingVO_.vmInstanceUuid, vmUuids)
.in(VmGpuPciMappingVO_.vmPciAddress, pciAddresses)
.list();

// 转换为Map
return mappings.stream()
.collect(Collectors.toMap(
vo -> vo.getVmInstanceUuid() + ":" + vo.getVmPciAddress(),
VmGpuPciMappingVO::getHostPciAddress
));
}
Comment on lines +107 to +129
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

批量查询存在潜在的数据不匹配问题

第 118-121 行的批量查询使用了两个 IN 子句分别查询 vmUuidspciAddresses,这会返回所有 vmUuid 在集合中 pciAddress 在集合中的记录,但不保证 vmUuid 和 pciAddress 是成对匹配的。

例如,如果 cacheKeys 包含 ["vm1:pci1", "vm2:pci2"],查询会返回 vm1+pci2vm2+pci1 的组合(如果存在),这是不正确的。

🔎 建议的修复方案

方案 1:使用多个单独的查询(适合小批量)

private Map<String, String> batchQueryFromDatabase(List<String> cacheKeys) {
    Map<String, String> result = new HashMap<>();
    
    for (String cacheKey : cacheKeys) {
        String[] parts = cacheKey.split(":");
        String vmUuid = parts[0];
        String vmPciAddress = parts[1];
        
        VmGpuPciMappingVO mapping = Q.New(VmGpuPciMappingVO.class)
            .eq(VmGpuPciMappingVO_.vmInstanceUuid, vmUuid)
            .eq(VmGpuPciMappingVO_.vmPciAddress, vmPciAddress)
            .find();
        
        if (mapping != null) {
            result.put(cacheKey, mapping.getHostPciAddress());
        }
    }
    
    return result;
}

方案 2:查询所有相关记录后在内存中过滤(适合大批量)

private Map<String, String> batchQueryFromDatabase(List<String> cacheKeys) {
    Set<String> vmUuids = cacheKeys.stream()
        .map(key -> key.split(":")[0])
        .collect(Collectors.toSet());
    
    List<VmGpuPciMappingVO> mappings = Q.New(VmGpuPciMappingVO.class)
        .in(VmGpuPciMappingVO_.vmInstanceUuid, vmUuids)
        .list();
    
    // Build a set of requested cache keys for filtering
    Set<String> requestedKeys = new HashSet<>(cacheKeys);
    
    return mappings.stream()
        .map(vo -> {
            String key = vo.getVmInstanceUuid() + ":" + vo.getVmPciAddress();
            return new AbstractMap.SimpleEntry<>(key, vo.getHostPciAddress());
        })
        .filter(entry -> requestedKeys.contains(entry.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
🤖 Prompt for AI Agents
In compute/src/main/java/org/zstack/compute/vm/VmGpuPciMappingService.java
around lines 107-129, the current batch query uses two IN filters which can
return mismatched vmUuid+pciAddress combinations; change to either (A) for small
batches loop per cacheKey and query by both vmInstanceUuid and vmPciAddress to
get exact matches, or (B, preferred for larger batches) query once by the set of
vmUuids only, build a Set of requested "vmUuid:vmPciAddress" cache keys, then
filter the returned mappings in-memory to keep only entries whose
vmUuid:vmPciAddress pair exists in the requested set and collect those into the
result map.


/**
* 创建映射关系
*/
public void createMapping(String vmUuid, String vmPciAddress, String hostPciAddress, String gpuSerial) {
VmGpuPciMappingVO existing = Q.New(VmGpuPciMappingVO.class)
.eq(VmGpuPciMappingVO_.vmInstanceUuid, vmUuid)
.eq(VmGpuPciMappingVO_.vmPciAddress, vmPciAddress)
.find();

if (existing != null) {
// 更新现有映射
existing.setHostPciAddress(hostPciAddress);
existing.setGpuSerial(gpuSerial);
dbf.update(existing);
logger.debug(String.format("Updated GPU PCI mapping for VM[%s]: VM PCI[%s] -> Host PCI[%s]",
vmUuid, vmPciAddress, hostPciAddress));
} else {
// 创建新映射
VmGpuPciMappingVO mapping = new VmGpuPciMappingVO();
mapping.setUuid(Platform.getUuid());
mapping.setVmInstanceUuid(vmUuid);
mapping.setVmPciAddress(vmPciAddress);
mapping.setHostPciAddress(hostPciAddress);
mapping.setGpuSerial(gpuSerial);
dbf.persist(mapping);
logger.debug(String.format("Created GPU PCI mapping for VM[%s]: VM PCI[%s] -> Host PCI[%s]",
vmUuid, vmPciAddress, hostPciAddress));
}

// 更新缓存
String cacheKey = vmUuid + ":" + vmPciAddress;
mappingCache.put(cacheKey, hostPciAddress);
cacheTimestamp.put(cacheKey, System.currentTimeMillis());
}

/**
* 删除VM的所有映射关系
*/
public void removeMappingsByVmUuid(String vmUuid) {
List<VmGpuPciMappingVO> mappings = Q.New(VmGpuPciMappingVO.class)
.eq(VmGpuPciMappingVO_.vmInstanceUuid, vmUuid)
.list();

for (VmGpuPciMappingVO mapping : mappings) {
dbf.remove(mapping);
}

if (!mappings.isEmpty()) {
logger.debug(String.format("Removed %d GPU PCI mappings for VM[%s]", mappings.size(), vmUuid));
}

// 清理缓存
String vmPrefix = vmUuid + ":";
mappingCache.entrySet().removeIf(entry -> entry.getKey().startsWith(vmPrefix));
cacheTimestamp.entrySet().removeIf(entry -> entry.getKey().startsWith(vmPrefix));
}

/**
* 删除特定的映射关系
*/
public void removeMapping(String vmUuid, String vmPciAddress) {
VmGpuPciMappingVO mapping = Q.New(VmGpuPciMappingVO.class)
.eq(VmGpuPciMappingVO_.vmInstanceUuid, vmUuid)
.eq(VmGpuPciMappingVO_.vmPciAddress, vmPciAddress)
.find();

if (mapping != null) {
dbf.remove(mapping);
logger.debug(String.format("Removed GPU PCI mapping for VM[%s]: VM PCI[%s]",
vmUuid, vmPciAddress));
}

// 清理缓存
String cacheKey = vmUuid + ":" + vmPciAddress;
mappingCache.remove(cacheKey);
cacheTimestamp.remove(cacheKey);
}

/**
* 预加载所有映射关系到缓存
*/
@PostConstruct
public void preloadCache() {
try {
List<VmGpuPciMappingVO> allMappings = Q.New(VmGpuPciMappingVO.class).list();

long currentTime = System.currentTimeMillis();
for (VmGpuPciMappingVO mapping : allMappings) {
String cacheKey = mapping.getVmInstanceUuid() + ":" + mapping.getVmPciAddress();
mappingCache.put(cacheKey, mapping.getHostPciAddress());
cacheTimestamp.put(cacheKey, currentTime);
}

logger.info(String.format("Preloaded %d GPU PCI mappings into cache", allMappings.size()));
} catch (Exception e) {
logger.warn("Failed to preload GPU PCI mapping cache", e);
}
}

/**
* 获取缓存统计信息
*/
public Map<String, Object> getCacheStats() {
Map<String, Object> stats = new HashMap<>();
stats.put("cacheSize", mappingCache.size());
stats.put("timestampSize", cacheTimestamp.size());
stats.put("cacheExpireMs", CACHE_EXPIRE_MS);

long expiredCount = cacheTimestamp.values().stream()
.mapToLong(timestamp -> System.currentTimeMillis() - timestamp)
.filter(age -> age > CACHE_EXPIRE_MS)
.count();
stats.put("expiredEntries", expiredCount);

return stats;
}

/**
* 清理过期缓存
*/
public void cleanExpiredCache() {
long currentTime = System.currentTimeMillis();
List<String> expiredKeys = cacheTimestamp.entrySet().stream()
.filter(entry -> currentTime - entry.getValue() > CACHE_EXPIRE_MS)
.map(Map.Entry::getKey)
.collect(Collectors.toList());

for (String key : expiredKeys) {
mappingCache.remove(key);
cacheTimestamp.remove(key);
}

if (!expiredKeys.isEmpty()) {
logger.debug(String.format("Cleaned %d expired cache entries", expiredKeys.size()));
}
}
}
48 changes: 48 additions & 0 deletions conf/db/upgrade/V5.5.0__addVmGpuPciMappingTable.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- ZStack Database Upgrade Script for VmGpuPciMappingVO
-- Version: 5.5.0
-- Description: Add VmGpuPciMappingVO table to maintain VM PCI address to Host PCI address mapping

DELIMITER $$

DROP PROCEDURE IF EXISTS addVmGpuPciMappingTable$$

CREATE PROCEDURE addVmGpuPciMappingTable()
BEGIN
DECLARE table_exists INT DEFAULT 0;

-- Check if table already exists
SELECT COUNT(*) INTO table_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'VmGpuPciMappingVO';

IF table_exists = 0 THEN
-- Create the VmGpuPciMappingVO table
CREATE TABLE `zstack`.`VmGpuPciMappingVO` (
`uuid` varchar(32) NOT NULL,
`vmInstanceUuid` varchar(32) NOT NULL COMMENT 'VM实例UUID',
`vmPciAddress` varchar(32) NOT NULL COMMENT 'VM内部看到的PCI地址',
`hostPciAddress` varchar(32) NOT NULL COMMENT 'Host上真实的PCI地址',
`gpuSerial` varchar(128) DEFAULT NULL COMMENT 'GPU序列号',
Comment on lines +23 to +26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

SQL 脚本中包含中文注释,违反编码规范

根据编码规范要求:"代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"。数据库表和列的 COMMENT 也属于代码的一部分,应使用英文。

🔎 建议的修复
         CREATE TABLE `zstack`.`VmGpuPciMappingVO` (
             `uuid` varchar(32) NOT NULL,
-            `vmInstanceUuid` varchar(32) NOT NULL COMMENT 'VM实例UUID',
-            `vmPciAddress` varchar(32) NOT NULL COMMENT 'VM内部看到的PCI地址',
-            `hostPciAddress` varchar(32) NOT NULL COMMENT 'Host上真实的PCI地址',
-            `gpuSerial` varchar(128) DEFAULT NULL COMMENT 'GPU序列号',
+            `vmInstanceUuid` varchar(32) NOT NULL COMMENT 'VM instance UUID',
+            `vmPciAddress` varchar(32) NOT NULL COMMENT 'PCI address seen inside the VM',
+            `hostPciAddress` varchar(32) NOT NULL COMMENT 'Real PCI address on the host',
+            `gpuSerial` varchar(128) DEFAULT NULL COMMENT 'GPU serial number',
             `createDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
             `lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
             PRIMARY KEY (`uuid`),
             UNIQUE KEY `ukVmGpuPciMappingVO` (`vmInstanceUuid`, `vmPciAddress`),
             KEY `fkVmGpuPciMappingVOVmInstanceVO` (`vmInstanceUuid`),
             CONSTRAINT `fkVmGpuPciMappingVOVmInstanceVO` FOREIGN KEY (`vmInstanceUuid`) REFERENCES `zstack`.`VmInstanceVO` (`uuid`) ON DELETE CASCADE
-        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='VM GPU PCI地址映射表';
+        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='VM GPU PCI address mapping table';

Also applies to: 33-33

🤖 Prompt for AI Agents
In conf/db/upgrade/V5.5.0__addVmGpuPciMappingTable.sql around lines 23-26 (and
also line 33), the column COMMENTs are written in Chinese which violates the
codebase rule to use English; update each column COMMENT to clear, concise
English equivalents (e.g., "VM instance UUID", "PCI address seen inside VM",
"Host PCI address", "GPU serial number") and ensure any other Chinese comments
in the file are replaced with proper English text.

`createDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`uuid`),
UNIQUE KEY `ukVmGpuPciMappingVO` (`vmInstanceUuid`, `vmPciAddress`),
KEY `fkVmGpuPciMappingVOVmInstanceVO` (`vmInstanceUuid`),
CONSTRAINT `fkVmGpuPciMappingVOVmInstanceVO` FOREIGN KEY (`vmInstanceUuid`) REFERENCES `zstack`.`VmInstanceVO` (`uuid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='VM GPU PCI地址映射表';

-- Log the table creation
SELECT 'VmGpuPciMappingVO table created successfully' AS message;
ELSE
SELECT 'VmGpuPciMappingVO table already exists' AS message;
END IF;
END$$

DELIMITER ;

-- Execute the procedure
CALL addVmGpuPciMappingTable();

-- Clean up
DROP PROCEDURE addVmGpuPciMappingTable;
Loading