From edc7d970a876f7d029b25aa85354d66fae82ab48 Mon Sep 17 00:00:00 2001 From: lkddi Date: Wed, 6 May 2026 09:23:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=BC=80=E6=BA=90=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E9=85=8D=E7=BD=AE=E5=92=8C=E4=BD=BF=E7=94=A8=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 14 ++ .env.example | 3 + .github/workflows/docker-build.yml | 88 ++++----- .gitignore | 9 +- CHANGES.md | 32 ++-- Dockerfile | 7 +- LICENSE | 21 +++ Makefile | 3 +- README.md | 277 ++++++++++++++++------------- controller/client.py | 6 + controller/ipmi.py | 29 +-- controller/logger.py | 3 + docker-compose.yml | 12 +- start.py | 10 +- 14 files changed, 296 insertions(+), 218 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 LICENSE diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d983e12 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.git +.github +.env +.env.* +!.env.example +.idea +.vscode +__pycache__ +*.py[cod] +*.pyo +*.pyd +.DS_Store +README.md +CHANGES.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0806a22 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +HOST=192.168.1.100 +USERNAME=root +PASSWORD=your_idrac_password diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 3a03151..5f7f94e 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -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 \ No newline at end of file + - 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 diff --git a/.gitignore b/.gitignore index 723ef36..3cc4780 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ -.idea \ No newline at end of file +.env +.idea +.vscode +__pycache__/ +*.py[cod] +*.pyo +*.pyd +.DS_Store diff --git a/CHANGES.md b/CHANGES.md index bc9a0a7..7c1b7a5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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` \ No newline at end of file +## Previous Improvements + +- 使用 `ipmitool sdr` 读取温度和风扇传感器数据。 +- 根据最高温度自动选择风扇转速,高温时交还 iDRAC 自动模式。 +- 对 IPMI 会话失败和命令超时增加重试和日志。 +- 使用 RPM 估算风扇百分比,降低部分 Dell 机型 raw 命令返回 0 时的影响。 diff --git a/Dockerfile b/Dockerfile index ae099e2..a69c036 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] - diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8ea01dd --- /dev/null +++ b/LICENSE @@ -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. diff --git a/Makefile b/Makefile index 5d5e012..dad562e 100644 --- a/Makefile +++ b/Makefile @@ -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 . diff --git a/README.md b/README.md index 287bff8..64b279d 100644 --- a/README.md +++ b/README.md @@ -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 / 示例: -该项目遵循原项目的开源协议。 \ No newline at end of file +```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 diff --git a/controller/client.py b/controller/client.py index 6315a5e..b2f4def 100644 --- a/controller/client.py +++ b/controller/client.py @@ -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}') diff --git a/controller/ipmi.py b/controller/ipmi.py index 7572238..53e6ba9 100644 --- a/controller/ipmi.py +++ b/controller/ipmi.py @@ -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: """ diff --git a/controller/logger.py b/controller/logger.py index d7265d6..f39dda5 100644 --- a/controller/logger.py +++ b/controller/logger.py @@ -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) diff --git a/docker-compose.yml b/docker-compose.yml index 77af6d1..b6a5c7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 \ No newline at end of file + env_file: + - .env diff --git a/start.py b/start.py index 949bed8..ea87f69 100644 --- a/start.py +++ b/start.py @@ -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)