Skip to content

SynVirt Migrator (v0.1)

Synced read-only from /home/synnet/mirrors/synvirt-product/docs/MIGRATOR.md. Edit at the source, not here.

The migrator is SynVirt’s ingress engine for bringing virtual machines from somewhere else into a SynVirt host. It lives in crates/migrator/ (package synvirt-migrator) and is mounted by the daemon under /api/v1/migrator/*.

v0.1 delivers the full orchestration surface (plan submission, warm and cold paths, offline passthrough, SSE progress, sled persistence, AES-256-GCM credential sealing) together with:

  • One real live control-plane integration: the VIM-family SOAP API adapter (synvirt_migrator::adapters::vim_api). Its control plane (connect, list VMs, inspect, enable CBT, snapshot, shutdown, mark, cleanup) is wired end-to-end through a Go-based C-archive shim (adapters/vim_api/go-shim/). The shim is compiled in v0.2; in v0.1 every entry point short-circuits with Error::Internal so the pipeline is exercisable without the external dependency.
  • Five live adapter stubs: WmiApi, PveApi, LibvirtRemote, XenApi, PrismApi. Each implements SourceAdapter completely (connect validates the endpoint; every data-plane method errors with Error::Internal or Error::SnapshotFailed). TODO markers name the real wire protocols that will land in v0.3–v0.4.
  • Nine offline importers: OvaArchive, OvfFolder, VmxFolder, Vhdx, Qcow2, Vmdk, VmcxExport (passthrough to virt-v2v), VboxFolder, XvaArchive. Every importer has real inspect logic (tarball walkers, VMX / OVF / VBOX parsers, magic-byte probes, qemu-img info fallbacks); data streaming uses either direct Read over the on-disk file, a Read positioned at a tar member byte offset, or qemu-img convert -O raw <input> /dev/stdout piped through a 4 MiB chunker with zero-run detection.

v0.1 explicitly does not deliver:

  • Zero-downtime vMotion-style cross-vendor migration.
  • Application-aware guest configuration (cluster-service resources, database reconfiguration, Active Directory rejoin).
  • Remote URL sources (S3, SMB, HTTP) — scheduled for v0.3.
  • VDDK-accelerated live reads for the VIM-API adapter — scheduled for v0.7.
┌───────────────────────────────────────┐
│ MigrationPlan │
└───────────────┬───────────────────────┘
┌──────────────────────┴──────────────────────┐
│ │
plan.source = Live(…) plan.source = Offline(…)
│ │
┌─────────▼─────────┐ ┌─────────▼─────────┐
│ Live strategy │ │ Offline strategy │
│ SourceAdapter │ │ OfflineImporter │
└─────────┬─────────┘ └─────────┬─────────┘
│ │
┌──────────▼──────────┐ ┌─────────▼──────────┐
│ Warm mode? │ │ Passthrough? │
│ │ │ │
│ yes → CopyingFull → │ │ yes → run_full_ │
│ DeltaSyncing →│ │ import() │
│ CuttingOver │ │ no → read_disk() │
│ │ │ loop │
│ no → CopyingFull │ │ │
└──────────┬──────────┘ └─────────┬──────────┘
│ │
└──────────────────────┬──────────────────────┘
┌───────────────▼───────────────┐
│ V2vProcessing (Windows only) │
└───────────────┬───────────────┘
┌───────────────▼───────────────┐
│ Starting (DomainStarter) │
└───────────────┬───────────────┘
Completed

All three paths land on the same DestinationSink. The default is ZfsZvolSink, which creates one zfs create -s -V zvol per disk under <pool>/synvirt/migrations/<job-id>/<sanitized-label> with lz4 compression and a 64 KiB volblocksize. Zero runs are punched through blkdiscard with a fallback to explicit zero-fill.

If plan.mode == Warm but the source adapter reports supports_warm() == false, or VmManifest::cbt_available returns false during inspect, the orchestrator automatically downgrades to cold and records a non-fatal E_MIG_CBT_UNSUPPORTED entry on job.last_error. The job continues to advance; the UI surfaces the message as a banner.

Code Surface
E_MIG_SOURCE_UNREACHABLE control plane not responding
E_MIG_AUTH_FAILED credentials rejected
E_MIG_VM_NOT_FOUND inspect / list miss
E_MIG_CBT_UNSUPPORTED warm→cold downgrade (non-fatal)
E_MIG_CBT_RESET CBT invalidated mid-migration
E_MIG_SNAPSHOT_FAILED snapshot creation failed
E_MIG_DISK_READ_FAILED source disk read error
E_MIG_DISK_WRITE_FAILED destination disk write error
E_MIG_V2V_FAILED virtio injection failed
E_MIG_DOMAIN_DEFINE_FAILED libvirt domain define failed
E_MIG_GUEST_BOOT_FAILED destination did not boot
E_MIG_DELTA_TOO_LARGE warm delta loop did not converge
E_MIG_CANCELLED cancelled by operator
E_MIG_INVALID_PLAN plan validation error
E_MIG_PERSIST_FAILED sled I/O error
E_MIG_UNSUPPORTED_FORMAT offline kind not recognised
E_MIG_MALFORMED_ARCHIVE tar / OVF / VMX / VBOX parse error
E_MIG_UPLOAD_NOT_FOUND upload expired or removed
E_MIG_UPLOAD_REJECTED size cap / sha256 mismatch
E_MIG_HOST_PATH_REJECTED path escapes the import root
E_MIG_NOT_FOUND generic 404
E_MIG_CONFLICT generic 409
E_MIG_BAD_REQUEST generic 400
E_MIG_INTERNAL unclassified

Host binaries expected on the installed appliance:

  • zfs, blkdiscard — destination sink.
  • virt-v2v-in-place — Windows virtio injection.
  • virt-v2v with the hyperv input driver compiled in — VMCX passthrough import.
  • qemu-img — offline format streaming (QCOW2, VHDX dynamic, VMDK sparse, VMDK monolithic).
  • /usr/share/virtio-win/virtio-win.iso — sourced by VirtV2vInPlacePreparer. Override via VirtV2vInPlacePreparer::with_virtio_win_iso at construction time.
  • /var/lib/synvirt/imports/ — canonical offline import directory (mode 0750, owned by the daemon user). Uploaded archives land in .uploads/ underneath; the rest of the directory is available for administrators to drop files in via SCP / NFS / SMB.

Build-time dependency:

  • go >= 1.21 — needed to compile the VIM-API control-plane shim under crates/migrator/src/adapters/vim_api/go-shim/. If Go is missing at build time, build.rs emits a warning and the Rust side falls back to its stub path. Set SYNVIRT_MIGRATOR_SKIP_GO_SHIM=1 to skip even the probe.
/var/lib/synvirt/imports/
.uploads/
<upload-id> # multipart-uploaded archives
<arbitrary admin drops> # operator-placed archives
/var/lib/synvirt/keyring/
migrator.key # 32 bytes, AES-256-GCM key, mode 0600
/var/lib/synvirt/migrator.db # sled directory (jobs, watermarks,
# sources, uploads)

Mounted by the daemon under /api/v1/migrator. Authentication is handled by an upstream middleware (PAM Basic Auth today).

Method Path Purpose
GET /sources list registered sources
POST /sources register a new source
GET /sources/:id load one source
DELETE /sources/:id remove a source
POST /sources/:id/test probe connectivity
GET /sources/:id/vms discover VMs on the source
GET /sources/:id/vms/:vm_id inspect one source VM
GET /migrations list all migrations
POST /migrations submit a live or offline plan
GET /migrations/:id load one migration
POST /migrations/:id/cancel request cancellation
GET /migrations/stream SSE stream of updates
GET /uploads list uploads
POST /uploads multipart upload (20 GiB cap)
GET /uploads/:id load one upload
DELETE /uploads/:id drop an upload
POST /uploads/:id/inspect detect format + manifest
GET /host-files?path= browse inside the import root
POST /host-files/inspect detect format + manifest on host

POST /migrations accepts both forms:

{ "plan": { "source": { "Live": "vim_api" }, "source_ref": { "kind": "live_vm", "source_id": "<uuid>", "vm_id": "vm-42" }, } }
{ "plan": { "source": { "Offline": "ova_archive" }, "source_ref": { "kind": "offline_host_path", "path": "/var/lib/synvirt/imports/example.ova" }, } }
Milestone Work
v0.2 Compile the VIM-API Go shim; NBD read-side for warm + cold.
v0.3 Real WMI-API adapter; S3 / SMB / HTTP remote offline inputs.
v0.4 Real XenAPI and Prism-API adapters.
v0.5 Bulk import (multi-VM plans + dependency ordering).
v0.6 Scheduled delta syncs (Warm runs periodically, cutover on operator command).
v0.7 VDDK-accelerated reads for the VIM-API data plane.