插件开发指南¶
DNSBuilder 支持插件系统,允许你通过自定义 DNS 服务器实现、行为、区域格式和资源来扩展其功能。
概述¶
插件可以注册以下组件来扩展 DNSBuilder:
| 组件 | 说明 |
|---|---|
| Image | DNS 软件的 Docker 镜像构建器 |
| Behavior | DNS 服务器的行为模式(master、forward、stub 等) |
| Includer | 配置文件 include 模式处理 |
| ZoneGenerator | 自定义区域文件格式生成(非BIND格式zonefile) |
| Resources | 模板、规则、默认值、控制文件、脚本 |
| Attributes | 扩展常量(通过 attributes 类属性) |
插件结构¶
典型的插件包结构:
dnsb_mydns/
├── pyproject.toml
├── src/
│ └── dnsb_mydns/
│ ├── __init__.py # 插件入口
│ ├── image.py # Image 实现
│ ├── behavior.py # Behavior 实现
│ ├── includer.py # Includer 实现
│ ├── zone.py # ZoneGenerator(可选)
│ └── resources/
│ ├── images/
│ │ ├── templates/
│ │ │ └── mydns # Dockerfile 模板
│ │ ├── rules/
│ │ │ └── mydns # 版本规则
│ │ └── defaults/
│ │ └── mydns # 默认依赖/工具
│ └── configs/
│ ├── mydns_master_base.conf
│ └── mydns_recursor_base.conf
创建插件¶
1. 插件类¶
创建继承自 Plugin 的类:
# src/dnsb_mydns/__init__.py
import logging
from typing import Dict, Any
from dnsbuilder.plugins import Plugin, PluginRegistry
from .zone import MyDNSZoneGenerator
from .behavior import MyDNSMasterBehavior
from .includer import MyDNSIncluder
from .image import MyDNSImage
logger = logging.getLogger(__name__)
__version__ = "0.0.1"
class MyDNSPlugin(Plugin):
"""MyDNS 插件"""
# 必需的元数据
name = "mydns"
version = __version__
description = "MyDNS 服务器支持"
author = "Your Name"
priority = 50 # 数值越小越早加载
# 扩展常量(与 .dnsbattribute 相同的合并逻辑)
attributes: Dict[str, Any] = {
"DNS_SOFTWARE_BLOCKS": {
"mydns": {"global", "zone"}
},
"RECOGNIZED_PATTERNS": {
"mydns": [r"\bmydns\b", r"\bmy-dns\b"]
}
}
def on_load(self, registry: PluginRegistry):
"""注册 MyDNS 实现"""
logger.info("[MyDNSPlugin] 加载 MyDNS 插件...")
# 注册 Image
registry.register_image("mydns", MyDNSImage)
# 注册 Behavior
registry.register_behavior("mydns", "master", MyDNSMasterBehavior)
# 注册 Includer
registry.register_includer("mydns", MyDNSIncluder)
# 注册 Zone Generator(可选,用于自定义格式)
registry.register_zone_generator("mydns", MyDNSZoneGenerator)
# 注册资源
registry.register_resources(
"mydns",
"dnsb_mydns.resources",
templates=True, # images/templates/mydns
rules=True, # images/rules/mydns
defaults=True, # images/defaults/mydns
controls=False, # images/controls/mydns
scripts=False, # scripts/mydns
configs=True # configs/
)
def on_unload(self):
"""插件卸载时的清理工作"""
logger.info("[MyDNSPlugin] 卸载 MyDNS 插件...")
# 导出供入口点使用
__all__ = ['MyDNSPlugin']
2. Image 实现¶
# src/dnsb_mydns/image.py
from dnsbuilder.bases.internal import InternalImage
class MyDNSImage(InternalImage):
"""MyDNS Docker 镜像构建器"""
def _setup_software(self):
"""软件特定的初始化钩子"""
# 在默认初始化之后调用
# 可重写以自定义依赖处理
pass
3. Behavior 实现¶
# src/dnsb_mydns/behavior.py
from dnsbuilder.bases import ForwardBehavior
class MyDNSMasterBehavior(ForwardBehavior):
"""MyDNS master 区域行为"""
def generate_config(self) -> str:
"""生成区域配置"""
lines = []
for zone in self.zones:
lines.append(f"zone \"{zone.name}\" {{")
lines.append(f" file \"{zone.file}\";")
lines.append("};")
return "\n".join(lines)
4. Includer 实现¶
# src/dnsb_mydns/includer.py
from dnsbuilder.bases import Includer
class MyDNSIncluder(Includer):
"""MyDNS 配置 include 处理器"""
def get_include_pattern(self, config_name: str) -> str:
"""返回 include 指令模式"""
return f'include "/etc/mydns/{config_name}";'
5. Zone Generator(可选)¶
用于自定义区域文件格式:
# src/dnsb_mydns/zone.py
from dnsbuilder.bases import ZoneGenerator
class MyDNSZoneGenerator(ZoneGenerator):
"""MyDNS 自定义区域文件格式"""
def generate_soa(self, zone) -> str:
"""以 MyDNS 格式生成 SOA 记录"""
return f"SOA {zone.name} {zone.primary_ns} {zone.admin_email}"
def generate_rr(self, record) -> str:
"""以 MyDNS 格式生成资源记录"""
return f"{record.name} {record.ttl} {record.type} {record.value}"
资源文件¶
templates/{software}¶
Dockerfile 模板(Jinja2 格式):
# images/templates/mydns
FROM {{ base_image }}
# 安装依赖
RUN apt-get update && apt-get install -y \
{% for dep in dependencies %}
{{ dep }} \
{% endfor %}
&& rm -rf /var/lib/apt/lists/*
# 安装 MyDNS
RUN wget {{ download_url }} && \
tar xzf mydns-{{ version }}.tar.gz && \
cd mydns-{{ version }} && \
./configure && make && make install
# 复制配置
COPY {{ config_file }} /etc/mydns/mydns.conf
CMD ["mydns", "-g"]
rules/{software}¶
版本到基础镜像的映射(JSON 格式):
{
"1.0.0": "ubuntu:20.04",
"[1.0.0, 2.0.0]": "ubuntu:22.04",
"2.0.0": null
}
规则说明:
- "version": "base_image" — 指定版本使用指定基础镜像
- "[min, max]": "base_image" — 版本区间内的版本使用指定基础镜像
- "version": null — 该版本能够支持
defaults/{software}¶
默认依赖和工具包(JSON 格式):
{
"default_deps": [
"build-essential",
"libssl-dev"
],
"default_utils": [
"vim",
"dnsutils",
"tcpdump"
]
}
字段说明:
- default_deps — 构建期依赖包
- default_utils — 运行期工具包
插件加载方式¶
插件可以通过三种方式加载:
1. 入口点(推荐)¶
在 pyproject.toml 中配置:
[project.entry-points."dnsb.plugins"]
mydns = "dnsb_mydns:MyDNSPlugin"
在config中指定
plugins:
- "dnsb_mydns:MyDNSPlugin"
2. PYTHONPATH¶
export PYTHONPATH=/path/to/mydns/plugin
在 config 中指定:
plugins:
- "dnsb_mydns:MyDNSPlugin"
3. DNSB_PLUGINS¶
export DNSB_PLUGINS="dnsb_mydns:MyDNSPlugin"
在 config 中指定:
plugins:
- "dnsb_mydns:MyDNSPlugin"
扩展常量(attributes)¶
插件的 attributes 类属性用于在加载时扩展 DNSBuilder 的全局常量,与 .dnsbattribute 文件使用相同的合并逻辑。
合并策略¶
| 类型 | 合并策略 | 说明 |
|---|---|---|
| dict | 深度合并 | 递归合并字典,保留原有键值 |
| list | 扩展 | 将新元素追加到列表末尾 |
| 其他 | 替换 | 直接用新值覆盖旧值 |
可扩展的常量¶
| 常量名 | 类型 | 说明 |
|---|---|---|
DNS_SOFTWARE_BLOCKS |
dict | DNS 软件的配置块类型定义 |
RECOGNIZED_PATTERNS |
dict | DNS 软件识别的正则模式 |
LOG_ALIAS_MAP |
dict | 日志模块名称别名 |
SUPPORTED_OS |
list | 支持的操作系统列表 |
DEFAULT_OS |
str | 默认操作系统 |
BASE_PACKAGE_MANAGERS |
dict | 基础包管理器配置 |
SOFT_PACKAGE_MANAGERS |
dict | 软件包管理器配置 |
ALIAS_MAP |
dict | 配置字段别名映射 |
KNOWN_PROTOCOLS |
set | 已知的路径协议 |
BEHAVIOR_TYPES |
set | 支持的行为类型 |
常量详解¶
DNS_SOFTWARE_BLOCKS¶
定义 DNS 软件支持的配置块类型,影响 behavior DSL 的解析:
attributes = {
"DNS_SOFTWARE_BLOCKS": {
"mydns": {"global", "zone", "forward"} # 支持的配置块
}
}
配置块类型说明:
- global — 全局配置块(所有软件必须支持)
- zone — 区域配置块
- forward-zone — 转发区域配置块
- stub-zone — 存根区域配置块
- options — 选项配置块
RECOGNIZED_PATTERNS¶
定义如何从镜像名称识别 DNS 软件类型:
attributes = {
"RECOGNIZED_PATTERNS": {
"mydns": [
r"\bmydns\b", # 匹配 mydns
r"\bmy-dns\b", # 匹配 my-dns
r"\bmydns\d", # 匹配 mydns1, mydns2 等
]
}
}
BASE_PACKAGE_MANAGERS¶
添加新的基础包管理器(用于不同操作系统):
attributes = {
"BASE_PACKAGE_MANAGERS": {
"pacman": {
"supported_os": ["arch"],
"check_cmd": "command -v pacman >/dev/null 2>&1",
"install_cmd": "pacman -Sy --noconfirm {packages}",
"cleanup_cmd": "pacman -Sc --noconfirm"
}
},
"SUPPORTED_OS": ["arch"] # 同时添加支持的操作系统
}
SOFT_PACKAGE_MANAGERS¶
添加新的软件包管理器(如 pip、npm 等):
attributes = {
"SOFT_PACKAGE_MANAGERS": {
"uv": {
"check_cmd": "command -v uv >/dev/null 2>&1",
"install_cmd": "uv pip install {packages}",
"cleanup_cmd": "",
"base_requirements": {
"apt": ["uv"],
"apk": None # 不支持
}
}
}
}
完整示例¶
class MyDNSPlugin(Plugin):
name = "mydns"
version = "0.0.1"
attributes: Dict[str, Any] = {
# 深度合并:添加 mydns 的配置块定义
"DNS_SOFTWARE_BLOCKS": {
"mydns": {"global", "zone", "forward-zone"}
},
# 深度合并:添加 mydns 的识别模式
"RECOGNIZED_PATTERNS": {
"mydns": [
r"\bmydns\b",
r"\bmy-dns\b",
]
},
# 扩展列表:添加新的行为类型
"BEHAVIOR_TYPES": {"CustomBehavior"},
# 替换:修改默认操作系统
"DEFAULT_OS": "alpine",
# 扩展列表:添加支持的操作系统
"SUPPORTED_OS": ["arch", "fedora"],
}
def on_load(self, registry: PluginRegistry):
# attributes 会在插件加载前自动合并
registry.register_image("mydns", MyDNSImage)
# ...
加载顺序¶
常量合并发生在插件 on_load 方法调用之前:
- 加载 DNSBuilder 内置常量
- 加载
.dnsbattribute文件(如存在) - 按插件
priority排序,依次合并各插件的attributes - 调用各插件的
on_load方法
PluginRegistry API¶
Image 注册¶
registry.register_image(
software="mydns", # 软件标识符
image_class=MyDNSImage, # Image 类
override=False # 是否允许覆盖已有注册
)
Behavior 注册¶
registry.register_behavior(
software="mydns",
behavior_type="master", # master, forward, stub, hint 等
behavior_class=MyDNSMasterBehavior,
override=False
)
Includer 注册¶
registry.register_includer(
software="mydns",
includer_class=MyDNSIncluder,
override=False
)
Zone Generator 注册¶
registry.register_zone_generator(
software="mydns",
generator_class=MyDNSZoneGenerator,
override=False
)
Resource 注册¶
registry.register_resources(
software="mydns",
package="dnsb_mydns.resources",
image_templates=True, # 注册 resource:/images/templates/mydns
build_templates=True, # 注册 resource:/builder/templates/mydns
rules=True, # 注册 resource:/images/rules/mydns
defaults=True, # 注册 resource:/images/defaults/mydns
controls=True, # 注册 resource:/images/controls/mydns
scripts=False, # 注册 resource:/scripts/mydns
configs=True # 注册 resource:/configs/
)
完整示例:CoreDNS 插件¶
参见 test/dnsb_coredns/ 目录,这是一个完整可运行的示例