Python脚本如何生成Chef配置

wen 实用脚本 2

本文目录导读:

Python脚本如何生成Chef配置

  1. 使用模板引擎生成Chef资源文件
  2. 使用Python内置字符串格式化
  3. 使用自定义类生成复杂配置
  4. 从YAML/JSON配置生成Chef配置
  5. 完整的自动化生成脚本示例
  6. 使用建议

在Python中生成Chef配置,可以通过以下几种方式实现:

使用模板引擎生成Chef资源文件

使用Jinja2模板

from jinja2 import Template
# 定义模板
template_str = """
package '{{ package_name }}' do
  action :install
end
template '/etc/{{ config_name }}.conf' do
  source '{{ template_name }}.erb'
  owner 'root'
  group 'root'
  mode '0644'
  variables(
    :port => {{ port }},
    :host => '{{ host }}'
  )
end
service '{{ service_name }}' do
  supports :status => true, :restart => true
  action [:enable, :start]
end
"""
# 创建模板对象
template = Template(template_str)
# 填充数据
config = {
    'package_name': 'nginx',
    'config_name': 'nginx',
    'template_name': 'nginx',
    'port': 8080,
    'host': 'localhost',
    'service_name': 'nginx'
}
# 渲染配置
chef_config = template.render(config)
# 写入文件
with open('recipes/default.rb', 'w') as f:
    f.write(chef_config)
print(chef_config)

使用Python内置字符串格式化

def generate_chef_config(service_name, package_name, config_values):
    """生成Chef配置"""
    config_template = """
# Cookbook: {service_name}
# Recipe: default
package '{package_name}' do
  action :install
  version '{version}'
end
service '{service_name}' do
  supports :status => true, :restart => true
  action [:enable, :start]
end
# 配置文件管理
directory '/etc/{service_name}' do
  owner 'root'
  group 'root'
  mode '0755'
  recursive true
  action :create
end
template '/etc/{service_name}/{service_name}.conf' do
  source '{service_name}.erb'
  owner 'root'
  group 'root'
  mode '0644'
  variables(
    :port => {port},
    :host => '{host}',
    :max_clients => {max_clients}
  )
end
"""
    return config_template.format(
        service_name=service_name,
        package_name=package_name,
        version=config_values.get('version', 'latest'),
        port=config_values.get('port', 80),
        host=config_values.get('host', 'localhost'),
        max_clients=config_values.get('max_clients', 100)
    )
# 使用示例
config = {
    'version': '1.18.0',
    'port': 8080,
    'host': 'web.example.com',
    'max_clients': 500
}
chef_recipe = generate_chef_config('nginx', 'nginx', config)
print(chef_recipe)
# 保存到文件
with open('nginx_recipe.rb', 'w') as f:
    f.write(chef_recipe)

使用自定义类生成复杂配置

class ChefConfigGenerator:
    """Chef配置生成器"""
    def __init__(self, cookbook_name):
        self.cookbook_name = cookbook_name
        self.resources = []
    def add_package(self, name, version=None, action='install'):
        """添加包资源"""
        self.resources.append({
            'type': 'package',
            'name': name,
            'version': version,
            'action': action
        })
    def add_service(self, name, action=['enable', 'start'], supports=None):
        """添加服务资源"""
        if supports is None:
            supports = {'status': True, 'restart': True}
        self.resources.append({
            'type': 'service',
            'name': name,
            'action': action,
            'supports': supports
        })
    def add_template(self, name, source, variables=None, 
                    owner='root', group='root', mode='0644'):
        """添加模板资源"""
        if variables is None:
            variables = {}
        self.resources.append({
            'type': 'template',
            'name': name,
            'source': source,
            'owner': owner,
            'group': group,
            'mode': mode,
            'variables': variables
        })
    def add_directory(self, name, owner='root', group='root', 
                     mode='0755', recursive=True):
        """添加目录资源"""
        self.resources.append({
            'type': 'directory',
            'name': name,
            'owner': owner,
            'group': group,
            'mode': mode,
            'recursive': recursive
        })
    def generate_resource_code(self, resource):
        """生成资源代码"""
        type_map = {
            'package': self._generate_package,
            'service': self._generate_service,
            'template': self._generate_template,
            'directory': self._generate_directory
        }
        generator = type_map.get(resource['type'])
        if generator:
            return generator(resource)
        return ''
    def _generate_package(self, resource):
        code = f"package '{resource['name']}' do\n"
        if resource['version']:
            code += f"  version '{resource['version']}'\n"
        code += f"  action :{resource['action']}\nend\n"
        return code
    def _generate_service(self, resource):
        code = f"service '{resource['name']}' do\n"
        supports = resource['supports']
        code += f"  supports :status => {supports.get('status', True)}, :restart => {supports.get('restart', True)}\n"
        action_list = resource['action']
        code += f"  action [:{', :'.join(action_list)}]\nend\n"
        return code
    def _generate_template(self, resource):
        code = f"template '{resource['name']}' do\n"
        code += f"  source '{resource['source']}'\n"
        code += f"  owner '{resource['owner']}'\n"
        code += f"  group '{resource['group']}'\n"
        code += f"  mode '{resource['mode']}'\n"
        if resource['variables']:
            code += "  variables(\n"
            for key, value in resource['variables'].items():
                if isinstance(value, str):
                    code += f"    :{key} => '{value}',\n"
                else:
                    code += f"    :{key} => {value},\n"
            code += "  )\n"
        code += "end\n"
        return code
    def _generate_directory(self, resource):
        code = f"directory '{resource['name']}' do\n"
        code += f"  owner '{resource['owner']}'\n"
        code += f"  group '{resource['group']}'\n"
        code += f"  mode '{resource['mode']}'\n"
        if resource['recursive']:
            code += "  recursive true\n"
        code += "  action :create\nend\n"
        return code
    def generate_recipe(self):
        """生成完整食谱"""
        header = f"# Cookbook: {self.cookbook_name}\n# Recipe: default\n\n"
        body = "\n".join(self.generate_resource_code(r) for r in self.resources)
        return header + body
