diff --git a/builder/vmware/builder.go b/builder/vmware/builder.go index 7a2da502f..3ea85f39f 100644 --- a/builder/vmware/builder.go +++ b/builder/vmware/builder.go @@ -359,7 +359,7 @@ func (b *Builder) Cancel() { } } -func (b *Builder) newDriver() (Driver, error) { +func (b *Builder) newFusionDriver() (Driver, error) { fusionAppPath := "/Applications/VMware Fusion.app" driver := &Fusion5Driver{fusionAppPath} if err := driver.Verify(); err != nil { @@ -368,3 +368,27 @@ func (b *Builder) newDriver() (Driver, error) { return driver, nil } + +func (b *Builder) newWorkstationLinuxDriver() (Driver, error) { + driver := &WS9LnxDriver{} + if err := driver.Verify(); err != nil { + return nil, err + } + + return driver, nil +} + +func (b *Builder) newDriver() (Driver, error) { + fusion, fusionErr := b.newFusionDriver() + ws, wsErr := b.newWorkstationLinuxDriver() + + if fusionErr == nil { + return fusion, nil + } + + if wsErr == nil { + return ws, nil + } + + return nil, fmt.Errorf("Unable to initialise VMware driver:\nFusion 5: %s\nWorkstation 9: %s", fusionErr, wsErr) +} diff --git a/builder/vmware/driver.go b/builder/vmware/driver.go index 7e3588b29..d4ef6e3a3 100644 --- a/builder/vmware/driver.go +++ b/builder/vmware/driver.go @@ -30,6 +30,9 @@ type Driver interface { // Get the path to the VMware ISO for the given flavor. ToolsIsoPath(string) string + // Get the path to the DHCP leases file for the given device. + DhcpLeasesPath(string) string + // Verify checks to make sure that this driver should function // properly. This should check that all the files it will use // appear to exist and so on. If everything is okay, this doesn't @@ -150,6 +153,10 @@ func (d *Fusion5Driver) ToolsIsoPath(k string) string { return filepath.Join(d.AppPath, "Contents", "Library", "isoimages", k+".iso") } +func (d *Fusion5Driver) DhcpLeasesPath(device string) string { + return "/etc/vmware/vmnet-dhcpd-" + device + ".leases" +} + func (d *Fusion5Driver) runAndLog(cmd *exec.Cmd) (string, string, error) { var stdout, stderr bytes.Buffer @@ -163,3 +170,143 @@ func (d *Fusion5Driver) runAndLog(cmd *exec.Cmd) (string, string, error) { return stdout.String(), stderr.String(), err } + +// WS9LnxDriver is a driver that can run VMware Workstation 9 on Linux. +type WS9LnxDriver struct { + // These are paths to useful VMware Workstation binaries + AppPath string + VdiskManagerPath string + VmrunPath string +} + +func (d *WS9LnxDriver) CompactDisk(diskPath string) error { + defragCmd := exec.Command(d.VdiskManagerPath, "-d", diskPath) + if _, _, err := d.runAndLog(defragCmd); err != nil { + return err + } + + shrinkCmd := exec.Command(d.VdiskManagerPath, "-k", diskPath) + if _, _, err := d.runAndLog(shrinkCmd); err != nil { + return err + } + + return nil +} + +func (d *WS9LnxDriver) CreateDisk(output string, size string) error { + cmd := exec.Command(d.VdiskManagerPath, "-c", "-s", size, "-a", "lsilogic", "-t", "1", output) + if _, _, err := d.runAndLog(cmd); err != nil { + return err + } + + return nil +} + +func (d *WS9LnxDriver) IsRunning(vmxPath string) (bool, error) { + vmxPath, err := filepath.Abs(vmxPath) + if err != nil { + return false, err + } + + cmd := exec.Command(d.VmrunPath, "-T", "ws", "list") + stdout, _, err := d.runAndLog(cmd) + if err != nil { + return false, err + } + + for _, line := range strings.Split(stdout, "\n") { + if line == vmxPath { + return true, nil + } + } + + return false, nil +} + +func (d *WS9LnxDriver) Start(vmxPath string, headless bool) error { + guiArgument := "gui" + if headless { + guiArgument = "nogui" + } + + cmd := exec.Command(d.VmrunPath, "-T", "ws", "start", vmxPath, guiArgument) + if _, _, err := d.runAndLog(cmd); err != nil { + return err + } + + return nil +} + +func (d *WS9LnxDriver) Stop(vmxPath string) error { + cmd := exec.Command(d.VmrunPath, "-T", "ws", "stop", vmxPath, "hard") + if _, _, err := d.runAndLog(cmd); err != nil { + return err + } + + return nil +} + +func (d *WS9LnxDriver) Verify() error { + if err := d.findApp(); err != nil { + return fmt.Errorf("VMware Workstation application ('vmware') not found in path.") + } + + if err := d.findVmrun(); err != nil { + return fmt.Errorf("Critical application 'vmrun' not found in path.") + } + + if err := d.findVdiskManager(); err != nil { + return fmt.Errorf("Critical application 'vmware-vdiskmanager' not found in path.") + } + + return nil +} + +func (d *WS9LnxDriver) findApp() error { + path, err := exec.LookPath("vmware") + if err != nil { + return err + } + d.AppPath = path + return nil +} + +func (d *WS9LnxDriver) findVdiskManager() error { + path, err := exec.LookPath("vmware-vdiskmanager") + if err != nil { + return err + } + d.VdiskManagerPath = path + return nil +} + +func (d *WS9LnxDriver) findVmrun() error { + path, err := exec.LookPath("vmrun") + if err != nil { + return err + } + d.VmrunPath = path + return nil +} + +func (d *WS9LnxDriver) ToolsIsoPath(flavor string) string { + return "/usr/lib/vmware/isoimages/" + flavor + ".iso" +} + +func (d *WS9LnxDriver) DhcpLeasesPath(device string) string { + return "/etc/vmware/" + device + "/dhcpd/dhcpd.leases" +} + +func (d *WS9LnxDriver) runAndLog(cmd *exec.Cmd) (string, string, error) { + var stdout, stderr bytes.Buffer + + log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:]) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + log.Printf("stdout: %s", strings.TrimSpace(stdout.String())) + log.Printf("stderr: %s", strings.TrimSpace(stderr.String())) + + return stdout.String(), stderr.String(), err +} diff --git a/builder/vmware/guest_ip.go b/builder/vmware/guest_ip.go index 5d1428e10..2a79d52ff 100644 --- a/builder/vmware/guest_ip.go +++ b/builder/vmware/guest_ip.go @@ -2,7 +2,6 @@ package vmware import ( "errors" - "fmt" "io/ioutil" "os" "regexp" @@ -18,6 +17,9 @@ type GuestIPFinder interface { // DHCPLeaseGuestLookup looks up the IP address of a guest using DHCP // lease information from the VMware network devices. type DHCPLeaseGuestLookup struct { + // Driver that is being used (to find leases path) + Driver Driver + // Device that the guest is connected to. Device string @@ -26,7 +28,7 @@ type DHCPLeaseGuestLookup struct { } func (f *DHCPLeaseGuestLookup) GuestIP() (string, error) { - fh, err := os.Open(fmt.Sprintf("/var/db/vmware/vmnet-dhcpd-%s.leases", f.Device)) + fh, err := os.Open(f.Driver.DhcpLeasesPath(f.Device)) if err != nil { return "", err } diff --git a/builder/vmware/host_ip.go b/builder/vmware/host_ip.go index 90fc9a6fc..053e70432 100644 --- a/builder/vmware/host_ip.go +++ b/builder/vmware/host_ip.go @@ -33,7 +33,7 @@ func (f *IfconfigIPFinder) HostIP() (string, error) { return "", err } - re := regexp.MustCompile(`inet\s*(.+?)\s`) + re := regexp.MustCompile(`inet\s*(?:addr:)?(.+?)\s`) matches := re.FindStringSubmatch(stdout.String()) if matches == nil { return "", errors.New("IP not found in ifconfig output...") diff --git a/builder/vmware/ssh.go b/builder/vmware/ssh.go index e2de12eb2..2fa27afff 100644 --- a/builder/vmware/ssh.go +++ b/builder/vmware/ssh.go @@ -12,6 +12,7 @@ import ( func sshAddress(state map[string]interface{}) (string, error) { config := state["config"].(*config) + driver := state["driver"].(Driver) vmxPath := state["vmx_path"].(string) log.Println("Lookup up IP information...") @@ -37,6 +38,7 @@ func sshAddress(state map[string]interface{}) (string, error) { } ipLookup := &DHCPLeaseGuestLookup{ + Driver: driver, Device: "vmnet8", MACAddress: macAddress, }