Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ Applies to `cocoon vm clone`:
| Flag | Default | Description |
| ----------- | ------------------------ | ------------------------------------------------------- |
| `--name` | `cocoon-clone-<id>` | VM name |
| `--nics` | inherit from snapshot | Override NIC count at clone time; lets a 0-NIC snapshot clone with networking (CH hot-swaps NICs after restore) |
| `--queue-size` | `0` (inherit) | Virtio-net ring depth per queue (0 = inherit from snapshot) |
| `--disk-queue-size` | `0` (inherit) | Virtio-blk ring depth per device (0 = inherit from snapshot; CH only) |
| `--network` | empty (inherit) | CNI conflist name (empty = inherit from source VM) |
Expand All @@ -215,10 +216,23 @@ Applies to `cocoon vm clone`:
| `--pull` | `false` | Auto-pull base image if not found locally (for cross-node clone) |
| `--from-dir` | empty | Clone from a snapshot directory (must contain `snapshot.json`); mutually exclusive with positional `SNAPSHOT` |

CPU, memory, storage, and NIC count all inherit from the snapshot — both
hypervisors restore the guest from the snapshot's binary device state, so
those values are fixed at snapshot time. Use `cocoon vm run` to create a
fresh VM with different resources.
CPU, memory, and storage all inherit from the snapshot — both hypervisors
restore the guest from the snapshot's binary device state, so those values
are fixed at snapshot time. NIC count inherits by default but `--nics N`
overrides it (CH only) by hot-swapping the snapshot's NICs for a fresh set
right after restore. Use `cocoon vm run` to create a fresh VM with different
CPU/memory/storage.

**Network backend** is decided per clone (the snapshot does not persist a
bridge device). Precedence:

1. `--bridge X` → bridge backend with bridge device `X`.
2. `--network Y` (no `--bridge`) → CNI backend with conflist `Y`.
3. neither → CNI backend, conflist inherited from the snapshot's recorded
`vmCfg.Network` (empty = CNI default).

A bridge-backed source snapshot cloned without `--bridge` silently defaults
to CNI. Pass `--bridge X` at clone time to keep bridge mode.

### Restore Flags

Expand Down Expand Up @@ -535,7 +549,7 @@ cocoon vm net my-vm --nics 1

Cocoon manages **host-side** plumbing only. CH's `vm.remove-device` marks the slot for ejection but the actual eject only happens when the guest cooperates via ACPI (B0EJ write). The host TAP / veth / CNI lease are torn down immediately after the API call regardless. Quiesce in-guest NIC state (driver unbind, NetworkManager removal, Windows NDIS halt) **before** reducing the count, or the in-guest driver will reference plumbing that no longer exists.

A VM started with zero NICs cannot be resized up (the VM record carries no provider hint). Start with at least one NIC if you plan to resize.
A VM started with zero NICs cannot be resized up — CH was launched in the host netns (no `NetworkConfigs` to derive a per-VM netns from), so later plumbing can't reach it. To recover networking on a 0-NIC snapshot, clone with `cocoon vm clone --nics 1 --network <conflist>` (or `--bridge <dev>`): the clone starts with NICs from the start, putting CH in the right netns from boot.

## Windows Support

Expand Down
1 change: 1 addition & 0 deletions cmd/vm/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ func addVMFlags(cmd *cobra.Command) {

func addCloneFlags(cmd *cobra.Command) {
cmd.Flags().String("name", "", "VM name (default: cocoon-clone-<id>)")
cmd.Flags().Int("nics", 0, "override NIC count (omit to inherit from snapshot)")
cmd.Flags().Int("queue-size", 0, "virtio-net ring depth per queue (0 = inherit from snapshot)") //nolint:mnd
cmd.Flags().Int("disk-queue-size", 0, "virtio-blk ring depth per device (0 = inherit from snapshot)") //nolint:mnd
cmd.Flags().String("network", "", "CNI conflist name (empty = inherit from source VM)")
Expand Down
4 changes: 2 additions & 2 deletions cmd/vm/netresize.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ func (h Handler) NetResize(cmd *cobra.Command, args []string) error {
return nil
}

// plumbingForVM picks the provider for the VM's existing NICs; fails on zero NICs (VMConfig has no bridge hint).
// plumbingForVM picks the provider matching the VM's existing NICs; zero NICs is fatal (use `vm clone --nics N` instead).
func plumbingForVM(conf *config.Config, configs []*types.NetworkConfig) (network.Network, error) {
if len(configs) == 0 {
return nil, fmt.Errorf("vm has zero NICs; resize up is not supported (start the VM with at least one NIC)")
return nil, fmt.Errorf("zero NICs; resize up not supported (use `vm clone --nics N` instead)")
}
return providerForVM(conf, nil, map[string]network.Network{}, configs)
}
6 changes: 5 additions & 1 deletion cmd/vm/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,11 @@ func (h Handler) prepareClone(ctx context.Context, cmd *cobra.Command, conf *con
}

bridgeDev, _ := cmd.Flags().GetString("bridge")
netProvider, networkConfigs, err := initNetwork(ctx, conf, vmID, cfg.NICs, vmCfg, tapQueues(vmCfg.CPU, conf.UseFirecracker), bridgeDev)
nics := cfg.NICs
if cmd.Flags().Changed("nics") {
nics, _ = cmd.Flags().GetInt("nics")
}
netProvider, networkConfigs, err := initNetwork(ctx, conf, vmID, nics, vmCfg, tapQueues(vmCfg.CPU, conf.UseFirecracker), bridgeDev)
if err != nil {
return nil, "", nil, nil, err
}
Expand Down
Loading