diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 32589c696..25298a8cb 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -14,6 +14,7 @@ import ( "path/filepath" "regexp" "runtime" + "strconv" "strings" "syscall" "time" @@ -71,7 +72,7 @@ func (d *QemuDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, return false, fmt.Errorf("Unable to parse Qemu version string: %#v", matches) } - node.Attributes["driver.qemu"] = "true" + node.Attributes["driver.qemu"] = "1" node.Attributes["driver.qemu.version"] = matches[1] return true, nil @@ -92,6 +93,10 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, return nil, fmt.Errorf("Missing required Task Resource: Memory") } + if len(task.Resources.Networks) == 0 { + return nil, fmt.Errorf("[WARN] driver.qemu: No networks are available for port mapping") + } + // Attempt to download the thing // Should be extracted to some kind of Http Fetcher // Right now, assume publicly accessible HTTP url @@ -162,16 +167,40 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // TODO: Consolidate these into map of host/guest port when we have HCL // Note: Host port must be open and available - if task.Config["guest_port"] != "" && task.Config["host_port"] != "" { - args = append(args, - "-netdev", - fmt.Sprintf("user,id=user.0,hostfwd=tcp::%s-:%s", - task.Config["host_port"], - task.Config["guest_port"]), - "-device", "virtio-net,netdev=user.0", - ) + // Get and split guest ports. The guest_ports configuration must match up with + // the Reserved ports in the Task Resources + // Users can supply guest_hosts as a list of posts to map on the guest vm. + // These map 1:1 with the requested Reserved Ports from the hostmachine. + ports := strings.Split(task.Config["guest_ports"], ",") + if len(ports) == 0 { + return nil, fmt.Errorf("[ERR] driver.qemu: Error parsing required Guest Ports") } + // TODO: support more than a single, default Network + if len(ports) != len(task.Resources.Networks[0].ReservedPorts) { + return nil, fmt.Errorf("[ERR] driver.qemu: Error matching Guest Ports with Reserved ports") + } + + // Loop through the reserved ports and construct the hostfwd string, to map + // reserved ports to the ports listenting in the VM + // Ex: + // hostfwd=tcp::22000-:22,hostfwd=tcp::80-:8080 + reservedPorts := task.Resources.Networks[0].ReservedPorts + var forwarding string + for i, p := range ports { + forwarding = fmt.Sprintf("%s,hostfwd=tcp::%s-:%s", forwarding, strconv.Itoa(reservedPorts[i]), p) + } + + if "" == forwarding { + return nil, fmt.Errorf("[ERR] driver.qemu: Error constructing port forwarding") + } + + args = append(args, + "-netdev", + fmt.Sprintf("user,id=user.0%s", forwarding), + "-device", "virtio-net,netdev=user.0", + ) + // If using KVM, add optimization args if accelerator == "kvm" { args = append(args, diff --git a/client/driver/qemu_test.go b/client/driver/qemu_test.go index 0acde6a39..e7f216708 100644 --- a/client/driver/qemu_test.go +++ b/client/driver/qemu_test.go @@ -55,14 +55,16 @@ func TestQemuDriver_Start(t *testing.T) { "image_source": "https://dl.dropboxusercontent.com/u/47675/jar_thing/linux-0.2.img", "checksum": "a5e836985934c3392cbbd9b26db55a7d35a8d7ae1deb7ca559dd9c0159572544", "accelerator": "tcg", - "host_port": "8080", - "guest_port": "8081", + "guest_ports": "22,8080", + }, + Resources: &structs.Resources{ + MemoryMB: 512, + Networks: []*structs.NetworkResource{ + &structs.NetworkResource{ + ReservedPorts: []int{22000, 80}, + }, + }, }, - } - - // add requred memory resource - task.Resources = &structs.Resources{ - MemoryMB: 512, } handle, err := d.Start(ctx, task)