# 使用示例
generator = ChefConfigGenerator('my_app')
generator.add_package('nginx', version='1.18.0')
generator.add_directory('/etc/nginx')
generator.add_template(
    '/etc/nginx/nginx.conf', 
    'nginx.erb',
    variables={'port': 8080, 'host': 'localhost'}
)
generator.add_service('nginx')
recipe = generator.generate_recipe()
print(recipe)
# 保存到文件
with open('recipes/default.rb', 'w') as f:
    f.write(recipe)

从YAML/JSON配置生成Chef配置

import yaml
import json
def generate_chef_from_yaml(yaml_file):
    """从YAML文件生成Chef配置"""
    with open(yaml_file, 'r') as f:
        config = yaml.safe_load(f)
    chef_code = []
    chef_code.append(f"# Generated Chef Cookbook: {config.get('cookbook_name', 'default')}")
    # 处理包安装
    for package in config.get('packages', []):
        chef_code.append(f"package '{package['name']}' do")
        if 'version' in package:
            chef_code.append(f"  version '{package['version']}'")
        chef_code.append("  action :install")
        chef_code.append("end\n")
    # 处理服务
    for service in config.get('services', []):
        chef_code.append(f"service '{service['name']}' do")
        chef_code.append("  action [:enable, :start]")
        chef_code.append("end\n")
    # 处理模板
    for template in config.get('templates', []):
        chef_code.append(f"template '{template['path']}' do")
        chef_code.append(f"  source '{template['source']}'")
        chef_code.append(f"  owner '{template.get('owner', 'root')}'")
        chef_code.append(f"  group '{template.get('group', 'root')}'")
        chef_code.append(f"  mode '{template.get('mode', '0644')}'")
        if 'variables' in template:
            chef_code.append("  variables(")
            for key, value in template['variables'].items():
                chef_code.append(f"    :{key} => '{value}',")
            chef_code.append("  )")
        chef_code.append("end\n")
    return "\n".join(chef_code)
# YAML配置示例
yaml_config = """
cookbook_name: web_server
packages:
  - name: nginx
    version: 1.18.0
services:
  - name: nginx
templates:
  - path: /etc/nginx/nginx.conf
    source: nginx.erb
    owner: root
    group: root
    mode: '0644'
    variables:
      port: 8080
      host: localhost
"""
# 使用示例
with open('config.yaml', 'w') as f:
    f.write(yaml_config)
chef_config = generate_chef_from_yaml('config.yaml')
print(chef_config)

完整的自动化生成脚本示例

