diff --git a/mantle/cmd/kola/qemuexec.go b/mantle/cmd/kola/qemuexec.go index 30b8b827e9bb0c2a8e4f9828bb795eef58ea504d..5a16be4011759c9dbfcfd3456f92d91d3dfa2b48 100644 --- a/mantle/cmd/kola/qemuexec.go +++ b/mantle/cmd/kola/qemuexec.go @@ -70,6 +70,11 @@ var ( sshCommand string additionalNics int + + netboot string + netbootDir string + + usernetAddr string ) const maxAdditionalNics = 16 @@ -96,7 +101,9 @@ func init() { cmdQemuExec.Flags().StringVarP(&consoleFile, "console-to-file", "", "", "Filepath in which to save serial console logs") cmdQemuExec.Flags().IntVarP(&additionalNics, "additional-nics", "", 0, "Number of additional NICs to add") cmdQemuExec.Flags().StringVarP(&sshCommand, "ssh-command", "x", "", "Command to execute instead of spawning a shell") - + cmdQemuExec.Flags().StringVarP(&netboot, "netboot", "", "", "Filepath to BOOTP program (e.g. PXELINUX/GRUB binary or iPXE script") + cmdQemuExec.Flags().StringVarP(&netbootDir, "netboot-dir", "", "", "Directory to serve over TFTP (default: BOOTP parent dir). If specified, --netboot is relative to this dir.") + cmdQemuExec.Flags().StringVarP(&usernetAddr, "usernet-addr", "", "", "Guest IP network (QEMU default is '10.0.2.0/24')") } func renderFragments(fragments []string, c *conf.Conf) error { @@ -306,21 +313,19 @@ func runQemuExec(cmd *cobra.Command, args []string) error { if kola.QEMUOptions.Firmware != "" { builder.Firmware = kola.QEMUOptions.Firmware } - if kola.QEMUOptions.DiskImage != "" { + if kola.QEMUOptions.DiskImage != "" && netboot == "" { if err := builder.AddBootDisk(buildDiskFromOptions()); err != nil { return err } - if err != nil { return err } } - if kola.QEMUIsoOptions.IsoPath != "" { + if kola.QEMUIsoOptions.IsoPath != "" && netboot == "" { err := builder.AddIso(kola.QEMUIsoOptions.IsoPath, "bootindex=3", kola.QEMUIsoOptions.AsDisk) if err != nil { return err } - // TODO: if kola.QEMUOptions.DiskImage != "" & kola.QEMUIsoOptions.IsoPath != "", still add a blank disk? // Add a blank disk (this is a disk we can install to) if err := builder.AddBootDisk(buildDiskFromOptions()); err != nil { return err @@ -345,11 +350,14 @@ func runQemuExec(cmd *cobra.Command, args []string) error { if cpuCountHost { builder.Processors = -1 } - if usernet { + if usernet || usernetAddr != "" { h := []platform.HostForwardPort{ {Service: "ssh", HostPort: 0, GuestPort: 22}, } - builder.EnableUsermodeNetworking(h) + builder.EnableUsermodeNetworking(h, usernetAddr) + } + if netboot != "" { + builder.SetNetbootP(netboot, netbootDir) } if additionalNics != 0 { if additionalNics < 0 || additionalNics > maxAdditionalNics { diff --git a/mantle/platform/machine/qemu/cluster.go b/mantle/platform/machine/qemu/cluster.go index 874b5387e085b62f13d549fb9ceff4df85c2b881..8b0e4dce99cdbeca01b4ac8fe15e4612c4b49b89 100644 --- a/mantle/platform/machine/qemu/cluster.go +++ b/mantle/platform/machine/qemu/cluster.go @@ -167,12 +167,12 @@ func (qc *Cluster) NewMachineWithQemuOptions(userdata *conf.UserData, options pl } if len(options.HostForwardPorts) > 0 { - builder.EnableUsermodeNetworking(options.HostForwardPorts) + builder.EnableUsermodeNetworking(options.HostForwardPorts, "") } else { h := []platform.HostForwardPort{ {Service: "ssh", HostPort: 0, GuestPort: 22}, } - builder.EnableUsermodeNetworking(h) + builder.EnableUsermodeNetworking(h, "") } if options.AdditionalNics > 0 { builder.AddAdditionalNics(options.AdditionalNics) diff --git a/mantle/platform/machine/qemuiso/cluster.go b/mantle/platform/machine/qemuiso/cluster.go index b430bb35a6b74fef47bfe2e77da95eb0ceed686b..9223192149122f306aa21c30b5b061374e38492b 100644 --- a/mantle/platform/machine/qemuiso/cluster.go +++ b/mantle/platform/machine/qemuiso/cluster.go @@ -124,12 +124,12 @@ func (qc *Cluster) NewMachineWithQemuOptions(userdata *conf.UserData, options pl } if len(options.HostForwardPorts) > 0 { - builder.EnableUsermodeNetworking(options.HostForwardPorts) + builder.EnableUsermodeNetworking(options.HostForwardPorts, "") } else { h := []platform.HostForwardPort{ {Service: "ssh", HostPort: 0, GuestPort: 22}, } - builder.EnableUsermodeNetworking(h) + builder.EnableUsermodeNetworking(h, "") } if options.AdditionalNics > 0 { diff --git a/mantle/platform/qemu.go b/mantle/platform/qemu.go index 1d1f46f75afcaa1e5d564bce456894eceefda3db..ccdbe66e7678d950d5efe1167e7fb521928a1307 100644 --- a/mantle/platform/qemu.go +++ b/mantle/platform/qemu.go @@ -478,9 +478,12 @@ type QemuBuilder struct { ignitionRendered bool UsermodeNetworking bool + usermodeNetworkingAddr string RestrictNetworking bool requestedHostForwardPorts []HostForwardPort additionalNics int + netbootP string + netbootDir string finalized bool diskID uint @@ -597,9 +600,16 @@ func virtio(arch, device, args string) string { // EnableUsermodeNetworking configure forwarding for all requested ports, // via usermode network helpers. -func (builder *QemuBuilder) EnableUsermodeNetworking(h []HostForwardPort) { +func (builder *QemuBuilder) EnableUsermodeNetworking(h []HostForwardPort, usernetAddr string) { builder.UsermodeNetworking = true builder.requestedHostForwardPorts = h + builder.usermodeNetworkingAddr = usernetAddr +} + +func (builder *QemuBuilder) SetNetbootP(filename, dir string) { + builder.UsermodeNetworking = true + builder.netbootP = filename + builder.netbootDir = dir } func (builder *QemuBuilder) AddAdditionalNics(additionalNics int) { @@ -629,6 +639,34 @@ func (builder *QemuBuilder) setupNetworking() error { if builder.RestrictNetworking { netdev += ",restrict=on" } + if builder.usermodeNetworkingAddr != "" { + netdev += ",net=" + builder.usermodeNetworkingAddr + } + if builder.netbootP != "" { + // do an early stat so we fail with a nicer error now instead of in the VM + if _, err := os.Stat(filepath.Join(builder.netbootDir, builder.netbootP)); err != nil { + return err + } + tftpDir := "" + relpath := "" + if builder.netbootDir == "" { + absPath, err := filepath.Abs(builder.netbootP) + if err != nil { + return err + } + tftpDir = filepath.Dir(absPath) + relpath = filepath.Base(absPath) + } else { + absPath, err := filepath.Abs(builder.netbootDir) + if err != nil { + return err + } + tftpDir = absPath + relpath = builder.netbootP + } + netdev += fmt.Sprintf(",tftp=%s,bootfile=/%s", tftpDir, relpath) + builder.Append("-boot", "order=n") + } builder.Append("-netdev", netdev, "-device", virtio(builder.architecture, "net", "netdev=eth0")) return nil