From 43e21c7963be399bbe1932fc211001194371d673 Mon Sep 17 00:00:00 2001 From: Nikolay Edigaryev Date: Fri, 26 Sep 2025 17:42:44 +0200 Subject: [PATCH] orchard create vm: "--nested" flag to enable nested virtualization (#346) --- api/openapi.yaml | 3 +++ internal/command/create/vm.go | 3 +++ internal/command/get/vm.go | 1 + internal/worker/vmmanager/vm.go | 19 ++++++++++++------- pkg/resource/v1/v1.go | 1 + 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/api/openapi.yaml b/api/openapi.yaml index 9b173c7..671c98f 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -439,6 +439,9 @@ components: for this VM to be scheduled additionalProperties: type: integer + nested: + type: boolean + description: Enable nested virtualization hostDirs: type: array items: diff --git a/internal/command/create/vm.go b/internal/command/create/vm.go index e857e34..f696083 100644 --- a/internal/command/create/vm.go +++ b/internal/command/create/vm.go @@ -22,6 +22,7 @@ var diskSize uint64 var netSoftnet bool var netBridged string var headless bool +var nested bool var username string var password string var resources map[string]string @@ -48,6 +49,7 @@ func newCreateVMCommand() *cobra.Command { command.Flags().BoolVar(&netSoftnet, "net-softnet", false, "whether to use Softnet network isolation") command.Flags().StringVar(&netBridged, "net-bridged", "", "whether to use Bridged network mode") command.Flags().BoolVar(&headless, "headless", true, "whether to run without graphics") + command.Flags().BoolVar(&nested, "nested", true, "enable nested virtualization") command.Flags().StringVar(&username, "username", "admin", "SSH username to use when executing a startup script on the VM") command.Flags().StringVar(&password, "password", "admin", @@ -106,6 +108,7 @@ func runCreateVM(cmd *cobra.Command, args []string) error { NetSoftnet: netSoftnet, NetBridged: netBridged, Headless: headless, + Nested: nested, Username: username, Password: password, RandomSerial: randomSerial, diff --git a/internal/command/get/vm.go b/internal/command/get/vm.go index aef5d95..ae10486 100644 --- a/internal/command/get/vm.go +++ b/internal/command/get/vm.go @@ -95,6 +95,7 @@ func runGetVM(cmd *cobra.Command, args []string) error { table.AddRow("Softnet enabled", vm.NetSoftnet) table.AddRow("Bridged networking interface", nonEmptyOrNone(vm.NetBridged)) table.AddRow("Headless mode", vm.Headless) + table.AddRow("Nested virtualization", vm.Nested) table.AddRow("Status", vm.Status) table.AddRow("Status message", vm.StatusMessage) table.AddRow("Assigned worker", nonEmptyOrNone(vm.Worker)) diff --git a/internal/worker/vmmanager/vm.go b/internal/worker/vmmanager/vm.go index 1dc209e..98aeb5b 100644 --- a/internal/worker/vmmanager/vm.go +++ b/internal/worker/vmmanager/vm.go @@ -5,6 +5,14 @@ import ( "context" "errors" "fmt" + "io" + "net" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "github.com/avast/retry-go" "github.com/cirruslabs/chacha/pkg/localnetworkhelper" "github.com/cirruslabs/orchard/internal/worker/ondiskname" @@ -15,13 +23,6 @@ import ( "go.opentelemetry.io/otel/metric" "go.uber.org/zap" "golang.org/x/crypto/ssh" - "io" - "net" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" ) var ErrVMFailed = errors.New("VM failed") @@ -346,6 +347,10 @@ func (vm *VM) run(ctx context.Context) error { runArgs = append(runArgs, "--no-graphics") } + if vm.Resource.Nested { + runArgs = append(runArgs, "--nested") + } + for _, hostDir := range vm.Resource.HostDirs { runArgs = append(runArgs, fmt.Sprintf("--dir=%s", hostDir.String())) } diff --git a/pkg/resource/v1/v1.go b/pkg/resource/v1/v1.go index e260f8f..8164010 100644 --- a/pkg/resource/v1/v1.go +++ b/pkg/resource/v1/v1.go @@ -27,6 +27,7 @@ type VM struct { NetSoftnet bool `json:"net-softnet,omitempty"` NetBridged string `json:"net-bridged,omitempty"` Headless bool `json:"headless,omitempty"` + Nested bool `json:"nested,omitempty"` // Status field is used to track the lifecycle of the VM associated with this resource. Status VMStatus `json:"status,omitempty"`