#!/usr/bin/env python3
"""
Chef配置一键生成工具
支持生成完整的Cookbook结构
"""
import os
import json
from pathlib import Path
class ChefCookbookGenerator:
    def __init__(self, cookbook_name, cookbook_path='.'):
        self.cookbook_name = cookbook_name
        self.cookbook_path = Path(cookbook_path) / cookbook_name
    def create_cookbook_structure(self):
        """创建Cookbook目录结构"""
        directories = [
            'recipes',
            'templates/default',
            'attributes',
            'files/default',
            'metadata.rb'
        ]
        for directory in directories:
            path = self.cookbook_path / directory
            if '.' not in directory:  # 不是文件
                path.mkdir(parents=True, exist_ok=True)
    def generate_metadata(self):
        """生成metadata.rb"""
        metadata_content = f"""
name '{self.cookbook_name}'
maintainer 'Generated by Python'
maintainer_email 'admin@example.com'
license 'Apache-2.0'
description 'Installs/Configures {self.cookbook_name}'
version '0.1.0'
supports 'centos'
supports 'ubuntu'
depends 'yum'
depends 'systemd'
"""
        with open(self.cookbook_path / 'metadata.rb', 'w') as f:
            f.write(metadata_content.strip())
    def generate_default_recipe(self, packages, services, config_templates):
        """生成默认食谱"""
        recipe_content = [
            "#",
            f"# Cookbook:: {self.cookbook_name}",
            "# Recipe:: default",
            "#",
            "",
            "# Update package list",
            "execute 'update_package_list' do",
            "  command 'apt-get update'",
            "  action :nothing",
            "end",
            ""
        ]
        # 添加包安装
        for package in packages:
            recipe_content.append(f"package '{package}' do")
            recipe_content.append("  action :install")
            recipe_content.append("  notifies :run, 'execute[update_package_list]', :immediately")
            recipe_content.append("end")
            recipe_content.append("")
        # 添加配置文件
        for template_name, template_config in config_templates.items():
            recipe_content.append(f"template '{template_config['path']}' do")
            recipe_content.append(f"  source '{template_name}.erb'")
            recipe_content.append(f"  owner '{template_config.get('owner', 'root')}'")
            recipe_content.append(f"  group '{template_config.get('group', 'root')}'")
            recipe_content.append(f"  mode '{template_config.get('mode', '0644')}'")
            if 'variables' in template_config:
                recipe_content.append("  variables(")
                for key, value in template_config['variables'].items():
                    recipe_content.append(f"    :{key} => '{value}',")
                recipe_content.append("  )")
            recipe_content.append("end")
            recipe_content.append("")
        # 添加服务
        for service_name, service_config in services.items():
            recipe_content.append(f"service '{service_name}' do")
            recipe_content.append("  supports :status => true, :restart => true, :reload => true")
            recipe_content.append(f"  action [:{', :'.join(service_config.get('actions', ['enable', 'start']))}]")
            if 'notifies' in service_config:
                for notification in service_config['notifies']:
                    recipe_content.append(f"  notifies :{notification['action']}, '{notification['resource']}'")
            recipe_content.append("end")
            recipe_content.append("")
        # 写入文件
        recipe_path = self.cookbook_path / 'recipes' / 'default.rb'
        with open(recipe_path, 'w') as f:
            f.write('\n'.join(recipe_content))
    def generate(self, config):
        """根据配置生成完整的Cookbook"""
        self.create_cookbook_structure()
        self.generate_metadata()
        self.generate_default_recipe(
            config.get('packages', []),
            config.get('services', {}),
            config.get('templates', {})
        )
# 使用示例
if __name__ == '__main__':
    # 配置
    config = {
        'packages': ['nginx', 'php-fpm', 'mysql-server'],
        'services': {
            'nginx': {
                'actions': ['enable', 'start'],
                'notifies': [
                    {'action': 'reload', 'resource': 'service[php-fpm]'}
                ]
            },
            'php-fpm': {
                'actions': ['enable', 'start']
            },
            'mysql': {
                'actions': ['enable', 'start']
            }
        },
        'templates': {
            'nginx': {
                'path': '/etc/nginx/nginx.conf',
                'owner': 'root',
                'group': 'root',
                'mode': '0644',
                'variables': {
                    'server_name': 'example.com',
                    'document_root': '/var/www/html',
                    'port': 80
                }
            },
            'php': {
                'path': '/etc/php/7.4/fpm/pool.d/www.conf',
                'owner': 'root',
                'group': 'root',
                'mode': '0644',
                'variables': {
                    'user': 'www-data',
                    'group': 'www-data',
                    'listen_port': 9000
                }
            }
        }
    }
    # 生成Cookbook
    generator = ChefCookbookGenerator('web_cookbook', './cookbooks')
    generator.generate(config)
    print("Chef Cookbook 生成完成!")
    print(f"路径: ./cookbooks/web_cookbook")

使用建议

  1. 选择合适的工具

    • 简单配置:使用字符串格式化
    • 复杂配置:使用Jinja2模板
    • 需要代码逻辑:使用面向对象方式
  2. 模板管理

    • 将常用的配置模板保存为文件
    • 支持变量替换和条件逻辑
  3. 错误处理

    def safe_generate_chef_config(config_data):
     """安全的配置生成函数"""
     try:
         # 验证配置数据
         if not config_data.get('name'):
             raise ValueError("Cookbook name is required")
         # 生成配置
         chef_code = generate_config(config_data)
         # 验证生成的代码语法
         # (可以集成Chef验证工具)
         return chef_code
     except Exception as e:
         print(f"生成配置失败: {e}")
         return None
  4. 集成测试

    def test_generated_config():
     """测试生成的配置"""
     test_config = {
         'package_name': 'test-package',
         'service_name': 'test-service'
     }
     result = generate_chef_config(test_config)
     # 验证配置包含必要元素
     assert 'package' in result
     assert 'service' in result
     assert 'end' in result
     print("测试通过!")

这种方法可以自动化生成大量Chef配置,特别适合管理多个环境或需要批量生成配置文件的情况。

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