一、开篇:为什么我们需要用API来管理存储?
想象一下,你管理着一整个机房的超聚变服务器。每次需要给服务器配置存储,比如创建一个RAID阵列来保护数据,或者增加几块硬盘扩展存储空间,你都得跑到机房,或者通过远程控制台,点开那个图形化的管理界面,点点这个,选选那个。
如果只有一两台服务器,这倒没什么。但如果要管理几十、上百台,而且需要在凌晨业务低峰期批量操作,这种手动方式就变得非常低效且容易出错了。这时候,API的价值就体现出来了。
Redfish,你可以把它理解为一套专门为服务器管理设计的“通用语言”。它基于我们熟悉的RESTful API和JSON格式,让我们能够用写代码的方式,去完成所有在图形界面上能做的操作。今天,我们就来聊聊如何通过Redfish API,对超聚变服务器的存储部分进行“编程式”管理,实现RAID阵列的创建和磁盘组的管理。全程使用Python,因为它的语法清晰,非常适合演示。
二、准备工作:认识你的服务器和工具
在开始写代码之前,我们需要做一些准备工作。
首先,你得知道你的超聚变服务器的BMC(基板管理控制器)的IP地址、用户名和密码。这些信息通常由你的系统管理员提供。BMC就是服务器上那个负责带外管理的小心脏,Redfish API就运行在它上面。
其次,我们需要一个能发送HTTP请求的工具。在Python里,requests库是我们的不二之选。它简单又好用。
# 技术栈:Python + requests
# 安装命令:pip install requests
import requests
import json
import time
from requests.auth import HTTPBasicAuth
# 1. 定义连接信息
# 请将以下信息替换为你自己服务器的实际信息
BMC_IP = “192.168.1.100” # 你的服务器BMC IP
USERNAME = “admin” # BMC用户名
PASSWORD = “your_strong_password” # BMC密码
BASE_URL = f“https://{BMC_IP}” # Redfish API的根地址
# 2. 创建一个会话,并禁用SSL证书警告(仅用于测试环境,生产环境请使用有效证书)
# 注意:在生产环境中,务必验证证书以确保安全!
session = requests.Session()
session.auth = HTTPBasicAuth(USERNAME, PASSWORD)
session.verify = False # 跳过SSL验证,仅用于演示
requests.packages.urllib3.disable_warnings() # 关闭警告
# 3. 获取Redfish服务的根目录,验证连接
try:
response = session.get(f“{BASE_URL}/redfish/v1/”)
response.raise_for_status() # 如果状态码不是200,会抛出异常
service_root = response.json()
print(“连接成功!Redfish服务版本:”, service_root.get(‘RedfishVersion’))
except requests.exceptions.RequestException as e:
print(f“连接失败:{e}”)
exit(1)
这段代码是我们的“敲门砖”。它建立了与服务器BMC的通信,并确认Redfish服务是可用的。记住,生产环境一定要处理SSL证书,这里跳过只是为了演示方便。
三、核心操作:探索存储系统与磁盘
在创建RAID之前,我们得先知道服务器里有什么“存货”。Redfish通过一个清晰的资源模型来展示硬件信息。
# 技术栈:Python + requests
# 续接上面的代码
# 4. 找到存储系统的链接
# 通常,存储信息位于 `Systems` 和 `Storage` 集合下
systems_url = f“{BASE_URL}/redfish/v1/Systems”
systems_response = session.get(systems_url)
systems_data = systems_response.json()
# 假设我们取第一个系统(对于单机服务器通常只有一个)
system_member_url = systems_data[‘Members’][0][‘@odata.id’]
system_response = session.get(f“{BASE_URL}{system_member_url}”)
system_data = system_response.json()
# 获取该系统的存储集合链接
storage_url = system_data.get(‘Storage’, {}).get(‘@odata.id’)
if not storage_url:
print(“未找到存储系统信息。”)
exit(1)
# 5. 列出所有存储控制器和磁盘
storage_response = session.get(f“{BASE_URL}{storage_url}”)
storage_data = storage_response.json()
print(“\n=== 发现存储控制器 ===")
for controller_member in storage_data.get(‘Members’, []):
controller_url = controller_member[‘@odata.id’]
controller_response = session.get(f“{BASE_URL}{controller_url}”)
controller_data = controller_response.json()
controller_id = controller_url.split(‘/’)[-1]
print(f“控制器ID: {controller_id}”)
print(f“ 名称: {controller_data.get(‘Name’)}”)
print(f“ 型号: {controller_data.get(‘Model’)}”)
# 获取该控制器下的磁盘驱动器(物理硬盘)信息
drives_url = controller_data.get(‘Drives’)
if drives_url:
drives_response = session.get(f“{BASE_URL}{drives_url[‘@odata.id’]}")
drives_data = drives_response.json()
print(f“ 连接的物理磁盘:”)
for drive_member in drives_data.get(‘Members’, []):
drive_url = drive_member[‘@odata.id’]
drive_response = session.get(f“{BASE_URL}{drive_url}”)
drive_info = drive_response.json()
drive_name = drive_info.get(‘Name’, ‘N/A’)
drive_capacity = drive_info.get(‘CapacityBytes’, 0)
# 将字节转换为GB
capacity_gb = round(drive_capacity / (1024**3), 2) if drive_capacity else 0
print(f“ - {drive_name}: {capacity_gb} GB”)
print(“-” * 30)
运行这段代码,你就能在控制台看到服务器里所有RAID卡(存储控制器)和每块硬盘的详细信息,比如容量、型号。这是我们后续操作的基础。
四、实战演练:创建RAID阵列(磁盘组)
现在进入正题。假设我们手头有几块空闲硬盘,想用它们创建一个RAID5阵列,用来提供数据冗余保护。
在Redfish中,创建RAID阵列(在超聚变或其他支持Redfish的服务器中,常被称为Volume或逻辑磁盘)通常需要向存储控制器的Volumes集合发送一个POST请求。请求体里要详细说明我们的配置意图。
# 技术栈:Python + requests
# 续接上面的代码,假设我们已经找到了目标控制器,其URL存储在 `target_controller_url` 变量中
# 6. 创建RAID5阵列(逻辑卷)
# 假设我们选择控制器 ‘1’,并且有3块空闲磁盘(ID为 32:0, 32:1, 32:2)用于创建RAID5
target_controller_url = f“{BASE_URL}/redfish/v1/Systems/1/Storage/RAID.Integrated.1-1” # 示例URL,需根据实际情况修改
# 首先,确认控制器的操作链接
controller_response = session.get(target_controller_url)
controller_data = controller_response.json()
volumes_collection_url = controller_data.get(‘Volumes’, {}).get(‘@odata.id’)
if not volumes_collection_url:
print(“该控制器不支持创建逻辑卷或URL未找到。”)
exit(1)
# 准备创建逻辑卷的请求体
create_volume_payload = {
“@odata.type”: “#Volume.v1_6_0.Volume”, # 根据Redfish Schema版本调整
“Name”: “My_Data_RAID5”, # 给你的阵列起个名字
“RAIDType”: “RAID5”, # 阵列类型,可选 RAID0, RAID1, RAID5, RAID6, RAID10等
“CapacityBytes”: 0, # 设置为0表示使用所有选定磁盘的全部可用容量
“OptimumIOSizeBytes”: 65536, # 最佳IO大小,通常64KB是个好选择
“Drives”: [ # 指定用于创建阵列的物理磁盘列表
{
“@odata.id”: f“{target_controller_url}/Drives/Disk.32:0” # 磁盘的完整Redfish ID
},
{
“@odata.id”: f“{target_controller_url}/Drives/Disk.32:1”
},
{
“@odata.id”: f“{target_controller_url}/Drives/Disk.32:2”
}
]
}
print(f“\n正在向 {volumes_collection_url} 发送创建请求...”)
try:
# 发送POST请求创建逻辑卷
create_response = session.post(
f“{BASE_URL}{volumes_collection_url}”,
json=create_volume_payload,
headers={‘Content-Type’: ‘application/json’}
)
if create_response.status_code in [200, 201, 202, 204]:
# 202 Accepted表示任务已接受,通常意味着后台开始初始化
print(“创建请求已接受!服务器正在后台初始化RAID阵列。”)
# 如果返回了任务(Task)链接,可以跟踪进度
task_location = create_response.headers.get(‘Location’)
if task_location:
print(f“可以访问此链接跟踪任务进度:{task_location}”)
# 这里可以编写轮询任务状态的代码,直到完成
else:
print(f“创建失败!状态码:{create_response.status_code}”)
print(“响应内容:”, create_response.text)
except Exception as e:
print(f“请求过程中发生错误:{e}”)
这段代码的核心是构造一个JSON请求。RAIDType指定了阵列级别,Drives数组列出了要用的具体硬盘。发送后,服务器通常会返回一个“任务”(Task)链接,你可以通过不断查询这个链接来了解初始化进度(比如“正在初始化,完成15%”)。
五、管理磁盘组:查询与删除
创建了阵列,我们自然也需要能查看和清理它们。
# 技术栈:Python + requests
# 续接上面的代码
# 7. 查询已创建的逻辑卷(磁盘组)
print(“\n=== 查询现有逻辑卷 ===")
volumes_response = session.get(f“{BASE_URL}{volumes_collection_url}”)
volumes_data = volumes_response.json()
for volume_member in volumes_data.get(‘Members’, []):
volume_url = volume_member[‘@odata.id’]
volume_response = session.get(f“{BASE_URL}{volume_url}”)
volume_info = volume_response.json()
vol_id = volume_url.split(‘/’)[-1]
print(f“逻辑卷ID: {vol_id}”)
print(f“ 名称: {volume_info.get(‘Name’)}”)
print(f“ RAID类型: {volume_info.get(‘RAIDType’)}”)
print(f“ 容量: {round(volume_info.get(‘CapacityBytes’,0) / (1024**3), 2)} GB”)
print(f“ 操作状态: {volume_info.get(‘Status’, {}).get(‘State’)}”)
print(“-” * 20)
# 8. 删除一个逻辑卷(谨慎操作!数据会丢失!)
# 假设我们要删除上面创建的名为 ‘My_Data_RAID5’ 的卷
volume_to_delete = None
for volume_member in volumes_data.get(‘Members’, []):
volume_url = volume_member[‘@odata.id’]
volume_response = session.get(f“{BASE_URL}{volume_url}”)
volume_info = volume_response.json()
if volume_info.get(‘Name’) == “My_Data_RAID5”:
volume_to_delete = volume_url
break
if volume_to_delete:
confirm = input(f“\n确认要删除逻辑卷 ‘My_Data_RAID5’ 吗?此操作不可逆!(输入 ‘yes’ 确认): “)
if confirm.lower() == ‘yes’:
delete_response = session.delete(f“{BASE_URL}{volume_to_delete}”)
if delete_response.status_code in [200, 202, 204]:
print(“删除请求已接受。”)
else:
print(f“删除失败。状态码:{delete_response.status_code}”)
else:
print(“操作已取消。”)
else:
print(“未找到要删除的逻辑卷。”)
删除操作非常简单,就是一个DELETE请求,但危险性极高,所以代码里加了确认环节。在实际的自动化脚本中,这个确认环节可能会被一个明确的配置参数所替代。
六、深入理解:技术细节与注意事项
应用场景:
- 大规模自动化部署:在数据中心,结合Ansible、Terraform等工具,实现新服务器上架后存储的自动配置。
- 运维自动化:定时巡检存储健康状态,自动替换故障硬盘并触发重建。
- 云平台与虚拟化集成:为私有云平台提供底层硬件存储资源的按需分配和管理能力。
- 灾难恢复演练:通过脚本快速重建测试环境的存储结构。
技术优缺点:
- 优点:
- 标准化:Redfish是DMTF主导的开放标准,避免了不同厂商私有接口的锁定。
- 自动化友好:基于HTTP/JSON,极易与任何编程语言和运维工具集成。
- 功能强大:覆盖了服务器管理的方方面面,存储配置只是其中之一。
- 带外管理:不依赖主机操作系统,即使系统宕机也能管理硬件。
- 缺点:
- 学习曲线:需要理解Redfish的资源模型和Schema,初期有一定学习成本。
- 厂商实现差异:虽然标准统一,但不同厂商、不同型号的服务器在细节(如支持的RAID级别、属性名)上可能有细微差别,需要查阅具体产品的文档。
- 异步操作:像创建RAID这种耗时操作,返回的是任务链接,需要额外编写代码来轮询状态,增加了逻辑复杂度。
注意事项:
- 安全第一:务必在HTTPS下使用,并验证证书。管理账号使用强密码,并遵循最小权限原则。
- 充分测试:任何删除或重构存储的操作,务必在测试环境中充分验证后再在生产环境执行。误操作会导致数据丢失。
- 查阅官方文档:超聚变或其他服务器厂商都会提供其Redfish API的详细指南和Schema文件,这是最权威的参考资料,里面会列出所有可用的端点、参数和枚举值。
- 错误处理:生产代码必须包含完善的错误处理(网络超时、认证失败、API错误码解析等)和日志记录。
- 任务状态跟踪:对于长时间运行的操作,一定要实现任务状态的跟踪机制,避免脚本误以为操作已完成。
七、总结
通过这篇指南,我们走完了使用Redfish API管理超聚变服务器存储的核心流程:从建立连接、发现硬件,到创建RAID阵列,再到查询和删除。我们使用了Python和requests库,将图形界面上的点击操作,转化为了清晰、可重复、可集成的代码。
这种方式将我们从繁琐的重复性手动操作中解放出来,为构建现代化、自动化的数据中心运维体系打下了坚实的基础。虽然初期需要投入一些时间理解Redfish模型和编写脚本,但长远来看,其带来的效率提升和可靠性保障是巨大的。下次当你需要面对一堆服务器时,不妨试试用代码和它们“对话”。
评论