本文目录导读:

我来介绍几种用Python生成Puppet配置的方法:
使用模板引擎(推荐:Jinja2)
安装依赖
pip install jinja2 pyyaml
Puppet模板文件 (nginx.pp.j2)
# nginx.pp.j2
class {{ class_name }} {
package { 'nginx':
ensure => '{{ package_version }}',
}
service { 'nginx':
ensure => '{{ service_status }}',
enable => {{ service_enable }},
require => Package['nginx'],
}
file { '{{ config_path }}':
ensure => file,
content => template('nginx/nginx.conf.erb'),
require => Package['nginx'],
notify => Service['nginx'],
}
}
Python生成脚本
from jinja2 import Template
import yaml
import json
def generate_puppet_nginx_config(config_data):
"""生成Nginx的Puppet配置"""
template_str = """
class {{ class_name }} {
package { 'nginx':
ensure => '{{ package_version }}',
}
service { 'nginx':
ensure => '{{ service_status }}',
enable => {{ service_enable }},
require => Package['nginx'],
}
}
"""
template = Template(template_str)
return template.render(
class_name=config_data.get('class_name', 'nginx'),
package_version=config_data.get('version', 'latest'),
service_status=config_data.get('status', 'running'),
service_enable=config_data.get('enable', True)
)
# 从YAML读取配置
def generate_from_yaml(yaml_file):
with open(yaml_file, 'r') as f:
config = yaml.safe_load(f)
return generate_puppet_nginx_config(config)
# 从JSON读取配置
def generate_from_json(json_file):
with open(json_file, 'r') as f:
config = json.load(f)
return generate_puppet_nginx_config(config)
高级示例:多层Puppet配置生成
import os
from jinja2 import Environment, FileSystemLoader
class PuppetConfigGenerator:
def __init__(self, template_dir='./templates'):
self.env = Environment(loader=FileSystemLoader(template_dir))
def generate_web_server(self, server_data):
"""生成Web服务器Puppet配置"""
template = self.env.get_template('web_server.pp.j2')
return template.render(
server_name=server_data['name'],
document_root=server_data.get('doc_root', '/var/www/html'),
ports=server_data.get('ports', [80, 443]),
ssl_enabled=server_data.get('ssl', False),
ssl_cert=server_data.get('ssl_cert', ''),
ssl_key=server_data.get('ssl_key', ''),
upstreams=server_data.get('upstreams', [])
)
def generate_full_stack(self, config_file):
"""生成完整应用栈配置"""
import yaml
with open(config_file, 'r') as f:
config = yaml.safe_load(f)
puppet_code = ""
# 生成各个模块的配置
for server in config.get('servers', []):
puppet_code += self.generate_web_server(server) + "\n"
for db in config.get('databases', []):
puppet_code += self.generate_database(db) + "\n"
return puppet_code
def save_puppet_config(self, config_str, output_path):
"""保存生成的Puppet配置"""
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'w') as f:
f.write(config_str)
print(f"Puppet配置已保存到: {output_path}")
# 使用示例
def example_usage():
generator = PuppetConfigGenerator()
# 配置数据
config = {
'servers': [
{
'name': 'example.com',
'doc_root': '/var/www/example',
'ports': [80, 443],
'ssl': True,
'ssl_cert': '/etc/ssl/certs/example.crt',
'ssl_key': '/etc/ssl/private/example.key',
'upstreams': [
{'name': 'app1', 'host': 'localhost', 'port': 3000}
]
}
],
'databases': [
{
'name': 'myapp_db',
'user': 'myapp',
'password': 'secure_password',
'host': 'db.example.com'
}
]
}
# 保存配置到YAML文件
with open('config.yaml', 'w') as f:
yaml.dump(config, f)
# 生成Puppet配置
puppet_code = generator.generate_full_stack('config.yaml')
generator.save_puppet_config(puppet_code, './puppet/manifests/site.pp')
# 运行示例
if __name__ == "__main__":
example_usage()
使用专门库:py-puppet
# 简单的Puppet DSL生成器
class PuppetDSL:
def __init__(self):
self.resources = []
def package(self, name, **kwargs):
resource = {
'type': 'package',
'name': name,
'params': kwargs
}
self.resources.append(resource)
return self
def service(self, name, **kwargs):
resource = {
'type': 'service',
'name': name,
'params': kwargs
}
self.resources.append(resource)
return self
def file(self, name, **kwargs):
resource = {
'type': 'file',
'name': name,
'params': kwargs
}
self.resources.append(resource)
return self
def user(self, name, **kwargs):
resource = {
'type': 'user',
'name': name,
'params': kwargs
}
self.resources.append(resource)
return self
def to_pp(self):
"""转换为Puppet DSL格式"""
output = []
for res in self.resources:
output.append(f"{res['type']} {{ '{res['name']}':")
for key, value in res['params'].items():
if isinstance(value, bool):
output.append(f" {key} => {str(value).lower()},")
elif isinstance(value, (int, float)):
output.append(f" {key} => {value},")
else:
output.append(f" {key} => '{value}',")
output.append("}\n")
return '\n'.join(output)
def save(self, filename):
"""保存到文件"""
with open(filename, 'w') as f:
f.write(self.to_pp())
# 使用示例
def create_web_server_config():
puppet = PuppetDSL()
# 添加Nginx配置
puppet.package('nginx', ensure='latest')
puppet.service('nginx', ensure='running', enable=True)
puppet.file('/etc/nginx/nginx.conf',
ensure='file',
source='puppet:///modules/nginx/nginx.conf',
require='Package[nginx]',
notify='Service[nginx]')
# 添加PHP-FPM配置
puppet.package('php-fpm', ensure='installed')
puppet.service('php-fpm', ensure='running', enable=True)
# 添加应用用户
puppet.user('www-data', ensure='present', shell='/sbin/nologin')
return puppet
# 生成配置
config = create_web_server_config()
config.save('web_server.pp')
使用模板继承(复杂项目)
from string import Template
class PuppetTemplateInheritance:
def __init__(self):
self.base_template = """
# Base configuration for $app_name
class $class_name {
include ::stdlib
include ::logrotate
# Common resources
$resources
# Additional configuration
$additional_config
}
"""
def create_application_config(self, app_name, config):
"""创建应用配置"""
# 生成资源字符串
resources = []
# 处理包
if config.get('packages'):
for pkg in config['packages']:
resources.append(f"""
package {{ '{pkg_name}':
ensure => '{version}',
}}""".format(pkg_name=pkg['name'], version=pkg.get('version', 'present')))
# 处理服务
if config.get('services'):
for svc in config['services']:
resources.append(f"""
service {{ '{svc_name}':
ensure => '{status}',
enable => {str(svc.get('enable', True)).lower()},
require => Package['{svc.get('require', '')}'],
}}""".format(svc_name=svc['name'],
status=svc.get('status', 'running'),
**svc))
# 创建模板
t = Template(self.base_template)
return t.substitute(
app_name=app_name,
class_name=f"app_{app_name}".replace('-', '_'),
resources='\n'.join(resources),
additional_config=config.get('additional_config', '')
)
# 使用示例
def generate_complex_config():
generator = PuppetTemplateInheritance()
config = {
'packages': [
{'name': 'python3', 'version': 'latest'},
{'name': 'nginx', 'version': '1.18.0'},
{'name': 'supervisor', 'version': 'present'}
],
'services': [
{
'name': 'nginx',
'status': 'running',
'enable': True,
'require': 'nginx'
},
{
'name': 'supervisor',
'status': 'running',
'enable': True,
'require': 'supervisor'
}
],
'additional_config': """
file { '/etc/nginx/sites-enabled/default':
ensure => absent,
}
"""
}
return generator.create_application_config('myapp', config)
# 使用hiera数据生成(更接近实际Puppet)
def generate_with_hiera():
import yaml
hiera_data = {
'myapp::packages': ['nginx', 'python3', 'git'],
'myapp::services': ['nginx', 'sshd'],
'myapp::config_file': '/etc/myapp/config.yaml',
'myapp::port': 8080,
'myapp::users': ['deploy', 'admin']
}
# 保存为hiera数据
with open('hiera_data.yaml', 'w') as f:
yaml.dump(hiera_data, f)
# 生成对应的Puppet配置
puppet_code = f"""
class myapp (
$packages = hiera_array('myapp::packages'),
$services = hiera_array('myapp::services'),
$config_file = hiera('myapp::config_file'),
$port = hiera('myapp::port'),
) {{
package {{ $packages:
ensure => installed,
}}
service {{ $services:
ensure => running,
enable => true,
}}
file {{ $config_file:
ensure => file,
content => template('myapp/config.erb'),
}}
}}
"""
return puppet_code
完整项目示例:配置管理工具
#!/usr/bin/env python3
"""
Puppet配置生成器 - 命令行工具
"""
import argparse
import yaml
import json
from jinja2 import Environment, FileSystemLoader
from pathlib import Path
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class PuppetConfigGenerator:
def __init__(self, template_dir='./templates', output_dir='./output'):
self.template_dir = Path(template_dir)
self.output_dir = Path(output_dir)
self.env = Environment(loader=FileSystemLoader(str(template_dir)))
self.env.line_statement_prefix = '#'
def parse_config(self, config_file):
"""解析配置文件"""
ext = Path(config_file).suffix.lower()
with open(config_file, 'r') as f:
if ext == '.yaml' or ext == '.yml':
return yaml.safe_load(f)
elif ext == '.json':
return json.load(f)
else:
raise ValueError(f"Unsupported file format: {ext}")
def generate_puppet_code(self, config):
"""生成Puppet代码"""
if 'template' in config:
template = self.env.get_template(config['template'])
return template.render(config)
# 默认生成逻辑
code = []
# 处理节点定义
if 'nodes' in config:
for node_name, node_config in config['nodes'].items():
code.append(f"node '{node_name}' {{\n")
code.append(self._generate_node_content(node_config))
code.append("}\n\n")
return ''.join(code)
def _generate_node_content(self, config):
"""生成节点内容"""
lines = []
# 处理类声明
if 'classes' in config:
for cls in config['classes']:
lines.append(f" include {cls}\n")
# 处理资源
if 'resources' in config:
for resource in config['resources']:
lines.append(self._generate_resource(resource))
return ''.join(lines)
def _generate_resource(self, resource):
"""生成单个资源定义"""
lines = []
resource_type = resource['type']
title = resource['title']
lines.append(f" {resource_type} {{ '{title}':\n")
for key, value in resource.get('params', {}).items():
if isinstance(value, bool):
lines.append(f" {key} => {str(value).lower()},\n")
else:
lines.append(f" {key} => '{value}',\n")
lines.append(" }\n\n")
return ''.join(lines)
def save_config(self, config_name, puppet_code):
"""保存生成的配置"""
output_file = self.output_dir / f"{config_name}.pp"
self.output_dir.mkdir(parents=True, exist_ok=True)
with open(output_file, 'w') as f:
f.write(puppet_code)
logger.info(f"Generated {output_file}")
def main():
parser = argparse.ArgumentParser(description='Generate Puppet configuration from Python')
parser.add_argument('config_file', help='Configuration file (YAML or JSON)')
parser.add_argument('--template-dir', default='./templates', help='Template directory')
parser.add_argument('--output-dir', default='./output', help='Output directory')
parser.add_argument('--output-name', help='Output file name (without extension)')
args = parser.parse_args()
generator = PuppetConfigGenerator(args.template_dir, args.output_dir)
config = generator.parse_config(args.config_file)
puppet_code = generator.generate_puppet_code(config)
output_name = args.output_name or Path(args.config_file).stem
generator.save_config(output_name, puppet_code)
if __name__ == '__main__':
main()
使用示例配置文件
YAML配置示例 (config.yaml)
template: base_server.pp.j2
nodes:
webserver01.example.com:
classes:
- nginx
- php7.4-fpm
- supervisor
resources:
- type: file
title: /etc/nginx/nginx.conf
params:
ensure: file
source: puppet:///modules/nginx/nginx.conf
require: Package[nginx]
notify: Service[nginx]
- type: service
title: nginx
params:
ensure: running
enable: true
命令行使用
# 生成Puppet配置 python puppet_generator.py config.yaml # 指定输出目录 python puppet_generator.py config.yaml --output-dir ./puppet/manifests # 使用JSON配置 python puppet_generator.py config.json
这些方法可以根据你的具体需求选择使用,对于简单的场景,Jinja2模板就足够了;对于复杂的项目,建议使用完整的配置管理工具,关键是要保持生成的Puppet代码符合Puppet的语法规则。