did stuff
This commit is contained in:
parent
3f2346b201
commit
ee02c74250
10 changed files with 1511 additions and 0 deletions
445
debian_atomic_blueprint_generator.py
Normal file
445
debian_atomic_blueprint_generator.py
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debian Atomic Blueprint Generator for Debian Forge
|
||||
|
||||
This module provides enhanced blueprint generation for Debian atomic images,
|
||||
integrating with repository management and dependency resolution systems.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Dict, List, Optional, Any
|
||||
from dataclasses import dataclass, asdict
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
from debian_repository_manager import DebianRepositoryManager
|
||||
from debian_package_resolver import DebianPackageResolver
|
||||
except ImportError:
|
||||
DebianRepositoryManager = None
|
||||
DebianPackageResolver = None
|
||||
|
||||
@dataclass
|
||||
class AtomicBlueprintConfig:
|
||||
"""Configuration for atomic blueprint generation"""
|
||||
name: str
|
||||
description: str
|
||||
version: str
|
||||
base_packages: List[str]
|
||||
additional_packages: List[str] = None
|
||||
excluded_packages: List[str] = None
|
||||
suite: str = "bookworm"
|
||||
architecture: str = "amd64"
|
||||
include_recommends: bool = False
|
||||
ostree_ref: str = None
|
||||
users: List[Dict[str, Any]] = None
|
||||
services: Dict[str, List[str]] = None
|
||||
filesystem_customizations: Dict[str, Any] = None
|
||||
|
||||
class DebianAtomicBlueprintGenerator:
|
||||
"""Generates optimized Debian atomic blueprints"""
|
||||
|
||||
def __init__(self, config_dir: str = None):
|
||||
if DebianRepositoryManager and config_dir:
|
||||
self.repository_manager = DebianRepositoryManager(config_dir)
|
||||
elif DebianRepositoryManager:
|
||||
# Use temporary directory for testing
|
||||
import tempfile
|
||||
temp_dir = tempfile.mkdtemp(prefix="debian-forge-")
|
||||
self.repository_manager = DebianRepositoryManager(temp_dir)
|
||||
else:
|
||||
self.repository_manager = None
|
||||
|
||||
self.package_resolver = DebianPackageResolver() if DebianPackageResolver else None
|
||||
self.base_packages = [
|
||||
"systemd",
|
||||
"systemd-sysv",
|
||||
"dbus",
|
||||
"udev",
|
||||
"ostree",
|
||||
"linux-image-amd64"
|
||||
]
|
||||
|
||||
def generate_base_blueprint(self, config: AtomicBlueprintConfig = None) -> Dict[str, Any]:
|
||||
"""Generate base atomic blueprint"""
|
||||
if config is None:
|
||||
config = AtomicBlueprintConfig(
|
||||
name="debian-atomic-base",
|
||||
description="Debian Atomic Base System",
|
||||
version="1.0.0",
|
||||
base_packages=self.base_packages
|
||||
)
|
||||
|
||||
# Resolve package dependencies
|
||||
all_packages = config.base_packages + (config.additional_packages or [])
|
||||
resolved_packages = self._resolve_packages(all_packages, config.suite, config.architecture)
|
||||
|
||||
# Generate blueprint
|
||||
blueprint = {
|
||||
"name": config.name,
|
||||
"description": config.description,
|
||||
"version": config.version,
|
||||
"distro": f"debian-{config.suite}",
|
||||
"arch": config.architecture,
|
||||
"packages": [{"name": pkg} for pkg in resolved_packages],
|
||||
"modules": [],
|
||||
"groups": [],
|
||||
"customizations": self._generate_base_customizations(config)
|
||||
}
|
||||
|
||||
# Add OSTree configuration
|
||||
if config.ostree_ref:
|
||||
blueprint["ostree"] = {
|
||||
"ref": config.ostree_ref,
|
||||
"parent": f"debian/{config.suite}/base"
|
||||
}
|
||||
|
||||
return blueprint
|
||||
|
||||
def generate_workstation_blueprint(self) -> Dict[str, Any]:
|
||||
"""Generate workstation atomic blueprint"""
|
||||
workstation_packages = [
|
||||
"firefox-esr",
|
||||
"libreoffice",
|
||||
"gnome-core",
|
||||
"gdm3",
|
||||
"network-manager",
|
||||
"pulseaudio",
|
||||
"fonts-dejavu"
|
||||
]
|
||||
|
||||
config = AtomicBlueprintConfig(
|
||||
name="debian-atomic-workstation",
|
||||
description="Debian Atomic Workstation",
|
||||
version="1.0.0",
|
||||
base_packages=self.base_packages,
|
||||
additional_packages=workstation_packages,
|
||||
ostree_ref="debian/bookworm/workstation"
|
||||
)
|
||||
|
||||
blueprint = self.generate_base_blueprint(config)
|
||||
blueprint["customizations"]["services"]["enabled"].extend([
|
||||
"gdm3",
|
||||
"NetworkManager",
|
||||
"pulseaudio"
|
||||
])
|
||||
|
||||
return blueprint
|
||||
|
||||
def generate_server_blueprint(self) -> Dict[str, Any]:
|
||||
"""Generate server atomic blueprint"""
|
||||
server_packages = [
|
||||
"nginx",
|
||||
"postgresql",
|
||||
"redis",
|
||||
"fail2ban",
|
||||
"logrotate",
|
||||
"rsyslog"
|
||||
]
|
||||
|
||||
config = AtomicBlueprintConfig(
|
||||
name="debian-atomic-server",
|
||||
description="Debian Atomic Server",
|
||||
version="1.0.0",
|
||||
base_packages=self.base_packages,
|
||||
additional_packages=server_packages,
|
||||
ostree_ref="debian/bookworm/server"
|
||||
)
|
||||
|
||||
blueprint = self.generate_base_blueprint(config)
|
||||
blueprint["customizations"]["services"]["enabled"].extend([
|
||||
"nginx",
|
||||
"postgresql",
|
||||
"redis-server",
|
||||
"fail2ban"
|
||||
])
|
||||
|
||||
return blueprint
|
||||
|
||||
def generate_container_blueprint(self) -> Dict[str, Any]:
|
||||
"""Generate container atomic blueprint"""
|
||||
container_packages = [
|
||||
"podman",
|
||||
"buildah",
|
||||
"skopeo",
|
||||
"containers-common",
|
||||
"crun"
|
||||
]
|
||||
|
||||
config = AtomicBlueprintConfig(
|
||||
name="debian-atomic-container",
|
||||
description="Debian Atomic Container Host",
|
||||
version="1.0.0",
|
||||
base_packages=self.base_packages,
|
||||
additional_packages=container_packages,
|
||||
ostree_ref="debian/bookworm/container"
|
||||
)
|
||||
|
||||
blueprint = self.generate_base_blueprint(config)
|
||||
blueprint["customizations"]["services"]["enabled"].extend([
|
||||
"podman"
|
||||
])
|
||||
|
||||
# Add container-specific configurations
|
||||
blueprint["customizations"]["filesystem"] = {
|
||||
"/var/lib/containers": {
|
||||
"type": "directory",
|
||||
"mode": "0755"
|
||||
}
|
||||
}
|
||||
|
||||
return blueprint
|
||||
|
||||
def generate_minimal_blueprint(self) -> Dict[str, Any]:
|
||||
"""Generate minimal atomic blueprint"""
|
||||
minimal_packages = [
|
||||
"systemd",
|
||||
"systemd-sysv",
|
||||
"ostree",
|
||||
"linux-image-amd64"
|
||||
]
|
||||
|
||||
config = AtomicBlueprintConfig(
|
||||
name="debian-atomic-minimal",
|
||||
description="Debian Atomic Minimal System",
|
||||
version="1.0.0",
|
||||
base_packages=minimal_packages,
|
||||
ostree_ref="debian/bookworm/minimal"
|
||||
)
|
||||
|
||||
return self.generate_base_blueprint(config)
|
||||
|
||||
def _resolve_packages(self, packages: List[str], suite: str, architecture: str) -> List[str]:
|
||||
"""Resolve package dependencies"""
|
||||
if not self.package_resolver:
|
||||
return packages
|
||||
|
||||
try:
|
||||
resolution = self.package_resolver.resolve_package_dependencies(
|
||||
packages, suite, architecture, include_recommends=False
|
||||
)
|
||||
|
||||
if resolution.conflicts:
|
||||
print(f"Warning: Package conflicts detected: {resolution.conflicts}")
|
||||
|
||||
if resolution.missing:
|
||||
print(f"Warning: Missing packages: {resolution.missing}")
|
||||
|
||||
return resolution.install_order
|
||||
|
||||
except Exception as e:
|
||||
print(f"Package resolution failed: {e}")
|
||||
return packages
|
||||
|
||||
def _generate_base_customizations(self, config: AtomicBlueprintConfig) -> Dict[str, Any]:
|
||||
"""Generate base customizations for blueprint"""
|
||||
customizations = {
|
||||
"user": config.users or [
|
||||
{
|
||||
"name": "debian",
|
||||
"description": "Debian atomic user",
|
||||
"password": "$6$rounds=656000$debian$atomic.system.user",
|
||||
"home": "/home/debian",
|
||||
"shell": "/bin/bash",
|
||||
"groups": ["wheel", "sudo"],
|
||||
"uid": 1000,
|
||||
"gid": 1000
|
||||
}
|
||||
],
|
||||
"services": config.services or {
|
||||
"enabled": ["sshd", "systemd-networkd", "systemd-resolved"],
|
||||
"disabled": ["systemd-timesyncd"]
|
||||
},
|
||||
"kernel": {
|
||||
"append": "ostree=/ostree/boot.1/debian/bookworm/0"
|
||||
}
|
||||
}
|
||||
|
||||
if config.filesystem_customizations:
|
||||
customizations["filesystem"] = config.filesystem_customizations
|
||||
|
||||
return customizations
|
||||
|
||||
def generate_osbuild_manifest(self, blueprint: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Generate OSBuild manifest from blueprint"""
|
||||
manifest = {
|
||||
"version": "2",
|
||||
"pipelines": [
|
||||
{
|
||||
"name": "build",
|
||||
"runner": "org.osbuild.linux",
|
||||
"stages": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Add debootstrap stage
|
||||
debootstrap_stage = {
|
||||
"type": "org.osbuild.debootstrap",
|
||||
"options": {
|
||||
"suite": "bookworm",
|
||||
"mirror": "http://deb.debian.org/debian",
|
||||
"arch": blueprint.get("arch", "amd64"),
|
||||
"variant": "minbase",
|
||||
"apt_proxy": "http://192.168.1.101:3142"
|
||||
}
|
||||
}
|
||||
manifest["pipelines"][0]["stages"].append(debootstrap_stage)
|
||||
|
||||
# Add APT configuration stage
|
||||
apt_config_stage = {
|
||||
"type": "org.osbuild.apt.config",
|
||||
"options": {
|
||||
"sources": self._get_apt_sources(),
|
||||
"preferences": {},
|
||||
"proxy": "http://192.168.1.101:3142"
|
||||
}
|
||||
}
|
||||
manifest["pipelines"][0]["stages"].append(apt_config_stage)
|
||||
|
||||
# Add package installation stage
|
||||
package_names = [pkg["name"] for pkg in blueprint["packages"]]
|
||||
apt_stage = {
|
||||
"type": "org.osbuild.apt",
|
||||
"options": {
|
||||
"packages": package_names,
|
||||
"recommends": False,
|
||||
"update": True,
|
||||
"apt_proxy": "http://192.168.1.101:3142"
|
||||
}
|
||||
}
|
||||
manifest["pipelines"][0]["stages"].append(apt_stage)
|
||||
|
||||
# Add OSTree commit stage
|
||||
ostree_stage = {
|
||||
"type": "org.osbuild.ostree.commit",
|
||||
"options": {
|
||||
"repo": blueprint.get("name", "debian-atomic"),
|
||||
"branch": blueprint.get("ostree", {}).get("ref", f"debian/bookworm/{blueprint['name']}"),
|
||||
"subject": f"Debian atomic {blueprint['name']} system",
|
||||
"body": f"Built from blueprint: {blueprint['name']} v{blueprint['version']}"
|
||||
}
|
||||
}
|
||||
manifest["pipelines"][0]["stages"].append(ostree_stage)
|
||||
|
||||
return manifest
|
||||
|
||||
def _get_apt_sources(self) -> Dict[str, Any]:
|
||||
"""Get APT sources configuration"""
|
||||
if not self.repository_manager:
|
||||
return {
|
||||
"main": "deb http://deb.debian.org/debian bookworm main",
|
||||
"security": "deb http://security.debian.org/debian-security bookworm-security main",
|
||||
"updates": "deb http://deb.debian.org/debian bookworm-updates main"
|
||||
}
|
||||
|
||||
return self.repository_manager.generate_apt_config("bookworm", proxy="http://192.168.1.101:3142")
|
||||
|
||||
def save_blueprint(self, blueprint: Dict[str, Any], output_dir: str = "blueprints") -> str:
|
||||
"""Save blueprint to file"""
|
||||
output_path = Path(output_dir) / f"{blueprint['name']}.json"
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(blueprint, f, indent=2)
|
||||
|
||||
return str(output_path)
|
||||
|
||||
def validate_blueprint(self, blueprint: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Validate blueprint structure and content"""
|
||||
validation = {
|
||||
"valid": True,
|
||||
"errors": [],
|
||||
"warnings": [],
|
||||
"suggestions": []
|
||||
}
|
||||
|
||||
# Check required fields
|
||||
required_fields = ["name", "description", "version", "packages"]
|
||||
for field in required_fields:
|
||||
if field not in blueprint:
|
||||
validation["valid"] = False
|
||||
validation["errors"].append(f"Missing required field: {field}")
|
||||
|
||||
# Validate packages
|
||||
if "packages" in blueprint:
|
||||
if not blueprint["packages"]:
|
||||
validation["warnings"].append("No packages specified")
|
||||
|
||||
package_names = [pkg.get("name") if isinstance(pkg, dict) else pkg for pkg in blueprint["packages"]]
|
||||
|
||||
# Check for essential packages
|
||||
essential_packages = ["systemd", "ostree"]
|
||||
missing_essential = [pkg for pkg in essential_packages if pkg not in package_names]
|
||||
if missing_essential:
|
||||
validation["suggestions"].append(f"Consider adding essential packages: {missing_essential}")
|
||||
|
||||
# Validate customizations
|
||||
if "customizations" in blueprint and "services" in blueprint["customizations"]:
|
||||
services = blueprint["customizations"]["services"]
|
||||
if "enabled" in services and "disabled" in services:
|
||||
conflicts = set(services["enabled"]) & set(services["disabled"])
|
||||
if conflicts:
|
||||
validation["valid"] = False
|
||||
validation["errors"].append(f"Services both enabled and disabled: {list(conflicts)}")
|
||||
|
||||
return validation
|
||||
|
||||
def generate_all_blueprints(self, output_dir: str = "blueprints") -> List[str]:
|
||||
"""Generate all standard blueprints"""
|
||||
blueprints = [
|
||||
("base", self.generate_base_blueprint()),
|
||||
("workstation", self.generate_workstation_blueprint()),
|
||||
("server", self.generate_server_blueprint()),
|
||||
("container", self.generate_container_blueprint()),
|
||||
("minimal", self.generate_minimal_blueprint())
|
||||
]
|
||||
|
||||
saved_files = []
|
||||
for name, blueprint in blueprints:
|
||||
try:
|
||||
output_path = self.save_blueprint(blueprint, output_dir)
|
||||
saved_files.append(output_path)
|
||||
print(f"Generated {name} blueprint: {output_path}")
|
||||
except Exception as e:
|
||||
print(f"Failed to generate {name} blueprint: {e}")
|
||||
|
||||
return saved_files
|
||||
|
||||
def main():
|
||||
"""Example usage of blueprint generator"""
|
||||
print("Debian Atomic Blueprint Generator")
|
||||
|
||||
generator = DebianAtomicBlueprintGenerator()
|
||||
|
||||
# Generate all blueprints
|
||||
print("\nGenerating all blueprints...")
|
||||
saved_files = generator.generate_all_blueprints()
|
||||
|
||||
print(f"\nGenerated {len(saved_files)} blueprints:")
|
||||
for file_path in saved_files:
|
||||
print(f" - {file_path}")
|
||||
|
||||
# Example: Generate and validate a custom blueprint
|
||||
print("\nGenerating custom blueprint...")
|
||||
config = AtomicBlueprintConfig(
|
||||
name="debian-atomic-custom",
|
||||
description="Custom Debian Atomic System",
|
||||
version="1.0.0",
|
||||
base_packages=["systemd", "ostree"],
|
||||
additional_packages=["vim", "curl", "wget"],
|
||||
ostree_ref="debian/bookworm/custom"
|
||||
)
|
||||
|
||||
custom_blueprint = generator.generate_base_blueprint(config)
|
||||
validation = generator.validate_blueprint(custom_blueprint)
|
||||
|
||||
print(f"Custom blueprint validation: {'Valid' if validation['valid'] else 'Invalid'}")
|
||||
if validation['errors']:
|
||||
print(f"Errors: {validation['errors']}")
|
||||
if validation['warnings']:
|
||||
print(f"Warnings: {validation['warnings']}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue