Reduce connection overhead for concurrent exports by reusing vSphere connections.
import "hypersdk/providers/vsphere"
// Create connection pool
poolConfig := &vsphere.PoolConfig{
MaxConnections: 5,
IdleTimeout: 5 * time.Minute,
HealthCheckInterval: 30 * time.Second,
}
pool := vsphere.NewConnectionPool(cfg, poolConfig, logger)
defer pool.Close()
// Get connection from pool
client, err := pool.Get(ctx)
if err != nil {
log.Fatal(err)
}
// Use client...
result, err := client.ExportOVF(ctx, vmPath, opts)
// Return to pool
pool.Put(client)
// View statistics
stats := pool.Stats()
fmt.Printf("Reuse ratio: %.2f%%\n", stats["reuse_ratio"].(float64)*100)
Get real-time notifications for all job lifecycle events.
Configuration (config.yaml):
webhooks:
- url: "https://your-server.com/webhook"
events: ["job.started", "job.completed", "job.failed"]
headers:
Authorization: "Bearer your-token"
timeout: 10s
retry: 3
enabled: true
Webhook Payload Example:
{
"event": "job.completed",
"timestamp": "2026-01-20T10:30:00Z",
"data": {
"job_id": "abc123",
"job_name": "Export Production VM",
"vm_path": "/Datacenter/vm/prod-web-01",
"duration_seconds": 1234.56,
"ovf_path": "/exports/prod-web-01.ovf"
}
}
Export VMs as compressed OVA archives.
opts := vsphere.ExportOptions{
Format: "ova",
Compress: true,
CompressionLevel: 6, // 0-9, default is 6
CleanupOVF: true, // Remove intermediate .ovf/.vmdk files
OutputPath: "/exports",
}
result, err := client.ExportOVF(ctx, vmPath, opts)
if err != nil {
log.Fatal(err)
}
fmt.Printf("OVA created: %s\n", result.OVAPath)
fmt.Printf("Format: %s\n", result.Format)
fmt.Printf("Size: %d bytes\n", result.TotalSize)
File naming:
vm-name.ovavm-name.ova.gzSchedule recurring exports that survive daemon restarts.
import "hypersdk/daemon/scheduler"
// Create scheduler with persistence
store := store.NewSQLiteStore("./hypersdk.db")
sched := scheduler.NewScheduler(jobManager, logger)
sched.SetStore(store)
// Define scheduled job
scheduledJob := &scheduler.ScheduledJob{
ID: "daily-backup",
Name: "Daily Production Backup",
Description: "Export production VMs every night",
Schedule: "0 2 * * *", // 2 AM daily (cron format)
Enabled: true,
JobTemplate: models.JobDefinition{
Name: "prod-backup",
VMPath: "/Datacenter/vm/prod-*",
OutputPath: "/backups",
Format: "ova",
Compress: true,
},
}
// Add to scheduler
err := sched.AddScheduledJob(scheduledJob)
// Start scheduler
sched.Start()
// On daemon startup - restore all schedules
sched.LoadSchedules()
Query execution history:
history, err := store.GetExecutionHistory("daily-backup", 10)
for _, exec := range history {
fmt.Printf("%s: Job %s - %s (%.2fs)\n",
exec.ExecutedAt,
exec.JobID,
exec.Status,
exec.DurationSeconds)
}
Work with multiple cloud providers using a single interface.
import "hypersdk/providers"
// Create provider registry
registry := providers.NewRegistry()
// Register providers
registry.Register(providers.ProviderVSphere, vsphere.NewProvider)
registry.Register(providers.ProviderAWS, aws.NewProvider)
registry.Register(providers.ProviderAzure, azure.NewProvider)
// Create provider instance
config := providers.ProviderConfig{
Type: providers.ProviderVSphere,
Endpoint: "https://vcenter.example.com",
Username: "admin@vsphere.local",
Password: "password",
Insecure: true,
}
provider, err := registry.Create(providers.ProviderVSphere, config)
defer provider.Disconnect()
// Connect
err = provider.Connect(ctx, config)
// List VMs
vms, err := provider.SearchVMs(ctx, "web-server")
// Export VM
opts := providers.ExportOptions{
OutputPath: "/exports",
Format: "ova",
Compress: true,
}
result, err := provider.ExportVM(ctx, vms[0].ID, opts)
Check provider capabilities:
caps := provider.GetExportCapabilities()
fmt.Printf("Supported formats: %v\n", caps.SupportedFormats)
fmt.Printf("Compression: %v\n", caps.SupportsCompression)
fmt.Printf("Streaming: %v\n", caps.SupportsStreaming)
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"hypersdk/config"
"hypersdk/daemon/jobs"
"hypersdk/daemon/scheduler"
"hypersdk/daemon/store"
"hypersdk/daemon/webhooks"
"hypersdk/logger"
"hypersdk/providers"
"hypersdk/providers/vsphere"
)
func main() {
// Load configuration
cfg, err := config.FromFile("config.yaml")
if err != nil {
log.Fatal(err)
}
// Create logger
log := logger.New(cfg.LogLevel)
// Initialize database
db, err := store.NewSQLiteStore("./hypersdk.db")
if err != nil {
log.Fatal("Failed to open database:", err)
}
defer db.Close()
// Create job manager
jobManager := jobs.NewManager(log, detector)
// Setup webhooks
if len(cfg.Webhooks) > 0 {
webhookMgr := webhooks.NewManager(cfg.Webhooks, log)
jobManager.SetWebhookManager(webhookMgr)
log.Info("Webhooks enabled", "count", len(cfg.Webhooks))
}
// Setup connection pool
if cfg.ConnectionPool != nil && cfg.ConnectionPool.Enabled {
poolCfg := &vsphere.PoolConfig{
MaxConnections: cfg.ConnectionPool.MaxConnections,
IdleTimeout: cfg.ConnectionPool.IdleTimeout,
HealthCheckInterval: cfg.ConnectionPool.HealthCheckInterval,
}
pool := vsphere.NewConnectionPool(cfg, poolCfg, log)
defer pool.Close()
log.Info("Connection pool enabled", "max", poolCfg.MaxConnections)
}
// Setup scheduler with persistence
sched := scheduler.NewScheduler(jobManager, log)
sched.SetStore(db)
// Load existing schedules
if err := sched.LoadSchedules(); err != nil {
log.Error("Failed to load schedules", "error", err)
}
sched.Start()
defer sched.Stop()
// Setup provider registry
registry := providers.NewRegistry()
registry.Register(providers.ProviderVSphere, vsphere.NewProvider)
log.Info("Provider registry initialized")
// Start API server
// api.Start(cfg.DaemonAddr, jobManager, sched)
// Wait for shutdown signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
log.Info("Shutting down gracefully...")
jobManager.Shutdown()
}
# Run with multiple concurrent jobs
for i in {1..10}; do
curl -X POST http://localhost:8080/jobs \
-H "Content-Type: application/json" \
-d '{
"vm_path": "/Datacenter/vm/test-'$i'",
"output_dir": "/exports",
"format": "ova"
}' &
done
# Check pool statistics
curl http://localhost:8080/stats/pool
# Start a webhook receiver
python3 -m http.server 8090 &
# Configure webhook in config.yaml
# Submit a job and watch for webhook calls
tail -f webhook-receiver.log
# Export with compression
hypersdk export \
--vm "/Datacenter/vm/large-vm" \
--format ova \
--compress \
--compression-level 9 \
--cleanup-ovf \
--output /exports
# Compare sizes
ls -lh /exports/*.ova*
# Add a schedule
curl -X POST http://localhost:8080/schedules \
-H "Content-Type: application/json" \
-d '{
"id": "test-schedule",
"name": "Test Schedule",
"schedule": "*/5 * * * *",
"enabled": true,
"job_template": {
"vm_path": "/Datacenter/vm/test",
"output_dir": "/exports"
}
}'
# Restart daemon
systemctl restart hypervisord
# Verify schedule restored
curl http://localhost:8080/schedules
# vCenter Connection
vcenter:
url: "https://vcenter.example.com"
username: "admin@vsphere.local"
password: "SecurePassword123"
insecure: true
timeout: 3600
# Connection Pooling (Phase 1.1)
connection_pool:
enabled: true
max_connections: 5
idle_timeout: 5m
health_check_interval: 30s
# Webhooks (Phase 1.2)
webhooks:
- url: "https://example.com/webhook"
events: ["*"] # or specific: ["job.started", "job.completed"]
headers:
Authorization: "Bearer token123"
X-Custom-Header: "value"
timeout: 10s
retry: 3
enabled: true
- url: "https://backup.example.com/notify"
events: ["job.completed"]
enabled: true
# Export Defaults (Phase 2.1, 2.2)
export:
format: "ova" # ovf or ova
compress: true # Enable gzip compression
compression_level: 6 # 0-9 (0=none, 9=best)
cleanup_ovf: true # Remove intermediate OVF files
parallel_downloads: 3 # Concurrent file downloads
remove_cdrom: true # Remove CD/DVD devices
# Database (Phase 2.3)
database:
path: "./hypersdk.db"
enable_wal: true
# Daemon
daemon:
address: "0.0.0.0:8080"
log_level: "info"
# Check pool statistics
curl http://localhost:8080/stats/pool
# Look for:
# - reuse_ratio < 0.5: Pool may be too small
# - total_connections == max_connections: Pool exhausted
# Check daemon logs
journalctl -u hypervisord -f | grep webhook
# Common issues:
# - Network connectivity
# - Invalid URL
# - Timeout too short
# - Server rejecting requests
# Validate OVA structure
hypersdk validate-ova /exports/vm-name.ova
# Extract for inspection
tar -tzf vm-name.ova | head
# First file should be .ovf
# Check database
sqlite3 hypersdk.db "SELECT * FROM scheduled_jobs;"
# Check daemon startup logs
journalctl -u hypervisord -n 100 | grep schedule
POST /jobs - Submit new jobGET /jobs - List all jobsGET /jobs/:id - Get job detailsDELETE /jobs/:id - Cancel jobPOST /schedules - Create scheduled jobGET /schedules - List all schedulesGET /schedules/:id - Get schedule detailsPUT /schedules/:id - Update scheduleDELETE /schedules/:id - Delete schedulePOST /schedules/:id/trigger - Manually triggerGET /schedules/:id/history - Get execution historyGET /stats - Daemon statisticsGET /stats/pool - Connection pool statisticsGET /stats/schedules - Schedule statisticsFor more information, see IMPLEMENTATION_SUMMARY.md.