
Byted Vedb Mysql
- 11 installs
- 411 repo stars
- Updated August 4, 2026
- bytedance/agentkit-samples
vedb-mysql is a Claude skill that creates and manages Volcengine VeDB MySQL cloud database instances through the Volcengine Python SDK.
About
vedb-mysql manages Volcengine VeDB MySQL (VEDBM) cloud database instances through the Volcengine Python SDK. It can create an instance with a chosen spec, list instances in a region, list database accounts, and fetch connection endpoints (domain, port, IP). A developer supplies VPC ID, subnet ID, zone and Volcengine access keys, then runs the bundled scripts to provision or inspect instances.
- Creates and lists Volcengine VeDB MySQL cloud database instances
- Manages database accounts and fetches connection endpoints
- Smart instance-spec matching from shorthand like 2c8g or 4c16g
Byted Vedb Mysql by the numbers
- 11 all-time installs (skills.sh)
- Ranked #651 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
byted-vedb-mysql capabilities & compatibility
Requires Volcengine credentials; provisioned instances incur Volcengine cloud costs.
- Capabilities
- database provisioning · instance management · connection endpoint lookup
- Works with
- mysql
- Use cases
- database · devops
- Pricing
- Bring your own API key
What byted-vedb-mysql says it does
python scripts/create_vedbm_instance.py
pip install volcengine-python-sdk
npx skills add https://github.com/bytedance/agentkit-samples --skill byted-vedb-mysqlAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11 |
|---|---|
| repo stars | ★ 411 |
| Last updated | August 4, 2026 |
| Repository | bytedance/agentkit-samples ↗ |
What it does
Create and manage Volcengine VeDB MySQL cloud database instances via the Python SDK.
Who is it for?
Provisioning and inspecting Volcengine VeDB MySQL instances programmatically.
Skip if: Managing databases outside Volcengine.
When should I use this skill?
You need to create a VEDBM instance, list instances, manage accounts or get connection endpoints.
What you get
A created VeDB MySQL instance with returned instance ID, endpoint, port and credentials.
By the numbers
- Default spec 2C8G
- Six documented node specs from 2C8G to 16C64G
Files
火山引擎 VEDBM 管理技能
本技能用于管理火山引擎 VEDBM(云数据库)实例,包含以下功能:
1. 创建实例:支持用户自定义实例规格,默认使用 2C8G 规格,用户名 root,密码随机生成 2. 查看实例列表:查询指定地域下的所有 VEDBM 实例 3. 查看数据库账号列表:查询指定实例中的所有数据库账号及其状态 4. 查看实例连接地址:查询指定实例的连接地址信息(域名、端口、IP等)
✨ 新特性
- 智能规格匹配:支持用户输入简单格式(如 "2c8g"、"4c16g")
- 灵活默认值:默认使用 2C8G 规格
- 自动降级/升级:如果用户提供的规格不存在,自动选择最接近的可用规格
使用流程
第一步:收集必要信息
当用户触发此技能时,先检查是否有必要信息。如果没有,引导用户提供:
| 信息 | 必填 | 说明 |
|---|---|---|
| VPC ID | ✅ | 虚拟私有云 ID |
| 子网 ID | ✅ | 子网 ID |
| 可用区 ID | ✅ | 可用区(如 cn-guangzhou-a) |
| AccessKey ID | ✅ | 火山引擎访问密钥 ID |
| Secret Access Key | ✅ | 火山引擎秘密访问密钥 |
第二步:信息收集方式
方式 A:对话引导(推荐首次使用)
询问用户:
"好的,我来帮你创建 VEDBM 实例!需要你提供一些信息:
>
1. VPC ID 是什么?
2. 子网 ID 是什么?
3. 想用哪个可用区?(如 cn-guangzhou-a)
4. AccessKey ID 是什么?
5. Secret Access Key 是什么?"
方式 B:环境变量配置(推荐频繁使用)
提示用户配置环境变量:
# 配置环境变量
export VOLCENGINE_ACCESS_KEY="你的AccessKeyId"
export VOLCENGINE_SECRET_KEY="你的SecretAccessKey"
export VEDBM_VPC_ID="你的VPC ID"
export VEDBM_SUBNET_ID="你的子网 ID"
export VEDBM_ZONE_ID="cn-guangzhou-a"或者使用 .env 文件:
cd scripts
cp .env.example .env
# 编辑 .env 文件填入真实信息第三步:执行创建
收集完信息后,执行创建命令。
访问密钥配置
方式 1:命令行参数
python scripts/create_vedbm_instance.py \
--vpc-id <你的VPC ID> \
--subnet-id <你的子网 ID> \
--zone-id cn-guangzhou-a \
--access-key-id <你的AccessKeyId> \
--secret-access-key <你的SecretAccessKey>方式 2:环境变量
export VOLCENGINE_ACCESS_KEY="your-access-key-id"
export VOLCENGINE_SECRET_KEY="your-secret-access-key"
export VEDBM_VPC_ID="your-vpc-id"
export VEDBM_SUBNET_ID="your-subnet-id"
export VEDBM_ZONE_ID="cn-guangzhou-a"
python scripts/create_vedbm_instance.py方式 3:混合使用
可以部分用环境变量,部分用命令行参数:
export VOLCENGINE_ACCESS_KEY="your-access-key-id"
export VOLCENGINE_SECRET_KEY="your-secret-access-key"
python scripts/create_vedbm_instance.py \
--vpc-id <你的VPC ID> \
--subnet-id <你的子网 ID> \
--zone-id cn-guangzhou-a可用区说明
广州区域可用区:
cn-guangzhou-a- 可用区 Acn-guangzhou-c- 可用区 C
注意:没有 cn-guangzhou-b
节点规格
| 规格 | CPU | 内存 | 说明 |
|---|---|---|---|
vedb.mysql.x2.large | 2核 | 8GB | 默认,入门级 |
vedb.mysql.x4.large | 4核 | 16GB | 性价比高 |
vedb.mysql.g4.large | 4核 | 16GB | 通用型 |
vedb.mysql.p4.large | 4核 | 16GB | 性能型 |
vedb.mysql.x4.xlarge | 8核 | 32GB | 高性能 |
vedb.mysql.x8.large | 16核 | 64GB | 旗舰级 |
🎯 规格输入格式
支持多种输入格式:
1. 简写格式(推荐)
2c8g- 2核8GB4c16g- 4核16GB8c32g- 8核32GB
2. 只指定 CPU
2c- 自动匹配 2核 的规格4c- 自动匹配 4核 的规格
3. 只指定内存
8g- 自动匹配 8GB 的规格16g- 自动匹配 16GB 的规格
4. 完整规格名
vedb.mysql.x2.largevedb.mysql.x4.large
🤖 智能匹配规则
如果用户提供的规格不存在,系统会自动选择最接近的规格:
1. 优先选择大于等于目标配置的规格 2. CPU 权重 > 内存权重 3. 如果没有足够大的规格,选择最大的可用规格
示例:
- 用户输入
3c10g→ 自动匹配4c16g - 用户输入
5c20g→ 自动匹配8c32g - 用户输入
1c4g→ 自动匹配2c8g(最小规格)
前置条件
1. 已安装火山引擎 Python SDK:
pip install volcengine-python-sdk2. 已配置火山引擎访问凭证(Access Key ID 和 Secret Access Key)
使用方法
运行脚本创建实例:
python scripts/create_vedbm_instance.py脚本参数
脚本支持以下可选参数:
--region: 区域,默认cn-guangzhou--instance-name: 实例名称,默认随机生成--node-spec: 节点规格(可选,默认2c8g)- 支持格式:
2c8g、4c16g、vedb.mysql.x2.large --vpc-id: VPC ID(必须提供)--subnet-id: 子网 ID(必须提供)--zone-id: 可用区 ID(必须提供)
输出信息
创建成功后会返回:
- 实例 ID
- 连接地址
- 端口
- 用户名(root)
- 密码(随机生成)
- 使用的规格(含描述和代码)
示例
示例 1:使用默认规格(2c8g)
python scripts/create_vedbm_instance.py \
--vpc-id vpc-123456 \
--subnet-id subnet-123456 \
--zone-id cn-guangzhou-a示例 2:使用简写规格
python scripts/create_vedbm_instance.py \
--vpc-id vpc-123456 \
--subnet-id subnet-123456 \
--zone-id cn-guangzhou-a \
--node-spec 4c16g示例 3:使用完整规格名
python scripts/create_vedbm_instance.py \
--vpc-id vpc-123456 \
--subnet-id subnet-123456 \
--zone-id cn-guangzhou-a \
--node-spec vedb.mysql.x4.large示例 4:只指定 CPU
python scripts/create_vedbm_instance.py \
--vpc-id vpc-123456 \
--subnet-id subnet-123456 \
--zone-id cn-guangzhou-a \
--node-spec 8c---
功能二:查看实例列表
使用流程
第一步:收集必要信息
当用户需要查看实例列表时,先检查是否有必要信息。如果没有,引导用户提供:
| 信息 | 必填 | 说明 |
|---|---|---|
| AccessKey ID | ✅ | 火山引擎访问密钥 ID |
| Secret Access Key | ✅ | 火山引擎秘密访问密钥 |
| 区域(可选) | ❌ | 查询的区域,默认 cn-guangzhou |
第二步:信息收集方式
方式 A:对话引导(推荐首次使用)
询问用户:
"好的,我来帮你查看 VEDBM 实例列表!需要你提供一些信息:
>
1. AccessKey ID 是什么?
2. Secret Access Key 是什么?
3. 要查询哪个区域?(默认 cn-guangzhou)"
方式 B:环境变量配置(推荐频繁使用)
提示用户配置环境变量(可以复用以创建实例的配置):
# 配置环境变量
export VOLCENGINE_ACCESS_KEY="你的AccessKeyId"
export VOLCENGINE_SECRET_KEY="你的SecretAccessKey"
export VEDBM_REGION="cn-guangzhou" # 可选使用方法
运行脚本查询实例列表:
python scripts/list_instances.py脚本参数
脚本支持以下参数:
--region: 区域,默认cn-guangzhou--access-key-id: 访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)--secret-access-key: 秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)
输出信息
查询成功后会返回:
- 区域
- 实例总数
- 每个实例的详细信息:
- 实例 ID
- 实例名称
- 实例状态
- 节点规格
- 可用区
- 创建时间
示例
示例 1:使用默认区域(cn-guangzhou)
python scripts/list_instances.py示例 2:指定区域
python scripts/list_instances.py --region cn-beijing输出示例
====================================================================================================
✅ VEDBM 实例列表查询成功!
====================================================================================================
区域: cn-guangzhou
实例总数: 2
📋 实例列表:
----------------------------------------------------------------------------------------------------
实例 ID 实例名称 状态 规格 可用区
----------------------------------------------------------------------------------------------------
vedbm-xxx123456 vedbm-instance-123456 Running vedb.mysql.x2.large cn-guangzhou-a
vedbm-yyy789012 vedbm-instance-789012 Running vedb.mysql.x4.large cn-guangzhou-c
----------------------------------------------------------------------------------------------------
====================================================================================================---
功能三:查看数据库账号列表
使用流程
第一步:收集必要信息
当用户需要查看数据库账号列表时,先检查是否有必要信息。如果没有,引导用户提供:
| 信息 | 必填 | 说明 |
|---|---|---|
| 实例 ID | ✅ | VEDBM 实例 ID |
| AccessKey ID | ✅ | 火山引擎访问密钥 ID |
| Secret Access Key | ✅ | 火山引擎秘密访问密钥 |
第二步:信息收集方式
方式 A:对话引导(推荐首次使用)
询问用户:
"好的,我来帮你查看 VEDBM 实例的数据库账号列表!需要你提供一些信息:
>
1. 实例 ID 是什么?
2. AccessKey ID 是什么?
3. Secret Access Key 是什么?"
方式 B:环境变量配置(推荐频繁使用)
提示用户配置环境变量(可以复用以创建实例的配置):
# 配置环境变量
export VOLCENGINE_ACCESS_KEY="你的AccessKeyId"
export VOLCENGINE_SECRET_KEY="你的SecretAccessKey"使用方法
运行脚本查询账号列表:
python scripts/list_db_accounts.py --instance-id <实例ID>脚本参数
脚本支持以下参数:
--region: 区域,默认cn-guangzhou--instance-id: 实例 ID(必须提供)--access-key-id: 访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)--secret-access-key: 秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)
输出信息
查询成功后会返回:
- 实例 ID
- 区域
- 账号总数
- 每个账号的详细信息:
- 账号名称
- 账号类型
- 账号状态
示例
python scripts/list_db_accounts.py \
--instance-id vedbm-xxx123456输出示例
======================================================================
✅ 数据库账号列表查询成功!
======================================================================
实例 ID: vedbm-xxx123456
区域: cn-guangzhou
账号总数: 2
📋 账号列表:
----------------------------------------------------------------------
账号名称 账号类型 账号状态
----------------------------------------------------------------------
root Super Available
testuser Normal Available
----------------------------------------------------------------------
======================================================================---
功能四:查看实例连接地址
使用流程
第一步:收集必要信息
当用户需要查看实例连接地址时,先检查是否有必要信息。如果没有,引导用户提供:
| 信息 | 必填 | 说明 |
|---|---|---|
| 实例 ID | ✅ | VEDBM 实例 ID |
| AccessKey ID | ✅ | 火山引擎访问密钥 ID |
| Secret Access Key | ✅ | 火山引擎秘密访问密钥 |
第二步:信息收集方式
方式 A:对话引导(推荐首次使用)
询问用户:
"好的,我来帮你查看 VEDBM 实例的连接地址!需要你提供一些信息:
>
1. 实例 ID 是什么?
2. AccessKey ID 是什么?
3. Secret Access Key 是什么?"
方式 B:环境变量配置(推荐频繁使用)
提示用户配置环境变量(可以复用其他功能的配置):
# 配置环境变量
export VOLCENGINE_ACCESS_KEY="你的AccessKeyId"
export VOLCENGINE_SECRET_KEY="你的SecretAccessKey"使用方法
运行脚本查询连接地址:
python scripts/get_instance_endpoint.py --instance-id <实例ID>脚本参数
脚本支持以下参数:
--region: 区域,默认cn-guangzhou--instance-id: 实例 ID(必须提供)--access-key-id: 访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)--secret-access-key: 秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)
输出信息
查询成功后会返回:
- 实例 ID
- 区域
- 终端总数
- 每个终端的详细信息:
- 终端 ID
- 终端类型(Primary、Cluster 等)
- 每个终端的连接地址:
- 域名
- 端口
- IP 地址
- 网络类型
示例
python scripts/get_instance_endpoint.py \
--instance-id vedbm-xxx123456输出示例
================================================================================
✅ 实例连接地址查询成功!
================================================================================
实例 ID: vedbm-xxx123456
区域: cn-guangzhou
终端总数: 2
📍 终端 1:
--------------------------------------------------------------------------------
终端 ID: endpoint-abc123
终端类型: Primary
📡 连接地址:
地址 1:
域名: vedbm-xxx123456.rds.volcengine.com
端口: 3306
IP地址: 10.0.0.100
网络类型: Private
📍 终端 2:
--------------------------------------------------------------------------------
终端 ID: endpoint-def456
终端类型: Cluster
📡 连接地址:
地址 1:
域名: vedbm-xxx123456-cluster.rds.volcengine.com
端口: 3306
IP地址: 10.0.0.101
网络类型: Private
--------------------------------------------------------------------------------
================================================================================---
功能五:查看实例详情
使用流程
第一步:收集必要信息
当用户需要查看实例详情时,先检查是否有必要信息。如果没有,引导用户提供:
| 信息 | 必填 | 说明 |
|---|---|---|
| 实例 ID | ✅ | VEDBM 实例 ID |
| AccessKey ID | ✅ | 火山引擎访问密钥 ID |
| Secret Access Key | ✅ | 火山引擎秘密访问密钥 |
第二步:信息收集方式
方式 A:对话引导(推荐首次使用)
询问用户:
"好的,我来帮你查看 VEDBM 实例详情!需要你提供一些信息:
>
1. 实例 ID 是什么?
2. AccessKey ID 是什么?
3. Secret Access Key 是什么?"
方式 B:环境变量配置(推荐频繁使用)
提示用户配置环境变量(可以复用其他功能的配置):
# 配置环境变量
export VOLCENGINE_ACCESS_KEY="你的AccessKeyId"
export VOLCENGINE_SECRET_KEY="你的SecretAccessKey"使用方法
运行脚本查询实例详情:
python scripts/describe_instance.py --instance-id <实例ID>脚本参数
脚本支持以下参数:
--region: 区域,默认cn-guangzhou--instance-id: 实例 ID(必须提供)--access-key-id: 访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)--secret-access-key: 秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)--full: 显示完整属性列表(调试用)
输出信息
查询成功后会返回:
基本信息
- 实例 ID
- 实例名称
- 实例状态
- 创建时间
- 项目名称
规格信息
- 节点规格
- 配置(CPU核数 + 内存)
- 节点数量
- 规格系列
存储信息
- 已用存储
- 存储计费类型
网络信息
- 区域
- 可用区
- VPC ID
- 子网 ID
数据库信息
- 数据库版本
- 内核版本
- 时区
计费信息
- 计费类型
- 计费状态
- 删除保护
节点详情
- 每个节点的 ID、类型、规格、可用区
示例
示例 1:查询实例详情
python scripts/describe_instance.py \
--instance-id vedbm-xxx123456示例 2:显示完整属性列表(调试用)
python scripts/describe_instance.py \
--instance-id vedbm-xxx123456 \
--full输出示例
====================================================================================================
✅ VEDBM 实例详情查询成功!
====================================================================================================
📋 基本信息:
----------------------------------------------------------------------------------------------------
实例 ID: vedbm-4wuvdt3jhlce
实例名称: free
实例状态: Running
创建时间: 2026-04-03T05:35:38Z
项目名称: default
💻 规格信息:
----------------------------------------------------------------------------------------------------
节点规格: vedb.mysql.g4.large
配置: 4核 16GB
节点数量: 2
规格系列: General
💾 存储信息:
----------------------------------------------------------------------------------------------------
已用存储: 2.093 GiB
存储计费类型: PostPaid
🌐 网络信息:
----------------------------------------------------------------------------------------------------
区域: cn-guangzhou
可用区: cn-guangzhou-a
VPC ID: vpc-3f03n0s2v50qo72200s6jj8ni
子网 ID: subnet-11wfhusxgt05c40yrhczqa9cl
🗄️ 数据库信息:
----------------------------------------------------------------------------------------------------
数据库版本: MySQL_8_0
内核版本: 3.3.2.7
时区: UTC -01:00
💰 计费信息:
----------------------------------------------------------------------------------------------------
计费类型: PostPaid
计费状态: Normal
删除保护: disabled
🔧 节点详情:
----------------------------------------------------------------------------------------------------
节点 ID 类型 规格 可用区
----------------------------------------------------------------------------------------------------
vedbm-4wuvdt3jhlce-0 ReadOnly vedb.mysql.g4.large cn-guangzhou-a
vedbm-4wuvdt3jhlce-1 Primary vedb.mysql.g4.large cn-guangzhou-a
====================================================================================================---
功能六:升级实例规格
使用场景
1. 用户指定规格:如果用户指定了规格,就按照用户指定的规格做变更 2. 自动升级:如果用户没有指定规格,先查看当前的实例规格,然后默认按照CPU的规格升级一个等级 3. 不跨规格类型:通用规格只能在通用规格内升级,独享规格只能在独享规格内升级 4. 最大规格提示:如果没有办法升级,告知用户当前规格已经是最大的了无法升级 5. 超时处理:如果升级任务10分钟还未执行完成,则告知用户通过控制台关注任务状态
使用流程
第一步:收集必要信息
当用户需要升级实例规格时,先检查是否有必要信息。如果没有,引导用户提供:
| 信息 | 必填 | 说明 |
|---|---|---|
| 实例 ID | ✅ | VEDBM 实例 ID |
| AccessKey ID | ✅ | 火山引擎访问密钥 ID |
| Secret Access Key | ✅ | 火山引擎秘密访问密钥 |
| 目标规格(可选) | ❌ | 不提供则自动升级一个等级 |
第二步:信息收集方式
方式 A:对话引导(推荐首次使用)
询问用户:
"好的,我来帮你升级 VEDBM 实例规格!需要你提供一些信息:
>
1. 实例 ID 是什么?
2. AccessKey ID 是什么?
3. Secret Access Key 是什么?
4. 目标规格是什么?(可选,不提供则自动升级一个等级)"
方式 B:环境变量配置(推荐频繁使用)
提示用户配置环境变量(可以复用其他功能的配置):
# 配置环境变量
export VOLCENGINE_ACCESS_KEY="你的AccessKeyId"
export VOLCENGINE_SECRET_KEY="你的SecretAccessKey"规格类型说明
| 系列 | 说明 | 可用规格 |
|---|---|---|
| G4 系列(通用型,默认) | 通用型 | 4c16g → 8c32g → 16c64g → 32c128g |
| X4 系列(标准型) | 标准型 | 4c16g → 8c32g → 16c64g → 32c128g → 64c256g |
| X8 系列(标准型X8) | 标准型X8 | 8c32g → 16c64g → 32c128g → 64c256g → 96c384g → 128c512g |
| G8 系列(通用型G8) | 通用型G8 | 16c64g |
⚠️ 注意:不支持跨系列升级!
使用方法
运行脚本升级实例规格:
python scripts/upgrade_instance_spec.py --instance-id <实例ID>脚本参数
脚本支持以下参数:
--region: 区域,默认cn-guangzhou--instance-id: 实例 ID(必须提供)--target-spec: 目标规格(可选,不提供则自动升级一个等级)- 支持格式:
2c8g、4c16g、vedb.mysql.x4.large --access-key-id: 访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)--secret-access-key: 秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)
输出信息
升级成功后会返回:
- 实例 ID
- 原规格
- 新规格
- 规格代码
- 是否完成(如果10分钟内完成)
示例
示例 1:自动升级一个等级(推荐)
python scripts/upgrade_instance_spec.py \
--instance-id vedbm-xxx123456示例 2:指定目标规格(使用简写格式)
python scripts/upgrade_instance_spec.py \
--instance-id vedbm-xxx123456 \
--target-spec 4c16g示例 3:指定目标规格(使用完整规格名)
python scripts/upgrade_instance_spec.py \
--instance-id vedbm-xxx123456 \
--target-spec vedb.mysql.x4.large输出示例
自动升级成功
================================================================================
✅ VEDBM 实例规格升级成功!
================================================================================
实例 ID: vedbm-xxx123456
原规格: vedb.mysql.x2.large
新规格: 4核16GB
规格代码: vedb.mysql.x4.large
================================================================================已经是最大规格
================================================================================
❌ VEDBM 实例规格升级失败!
================================================================================
错误信息: 当前规格 'vedb.mysql.x8.large' 已经是 X 系列的最大规格,无法继续升级
实例 ID: vedbm-xxx123456
================================================================================等待超时(10分钟)
================================================================================
⏳ VEDBM 实例规格变更请求已提交
================================================================================
实例 ID: vedbm-xxx123456
原规格: vedb.mysql.x2.large
目标规格: 4核16GB
规格代码: vedb.mysql.x4.large
⚠️ 注意: 规格变更请求已提交,但等待超时(10分钟)。请通过火山引擎控制台关注任务状态。
================================================================================跨系列升级被拒绝
================================================================================
❌ VEDBM 实例规格升级失败!
================================================================================
错误信息: 不支持跨规格类型升级(当前: X 系列, 目标: G 系列)
实例 ID: vedbm-xxx123456
================================================================================---
功能七:降级实例规格
使用场景
1. 用户指定规格:如果用户指定了规格,就按照用户指定的规格做变更 2. 自动降级:如果用户没有指定规格,先查看当前的实例规格,然后默认按照CPU的规格降级一个等级 3. 不跨规格类型:通用规格只能在通用规格内降级,独享规格只能在独享规格内降级 4. 最小规格提示:如果没有办法降级,告知用户当前规格已经是最小的了无法降级 5. 超时处理:如果降级任务10分钟还未执行完成,则告知用户通过控制台关注任务状态
使用流程
第一步:收集必要信息
当用户需要降级实例规格时,先检查是否有必要信息。如果没有,引导用户提供:
| 信息 | 必填 | 说明 |
|---|---|---|
| 实例 ID | ✅ | VEDBM 实例 ID |
| AccessKey ID | ✅ | 火山引擎访问密钥 ID |
| Secret Access Key | ✅ | 火山引擎秘密访问密钥 |
| 目标规格(可选) | ❌ | 不提供则自动降级一个等级 |
第二步:信息收集方式
方式 A:对话引导(推荐首次使用)
询问用户:
"好的,我来帮你降级 VEDBM 实例规格!需要你提供一些信息:
>
1. 实例 ID 是什么?
2. AccessKey ID 是什么?
3. Secret Access Key 是什么?
4. 目标规格是什么?(可选,不提供则自动降级一个等级)"
方式 B:环境变量配置(推荐频繁使用)
提示用户配置环境变量(可以复用其他功能的配置):
# 配置环境变量
export VOLCENGINE_ACCESS_KEY="你的AccessKeyId"
export VOLCENGINE_SECRET_KEY="你的SecretAccessKey"规格类型说明
| 系列 | 说明 | 可用规格 |
|---|---|---|
| G4 系列(通用型,默认) | 通用型 | 4c16g ← 8c32g ← 16c64g ← 32c128g |
| X4 系列(标准型) | 标准型 | 4c16g ← 8c32g ← 16c64g ← 32c128g ← 64c256g |
| X8 系列(标准型X8) | 标准型X8 | 8c32g ← 16c64g ← 32c128g ← 64c256g ← 96c384g ← 128c512g |
| G8 系列(通用型G8) | 通用型G8 | 16c64g |
⚠️ 注意:不支持跨系列降级!
使用方法
运行脚本降级实例规格:
python scripts/downgrade_instance_spec.py --instance-id <实例ID>脚本参数
脚本支持以下参数:
--region: 区域,默认cn-guangzhou--instance-id: 实例 ID(必须提供)--target-spec: 目标规格(可选,不提供则自动降级一个等级)- 支持格式:
2c8g、4c16g、vedb.mysql.x4.large --access-key-id: 访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)--secret-access-key: 秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)
输出信息
降级成功后会返回:
- 实例 ID
- 原规格
- 新规格
- 规格代码
- 是否完成(如果10分钟内完成)
示例
示例 1:自动降级一个等级(推荐)
python scripts/downgrade_instance_spec.py \
--instance-id vedbm-xxx123456示例 2:指定目标规格(使用简写格式)
python scripts/downgrade_instance_spec.py \
--instance-id vedbm-xxx123456 \
--target-spec 4c16g示例 3:指定目标规格(使用完整规格名)
python scripts/downgrade_instance_spec.py \
--instance-id vedbm-xxx123456 \
--target-spec vedb.mysql.x4.large输出示例
自动降级成功
================================================================================
✅ VEDBM 实例规格降级成功!
================================================================================
实例 ID: vedbm-xxx123456
原规格: vedb.mysql.x4.large
新规格: 4核16GB
规格代码: vedb.mysql.x4.large
================================================================================已经是最小规格
================================================================================
❌ VEDBM 实例规格降级失败!
================================================================================
错误信息: 当前规格 'vedb.mysql.x4.large' 已经是 X4 系列的最小规格,无法继续降级
实例 ID: vedbm-xxx123456
================================================================================等待超时(10分钟)
================================================================================
⏳ VEDBM 实例规格变更请求已提交
================================================================================
实例 ID: vedbm-xxx123456
原规格: vedb.mysql.x4.large
目标规格: 4核16GB
规格代码: vedb.mysql.x4.large
⚠️ 注意: 规格变更请求已提交,但等待超时(10分钟)。请通过火山引擎控制台关注任务状态。
================================================================================跨系列降级被拒绝
================================================================================
❌ VEDBM 实例规格降级失败!
================================================================================
错误信息: 不支持跨规格类型降级(当前: X 系列, 目标: G 系列)
实例 ID: vedbm-xxx123456
================================================================================/.trae
/.ruff_cache
/scripts/__pycache__
/scripts/.env
.DS_Store
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
# 火山引擎 VEDBM 实例创建 - 环境变量配置示例
# 复制此文件为 .env 并填入你的真实配置
# 或者直接在命令行中设置环境变量
# ==================== 火山引擎访问密钥 ====================
VOLCENGINE_ACCESS_KEY=your_access_key_id_here
VOLCENGINE_SECRET_KEY=your_secret_access_key_here
# ==================== VEDBM 实例配置 ====================
# 区域(可选,默认:cn-guangzhou)
VEDBM_REGION=cn-guangzhou
# VPC ID(必填)
VEDBM_VPC_ID=your_vpc_id_here
# 子网 ID(必填)
VEDBM_SUBNET_ID=your_subnet_id_here
# 可用区 ID(必填,广州区域:cn-guangzhou-a 或 cn-guangzhou-c)
VEDBM_ZONE_ID=cn-guangzhou-a
# 节点规格(可选,默认:2c8g)
# 支持多种格式:
# - 简写格式:2c8g, 4c16g, 8c32g
# - 只指定CPU:2c, 4c, 8c
# - 只指定内存:8g, 16g, 32g
# - 完整规格名:vedb.mysql.x2.large
#
# 可用规格:
# - vedb.mysql.x2.large (2核8GB,默认)
# - vedb.mysql.x4.large (4核16GB)
# - vedb.mysql.g4.large (4核16GB,通用型)
# - vedb.mysql.p4.large (4核16GB,性能型)
# - vedb.mysql.x4.xlarge (8核32GB)
# - vedb.mysql.x8.large (16核64GB)
VEDBM_NODE_SPEC=2c8g
#!/usr/bin/env python3
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" specific language governing permissions and
# limitations under the License.
"""
火山引擎 VEDBM 实例创建脚本
创建 4C16G 规格的 VEDBM 实例,用户名 root,密码随机生成
"""
import argparse
import os
import random
import string
import sys
import time
from typing import Dict, Optional
try:
from dotenv import load_dotenv
HAS_DOTENV = True
except ImportError:
HAS_DOTENV = False
try:
from volcenginesdkcore import Configuration, ApiClient
from volcenginesdkvedbm import (
VEDBMApi,
CreateDBInstanceRequest,
DescribeDBInstancesRequest,
DescribeDBEndpointRequest,
)
except ImportError as e:
print(f"错误:未安装火山引擎 Python SDK 或导入失败: {e}")
import traceback
traceback.print_exc()
print("请运行: pip install volcengine-python-sdk")
sys.exit(1)
def generate_password(length: int = 16) -> str:
"""
生成随机密码
包含大小写字母、数字和特殊字符
"""
characters = string.ascii_letters + string.digits + "!@#$%^&*"
password = []
# 确保至少包含一个大写字母、小写字母、数字和特殊字符
password.append(random.choice(string.ascii_uppercase))
password.append(random.choice(string.ascii_lowercase))
password.append(random.choice(string.digits))
password.append(random.choice("!@#$%^&*"))
# 填充剩余字符
for _ in range(length - 4):
password.append(random.choice(characters))
# 打乱顺序
random.shuffle(password)
return "".join(password)
def generate_instance_name() -> str:
"""生成随机实例名称"""
timestamp = int(time.time())
return f"vedbm-instance-{timestamp}"
def get_instance_status(api_instance: VEDBMApi, instance_id: str) -> Optional[str]:
"""
获取实例状态
Args:
api_instance: VEDBM API 实例
instance_id: 实例 ID
Returns:
实例状态
"""
try:
request = DescribeDBInstancesRequest(instance_id=instance_id)
response = api_instance.describe_db_instances(request)
if not response.instances or len(response.instances) == 0:
return None
instance = response.instances[0]
return (
instance.instance_status if hasattr(instance, "instance_status") else None
)
except Exception as e:
print(f"⚠️ 查询实例状态失败: {e}")
return None
def get_instance_endpoint(api_instance: VEDBMApi, instance_id: str) -> Optional[Dict]:
"""
获取实例连接地址
Args:
api_instance: VEDBM API 实例
instance_id: 实例 ID
Returns:
连接信息字典
"""
try:
request = DescribeDBEndpointRequest(instance_id=instance_id)
response = api_instance.describe_db_endpoint(request)
if not response.endpoints or len(response.endpoints) == 0:
return None
# 优先找主节点终端
primary_endpoint = None
default_endpoint = None
for endpoint in response.endpoints:
if hasattr(endpoint, "endpoint_type"):
if endpoint.endpoint_type == "Primary":
primary_endpoint = endpoint
elif endpoint.endpoint_type == "Cluster":
default_endpoint = endpoint
# 使用主节点终端,如果没有则用默认终端
selected_endpoint = (
primary_endpoint or default_endpoint or response.endpoints[0]
)
if hasattr(selected_endpoint, "addresses") and selected_endpoint.addresses:
for address in selected_endpoint.addresses:
if hasattr(address, "domain") and address.domain:
return {
"connection_address": address.domain,
"port": int(address.port)
if hasattr(address, "port") and address.port
else 3306,
}
return None
except Exception as e:
print(f"⚠️ 查询连接地址失败: {e}")
return None
def wait_for_instance_ready(
api_instance: VEDBMApi,
instance_id: str,
timeout: int = 1200,
poll_interval: int = 30,
) -> bool:
"""
等待实例就绪
Args:
api_instance: VEDBM API 实例
instance_id: 实例 ID
timeout: 超时时间(秒),默认 20 分钟
poll_interval: 轮询间隔(秒),默认 30 秒
Returns:
是否就绪
"""
start_time = time.time()
while time.time() - start_time < timeout:
status = get_instance_status(api_instance, instance_id)
if status:
print(f"⏳ 实例状态: {status}")
if status == "Running":
print("✅ 实例已就绪!")
return True
elapsed = int(time.time() - start_time)
remaining = timeout - elapsed
print(f" 已等待 {elapsed} 秒,剩余约 {remaining} 秒...")
time.sleep(poll_interval)
print("❌ 等待实例就绪超时")
return False
# 定义可用的规格列表
AVAILABLE_SPECS = [
{"name": "vedb.mysql.x2.large", "cpu": 2, "memory": 8, "description": "2核8GB"},
{"name": "vedb.mysql.x4.large", "cpu": 4, "memory": 16, "description": "4核16GB"},
{
"name": "vedb.mysql.g4.large",
"cpu": 4,
"memory": 16,
"description": "4核16GB(通用型)",
},
{
"name": "vedb.mysql.p4.large",
"cpu": 4,
"memory": 16,
"description": "4核16GB(性能型)",
},
{"name": "vedb.mysql.x4.xlarge", "cpu": 8, "memory": 32, "description": "8核32GB"},
{"name": "vedb.mysql.x8.large", "cpu": 16, "memory": 64, "description": "16核64GB"},
]
# 默认规格
DEFAULT_SPEC = AVAILABLE_SPECS[0] # 2核8GB
def parse_spec_input(spec_input: str) -> Optional[Dict]:
"""
解析用户输入的规格
Args:
spec_input: 用户输入的规格字符串,如 "2c8g"、"4c16g" 或完整规格名
Returns:
匹配到的规格字典,如果没有匹配则返回 None
"""
if not spec_input:
return None
spec_input = spec_input.strip().lower()
# 1. 精确匹配完整规格名
for spec in AVAILABLE_SPECS:
if spec["name"].lower() == spec_input:
return spec
# 2. 解析 "2c8g" 格式
import re
match = re.match(r"(\d+)\s*c\s*(\d+)\s*g", spec_input)
if match:
cpu = int(match.group(1))
memory = int(match.group(2))
return find_closest_spec(cpu, memory)
# 3. 只提供 CPU 或内存的情况
match_cpu = re.match(r"(\d+)\s*c", spec_input)
if match_cpu:
cpu = int(match_cpu.group(1))
return find_closest_spec(cpu, None)
match_mem = re.match(r"(\d+)\s*g", spec_input)
if match_mem:
memory = int(match_mem.group(1))
return find_closest_spec(None, memory)
return None
def find_closest_spec(
target_cpu: Optional[int] = None, target_memory: Optional[int] = None
) -> Dict:
"""
找到最接近的规格
Args:
target_cpu: 目标 CPU 核数(可选)
target_memory: 目标内存大小(GB,可选)
Returns:
最接近的规格字典
"""
if target_cpu is None and target_memory is None:
return DEFAULT_SPEC
best_spec = None
best_score = float("inf")
for spec in AVAILABLE_SPECS:
score = 0
# 计算 CPU 分数(如果提供了目标)
if target_cpu is not None:
cpu_diff = spec["cpu"] - target_cpu
# 如果规格 >= 目标,分数为差值;否则给予较大惩罚
if cpu_diff >= 0:
score += cpu_diff * 10
else:
score += abs(cpu_diff) * 1000
# 计算内存分数(如果提供了目标)
if target_memory is not None:
mem_diff = spec["memory"] - target_memory
if mem_diff >= 0:
score += mem_diff * 1
else:
score += abs(mem_diff) * 100
# 更新最佳匹配
if score < best_score:
best_score = score
best_spec = spec
return best_spec or DEFAULT_SPEC
def create_vedbm_instance(
region: str,
vpc_id: str,
subnet_id: str,
zone_id: str,
instance_name: Optional[str] = None,
node_spec: Optional[str] = None,
access_key_id: Optional[str] = None,
secret_access_key: Optional[str] = None,
) -> Dict:
"""
创建 VEDBM 实例
Args:
region: 区域
vpc_id: VPC ID
subnet_id: 子网 ID
zone_id: 可用区 ID
instance_name: 实例名称(可选,自动生成)
node_spec: 节点规格(可选,支持 "2c8g"、"4c16g" 或完整规格名)
access_key_id: 访问密钥 ID(可选,从环境变量读取)
secret_access_key: 秘密访问密钥(可选,从环境变量读取)
Returns:
创建结果字典
"""
if instance_name is None:
instance_name = generate_instance_name()
# 解析并选择合适的规格
selected_spec = DEFAULT_SPEC
if node_spec:
parsed_spec = parse_spec_input(node_spec)
if parsed_spec:
selected_spec = parsed_spec
print(
f"✅ 使用规格: {selected_spec['description']} ({selected_spec['name']})"
)
else:
selected_spec = find_closest_spec()
print(
f"⚠️ 无法解析规格 '{node_spec}',使用默认规格: {selected_spec['description']}"
)
else:
print(f"✅ 使用默认规格: {selected_spec['description']}")
# 生成密码
password = generate_password()
# 配置 SDK
configuration = Configuration()
configuration.region = region
if access_key_id:
configuration.ak = access_key_id
if secret_access_key:
configuration.sk = secret_access_key
# 创建 API 客户端
api_client = ApiClient(configuration)
api_instance = VEDBMApi(api_client)
# 创建实例请求
create_request = CreateDBInstanceRequest(
charge_type="PostPaid",
instance_name=instance_name,
node_spec=selected_spec["name"],
node_number=2,
vpc_id=vpc_id,
subnet_id=subnet_id,
zone_ids=zone_id,
db_engine_version="MySQL_8_0",
super_account_name="root",
super_account_password=password,
)
try:
# 调用 API 创建实例
print("⏳ 正在创建 VEDBM 实例...")
response = api_instance.create_db_instance(create_request)
instance_id = response.instance_id
print(f"✅ 实例创建请求已提交,实例 ID: {instance_id}")
print("⏳ 等待实例就绪(可能需要 5-15 分钟)...")
# 等待实例就绪
if wait_for_instance_ready(api_instance, instance_id):
# 获取连接地址
print("🔍 正在获取连接地址...")
endpoint_info = get_instance_endpoint(api_instance, instance_id)
result = {
"success": True,
"instance_id": instance_id,
"instance_name": instance_name,
"connection_address": endpoint_info.get("connection_address")
if endpoint_info
else None,
"port": endpoint_info.get("port") if endpoint_info else 3306,
"username": "root",
"password": password,
"region": region,
"node_spec": selected_spec["name"],
"node_spec_description": selected_spec["description"],
}
return result
else:
return {
"success": False,
"error": "等待实例就绪超时",
"instance_id": instance_id,
}
except Exception as e:
return {"success": False, "error": str(e)}
def print_result(result: Dict):
"""打印创建结果"""
if result["success"]:
print("\n" + "=" * 60)
print("✅ VEDBM 实例创建成功!")
print("=" * 60)
print(f"实例 ID: {result['instance_id']}")
print(f"实例名称: {result['instance_name']}")
if result.get("connection_address"):
print(f"连接地址: {result['connection_address']}")
print(f"端口: {result['port']}")
print(f"用户名: {result['username']}")
print(f"密码: {result['password']}")
print(f"区域: {result['region']}")
print(f"节点规格: {result.get('node_spec_description', result['node_spec'])}")
print(f"规格代码: {result['node_spec']}")
print("=" * 60 + "\n")
else:
print("\n" + "=" * 60)
print("❌ VEDBM 实例创建失败!")
print("=" * 60)
print(f"错误信息: {result['error']}")
if result.get("instance_id"):
print(f"实例 ID: {result['instance_id']}")
print("=" * 60 + "\n")
def main():
# 尝试加载 .env 文件
if HAS_DOTENV:
# 尝试从多个位置加载 .env
env_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"),
".env",
]
for env_path in env_paths:
if os.path.exists(env_path):
load_dotenv(env_path)
print(f"✅ 已加载配置文件: {env_path}")
break
# 显示可用规格
print("📋 可用的节点规格:")
for spec in AVAILABLE_SPECS:
print(f" - {spec['description']}: {spec['name']}")
print(" 提示:也可以使用简写格式,如 '2c8g'、'4c16g'")
print()
parser = argparse.ArgumentParser(description="创建火山引擎 VEDBM 实例")
parser.add_argument(
"--region",
default=os.getenv("VEDBM_REGION", "cn-guangzhou"),
help="区域(默认:cn-guangzhou,或环境变量 VEDBM_REGION)",
)
parser.add_argument("--instance-name", help="实例名称(默认自动生成)")
parser.add_argument(
"--node-spec",
default=os.getenv("VEDBM_NODE_SPEC"),
help="节点规格(可选,默认:2c8g,支持 '2c8g'、'4c16g' 或完整规格名,或环境变量 VEDBM_NODE_SPEC)",
)
parser.add_argument(
"--vpc-id",
default=os.getenv("VEDBM_VPC_ID"),
help="VPC ID(必填,或环境变量 VEDBM_VPC_ID)",
)
parser.add_argument(
"--subnet-id",
default=os.getenv("VEDBM_SUBNET_ID"),
help="子网 ID(必填,或环境变量 VEDBM_SUBNET_ID)",
)
parser.add_argument(
"--zone-id",
default=os.getenv("VEDBM_ZONE_ID"),
help="可用区 ID(必填,或环境变量 VEDBM_ZONE_ID)",
)
parser.add_argument(
"--access-key-id",
default=os.getenv("VOLCENGINE_ACCESS_KEY"),
help="访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)",
)
parser.add_argument(
"--secret-access-key",
default=os.getenv("VOLCENGINE_SECRET_KEY"),
help="秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)",
)
args = parser.parse_args()
# 检查必填参数
missing = []
if not args.vpc_id:
missing.append("VPC ID(--vpc-id 或 VEDBM_VPC_ID)")
if not args.subnet_id:
missing.append("子网 ID(--subnet-id 或 VEDBM_SUBNET_ID)")
if not args.zone_id:
missing.append("可用区 ID(--zone-id 或 VEDBM_ZONE_ID)")
if not args.access_key_id:
missing.append("AccessKey ID(--access-key-id 或 VOLCENGINE_ACCESS_KEY)")
if not args.secret_access_key:
missing.append(
"Secret Access Key(--secret-access-key 或 VOLCENGINE_SECRET_KEY)"
)
if missing:
print("❌ 缺少必要参数!")
print("\n请提供以下信息:")
for item in missing:
print(f" - {item}")
print("\n方式 1:命令行参数")
print(
" python create_vedbm_instance.py --vpc-id xxx --subnet-id xxx --zone-id xxx --access-key-id xxx --secret-access-key xxx"
)
print("\n方式 2:环境变量")
print(" export VEDBM_VPC_ID=xxx")
print(" export VEDBM_SUBNET_ID=xxx")
print(" export VEDBM_ZONE_ID=xxx")
print(" export VOLCENGINE_ACCESS_KEY=xxx")
print(" export VOLCENGINE_SECRET_KEY=xxx")
print(" python create_vedbm_instance.py")
print("\n方式 3:混合使用(部分环境变量,部分命令行)")
sys.exit(1)
result = create_vedbm_instance(
region=args.region,
vpc_id=args.vpc_id,
subnet_id=args.subnet_id,
zone_id=args.zone_id,
instance_name=args.instance_name,
node_spec=args.node_spec,
access_key_id=args.access_key_id,
secret_access_key=args.secret_access_key,
)
print_result(result)
sys.exit(0 if result["success"] else 1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" specific language governing permissions and
# limitations under the License.
"""
火山引擎 VEDBM 实例详情查询脚本
查看指定实例的完整详细信息
"""
import argparse
import os
import sys
from typing import Dict, Optional
try:
from dotenv import load_dotenv
HAS_DOTENV = True
except ImportError:
HAS_DOTENV = False
try:
from volcenginesdkcore import Configuration, ApiClient
from volcenginesdkvedbm import VEDBMApi, DescribeDBInstancesRequest
except ImportError as e:
print(f"错误:未安装火山引擎 Python SDK 或导入失败: {e}")
import traceback
traceback.print_exc()
print("请运行: pip install volcengine-python-sdk")
sys.exit(1)
def describe_instance(
region: str,
instance_id: str,
access_key_id: Optional[str] = None,
secret_access_key: Optional[str] = None,
show_full: bool = False,
) -> Dict:
"""
获取实例详细信息
Args:
region: 区域
instance_id: 实例 ID
access_key_id: 访问密钥 ID(可选,从环境变量读取)
secret_access_key: 秘密访问密钥(可选,从环境变量读取)
show_full: 是否显示完整属性列表
Returns:
实例详细信息
"""
# 配置 SDK
configuration = Configuration()
configuration.region = region
if access_key_id:
configuration.ak = access_key_id
if secret_access_key:
configuration.sk = secret_access_key
# 创建 API 客户端
api_client = ApiClient(configuration)
api_instance = VEDBMApi(api_client)
try:
print(f"⏳ 正在查询实例 {instance_id} 的详细信息...")
# 调用 API 获取实例详情
request = DescribeDBInstancesRequest(instance_id=instance_id)
response = api_instance.describe_db_instances(request)
if not response.instances or len(response.instances) == 0:
return {"success": False, "error": f"未找到实例 {instance_id}"}
instance = response.instances[0]
# 打印完整属性列表(如果需要)
if show_full:
print("\n" + "=" * 80)
print("📋 实例完整属性列表:")
print("=" * 80)
for attr in dir(instance):
if not attr.startswith("_"):
try:
value = getattr(instance, attr)
if not callable(value):
print(f" {attr}: {value}")
except Exception as e:
print(f" {attr}: <无法读取 - {e}>")
print("=" * 80 + "\n")
# 从 nodes 数组中提取信息
node_spec = None
node_number = 0
nodes_info = []
v_cpu = None
memory = None
if hasattr(instance, "nodes") and instance.nodes:
node_number = len(instance.nodes)
for i, node in enumerate(instance.nodes):
node_info = {
"node_id": getattr(node, "node_id", None),
"node_type": getattr(node, "node_type", None),
"node_spec": getattr(node, "node_spec", None),
"v_cpu": getattr(node, "v_cpu", None),
"memory": getattr(node, "memory", None),
"zone_id": getattr(node, "zone_id", None),
"failover_priority": getattr(node, "failover_priority", None),
}
nodes_info.append(node_info)
# 取第一个节点的规格作为实例规格
if i == 0:
node_spec = node_info["node_spec"]
v_cpu = node_info["v_cpu"]
memory = node_info["memory"]
# 从 charge_detail 中提取计费信息
charge_type = None
charge_status = None
if hasattr(instance, "charge_detail") and instance.charge_detail:
charge_type = getattr(instance.charge_detail, "charge_type", None)
charge_status = getattr(instance.charge_detail, "charge_status", None)
# 构建返回结果
return {
"success": True,
"instance_id": getattr(instance, "instance_id", None),
"instance_name": getattr(instance, "instance_name", None),
"instance_status": getattr(instance, "instance_status", None),
"node_spec": node_spec,
"v_cpu": v_cpu,
"memory": memory,
"node_number": node_number,
"region": getattr(instance, "region_id", None),
"zone_id": getattr(instance, "zone_ids", None),
"create_time": getattr(instance, "create_time", None),
"charge_type": charge_type,
"charge_status": charge_status,
"db_engine_version": getattr(instance, "db_engine_version", None),
"db_revision_version": getattr(instance, "db_revision_version", None),
"spec_family": getattr(instance, "spec_family", None),
"storage_used_gib": getattr(instance, "storage_used_gi_b", None),
"storage_charge_type": getattr(instance, "storage_charge_type", None),
"vpc_id": getattr(instance, "vpc_id", None),
"subnet_id": getattr(instance, "subnet_id", None),
"project_name": getattr(instance, "project_name", None),
"deletion_protection": getattr(instance, "deletion_protection", None),
"time_zone": getattr(instance, "time_zone", None),
"nodes": nodes_info,
"full_info": instance,
}
except Exception as e:
import traceback
traceback.print_exc()
return {"success": False, "error": str(e)}
def print_result(result: Dict):
"""打印查询结果"""
if result["success"]:
print("\n" + "=" * 100)
print("✅ VEDBM 实例详情查询成功!")
print("=" * 100)
# 基本信息
print("\n📋 基本信息:")
print("-" * 100)
print(f" 实例 ID: {result['instance_id']}")
print(f" 实例名称: {result['instance_name']}")
print(f" 实例状态: {result['instance_status']}")
print(f" 创建时间: {result['create_time']}")
print(f" 项目名称: {result['project_name']}")
# 规格信息
print("\n💻 规格信息:")
print("-" * 100)
print(f" 节点规格: {result['node_spec']}")
if result.get("v_cpu") and result.get("memory"):
print(f" 配置: {result['v_cpu']}核 {result['memory']}GB")
print(f" 节点数量: {result['node_number']}")
print(f" 规格系列: {result['spec_family']}")
# 存储信息
print("\n💾 存储信息:")
print("-" * 100)
print(
f" 已用存储: {result['storage_used_gib']} GiB"
if result.get("storage_used_gib")
else " 已用存储: -"
)
print(f" 存储计费类型: {result['storage_charge_type']}")
# 网络信息
print("\n🌐 网络信息:")
print("-" * 100)
print(f" 区域: {result['region']}")
print(f" 可用区: {result['zone_id']}")
print(f" VPC ID: {result['vpc_id']}")
print(f" 子网 ID: {result['subnet_id']}")
# 数据库信息
print("\n🗄️ 数据库信息:")
print("-" * 100)
print(f" 数据库版本: {result['db_engine_version']}")
print(f" 内核版本: {result['db_revision_version']}")
print(f" 时区: {result['time_zone']}")
# 计费信息
print("\n💰 计费信息:")
print("-" * 100)
print(f" 计费类型: {result['charge_type']}")
print(f" 计费状态: {result['charge_status']}")
print(f" 删除保护: {result['deletion_protection']}")
# 节点详情
if result.get("nodes"):
print("\n🔧 节点详情:")
print("-" * 100)
print(f" {'节点 ID':<35} {'类型':<12} {'规格':<25} {'可用区'}")
print("-" * 100)
for node in result["nodes"]:
node_id = node.get("node_id", "-")
node_type = node.get("node_type", "-")
node_spec = node.get("node_spec", "-")
zone_id = node.get("zone_id", "-")
print(f" {node_id:<35} {node_type:<12} {node_spec:<25} {zone_id}")
print("=" * 100 + "\n")
else:
print("\n" + "=" * 100)
print("❌ VEDBM 实例详情查询失败!")
print("=" * 100)
print(f"错误信息: {result['error']}")
print("=" * 100 + "\n")
def main():
# 尝试加载 .env 文件
if HAS_DOTENV:
# 尝试从多个位置加载 .env
env_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"),
".env",
]
for env_path in env_paths:
if os.path.exists(env_path):
load_dotenv(env_path)
print(f"✅ 已加载配置文件: {env_path}")
break
parser = argparse.ArgumentParser(description="查看火山引擎 VEDBM 实例详情")
parser.add_argument(
"--region",
default=os.getenv("VEDBM_REGION", "cn-guangzhou"),
help="区域(默认:cn-guangzhou,或环境变量 VEDBM_REGION)",
)
parser.add_argument("--instance-id", required=True, help="实例 ID(必填)")
parser.add_argument(
"--access-key-id",
default=os.getenv("VOLCENGINE_ACCESS_KEY"),
help="访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)",
)
parser.add_argument(
"--secret-access-key",
default=os.getenv("VOLCENGINE_SECRET_KEY"),
help="秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)",
)
parser.add_argument(
"--full", action="store_true", help="显示完整属性列表(调试用)"
)
args = parser.parse_args()
# 检查必填参数
missing = []
if not args.instance_id:
missing.append("实例 ID(--instance-id)")
if not args.access_key_id:
missing.append("AccessKey ID(--access-key-id 或 VOLCENGINE_ACCESS_KEY)")
if not args.secret_access_key:
missing.append(
"Secret Access Key(--secret-access-key 或 VOLCENGINE_SECRET_KEY)"
)
if missing:
print("❌ 缺少必要参数!")
print("\n请提供以下信息:")
for item in missing:
print(f" - {item}")
print("\n使用示例:")
print(" python describe_instance.py --instance-id vedbm-xxx123456")
sys.exit(1)
result = describe_instance(
region=args.region,
instance_id=args.instance_id,
access_key_id=args.access_key_id,
secret_access_key=args.secret_access_key,
show_full=args.full,
)
print_result(result)
sys.exit(0 if result["success"] else 1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" specific language governing permissions and
# limitations under the License.
"""
火山引擎 VEDBM 实例规格降级脚本
支持用户指定规格或自动降级一个等级
"""
import argparse
import os
import sys
import time
from typing import Dict, Optional
try:
from dotenv import load_dotenv
HAS_DOTENV = True
except ImportError:
HAS_DOTENV = False
try:
from volcenginesdkcore import Configuration, ApiClient
from volcenginesdkvedbm import (
VEDBMApi,
DescribeDBInstancesRequest,
ModifyDBInstanceSpecRequest,
)
except ImportError as e:
print(f"错误:未安装火山引擎 Python SDK 或导入失败: {e}")
import traceback
traceback.print_exc()
print("请运行: pip install volcengine-python-sdk")
sys.exit(1)
# 定义可用的规格列表,按类型分组
# 基于实际API返回的规格
SPEC_GROUPS = {
"g4": [
{
"name": "vedb.mysql.g4.large",
"cpu": 4,
"memory": 16,
"description": "4核16GB(通用型)",
},
{
"name": "vedb.mysql.g4.xlarge",
"cpu": 8,
"memory": 32,
"description": "8核32GB(通用型)",
},
{
"name": "vedb.mysql.g4.2xlarge",
"cpu": 16,
"memory": 64,
"description": "16核64GB(通用型)",
},
{
"name": "vedb.mysql.g4.4xlarge",
"cpu": 32,
"memory": 128,
"description": "32核128GB(通用型)",
},
],
"x4": [
{
"name": "vedb.mysql.x4.large",
"cpu": 4,
"memory": 16,
"description": "4核16GB(标准型)",
},
{
"name": "vedb.mysql.x4.xlarge",
"cpu": 8,
"memory": 32,
"description": "8核32GB(标准型)",
},
{
"name": "vedb.mysql.x4.2xlarge",
"cpu": 16,
"memory": 64,
"description": "16核64GB(标准型)",
},
{
"name": "vedb.mysql.x4.4xlarge",
"cpu": 32,
"memory": 128,
"description": "32核128GB(标准型)",
},
{
"name": "vedb.mysql.x4.8xlarge",
"cpu": 64,
"memory": 256,
"description": "64核256GB(标准型)",
},
],
"x8": [
{
"name": "vedb.mysql.x8.large",
"cpu": 8,
"memory": 32,
"description": "8核32GB(标准型X8)",
},
{
"name": "vedb.mysql.x8.xlarge",
"cpu": 16,
"memory": 64,
"description": "16核64GB(标准型X8)",
},
{
"name": "vedb.mysql.x8.2xlarge",
"cpu": 32,
"memory": 128,
"description": "32核128GB(标准型X8)",
},
{
"name": "vedb.mysql.x8.4xlarge",
"cpu": 64,
"memory": 256,
"description": "64核256GB(标准型X8)",
},
{
"name": "vedb.mysql.x8.6xlarge",
"cpu": 96,
"memory": 384,
"description": "96核384GB(标准型X8)",
},
{
"name": "vedb.mysql.x8.8xlarge",
"cpu": 128,
"memory": 512,
"description": "128核512GB(标准型X8)",
},
],
"g8": [
{
"name": "vedb.mysql.g8.2xlarge",
"cpu": 16,
"memory": 64,
"description": "16核64GB(通用型G8)",
},
],
}
def get_spec_type(spec_name: str) -> str:
"""
获取规格类型
Args:
spec_name: 规格名称
Returns:
规格类型(g4/x4/x8/g8)
"""
if not spec_name:
return "g4"
if "g8" in spec_name:
return "g8"
elif "g4" in spec_name:
return "g4"
elif "x8" in spec_name:
return "x8"
elif "x4" in spec_name:
return "x4"
elif "g2" in spec_name:
return "g4" # g2 映射到 g4
elif "x2" in spec_name:
return "x4" # x2 映射到 x4
elif "g" in spec_name:
return "g4"
elif "x" in spec_name:
return "x4"
else:
return "g4"
def get_spec_info(spec_name: str) -> Optional[Dict]:
"""
获取规格信息
Args:
spec_name: 规格名称
Returns:
规格信息字典
"""
for group in SPEC_GROUPS.values():
for spec in group:
if spec["name"] == spec_name:
return spec
return None
def parse_spec_input(spec_input: str) -> Optional[Dict]:
"""
解析用户输入的规格
Args:
spec_input: 用户输入的规格字符串,如 "2c8g"、"4c16g" 或完整规格名
Returns:
匹配到的规格字典,如果没有匹配则返回 None
"""
if not spec_input:
return None
spec_input = spec_input.strip().lower()
# 1. 精确匹配完整规格名
for group in SPEC_GROUPS.values():
for spec in group:
if spec["name"].lower() == spec_input:
return spec
# 2. 解析 "2c8g" 格式
import re
match = re.match(r"(\d+)\s*c\s*(\d+)\s*g", spec_input)
if match:
cpu = int(match.group(1))
memory = int(match.group(2))
# 默认在 g4 系列中查找
return find_closest_spec(cpu, memory, "g4")
# 3. 只提供 CPU 或内存的情况
match_cpu = re.match(r"(\d+)\s*c", spec_input)
if match_cpu:
cpu = int(match_cpu.group(1))
return find_closest_spec(cpu, None, "g4")
match_mem = re.match(r"(\d+)\s*g", spec_input)
if match_mem:
memory = int(match_mem.group(1))
return find_closest_spec(None, memory, "g4")
return None
def find_closest_spec(
target_cpu: Optional[int] = None,
target_memory: Optional[int] = None,
spec_type: str = "g4",
) -> Optional[Dict]:
"""
找到最接近的规格
Args:
target_cpu: 目标 CPU 核数(可选)
target_memory: 目标内存大小(GB,可选)
spec_type: 规格类型(g4/x4/x8/g8)
Returns:
最接近的规格字典
"""
specs = SPEC_GROUPS.get(spec_type, SPEC_GROUPS["g4"])
if target_cpu is None and target_memory is None:
return specs[0] if specs else None
best_spec = None
best_score = float("inf")
for spec in specs:
score = 0
# 计算 CPU 分数(如果提供了目标)
if target_cpu is not None:
cpu_diff = spec["cpu"] - target_cpu
# 如果规格 >= 目标,分数为差值;否则给予较大惩罚
if cpu_diff >= 0:
score += cpu_diff * 10
else:
score += abs(cpu_diff) * 1000
# 计算内存分数(如果提供了目标)
if target_memory is not None:
mem_diff = spec["memory"] - target_memory
if mem_diff >= 0:
score += mem_diff * 1
else:
score += abs(mem_diff) * 100
# 更新最佳匹配
if score < best_score:
best_score = score
best_spec = spec
return best_spec
def get_next_lower_spec(current_spec_name: str) -> Optional[Dict]:
"""
获取当前规格的下一个更低规格(同一类型内降级)
Args:
current_spec_name: 当前规格名称
Returns:
下一个更低规格,如果已经是最小规格则返回 None
"""
current_spec = get_spec_info(current_spec_name)
if not current_spec:
return None
spec_type = get_spec_type(current_spec_name)
specs = SPEC_GROUPS.get(spec_type, [])
# 找到当前规格在列表中的位置
current_index = -1
for i, spec in enumerate(specs):
if spec["name"] == current_spec_name:
current_index = i
break
if current_index == -1:
return None
# 如果已经是最小规格
if current_index <= 0:
return None
# 返回前一个规格
return specs[current_index - 1]
def get_instance_info(api_instance: VEDBMApi, instance_id: str) -> Optional[Dict]:
"""
获取实例详细信息
Args:
api_instance: VEDBM API 实例
instance_id: 实例 ID
Returns:
实例信息字典
"""
try:
request = DescribeDBInstancesRequest(instance_id=instance_id)
response = api_instance.describe_db_instances(request)
if not response.instances or len(response.instances) == 0:
return None
instance = response.instances[0]
# 从 nodes 数组中获取节点规格
node_spec = None
node_number = 0
if hasattr(instance, "nodes") and instance.nodes:
node_number = len(instance.nodes)
for node in instance.nodes:
if hasattr(node, "node_spec") and node.node_spec:
node_spec = node.node_spec
break
return {
"instance_id": getattr(instance, "instance_id", None),
"instance_name": getattr(instance, "instance_name", None),
"instance_status": getattr(instance, "instance_status", None),
"node_spec": node_spec,
"node_number": node_number,
"region": getattr(instance, "region_id", None),
"zone_id": getattr(instance, "zone_ids", None),
"create_time": getattr(instance, "create_time", None),
"spec_family": getattr(instance, "spec_family", None),
}
except Exception as e:
print(f"⚠️ 查询实例信息失败: {e}")
return None
def wait_for_spec_change_complete(
api_instance: VEDBMApi,
instance_id: str,
target_spec: str,
timeout: int = 600,
poll_interval: int = 30,
) -> Dict:
"""
等待规格变更完成
Args:
api_instance: VEDBM API 实例
instance_id: 实例 ID
target_spec: 目标规格
timeout: 超时时间(秒),默认 10 分钟
poll_interval: 轮询间隔(秒),默认 30 秒
Returns:
包含完成状态和最终规格信息的字典
"""
start_time = time.time()
while time.time() - start_time < timeout:
instance_info = get_instance_info(api_instance, instance_id)
if instance_info:
current_status = instance_info.get("instance_status", "Unknown")
current_spec = instance_info.get("node_spec", "Unknown")
print(f"⏳ 实例状态: {current_status}, 当前规格: {current_spec}")
# 如果状态是 Running,检查规格是否已更新
if current_status == "Running":
if current_spec == target_spec:
print("✅ 规格变更完成!")
return {
"completed": True,
"final_spec": current_spec,
"final_status": current_status,
"timeout": False,
}
else:
# 状态是 Running 但规格还没变,继续等待
pass
elapsed = int(time.time() - start_time)
remaining = timeout - elapsed
print(f" 已等待 {elapsed} 秒,剩余约 {remaining} 秒...")
time.sleep(poll_interval)
# 超时了,获取最后的实例信息
final_instance_info = get_instance_info(api_instance, instance_id)
final_spec = final_instance_info.get("node_spec") if final_instance_info else None
final_status = (
final_instance_info.get("instance_status") if final_instance_info else None
)
print("⚠️ 等待规格变更超时(10分钟)")
return {
"completed": False,
"final_spec": final_spec,
"final_status": final_status,
"timeout": True,
}
def downgrade_instance_spec(
region: str,
instance_id: str,
target_spec: Optional[str] = None,
access_key_id: Optional[str] = None,
secret_access_key: Optional[str] = None,
) -> Dict:
"""
降级实例规格
Args:
region: 区域
instance_id: 实例 ID
target_spec: 目标规格(可选,如果不提供则自动降级一个等级)
access_key_id: 访问密钥 ID(可选,从环境变量读取)
secret_access_key: 秘密访问密钥(可选,从环境变量读取)
Returns:
降级结果字典
"""
# 配置 SDK
configuration = Configuration()
configuration.region = region
if access_key_id:
configuration.ak = access_key_id
if secret_access_key:
configuration.sk = secret_access_key
# 创建 API 客户端
api_client = ApiClient(configuration)
api_instance = VEDBMApi(api_client)
# 第一步:获取降级前的实例信息
print(f"🔍 正在查询实例 {instance_id} 的信息...")
before_instance_info = get_instance_info(api_instance, instance_id)
if not before_instance_info:
return {"success": False, "error": f"无法获取实例 {instance_id} 的信息"}
before_spec = before_instance_info.get("node_spec")
before_status = before_instance_info.get("instance_status")
before_spec_info = get_spec_info(before_spec)
print(f"✅ 降级前 - 实例状态: {before_status}")
print(f"✅ 降级前 - 规格: {before_spec}")
if before_spec_info:
print(
f"✅ 降级前 - 配置: {before_spec_info['cpu']}核 {before_spec_info['memory']}GB"
)
# 检查实例状态
if before_status != "Running":
return {
"success": False,
"error": f"实例状态不是 Running(当前状态: {before_status}),无法进行规格变更",
}
# 第二步:确定目标规格
selected_spec = None
if target_spec:
# 用户指定了规格
parsed_spec = parse_spec_input(target_spec)
if parsed_spec:
selected_spec = parsed_spec
print(
f"✅ 使用用户指定的规格: {selected_spec['description']} ({selected_spec['name']})"
)
else:
return {"success": False, "error": f"无法解析规格 '{target_spec}'"}
else:
# 自动降级一个等级
current_spec_info = get_spec_info(before_spec)
if not current_spec_info:
# 当前规格不在我们的列表中,可能是旧规格
spec_type = get_spec_type(before_spec)
specs = SPEC_GROUPS.get(spec_type, [])
if not specs:
return {
"success": False,
"error": f"无法识别当前规格 '{before_spec}',且找不到对应的规格系列。请手动指定目标规格。",
}
# 对于旧规格,我们选择该系列的最小规格作为降级起点
print(f"⚠️ 当前规格 '{before_spec}' 是旧规格,不在可用列表中")
print(" 建议降级到同系列的可用规格")
# 选择该系列的最小规格
selected_spec = specs[0]
print(
f"✅ 建议降级到: {selected_spec['description']} ({selected_spec['name']})"
)
else:
# 当前规格在我们的列表中,正常降级
next_spec = get_next_lower_spec(before_spec)
if not next_spec:
spec_type = get_spec_type(before_spec)
type_name = {
"g4": "G4(通用型)",
"x4": "X4(标准型)",
"x8": "X8(标准型X8)",
"g8": "G8(通用型G8)",
}.get(spec_type, spec_type.upper())
return {
"success": False,
"error": f"当前规格 '{before_spec}' 已经是 {type_name} 系列的最小规格,无法继续降级",
}
selected_spec = next_spec
print(
f"✅ 自动选择前一个规格: {selected_spec['description']} ({selected_spec['name']})"
)
# 检查是否跨规格类型
before_spec_type = get_spec_type(before_spec)
target_spec_type = get_spec_type(selected_spec["name"])
if before_spec_type != target_spec_type:
before_type_name = {
"g4": "G4(通用型)",
"x4": "X4(标准型)",
"x8": "X8(标准型X8)",
"g8": "G8(通用型G8)",
}.get(before_spec_type, before_spec_type.upper())
target_type_name = {
"g4": "G4(通用型)",
"x4": "X4(标准型)",
"x8": "X8(标准型X8)",
"g8": "G8(通用型G8)",
}.get(target_spec_type, target_spec_type.upper())
return {
"success": False,
"error": f"不支持跨规格类型降级(当前: {before_type_name}, 目标: {target_type_name})",
}
# 检查是否需要变更
if before_spec == selected_spec["name"]:
return {
"success": True,
"message": f"当前规格已经是 {selected_spec['description']},无需变更",
"instance_id": instance_id,
"before_spec": before_spec,
"before_spec_info": before_spec_info,
"after_spec": selected_spec["name"],
"after_spec_info": selected_spec,
}
# 第三步:执行规格变更
try:
print("⏳ 正在提交规格变更请求...")
print(f" 从: {before_spec}")
print(f" 到: {selected_spec['name']} ({selected_spec['description']})")
print(f" 节点数量: {before_instance_info.get('node_number', 2)}")
modify_request = ModifyDBInstanceSpecRequest(
instance_id=instance_id,
node_spec=selected_spec["name"],
node_number=before_instance_info.get("node_number", 2),
)
api_instance.modify_db_instance_spec(modify_request)
print("✅ 规格变更请求已提交")
# 第四步:等待变更完成(最多10分钟)
print("⏳ 等待规格变更完成(最多10分钟)...")
print(" 提示:通过检查实例状态是否为 Running 来判断是否完成")
wait_result = wait_for_spec_change_complete(
api_instance, instance_id, selected_spec["name"]
)
# 获取降级后的规格信息
after_spec_info = get_spec_info(wait_result.get("final_spec"))
result = {
"success": True,
"instance_id": instance_id,
"before_spec": before_spec,
"before_spec_info": before_spec_info,
"after_spec": wait_result.get("final_spec"),
"after_spec_info": after_spec_info,
"target_spec": selected_spec["name"],
"target_spec_info": selected_spec,
"completed": wait_result.get("completed", False),
"timeout": wait_result.get("timeout", False),
"final_status": wait_result.get("final_status"),
}
if wait_result.get("timeout"):
result["message"] = (
"规格变更请求已提交,但等待超时(10分钟)。请通过火山引擎控制台关注任务状态。"
)
return result
except Exception as e:
return {
"success": False,
"error": str(e),
"instance_id": instance_id,
"before_spec": before_spec,
"before_spec_info": before_spec_info,
}
def print_result(result: Dict):
"""打印降级结果"""
if result["success"]:
print("\n" + "=" * 100)
if result.get("message") and "无需变更" in result["message"]:
print("ℹ️ 规格变更 - 无需操作")
print("=" * 100)
print(result["message"])
elif result.get("completed", False):
print("✅ VEDBM 实例规格降级成功!")
print("=" * 100)
print(f"实例 ID: {result['instance_id']}")
print()
print("📋 降级前:")
print("-" * 100)
print(f" 规格: {result['before_spec']}")
if result.get("before_spec_info"):
print(
f" 配置: {result['before_spec_info']['cpu']}核 {result['before_spec_info']['memory']}GB"
)
print(f" 描述: {result['before_spec_info']['description']}")
print()
print("⬇️ 降级后:")
print("-" * 100)
print(f" 规格: {result['after_spec']}")
if result.get("after_spec_info"):
print(
f" 配置: {result['after_spec_info']['cpu']}核 {result['after_spec_info']['memory']}GB"
)
print(f" 描述: {result['after_spec_info']['description']}")
else:
print("⏳ VEDBM 实例规格变更请求已提交")
print("=" * 100)
print(f"实例 ID: {result['instance_id']}")
print()
print("📋 降级前:")
print("-" * 100)
print(f" 规格: {result['before_spec']}")
if result.get("before_spec_info"):
print(
f" 配置: {result['before_spec_info']['cpu']}核 {result['before_spec_info']['memory']}GB"
)
print(f" 描述: {result['before_spec_info']['description']}")
print()
print("🎯 目标规格:")
print("-" * 100)
print(f" 规格: {result['target_spec']}")
if result.get("target_spec_info"):
print(
f" 配置: {result['target_spec_info']['cpu']}核 {result['target_spec_info']['memory']}GB"
)
print(f" 描述: {result['target_spec_info']['description']}")
print()
if result.get("after_spec"):
print("📊 当前状态:")
print("-" * 100)
print(f" 实例状态: {result.get('final_status', 'Unknown')}")
print(f" 当前规格: {result['after_spec']}")
print()
print(
f"⚠️ 注意: {result.get('message', '等待超时,请通过控制台关注任务状态')}"
)
print("=" * 100 + "\n")
else:
print("\n" + "=" * 100)
print("❌ VEDBM 实例规格降级失败!")
print("=" * 100)
print(f"错误信息: {result['error']}")
if result.get("instance_id"):
print(f"实例 ID: {result['instance_id']}")
if result.get("before_spec"):
print()
print("📋 降级前规格:")
print("-" * 100)
print(f" 规格: {result['before_spec']}")
if result.get("before_spec_info"):
print(
f" 配置: {result['before_spec_info']['cpu']}核 {result['before_spec_info']['memory']}GB"
)
print(f" 描述: {result['before_spec_info']['description']}")
print("=" * 100 + "\n")
def main():
# 尝试加载 .env 文件
if HAS_DOTENV:
# 尝试从多个位置加载 .env
env_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"),
".env",
]
for env_path in env_paths:
if os.path.exists(env_path):
load_dotenv(env_path)
print(f"✅ 已加载配置文件: {env_path}")
break
# 显示可用规格
print("📋 可用的节点规格(按类型分组):")
print()
for spec_type, specs in SPEC_GROUPS.items():
type_name = {
"g4": "G4 系列(通用型,默认)",
"x4": "X4 系列(标准型)",
"x8": "X8 系列(标准型X8)",
"g8": "G8 系列(通用型G8)",
}.get(spec_type, f"{spec_type.upper()} 系列")
print(f" {type_name}:")
for spec in specs:
print(f" - {spec['description']}: {spec['name']}")
print()
print(" 提示:也可以使用简写格式,如 '4c16g'、'8c32g'")
print(" 注意:不支持跨系列降级!")
print()
parser = argparse.ArgumentParser(description="降级火山引擎 VEDBM 实例规格")
parser.add_argument(
"--region",
default=os.getenv("VEDBM_REGION", "cn-guangzhou"),
help="区域(默认:cn-guangzhou,或环境变量 VEDBM_REGION)",
)
parser.add_argument("--instance-id", required=True, help="实例 ID(必填)")
parser.add_argument(
"--target-spec",
help="目标规格(可选,不提供则自动降级一个等级,支持 '4c16g'、'8c32g' 或完整规格名)",
)
parser.add_argument(
"--access-key-id",
default=os.getenv("VOLCENGINE_ACCESS_KEY"),
help="访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)",
)
parser.add_argument(
"--secret-access-key",
default=os.getenv("VOLCENGINE_SECRET_KEY"),
help="秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)",
)
args = parser.parse_args()
# 检查必填参数
missing = []
if not args.instance_id:
missing.append("实例 ID(--instance-id)")
if not args.access_key_id:
missing.append("AccessKey ID(--access-key-id 或 VOLCENGINE_ACCESS_KEY)")
if not args.secret_access_key:
missing.append(
"Secret Access Key(--secret-access-key 或 VOLCENGINE_SECRET_KEY)"
)
if missing:
print("❌ 缺少必要参数!")
print("\n请提供以下信息:")
for item in missing:
print(f" - {item}")
print("\n方式 1:命令行参数")
print(
" python downgrade_instance_spec.py --instance-id xxx --target-spec 8c32g --access-key-id xxx --secret-access-key xxx"
)
print("\n方式 2:环境变量")
print(" export VOLCENGINE_ACCESS_KEY=xxx")
print(" export VOLCENGINE_SECRET_KEY=xxx")
print(" python downgrade_instance_spec.py --instance-id xxx")
print("\n方式 3:混合使用")
print(" export VOLCENGINE_ACCESS_KEY=xxx")
print(" export VOLCENGINE_SECRET_KEY=xxx")
print(
" python downgrade_instance_spec.py --instance-id xxx --target-spec 16c64g"
)
sys.exit(1)
result = downgrade_instance_spec(
region=args.region,
instance_id=args.instance_id,
target_spec=args.target_spec,
access_key_id=args.access_key_id,
secret_access_key=args.secret_access_key,
)
print_result(result)
sys.exit(0 if result["success"] else 1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" specific language governing permissions and
# limitations under the License.
"""
火山引擎 VEDBM 实例连接地址查看脚本
查看指定实例的连接地址信息
"""
import argparse
import os
import sys
from typing import Dict, Optional
try:
from dotenv import load_dotenv
HAS_DOTENV = True
except ImportError:
HAS_DOTENV = False
try:
from volcenginesdkcore import Configuration, ApiClient
from volcenginesdkvedbm import VEDBMApi, DescribeDBEndpointRequest
except ImportError as e:
print(f"错误:未安装火山引擎 Python SDK 或导入失败: {e}")
import traceback
traceback.print_exc()
print("请运行: pip install volcengine-python-sdk")
sys.exit(1)
def get_instance_endpoint(
region: str,
instance_id: str,
access_key_id: Optional[str] = None,
secret_access_key: Optional[str] = None,
) -> Dict:
"""
获取实例连接地址
Args:
region: 区域
instance_id: 实例 ID
access_key_id: 访问密钥 ID(可选,从环境变量读取)
secret_access_key: 秘密访问密钥(可选,从环境变量读取)
Returns:
连接地址结果字典
"""
# 配置 SDK
configuration = Configuration()
configuration.region = region
if access_key_id:
configuration.ak = access_key_id
if secret_access_key:
configuration.sk = secret_access_key
# 创建 API 客户端
api_client = ApiClient(configuration)
api_instance = VEDBMApi(api_client)
try:
print(f"⏳ 正在查询实例 {instance_id} 的连接地址...")
# 调用 API 获取连接地址
request = DescribeDBEndpointRequest(instance_id=instance_id)
response = api_instance.describe_db_endpoint(request)
endpoints = []
if hasattr(response, "endpoints") and response.endpoints:
for endpoint in response.endpoints:
endpoint_info = {
"endpoint_id": getattr(endpoint, "endpoint_id", None),
"endpoint_type": getattr(endpoint, "endpoint_type", None),
"addresses": [],
}
if hasattr(endpoint, "addresses") and endpoint.addresses:
for address in endpoint.addresses:
address_info = {
"domain": getattr(address, "domain", None),
"port": getattr(address, "port", None),
"ip_address": getattr(address, "ip_address", None),
"net_type": getattr(address, "net_type", None),
}
endpoint_info["addresses"].append(address_info)
endpoints.append(endpoint_info)
return {
"success": True,
"instance_id": instance_id,
"region": region,
"endpoints": endpoints,
"total_count": len(endpoints),
}
except Exception as e:
return {"success": False, "error": str(e), "instance_id": instance_id}
def print_result(result: Dict):
"""打印查询结果"""
if result["success"]:
print("\n" + "=" * 80)
print("✅ 实例连接地址查询成功!")
print("=" * 80)
print(f"实例 ID: {result['instance_id']}")
print(f"区域: {result['region']}")
print(f"终端总数: {result['total_count']}")
if result["endpoints"]:
for i, endpoint in enumerate(result["endpoints"], 1):
print(f"\n📍 终端 {i}:")
print("-" * 80)
print(f" 终端 ID: {endpoint['endpoint_id'] or '-'}")
print(f" 终端类型: {endpoint['endpoint_type'] or '-'}")
if endpoint["addresses"]:
print("\n 📡 连接地址:")
for j, address in enumerate(endpoint["addresses"], 1):
print(f" 地址 {j}:")
print(f" 域名: {address['domain'] or '-'}")
print(f" 端口: {address['port'] or '-'}")
print(f" IP地址: {address['ip_address'] or '-'}")
print(f" 网络类型: {address['net_type'] or '-'}")
print("-" * 80)
else:
print("\nℹ️ 该实例暂无连接地址")
print("=" * 80 + "\n")
else:
print("\n" + "=" * 80)
print("❌ 实例连接地址查询失败!")
print("=" * 80)
print(f"错误信息: {result['error']}")
if result.get("instance_id"):
print(f"实例 ID: {result['instance_id']}")
print("=" * 80 + "\n")
def main():
# 尝试加载 .env 文件
if HAS_DOTENV:
# 尝试从多个位置加载 .env
env_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"),
".env",
]
for env_path in env_paths:
if os.path.exists(env_path):
load_dotenv(env_path)
print(f"✅ 已加载配置文件: {env_path}")
break
parser = argparse.ArgumentParser(description="查看火山引擎 VEDBM 实例连接地址")
parser.add_argument(
"--region",
default=os.getenv("VEDBM_REGION", "cn-guangzhou"),
help="区域(默认:cn-guangzhou,或环境变量 VEDBM_REGION)",
)
parser.add_argument("--instance-id", required=True, help="实例 ID(必填)")
parser.add_argument(
"--access-key-id",
default=os.getenv("VOLCENGINE_ACCESS_KEY"),
help="访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)",
)
parser.add_argument(
"--secret-access-key",
default=os.getenv("VOLCENGINE_SECRET_KEY"),
help="秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)",
)
args = parser.parse_args()
# 检查必填参数
missing = []
if not args.instance_id:
missing.append("实例 ID(--instance-id)")
if not args.access_key_id:
missing.append("AccessKey ID(--access-key-id 或 VOLCENGINE_ACCESS_KEY)")
if not args.secret_access_key:
missing.append(
"Secret Access Key(--secret-access-key 或 VOLCENGINE_SECRET_KEY)"
)
if missing:
print("❌ 缺少必要参数!")
print("\n请提供以下信息:")
for item in missing:
print(f" - {item}")
print("\n方式 1:命令行参数")
print(
" python get_instance_endpoint.py --instance-id xxx --access-key-id xxx --secret-access-key xxx"
)
print("\n方式 2:环境变量")
print(" export VOLCENGINE_ACCESS_KEY=xxx")
print(" export VOLCENGINE_SECRET_KEY=xxx")
print(" python get_instance_endpoint.py --instance-id xxx")
sys.exit(1)
result = get_instance_endpoint(
region=args.region,
instance_id=args.instance_id,
access_key_id=args.access_key_id,
secret_access_key=args.secret_access_key,
)
print_result(result)
sys.exit(0 if result["success"] else 1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" specific language governing permissions and
# limitations under the License.
"""
火山引擎 VEDBM 数据库账号列表查看脚本
查看指定实例中的数据库账号列表
"""
import argparse
import os
import sys
from typing import Dict, Optional
try:
from dotenv import load_dotenv
HAS_DOTENV = True
except ImportError:
HAS_DOTENV = False
try:
from volcenginesdkcore import Configuration, ApiClient
from volcenginesdkvedbm import VEDBMApi, DescribeDBAccountsRequest
except ImportError as e:
print(f"错误:未安装火山引擎 Python SDK 或导入失败: {e}")
import traceback
traceback.print_exc()
print("请运行: pip install volcengine-python-sdk")
sys.exit(1)
def list_db_accounts(
region: str,
instance_id: str,
access_key_id: Optional[str] = None,
secret_access_key: Optional[str] = None,
) -> Dict:
"""
获取数据库账号列表
Args:
region: 区域
instance_id: 实例 ID
access_key_id: 访问密钥 ID(可选,从环境变量读取)
secret_access_key: 秘密访问密钥(可选,从环境变量读取)
Returns:
账号列表结果字典
"""
# 配置 SDK
configuration = Configuration()
configuration.region = region
if access_key_id:
configuration.ak = access_key_id
if secret_access_key:
configuration.sk = secret_access_key
# 创建 API 客户端
api_client = ApiClient(configuration)
api_instance = VEDBMApi(api_client)
try:
print(f"⏳ 正在查询实例 {instance_id} 的数据库账号列表...")
# 调用 API 获取账号列表
request = DescribeDBAccountsRequest(instance_id=instance_id)
response = api_instance.describe_db_accounts(request)
accounts = []
if hasattr(response, "accounts") and response.accounts:
for account in response.accounts:
account_info = {
"account_name": getattr(account, "account_name", None),
"account_type": getattr(account, "account_type", None),
"account_status": getattr(account, "account_status", None),
}
accounts.append(account_info)
return {
"success": True,
"instance_id": instance_id,
"region": region,
"accounts": accounts,
"total_count": len(accounts),
}
except Exception as e:
return {"success": False, "error": str(e), "instance_id": instance_id}
def print_result(result: Dict):
"""打印查询结果"""
if result["success"]:
print("\n" + "=" * 70)
print("✅ 数据库账号列表查询成功!")
print("=" * 70)
print(f"实例 ID: {result['instance_id']}")
print(f"区域: {result['region']}")
print(f"账号总数: {result['total_count']}")
if result["accounts"]:
print("\n📋 账号列表:")
print("-" * 70)
print(f"{'账号名称':<20} {'账号类型':<15} {'账号状态':<15}")
print("-" * 70)
for account in result["accounts"]:
account_name = account["account_name"] or "-"
account_type = account["account_type"] or "-"
account_status = account["account_status"] or "-"
print(f"{account_name:<20} {account_type:<15} {account_status:<15}")
print("-" * 70)
else:
print("\nℹ️ 该实例中暂无数据库账号")
print("=" * 70 + "\n")
else:
print("\n" + "=" * 70)
print("❌ 数据库账号列表查询失败!")
print("=" * 70)
print(f"错误信息: {result['error']}")
if result.get("instance_id"):
print(f"实例 ID: {result['instance_id']}")
print("=" * 70 + "\n")
def main():
# 尝试加载 .env 文件
if HAS_DOTENV:
# 尝试从多个位置加载 .env
env_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"),
".env",
]
for env_path in env_paths:
if os.path.exists(env_path):
load_dotenv(env_path)
print(f"✅ 已加载配置文件: {env_path}")
break
parser = argparse.ArgumentParser(
description="查看火山引擎 VEDBM 实例数据库账号列表"
)
parser.add_argument(
"--region",
default=os.getenv("VEDBM_REGION", "cn-guangzhou"),
help="区域(默认:cn-guangzhou,或环境变量 VEDBM_REGION)",
)
parser.add_argument("--instance-id", required=True, help="实例 ID(必填)")
parser.add_argument(
"--access-key-id",
default=os.getenv("VOLCENGINE_ACCESS_KEY"),
help="访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)",
)
parser.add_argument(
"--secret-access-key",
default=os.getenv("VOLCENGINE_SECRET_KEY"),
help="秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)",
)
args = parser.parse_args()
# 检查必填参数
missing = []
if not args.instance_id:
missing.append("实例 ID(--instance-id)")
if not args.access_key_id:
missing.append("AccessKey ID(--access-key-id 或 VOLCENGINE_ACCESS_KEY)")
if not args.secret_access_key:
missing.append(
"Secret Access Key(--secret-access-key 或 VOLCENGINE_SECRET_KEY)"
)
if missing:
print("❌ 缺少必要参数!")
print("\n请提供以下信息:")
for item in missing:
print(f" - {item}")
print("\n方式 1:命令行参数")
print(
" python list_db_accounts.py --instance-id xxx --access-key-id xxx --secret-access-key xxx"
)
print("\n方式 2:环境变量")
print(" export VOLCENGINE_ACCESS_KEY=xxx")
print(" export VOLCENGINE_SECRET_KEY=xxx")
print(" python list_db_accounts.py --instance-id xxx")
sys.exit(1)
result = list_db_accounts(
region=args.region,
instance_id=args.instance_id,
access_key_id=args.access_key_id,
secret_access_key=args.secret_access_key,
)
print_result(result)
sys.exit(0 if result["success"] else 1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" specific language governing permissions and
# limitations under the License.
"""
火山引擎 VEDBM 实例列表查看脚本
查看指定地域下的所有 VEDBM 实例
"""
import argparse
import os
import sys
from typing import Dict, Optional
try:
from dotenv import load_dotenv
HAS_DOTENV = True
except ImportError:
HAS_DOTENV = False
try:
from volcenginesdkcore import Configuration, ApiClient
from volcenginesdkvedbm import VEDBMApi, DescribeDBInstancesRequest
except ImportError as e:
print(f"错误:未安装火山引擎 Python SDK 或导入失败: {e}")
import traceback
traceback.print_exc()
print("请运行: pip install volcengine-python-sdk")
sys.exit(1)
def list_instances(
region: str,
access_key_id: Optional[str] = None,
secret_access_key: Optional[str] = None,
) -> Dict:
"""
获取实例列表
Args:
region: 区域
access_key_id: 访问密钥 ID(可选,从环境变量读取)
secret_access_key: 秘密访问密钥(可选,从环境变量读取)
Returns:
实例列表结果字典
"""
# 配置 SDK
configuration = Configuration()
configuration.region = region
if access_key_id:
configuration.ak = access_key_id
if secret_access_key:
configuration.sk = secret_access_key
# 创建 API 客户端
api_client = ApiClient(configuration)
api_instance = VEDBMApi(api_client)
try:
print(f"⏳ 正在查询 {region} 区域下的 VEDBM 实例列表...")
# 调用 API 获取实例列表
request = DescribeDBInstancesRequest()
response = api_instance.describe_db_instances(request)
instances = []
if hasattr(response, "instances") and response.instances:
for instance in response.instances:
# 从 nodes 数组中获取节点规格和可用区
node_spec = None
zone_id = None
if hasattr(instance, "nodes") and instance.nodes:
for node in instance.nodes:
if hasattr(node, "node_spec") and node.node_spec:
node_spec = node.node_spec
if hasattr(node, "zone_id") and node.zone_id:
zone_id = node.zone_id
if node_spec and zone_id:
break
instance_info = {
"instance_id": getattr(instance, "instance_id", None),
"instance_name": getattr(instance, "instance_name", None),
"instance_status": getattr(instance, "instance_status", None),
"node_spec": node_spec,
"region": getattr(instance, "region", None),
"zone_id": zone_id,
"create_time": getattr(instance, "create_time", None),
}
instances.append(instance_info)
return {
"success": True,
"region": region,
"instances": instances,
"total_count": len(instances),
}
except Exception as e:
return {"success": False, "error": str(e), "region": region}
def print_result(result: Dict):
"""打印查询结果"""
if result["success"]:
print("\n" + "=" * 100)
print("✅ VEDBM 实例列表查询成功!")
print("=" * 100)
print(f"区域: {result['region']}")
print(f"实例总数: {result['total_count']}")
if result["instances"]:
print("\n📋 实例列表:")
print("-" * 100)
print(
f"{'实例 ID':<25} {'实例名称':<25} {'状态':<12} {'规格':<20} {'可用区':<15}"
)
print("-" * 100)
for instance in result["instances"]:
instance_id = instance["instance_id"] or "-"
instance_name = instance["instance_name"] or "-"
instance_status = instance["instance_status"] or "-"
node_spec = instance["node_spec"] or "-"
zone_id = instance["zone_id"] or "-"
print(
f"{instance_id:<25} {instance_name:<25} {instance_status:<12} {node_spec:<20} {zone_id:<15}"
)
print("-" * 100)
else:
print("\nℹ️ 该区域下暂无 VEDBM 实例")
print("=" * 100 + "\n")
else:
print("\n" + "=" * 100)
print("❌ VEDBM 实例列表查询失败!")
print("=" * 100)
print(f"错误信息: {result['error']}")
if result.get("region"):
print(f"区域: {result['region']}")
print("=" * 100 + "\n")
def main():
# 尝试加载 .env 文件
if HAS_DOTENV:
# 尝试从多个位置加载 .env
env_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"),
".env",
]
for env_path in env_paths:
if os.path.exists(env_path):
load_dotenv(env_path)
print(f"✅ 已加载配置文件: {env_path}")
break
parser = argparse.ArgumentParser(description="查看火山引擎 VEDBM 实例列表")
parser.add_argument(
"--region",
default=os.getenv("VEDBM_REGION", "cn-guangzhou"),
help="区域(默认:cn-guangzhou,或环境变量 VEDBM_REGION)",
)
parser.add_argument(
"--access-key-id",
default=os.getenv("VOLCENGINE_ACCESS_KEY"),
help="访问密钥 ID(或环境变量 VOLCENGINE_ACCESS_KEY)",
)
parser.add_argument(
"--secret-access-key",
default=os.getenv("VOLCENGINE_SECRET_KEY"),
help="秘密访问密钥(或环境变量 VOLCENGINE_SECRET_KEY)",
)
args = parser.parse_args()
# 检查必填参数
missing = []
if not args.access_key_id:
missing.append("AccessKey ID(--access-key-id 或 VOLCENGINE_ACCESS_KEY)")
if not args.secret_access_key:
missing.append(
"Secret Access Key(--secret-access-key 或 VOLCENGINE_SECRET_KEY)"
)
if missing:
print("❌ 缺少必要参数!")
print("\n请提供以下信息:")
for item in missing:
print(f" - {item}")
print("\n方式 1:命令行参数")
print(" python list_instances.py --access-key-id xxx --secret-access-key xxx")
print("\n方式 2:环境变量")
print(" export VOLCENGINE_ACCESS_KEY=xxx")
print(" export VOLCENGINE_SECRET_KEY=xxx")
print(" python list_instances.py")
print("\n方式 3:混合使用")
print(" export VOLCENGINE_ACCESS_KEY=xxx")
print(" export VOLCENGINE_SECRET_KEY=xxx")
print(" python list_instances.py --region cn-beijing")
sys.exit(1)
result = list_instances(
region=args.region,
access_key_id=args.access_key_id,
secret_access_key=args.secret_access_key,
)
print_result(result)
sys.exit(0 if result["success"] else 1)
if __name__ == "__main__":
main()
volcengine-python-sdk>=0.0.1
#!/usr/bin/env python3
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" specific language governing permissions and
# limitations under the License.
"""
测试规格匹配功能
"""
import sys
import os
# 添加脚本目录到路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 导入我们的函数
from create_vedbm_instance import (
AVAILABLE_SPECS,
DEFAULT_SPEC,
parse_spec_input,
find_closest_spec,
)
print("=" * 70)
print("🧪 测试规格匹配功能")
print("=" * 70)
print()
# 显示可用规格
print("📋 可用规格:")
for spec in AVAILABLE_SPECS:
print(f" - {spec['description']}: {spec['name']}")
print()
# 测试用例
test_cases = [
# (输入, 期望描述)
("", "默认规格"),
("2c8g", "精确匹配 2核8GB"),
("4c16g", "精确匹配 4核16GB"),
("8c32g", "精确匹配 8核32GB"),
("16c64g", "精确匹配 16核64GB"),
("vedb.mysql.x2.large", "完整规格名 2核8GB"),
("vedb.mysql.x4.large", "完整规格名 4核16GB"),
("3c10g", "智能匹配(接近 4核16GB)"),
("5c20g", "智能匹配(接近 8核32GB)"),
("1c4g", "智能匹配(最小规格 2核8GB)"),
("20c100g", "智能匹配(最大规格 16核64GB)"),
("2c", "只指定 CPU 2核"),
("4c", "只指定 CPU 4核"),
("8g", "只指定内存 8GB"),
("16g", "只指定内存 16GB"),
("invalid-spec", "无效规格,使用默认"),
]
print("🧪 开始测试:")
print("-" * 70)
all_passed = True
for spec_input, description in test_cases:
print(f"\n测试输入: '{spec_input}' ({description})")
result = parse_spec_input(spec_input) if spec_input else DEFAULT_SPEC
if result:
print(f" ✅ 匹配结果: {result['description']} ({result['name']})")
else:
result = find_closest_spec()
print(f" ⚠️ 无法解析,使用默认: {result['description']} ({result['name']})")
print("\n" + "=" * 70)
print("✅ 测试完成!")
print("=" * 70)
Related skills
FAQ
What credentials does it need?
It needs Volcengine VOLCENGINE_ACCESS_KEY and VOLCENGINE_SECRET_KEY, plus a VPC ID, subnet ID and zone ID.
What is the default instance spec?
The default spec is 2C8G (vedb.mysql.x2.large), and shorthand like 4c16g auto-matches an available spec.