mirror of
https://github.com/lkddi/dell-fans-controller-docker.git
synced 2026-05-18 13:47:27 +08:00
完善开源发布配置和使用文档
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
.git
|
||||
.github
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
.idea
|
||||
.vscode
|
||||
__pycache__
|
||||
*.py[cod]
|
||||
*.pyo
|
||||
*.pyd
|
||||
.DS_Store
|
||||
README.md
|
||||
CHANGES.md
|
||||
@@ -0,0 +1,3 @@
|
||||
HOST=192.168.1.100
|
||||
USERNAME=root
|
||||
PASSWORD=your_idrac_password
|
||||
@@ -1,58 +1,58 @@
|
||||
name: Build and Push Docker Image
|
||||
name: Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
- name: Login to Docker Hub
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
|
||||
- name: Login to Harbor Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: harbor.ay.lc
|
||||
username: ${{ secrets.HARBOR_USER }}
|
||||
password: ${{ secrets.HARBOR_PASS }}
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: lkddi/dell-fans-controller
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
lkddi/dell-fans-controller-docker
|
||||
harbor.ay.lc/library/dell-fans-controller
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=sha,prefix={{branch}}-
|
||||
latest
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
.env
|
||||
.idea
|
||||
.vscode
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.pyo
|
||||
*.pyd
|
||||
.DS_Store
|
||||
|
||||
+14
-18
@@ -1,21 +1,17 @@
|
||||
# 修复日志
|
||||
# Changelog / 更新日志
|
||||
|
||||
## 问题1:温度读取不准确
|
||||
- **问题**:之前的代码无法正确解析IPMI传感器输出中的温度值
|
||||
- **解决方案**:
|
||||
- 修改了`sensor()`方法,使用`ipmitool sdr`命令获取更准确的传感器数据
|
||||
- 更新了`temperature()`方法,使用正则表达式正确提取温度值
|
||||
- **结果**:现在能够准确读取所有温度传感器数据
|
||||
## Unreleased
|
||||
|
||||
## 问题2:风扇转速读取不准确
|
||||
- **问题**:IPMI原始命令无法返回设置的风扇占空比值
|
||||
- **解决方案**:
|
||||
- 通过校准实验确定了RPM与百分比的转换关系:20%设置对应4800 RPM
|
||||
- 实现了基于RPM的百分比估算算法
|
||||
- 添加了适当的四舍五入逻辑以匹配典型的5%步进
|
||||
- **结果**:现在能够准确估算当前风扇转速百分比
|
||||
- 准备公开开源发布流程,Docker Hub 镜像统一为 `lkddi/dell-fans-controller`。
|
||||
- GitHub Actions 改为 PR/master 构建验证,`v*` tag 才发布 Docker 镜像。
|
||||
- 新增 Docker、Docker Compose 和本机 Python 三种运行说明。
|
||||
- 移除代码中的默认 iDRAC 地址、账号和密码,改为必须通过环境变量配置。
|
||||
- 增加 `.env.example`、`.dockerignore` 和 MIT License。
|
||||
- 清理 Python 缓存文件,避免将运行产物提交到仓库。
|
||||
|
||||
## 技术细节
|
||||
- Dell服务器的IPMI系统在手动风扇模式下,可通过`ipmitool sdr`命令获取准确的RPM值
|
||||
- 风扇转速百分比通过公式计算:`(current_rpm / theoretical_max_rpm) * 100`
|
||||
- 理论最大RPM基于校准数据:`4800 RPM * (100/20) = 24000 RPM`
|
||||
## Previous Improvements
|
||||
|
||||
- 使用 `ipmitool sdr` 读取温度和风扇传感器数据。
|
||||
- 根据最高温度自动选择风扇转速,高温时交还 iDRAC 自动模式。
|
||||
- 对 IPMI 会话失败和命令超时增加重试和日志。
|
||||
- 使用 RPM 估算风扇百分比,降低部分 Dell 机型 raw 命令返回 0 时的影响。
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
FROM ubuntu:22.04
|
||||
LABEL maintainer="joestar817@foxmail.com"
|
||||
LABEL maintainer="lkddi"
|
||||
|
||||
# 安装依赖并设置时区
|
||||
RUN apt update && apt install -y \
|
||||
@@ -11,8 +11,8 @@ RUN apt update && apt install -y \
|
||||
&& echo 'Asia/Shanghai' > /etc/timezone
|
||||
|
||||
# 复制应用文件
|
||||
COPY . /dell-fans-controller-docker
|
||||
WORKDIR /dell-fans-controller-docker
|
||||
COPY . /dell-fans-controller
|
||||
WORKDIR /dell-fans-controller
|
||||
|
||||
# 如果有requirements.txt则安装Python依赖
|
||||
# COPY requirements.txt .
|
||||
@@ -23,4 +23,3 @@ WORKDIR /dell-fans-controller-docker
|
||||
|
||||
# 设置启动命令
|
||||
CMD ["python3", "start.py"]
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 lkddi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -5,5 +5,4 @@ help: ## show help message
|
||||
|
||||
|
||||
build: ## build docker image
|
||||
docker build -t dell-fans-controller-docker .
|
||||
|
||||
docker build -t lkddi/dell-fans-controller:test .
|
||||
|
||||
@@ -1,153 +1,176 @@
|
||||
# Dell风扇控制器
|
||||
# Dell Fans Controller / Dell 风扇控制器
|
||||
|
||||
## 项目简介
|
||||
Dockerized fan controller for Dell iDRAC/IPMI servers. It reads temperature sensors through `ipmitool` and adjusts fan speed automatically to reduce noise while keeping the server safe.
|
||||
|
||||
Dell风扇控制器是一个自动化工具,通过IPMI接口监控Dell服务器的温度并自动调节风扇转速。本项目基于原项目 joestar817/dell-fans-controller-docker 进行了大量改进和功能增强。
|
||||
通过 iDRAC/IPMI 自动读取 Dell 服务器温度,并根据温度调整风扇转速。适合希望降低噪音、但仍保留高温自动保护的家庭实验室、机房和 NAS 场景。
|
||||
|
||||
### 主要特性
|
||||
> Warning: manual fan control can cause overheating if configured incorrectly. Test with care and keep iDRAC automatic mode available as fallback.
|
||||
>
|
||||
> 警告:手动风扇控制配置不当可能导致过热。请先确认服务器散热状态,并保留 iDRAC 自动模式作为兜底。
|
||||
|
||||
- **精准温度监控**:通过IPMI接口获取服务器进出口、CPU等关键温度数据
|
||||
- **智能转速控制**:根据温度自动调节风扇转速,平衡散热效果和噪音
|
||||
- **网络容错能力**:具备强大的网络连接容错机制,能处理网络波动
|
||||
- **多架构支持**:支持 AMD64 和 ARM64 架构,兼容多种平台
|
||||
- **自动构建部署**:通过 GitHub Actions 自动构建和推送 Docker 镜像
|
||||
## Features / 功能
|
||||
|
||||
## 使用方法
|
||||
- Reads Dell iDRAC sensor data with IPMI LANPlus.
|
||||
- Automatically switches between manual fan speed and iDRAC automatic mode.
|
||||
- Reuses sensor data within one control cycle to reduce iDRAC session pressure.
|
||||
- Retries transient IPMI session failures and backs off after controller errors.
|
||||
- Supports Docker multi-arch images: `linux/amd64` and `linux/arm64`.
|
||||
|
||||
### 1. 准备工作
|
||||
- 通过 IPMI LANPlus 读取 Dell iDRAC 传感器数据。
|
||||
- 根据最高温度自动设置风扇转速,超过阈值时交还 iDRAC 自动控制。
|
||||
- 单次控制周期复用传感器数据,减少 iDRAC 会话压力。
|
||||
- 对临时 IPMI 会话失败进行重试,并在控制器异常后等待下一轮。
|
||||
- Docker 镜像支持 `linux/amd64` 和 `linux/arm64`。
|
||||
|
||||
在开始使用之前,请确保:
|
||||
## Quick Start / 快速开始
|
||||
|
||||
1. 登录iDRAC管理界面并启用IPMI服务
|
||||
2. 确保网络能够访问iDRAC管理接口
|
||||
3. 准备好iDRAC的用户名和密码
|
||||
Before running, enable IPMI over LAN in iDRAC and make sure the host running this container can reach the iDRAC management IP.
|
||||
|
||||
### 2. Docker运行
|
||||
运行前请先在 iDRAC 中启用 IPMI over LAN,并确认运行容器的主机可以访问 iDRAC 管理 IP。
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
# 基本运行命令
|
||||
docker run -d --name=dell-fans-controller \
|
||||
-e HOST=YOUR_IDRAC_IP \
|
||||
-e USERNAME=YOUR_USERNAME \
|
||||
-e PASSWORD=YOUR_PASSWORD \
|
||||
docker run -d --name dell-fans-controller \
|
||||
--restart always \
|
||||
lkddi/dell-fans-controller:latest
|
||||
```
|
||||
|
||||
### 3. 配置参数
|
||||
|
||||
| 环境变量 | 说明 | 默认值 |
|
||||
|---------|------|--------|
|
||||
| HOST | iDRAC管理接口IP地址 | 192.168.1.100 |
|
||||
| USERNAME | iDRAC用户名 | root |
|
||||
| PASSWORD | iDRAC密码 | your_idrac_password |
|
||||
|
||||
## 工作原理
|
||||
|
||||
### 温度控制策略
|
||||
|
||||
系统会监控服务器的多个温度传感器,取最高温度值作为控制依据:
|
||||
|
||||
| 温度范围(℃) | 风扇转速(%) | 说明 |
|
||||
|------------|------------|------|
|
||||
| 0-50 | 15 | 静音模式 |
|
||||
| 50-55 | 20 | 低速运行 |
|
||||
| 55-60 | 30 | 中速运行 |
|
||||
| 60-65 | 40 | 高速运行 |
|
||||
| >65 | 自动模式 | 由iDRAC自动调节 |
|
||||
|
||||
### 智能控制机制
|
||||
|
||||
- **模式切换**:系统在手动模式和自动模式间智能切换
|
||||
- **转速监测**:实时监测当前风扇转速,避免不必要的调整
|
||||
- **状态跟踪**:记录和跟踪风扇模式和转速设置历史
|
||||
|
||||
## Docker镜像
|
||||
|
||||
### 支持的架构
|
||||
|
||||
- AMD64 (x86_64)
|
||||
- ARM64 (aarch64)
|
||||
|
||||
### 镜像仓库
|
||||
|
||||
- Docker Hub: `lkddi/dell-fans-controller:latest`
|
||||
|
||||
### 自动构建
|
||||
|
||||
项目通过 GitHub Actions 实现了自动构建和部署:
|
||||
|
||||
- 每次推送代码到master分支时自动构建新镜像
|
||||
- 支持多架构镜像构建
|
||||
- 自动推送至Docker Hub和Harbor仓库
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 核心功能
|
||||
|
||||
1. **精确的温度读取**:使用正则表达式解析IPMI传感器数据,准确提取温度值
|
||||
2. **RPM到百分比转换**:通过校准数据建立RPM与风扇转速百分比的准确转换关系(20% = 4800 RPM)
|
||||
3. **网络容错机制**:
|
||||
- 5次重试机制
|
||||
- 60秒超时设置
|
||||
- IPMI会话建立失败的特殊处理
|
||||
- 10秒重试间隔
|
||||
|
||||
### 配置选项
|
||||
|
||||
- 运行间隔:每60秒检查一次温度并调整风扇转速
|
||||
- 网络超时:60秒超时限制
|
||||
- 重试机制:5次失败后停止
|
||||
|
||||
## 部署示例
|
||||
|
||||
### Proxmox VE (PVE)
|
||||
|
||||
```bash
|
||||
docker run -d --name=dell-fans-controller \
|
||||
-e HOST=192.168.1.100 \
|
||||
-e USERNAME=root \
|
||||
-e PASSWORD=calvin \
|
||||
--restart always \
|
||||
-e PASSWORD=your_idrac_password \
|
||||
lkddi/dell-fans-controller:latest
|
||||
```
|
||||
|
||||
### 群晖NAS
|
||||
|
||||
在群晖的Docker应用中:
|
||||
|
||||
1. 搜索并下载 `lkddi/dell-fans-controller` 镜像
|
||||
2. 创建容器并设置环境变量
|
||||
3. 启用自动重启选项
|
||||
|
||||
## 安全说明
|
||||
|
||||
- 项目使用IPMI协议与服务器通信,需要相应的管理权限
|
||||
- 建议使用专用的IPMI管理账户
|
||||
- 定期更新镜像以获得安全补丁
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **连接失败**:检查网络是否能访问iDRAC接口
|
||||
2. **认证失败**:确认用户名和密码正确
|
||||
3. **权限不足**:确保账户有IPMI控制权限
|
||||
|
||||
### 日志查看
|
||||
View logs / 查看日志:
|
||||
|
||||
```bash
|
||||
docker logs dell-fans-controller
|
||||
docker logs -f dell-fans-controller
|
||||
```
|
||||
|
||||
## 免责声明
|
||||
### Docker Compose
|
||||
|
||||
手动调整服务器风扇转速可能带来过热风险,使用本项目前请充分了解风险并做好数据备份。对于使用本项目引发的任何问题,作者概不负责。
|
||||
Copy the example environment file and edit the iDRAC settings:
|
||||
|
||||
## 贡献
|
||||
复制示例环境变量文件,并修改 iDRAC 配置:
|
||||
|
||||
欢迎提交 Issue 和 Pull Request 来改进项目。
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
## 许可证
|
||||
`.env` example / 示例:
|
||||
|
||||
该项目遵循原项目的开源协议。
|
||||
```env
|
||||
HOST=192.168.1.100
|
||||
USERNAME=root
|
||||
PASSWORD=your_idrac_password
|
||||
```
|
||||
|
||||
Start the service / 启动服务:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Run with Python / 直接使用 Python 运行
|
||||
|
||||
Install dependencies / 安装依赖:
|
||||
|
||||
```bash
|
||||
# Debian / Ubuntu
|
||||
sudo apt update
|
||||
sudo apt install -y ipmitool python3
|
||||
|
||||
# macOS with Homebrew
|
||||
brew install ipmitool python
|
||||
```
|
||||
|
||||
Run / 运行:
|
||||
|
||||
```bash
|
||||
export HOST=192.168.1.100
|
||||
export USERNAME=root
|
||||
export PASSWORD=your_idrac_password
|
||||
python3 start.py
|
||||
```
|
||||
|
||||
## Configuration / 配置
|
||||
|
||||
| Variable | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `HOST` | Yes | iDRAC management IP address. / iDRAC 管理 IP。 |
|
||||
| `USERNAME` | Yes | iDRAC username with IPMI permission. / 有 IPMI 权限的 iDRAC 用户名。 |
|
||||
| `PASSWORD` | Yes | iDRAC password. / iDRAC 密码。 |
|
||||
|
||||
The application does not include default credentials. Missing variables will stop startup with a clear error.
|
||||
|
||||
程序不内置默认地址、账号或密码。缺少环境变量时会直接停止并输出明确错误。
|
||||
|
||||
## Temperature Policy / 温控策略
|
||||
|
||||
The controller reads all temperature sensors and uses the highest value.
|
||||
|
||||
控制器读取所有温度传感器,并使用最高温度作为控制依据。
|
||||
|
||||
| Temperature / 温度 | Fan Speed / 风扇转速 |
|
||||
| --- | --- |
|
||||
| `0-50 C` | `20%` |
|
||||
| `50-55 C` | `25%` |
|
||||
| `55-60 C` | `30%` |
|
||||
| `60-65 C` | `40%` |
|
||||
| `>65 C` | iDRAC automatic mode / iDRAC 自动模式 |
|
||||
|
||||
## Troubleshooting / 故障排查
|
||||
|
||||
Test IPMI manually first / 先手动测试 IPMI:
|
||||
|
||||
```bash
|
||||
ipmitool -H 192.168.1.100 -I lanplus -U root -P your_idrac_password mc info
|
||||
ipmitool -H 192.168.1.100 -I lanplus -U root -P your_idrac_password sdr
|
||||
```
|
||||
|
||||
Common issues / 常见问题:
|
||||
|
||||
- `Unable to establish IPMI v2 / RMCP+ session`: iDRAC IPMI service may be busy or unstable. Check network latency, duplicate monitoring scripts, and consider resetting iDRAC with `mc reset cold`.
|
||||
- Connection failed / 连接失败:确认容器主机能访问 iDRAC 管理 IP。
|
||||
- Authentication failed / 认证失败:确认用户名、密码和 IPMI 权限。
|
||||
- Permission denied / 权限不足:建议使用专用 iDRAC 用户,并授予 IPMI 控制权限。
|
||||
|
||||
## Release / 发布新版本
|
||||
|
||||
Docker images are published only from Git tags matching `v*`. Pull requests and `master` pushes only build and verify the image.
|
||||
|
||||
Docker 镜像只在推送 `v*` tag 时发布。PR 和 `master` 推送只做构建验证,不覆盖 `latest`。
|
||||
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
The tag workflow publishes:
|
||||
|
||||
- `lkddi/dell-fans-controller:latest`
|
||||
- `lkddi/dell-fans-controller:v1.0.0`
|
||||
- `lkddi/dell-fans-controller:1.0.0`
|
||||
- `lkddi/dell-fans-controller:1.0`
|
||||
- `lkddi/dell-fans-controller:1`
|
||||
|
||||
GitHub Actions requires these repository secrets:
|
||||
|
||||
- `DOCKER_USER`: Docker Hub username.
|
||||
- `DOCKER_PASS`: Docker Hub access token. Do not use your account password.
|
||||
|
||||
## Development / 开发
|
||||
|
||||
Syntax check without generating tracked cache files:
|
||||
|
||||
```bash
|
||||
python3 -c "import ast, pathlib; [ast.parse(pathlib.Path(p).read_text(), filename=p) for p in ['start.py','controller/client.py','controller/ipmi.py']]"
|
||||
```
|
||||
|
||||
Build locally:
|
||||
|
||||
```bash
|
||||
docker build -t lkddi/dell-fans-controller:test .
|
||||
```
|
||||
|
||||
## License / 许可证
|
||||
|
||||
MIT
|
||||
|
||||
@@ -3,8 +3,10 @@ from controller.logger import logger
|
||||
from controller.ipmi import IpmiTool
|
||||
|
||||
|
||||
# 风扇控制器:根据iDRAC温度传感器结果自动切换风扇模式和转速
|
||||
class FanController:
|
||||
|
||||
# 初始化控制器并记录iDRAC连接信息
|
||||
def __init__(self, host: str, username: str, password: str):
|
||||
self.host = host
|
||||
self.username = username
|
||||
@@ -14,10 +16,12 @@ class FanController:
|
||||
self.last_set_speed = None # 记录最后设置的风扇速度
|
||||
self.is_auto_mode = False # 记录当前是否为自动模式
|
||||
|
||||
# 设置手动风扇速度
|
||||
def set_fan_speed(self, speed: int):
|
||||
logger.info(f'设置风扇速度: {speed}%')
|
||||
self.ipmi.set_fan_speed(speed)
|
||||
|
||||
# 根据最高温度计算目标风扇转速
|
||||
def get_required_fan_speed(self, temperature: int) -> int:
|
||||
"""
|
||||
根据温度确定所需的风扇转速
|
||||
@@ -35,7 +39,9 @@ class FanController:
|
||||
else:
|
||||
return -1 # 表示应切换到自动模式
|
||||
|
||||
# 执行一次完整的温度读取和风扇控制周期
|
||||
def run(self):
|
||||
# 同一轮控制周期复用一次SDR结果,减少iDRAC会话压力
|
||||
sensor_data = self.ipmi.sensor()
|
||||
temperature: int = max(self.ipmi.temperature(sensor_data))
|
||||
logger.info(f'当前最高温度: {temperature}')
|
||||
|
||||
+18
-11
@@ -3,7 +3,10 @@ import time
|
||||
import re
|
||||
from controller.logger import logger
|
||||
|
||||
|
||||
# IPMI命令封装器:负责调用ipmitool读取传感器并设置Dell风扇
|
||||
class IpmiTool:
|
||||
# 初始化iDRAC连接参数
|
||||
def __init__(self, host: str, username: str, password: str):
|
||||
if not host or not username or not password:
|
||||
raise ValueError("host, username and password must be provided")
|
||||
@@ -11,6 +14,7 @@ class IpmiTool:
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
# 执行ipmitool命令并处理重试、超时和会话异常
|
||||
def run_cmd(self, cmd: str) -> str:
|
||||
basecmd = f'ipmitool -H {self.host} -I lanplus -U {self.username} -P {self.password}'
|
||||
command = f'{basecmd} {cmd}'
|
||||
@@ -47,20 +51,23 @@ class IpmiTool:
|
||||
else:
|
||||
raise e
|
||||
|
||||
# 读取iDRAC控制器基本信息
|
||||
def mc_info(self) -> str:
|
||||
"""
|
||||
execute ipmitool command mc info
|
||||
执行ipmitool mc info命令
|
||||
:return:
|
||||
"""
|
||||
return self.run_cmd(cmd='mc info')
|
||||
|
||||
# 读取完整SDR传感器数据
|
||||
def sensor(self) -> str:
|
||||
"""
|
||||
execute ipmitool command sdr to get sensor data
|
||||
执行ipmitool sdr命令获取传感器数据
|
||||
:return:
|
||||
"""
|
||||
return self.run_cmd(cmd='sdr')
|
||||
|
||||
# 从SDR数据中解析温度传感器列表
|
||||
def temperature(self, data: str = None) -> list:
|
||||
"""
|
||||
获取当前温度传感器列表
|
||||
@@ -84,32 +91,30 @@ class IpmiTool:
|
||||
|
||||
return temperatures
|
||||
|
||||
# 从SDR数据中解析风扇RPM列表
|
||||
def fan_speeds(self) -> list:
|
||||
"""
|
||||
get current fan speeds
|
||||
:return: list of fan speeds in percentage
|
||||
获取当前风扇RPM列表
|
||||
:return: 风扇RPM列表
|
||||
"""
|
||||
data = self.sensor()
|
||||
fan_speeds = []
|
||||
|
||||
for line in data.splitlines():
|
||||
if 'Fan' in line and 'RPM' in line:
|
||||
# Extract numeric value from line - format is typically "Fan1 | 1234 | RPM |"
|
||||
# 从传感器行中提取RPM数值,典型格式为 "Fan1 RPM | 4800 RPM | ok"
|
||||
parts = line.split('|')
|
||||
if len(parts) >= 2:
|
||||
try:
|
||||
# Extract the value and convert RPM to percentage if possible
|
||||
# For Dell servers, we may need to get duty cycle instead
|
||||
value_str = parts[1].strip()
|
||||
if value_str.isdigit():
|
||||
rpm = int(value_str)
|
||||
# Placeholder: we might need to use raw commands to get duty cycle
|
||||
# For now, return the raw value
|
||||
fan_speeds.append(rpm)
|
||||
except ValueError:
|
||||
continue
|
||||
return fan_speeds
|
||||
|
||||
# 获取当前风扇占空比,raw命令不可用时用RPM估算
|
||||
def get_fan_duty_cycle(self, sensor_data: str = None) -> int:
|
||||
"""
|
||||
获取当前风扇占空比/百分比
|
||||
@@ -177,9 +182,10 @@ class IpmiTool:
|
||||
|
||||
return -1 # Return -1 if unable to determine
|
||||
|
||||
# 切换风扇自动/手动模式
|
||||
def switch_fan_mode(self, auto: bool):
|
||||
"""
|
||||
switch the fan mode
|
||||
切换风扇模式
|
||||
:param auto:
|
||||
:return:
|
||||
"""
|
||||
@@ -187,9 +193,10 @@ class IpmiTool:
|
||||
auto_cmd = 'raw 0x30 0x30 0x01 0x01'
|
||||
return self.run_cmd(cmd=auto_cmd) if auto else self.run_cmd(cmd=manual_cmd)
|
||||
|
||||
# 设置手动风扇速度百分比
|
||||
def set_fan_speed(self, speed: int):
|
||||
"""
|
||||
set fan speed
|
||||
设置风扇速度
|
||||
:param speed:
|
||||
:return:
|
||||
"""
|
||||
|
||||
@@ -5,7 +5,9 @@ logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
# 自定义日志格式器:统一输出北京时间,方便容器日志排查
|
||||
class CustomFormatter(logging.Formatter):
|
||||
# 格式化日志记录并注入北京时间字段
|
||||
def format(self, record):
|
||||
desired_timezone = timezone(timedelta(hours=8))
|
||||
current_time = datetime.now(desired_timezone).strftime('%Y-%m-%d %H:%M:%S')
|
||||
@@ -13,6 +15,7 @@ class CustomFormatter(logging.Formatter):
|
||||
return super().format(record)
|
||||
|
||||
|
||||
# 标准输出日志处理器:让Docker日志直接显示控制器运行状态
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setFormatter(CustomFormatter(' %(customtime)s [%(levelname)s] %(message)s'))
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
+4
-8
@@ -1,11 +1,7 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
dell-fans-controller-docker:
|
||||
dell-fans-controller:
|
||||
image: lkddi/dell-fans-controller:latest
|
||||
container_name: dell-fans-controller-docker
|
||||
container_name: dell-fans-controller
|
||||
restart: always
|
||||
environment:
|
||||
HOST: 192.168.1.1
|
||||
USERNAME: root
|
||||
PASSWORD: password
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
@@ -7,9 +7,10 @@ from controller.logger import logger
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
host = os.getenv('HOST', "192.168.1.100")
|
||||
username = os.getenv('USERNAME', "root")
|
||||
password = os.getenv('PASSWORD', "your_idrac_password")
|
||||
# 从环境变量读取iDRAC连接信息,开源版本不内置任何真实默认凭据
|
||||
host = os.getenv('HOST')
|
||||
username = os.getenv('USERNAME')
|
||||
password = os.getenv('PASSWORD')
|
||||
if not host:
|
||||
raise RuntimeError('未设置 HOST 环境变量')
|
||||
|
||||
@@ -19,14 +20,17 @@ if __name__ == '__main__':
|
||||
if not password:
|
||||
raise RuntimeError('未设置 PASSWORD 环境变量')
|
||||
|
||||
# 复用控制器实例,避免每轮循环丢失上次设置状态
|
||||
client = FanController(host=host, username=username, password=password)
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 执行一次温度读取和风扇控制周期
|
||||
client.run()
|
||||
time.sleep(60)
|
||||
except Exception as err:
|
||||
logger.error(
|
||||
f'运行控制器失败 {err}. {traceback.format_exc()}'
|
||||
)
|
||||
# iDRAC会话异常时等待下一轮,避免连续请求压垮IPMI服务
|
||||
time.sleep(60)
|
||||
|
||||
Reference in New Issue
Block a user