Python脚本如何生成Puppet配置

wen 实用脚本 3

本文目录导读:

Python脚本如何生成Puppet配置

  1. 使用模板引擎(推荐:Jinja2)
  2. 高级示例:多层Puppet配置生成
  3. 使用专门库:py-puppet
  4. 使用模板继承(复杂项目)
  5. 完整项目示例:配置管理工具
  6. 使用示例配置文件

我来介绍几种用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的语法规则。

抱歉,评论功能暂时关闭!