-
Notifications
You must be signed in to change notification settings - Fork 0
<feature>[root]: test gpu #3153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 5.5.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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; | ||
|
|
||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 代码中包含中文注释,违反编码规范 根据编码规范:"代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"。所有注释应使用英文。 🔎 建议的修复示例- // 缓存配置
- 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 |
||
|
|
||
| /** | ||
| * 根据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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 缺少 queryFromDatabase 方法定义 第 51 行调用了 🔎 建议添加缺失的方法/**
* 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 批量查询存在潜在的数据不匹配问题 第 118-121 行的批量查询使用了两个 例如,如果 cacheKeys 包含 🔎 建议的修复方案方案 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 |
||
|
|
||
| /** | ||
| * 创建映射关系 | ||
| */ | ||
| 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())); | ||
| } | ||
| } | ||
| } | ||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 |
||
| `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; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
包名错误
根据文件路径
compute/src/main/java/org/zstack/compute/vm/VmGpuPciMappingService.java,包名应为org.zstack.compute.vm,而非org.zstack.vm。🔎 建议的修复
📝 Committable suggestion
🤖 Prompt for AI Agents