From 750ffc8a54af1c0e5f1ba185498fb577fbea5aef Mon Sep 17 00:00:00 2001 From: Ross Smith II Date: Mon, 12 May 2014 18:35:37 -0700 Subject: [PATCH] builder/vmware: add VMWare Player 6 support Also added VMWare Player support in Windows Fixes #972 --- builder/vmware/common/driver.go | 22 +-- builder/vmware/common/driver_player5.go | 123 ++++++------ .../vmware/common/driver_player5_windows.go | 175 ++++++++++++++++++ builder/vmware/common/driver_player6.go | 37 ++++ .../vmware/common/driver_player6_windows.go | 35 ++++ builder/vmware/common/driver_player_unix.go | 68 +++++++ 6 files changed, 388 insertions(+), 72 deletions(-) create mode 100644 builder/vmware/common/driver_player5_windows.go create mode 100644 builder/vmware/common/driver_player6.go create mode 100644 builder/vmware/common/driver_player6_windows.go create mode 100644 builder/vmware/common/driver_player_unix.go diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index 35a358cc4..3378c42f7 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -78,19 +78,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) { }, } case "linux": - drivers = []Driver{ - &Workstation10Driver{ - Workstation9Driver: Workstation9Driver{ - SSHConfig: config, - }, - }, - &Workstation9Driver{ - SSHConfig: config, - }, - &Player5LinuxDriver{ - SSHConfig: config, - }, - } + fallthrough case "windows": drivers = []Driver{ &Workstation10Driver{ @@ -101,6 +89,14 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) { &Workstation9Driver{ SSHConfig: config, }, + &Player6Driver{ + Player5Driver: Player5Driver{ + SSHConfig: config, + }, + }, + &Player5Driver{ + SSHConfig: config, + }, } default: return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS) diff --git a/builder/vmware/common/driver_player5.go b/builder/vmware/common/driver_player5.go index 96c92e1e3..2444ca662 100644 --- a/builder/vmware/common/driver_player5.go +++ b/builder/vmware/common/driver_player5.go @@ -3,6 +3,7 @@ package common import ( "errors" "fmt" + "log" "os" "os/exec" "path/filepath" @@ -11,8 +12,8 @@ import ( "github.com/mitchellh/multistep" ) -// Player5LinuxDriver is a driver that can run VMware Player 5 on Linux. -type Player5LinuxDriver struct { +// Player5Driver is a driver that can run VMware Player 5 on Linux. +type Player5Driver struct { AppPath string VdiskManagerPath string QemuImgPath string @@ -22,11 +23,11 @@ type Player5LinuxDriver struct { SSHConfig *SSHConfig } -func (d *Player5LinuxDriver) Clone(dst, src string) error { - return errors.New("Cloning is not supported with Player 5. Please use Player 6+.") +func (d *Player5Driver) Clone(dst, src string) error { + return errors.New("Cloning is not supported with VMWare Player version 5. Please use VMWare Player version 6, or greater.") } -func (d *Player5LinuxDriver) CompactDisk(diskPath string) error { +func (d *Player5Driver) CompactDisk(diskPath string) error { if d.QemuImgPath != "" { return d.qemuCompactDisk(diskPath) } @@ -44,7 +45,7 @@ func (d *Player5LinuxDriver) CompactDisk(diskPath string) error { return nil } -func (d *Player5LinuxDriver) qemuCompactDisk(diskPath string) error { +func (d *Player5Driver) qemuCompactDisk(diskPath string) error { cmd := exec.Command(d.QemuImgPath, "convert", "-f", "vmdk", "-O", "vmdk", "-o", "compat6", diskPath, diskPath+".new") if _, _, err := runAndLog(cmd); err != nil { return err @@ -61,7 +62,7 @@ func (d *Player5LinuxDriver) qemuCompactDisk(diskPath string) error { return nil } -func (d *Player5LinuxDriver) CreateDisk(output string, size string, type_id string) error { +func (d *Player5Driver) CreateDisk(output string, size string, type_id string) error { var cmd *exec.Cmd if d.QemuImgPath != "" { cmd = exec.Command(d.QemuImgPath, "create", "-f", "vmdk", "-o", "compat6", output, size) @@ -75,7 +76,7 @@ func (d *Player5LinuxDriver) CreateDisk(output string, size string, type_id stri return nil } -func (d *Player5LinuxDriver) IsRunning(vmxPath string) (bool, error) { +func (d *Player5Driver) IsRunning(vmxPath string) (bool, error) { vmxPath, err := filepath.Abs(vmxPath) if err != nil { return false, err @@ -96,11 +97,11 @@ func (d *Player5LinuxDriver) IsRunning(vmxPath string) (bool, error) { return false, nil } -func (d *Player5LinuxDriver) SSHAddress(state multistep.StateBag) (string, error) { +func (d *Player5Driver) SSHAddress(state multistep.StateBag) (string, error) { return SSHAddressFunc(d.SSHConfig)(state) } -func (d *Player5LinuxDriver) Start(vmxPath string, headless bool) error { +func (d *Player5Driver) Start(vmxPath string, headless bool) error { guiArgument := "gui" if headless { guiArgument = "nogui" @@ -114,7 +115,7 @@ func (d *Player5LinuxDriver) Start(vmxPath string, headless bool) error { return nil } -func (d *Player5LinuxDriver) Stop(vmxPath string) error { +func (d *Player5Driver) Stop(vmxPath string) error { cmd := exec.Command(d.VmrunPath, "-T", "player", "stop", vmxPath, "hard") if _, _, err := runAndLog(cmd); err != nil { return err @@ -123,74 +124,78 @@ func (d *Player5LinuxDriver) Stop(vmxPath string) error { return nil } -func (d *Player5LinuxDriver) SuppressMessages(vmxPath string) error { +func (d *Player5Driver) SuppressMessages(vmxPath string) error { return nil } -func (d *Player5LinuxDriver) Verify() error { - if err := d.findApp(); err != nil { - return fmt.Errorf("VMware Player application ('vmplayer') 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 { - if err := d.findQemuImg(); err != nil { - return fmt.Errorf( - "Neither 'vmware-vdiskmanager', nor 'qemu-img' found in path.\n" + - "One of these is required to configure disks for VMware Player.") +func (d *Player5Driver) Verify() error { + var err error + if d.AppPath == "" { + if d.AppPath, err = playerFindVMware(); err != nil { + return err } } - return nil -} - -func (d *Player5LinuxDriver) findApp() error { - path, err := exec.LookPath("vmplayer") - if err != nil { - return err + if d.VmrunPath == "" { + if d.VmrunPath, err = playerFindVmrun(); err != nil { + return err + } } - d.AppPath = path - return nil -} -func (d *Player5LinuxDriver) findVdiskManager() error { - path, err := exec.LookPath("vmware-vdiskmanager") - if err != nil { - return err + if d.VdiskManagerPath == "" { + d.VdiskManagerPath, err = playerFindVdiskManager() } - d.VdiskManagerPath = path - return nil -} -func (d *Player5LinuxDriver) findQemuImg() error { - path, err := exec.LookPath("qemu-img") - if err != nil { - return err + if d.VdiskManagerPath == "" && d.QemuImgPath == "" { + d.QemuImgPath, err = playerFindQemuImg() } - d.QemuImgPath = path - return nil -} -func (d *Player5LinuxDriver) findVmrun() error { - path, err := exec.LookPath("vmrun") if err != nil { - return err + return fmt.Errorf( + "Neither 'vmware-vdiskmanager', nor 'qemu-img' found in path.\n" + + "One of these is required to configure disks for VMware Player.") } - d.VmrunPath = path + + log.Printf("VMware app path: %s", d.AppPath) + log.Printf("vmrun path: %s", d.VmrunPath) + log.Printf("vdisk-manager path: %s", d.VdiskManagerPath) + log.Printf("qemu-img path: %s", d.QemuImgPath) + + if _, err := os.Stat(d.AppPath); err != nil { + return fmt.Errorf("VMware application not found: %s", d.AppPath) + } + + if _, err := os.Stat(d.VmrunPath); err != nil { + return fmt.Errorf("'vmrun' application not found: %s", d.VmrunPath) + } + + if d.VdiskManagerPath != "" { + _, err = os.Stat(d.VdiskManagerPath) + } else { + _, err = os.Stat(d.QemuImgPath) + } + + if err != nil { + return fmt.Errorf( + "Neither 'vmware-vdiskmanager', nor 'qemu-img' found in path.\n" + + "One of these is required to configure disks for VMware Player.") + } + return nil } -func (d *Player5LinuxDriver) ToolsIsoPath(flavor string) string { - return "/usr/lib/vmware/isoimages/" + flavor + ".iso" +func (d *Player5Driver) ToolsIsoPath(flavor string) string { + return playerToolsIsoPath(flavor) } -func (d *Player5LinuxDriver) ToolsInstall() error { +func (d *Player5Driver) ToolsInstall() error { return nil } -func (d *Player5LinuxDriver) DhcpLeasesPath(device string) string { - return "/etc/vmware/" + device + "/dhcpd/dhcpd.leases" +func (d *Player5Driver) DhcpLeasesPath(device string) string { + return playerDhcpLeasesPath(device) } + +func (d *Player5Driver) VmnetnatConfPath() string { + return playerVmnetnatConfPath() +} \ No newline at end of file diff --git a/builder/vmware/common/driver_player5_windows.go b/builder/vmware/common/driver_player5_windows.go new file mode 100644 index 000000000..e16d275db --- /dev/null +++ b/builder/vmware/common/driver_player5_windows.go @@ -0,0 +1,175 @@ +// +build windows + +package common + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "syscall" +) + +func playerFindVdiskManager() (string, error) { + path, err := exec.LookPath("vmware-vdiskmanager.exe") + if err == nil { + return path, nil + } + + return findFile("vmware-vdiskmanager.exe", playerProgramFilePaths()), nil +} + +func playerFindQemuImg() (string, error) { + path, err := exec.LookPath("qemu-img.exe") + if err == nil { + return path, nil + } + + return findFile("qemu-img.exe", playerProgramFilePaths()), nil +} + +func playerFindVMware() (string, error) { + path, err := exec.LookPath("vmplayer.exe") + if err == nil { + return path, nil + } + + return findFile("vmplayer.exe", playerProgramFilePaths()), nil +} + +func playerFindVmrun() (string, error) { + path, err := exec.LookPath("vmrun.exe") + if err == nil { + return path, nil + } + + return findFile("vmrun.exe", playerProgramFilePaths()), nil +} + +func playerToolsIsoPath(flavor string) string { + return findFile(flavor+".iso", playerProgramFilePaths()) +} + +func playerDhcpLeasesPath(device string) string { + path, err := playerDhcpLeasesPathRegistry() + if err != nil { + log.Printf("Error finding leases in registry: %s", err) + } else if _, err := os.Stat(path); err == nil { + return path + } + + return findFile("vmnetdhcp.leases", playerDataFilePaths()) +} + +func playerVmnetnatConfPath() string { + return findFile("vmnetnat.conf", playerDataFilePaths()) +} + +// This reads the VMware installation path from the Windows registry. +func playerVMwareRoot() (s string, err error) { + key := `SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\vmplayer.exe` + subkey := "Path" + s, err = readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) + if err != nil { + log.Printf(`Unable to read registry key %s\%s`, key, subkey) + return + } + + return normalizePath(s), nil +} + +// This reads the VMware DHCP leases path from the Windows registry. +func playerDhcpLeasesPathRegistry() (s string, err error) { + key := "SYSTEM\\CurrentControlSet\\services\\VMnetDHCP\\Parameters" + subkey := "LeaseFile" + s, err = readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) + if err != nil { + log.Printf(`Unable to read registry key %s\%s`, key, subkey) + return + } + + return normalizePath(s), nil +} + +// playerProgramFilesPaths returns a list of paths that are eligible +// to contain program files we may want just as vmware.exe. +func playerProgramFilePaths() []string { + path, err := playerVMwareRoot() + if err != nil { + log.Printf("Error finding VMware root: %s", err) + } + + paths := make([]string, 0, 5) + if os.Getenv("VMWARE_HOME") != "" { + paths = append(paths, os.Getenv("VMWARE_HOME")) + } + + if path != "" { + paths = append(paths, path) + } + + if os.Getenv("ProgramFiles(x86)") != "" { + paths = append(paths, + filepath.Join(os.Getenv("ProgramFiles(x86)"), "/VMware/VMware Player")) + } + + if os.Getenv("ProgramFiles") != "" { + paths = append(paths, + filepath.Join(os.Getenv("ProgramFiles"), "/VMware/VMware Player")) + } + + if os.Getenv("QEMU_HOME") != "" { + paths = append(paths, os.Getenv("QEMU_HOME")) + } + + if os.Getenv("ProgramFiles(x86)") != "" { + paths = append(paths, + filepath.Join(os.Getenv("ProgramFiles(x86)"), "/QEMU")) + } + + if os.Getenv("ProgramFiles") != "" { + paths = append(paths, + filepath.Join(os.Getenv("ProgramFiles"), "/QEMU")) + } + + if os.Getenv("SystemDrive") != "" { + paths = append(paths, + filepath.Join(os.Getenv("SystemDrive"), "/QEMU")) + } + + return paths +} + +// playerDataFilePaths returns a list of paths that are eligible +// to contain data files we may want such as vmnet NAT configuration files. +func playerDataFilePaths() []string { + leasesPath, err := playerDhcpLeasesPathRegistry() + if err != nil { + log.Printf("Error getting DHCP leases path: %s", err) + } + + if leasesPath != "" { + leasesPath = filepath.Dir(leasesPath) + } + + paths := make([]string, 0, 5) + if os.Getenv("VMWARE_DATA") != "" { + paths = append(paths, os.Getenv("VMWARE_DATA")) + } + + if leasesPath != "" { + paths = append(paths, leasesPath) + } + + if os.Getenv("ProgramData") != "" { + paths = append(paths, + filepath.Join(os.Getenv("ProgramData"), "/VMware")) + } + + if os.Getenv("ALLUSERSPROFILE") != "" { + paths = append(paths, + filepath.Join(os.Getenv("ALLUSERSPROFILE"), "/Application Data/VMware")) + } + + return paths +} diff --git a/builder/vmware/common/driver_player6.go b/builder/vmware/common/driver_player6.go new file mode 100644 index 000000000..d1cc7be88 --- /dev/null +++ b/builder/vmware/common/driver_player6.go @@ -0,0 +1,37 @@ +package common + +import ( + "os/exec" +) + +const VMWARE_PLAYER_VERSION = "6" + +// Player6Driver is a driver that can run VMware Player 6 +// installations. + +type Player6Driver struct { + Player5Driver +} + +func (d *Player6Driver) Clone(dst, src string) error { + // TODO(rasa) check if running player+, not just player + + cmd := exec.Command(d.Player5Driver.VmrunPath, + "-T", "ws", + "clone", src, dst, + "full") + + if _, _, err := runAndLog(cmd); err != nil { + return err + } + + return nil +} + +func (d *Player6Driver) Verify() error { + if err := d.Player5Driver.Verify(); err != nil { + return err + } + + return playerVerifyVersion(VMWARE_PLAYER_VERSION) +} diff --git a/builder/vmware/common/driver_player6_windows.go b/builder/vmware/common/driver_player6_windows.go new file mode 100644 index 000000000..78fd06618 --- /dev/null +++ b/builder/vmware/common/driver_player6_windows.go @@ -0,0 +1,35 @@ +// +build windows + +package common + +import ( + "fmt" + "log" + "regexp" + "syscall" +) + +func playerVerifyVersion(version string) error { + key := `SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player` + subkey := "ProductVersion" + productVersion, err := readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) + if err != nil { + log.Printf(`Unable to read registry key %s\%s`, key, subkey) + key = `SOFTWARE\VMware, Inc.\VMware Player` + productVersion, err = readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) + if err != nil { + log.Printf(`Unable to read registry key %s\%s`, key, subkey) + return err + } + } + + versionRe := regexp.MustCompile(`^(\d+)\.`) + matches := versionRe.FindStringSubmatch(productVersion) + if matches == nil { + return fmt.Errorf( + `Could not find a VMware Player version in registry key %s\%s: '%s'`, key, subkey, productVersion) + } + log.Printf("Detected VMware Player version: %s", matches[1]) + + return compareVersions(matches[1], version) +} diff --git a/builder/vmware/common/driver_player_unix.go b/builder/vmware/common/driver_player_unix.go new file mode 100644 index 000000000..7f23c4453 --- /dev/null +++ b/builder/vmware/common/driver_player_unix.go @@ -0,0 +1,68 @@ +// +build !windows + +// These functions are compatible with WS 9 and 10 on *NIX +package common + +import ( + "bytes" + "fmt" + "log" + "os/exec" + "regexp" + "runtime" +) + +func playerFindVdiskManager() (string, error) { + return exec.LookPath("vmware-vdiskmanager") +} + +func playerFindQemuImg() (string, error) { + return exec.LookPath("qemu-img") +} + +func playerFindVMware() (string, error) { + return exec.LookPath("vmplayer") +} + +func playerFindVmrun() (string, error) { + return exec.LookPath("vmrun") +} + +func playerDhcpLeasesPath(device string) string { + return "/etc/vmware/" + device + "/dhcpd/dhcpd.leases" +} + +func playerToolsIsoPath(flavor string) string { + return "/usr/lib/vmware/isoimages/" + flavor + ".iso" +} + +func playerVmnetnatConfPath() string { + return "" +} + +func playerVerifyVersion(version string) error { + if runtime.GOOS != "linux" { + return fmt.Errorf("The VMWare Player version %s driver is only supported on Linux, and Windows, at the moment. Your OS: %s", version, runtime.GOOS) + } + + //TODO(pmyjavec) there is a better way to find this, how? + //the default will suffice for now. + vmxpath := "/usr/lib/vmware/bin/vmware-vmx" + + var stderr bytes.Buffer + cmd := exec.Command(vmxpath, "-v") + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return err + } + + versionRe := regexp.MustCompile(`(?i)VMware Player (\d+)\.`) + matches := versionRe.FindStringSubmatch(stderr.String()) + if matches == nil { + return fmt.Errorf( + "Could not find VMWare Player version in output: %s", stderr.String()) + } + log.Printf("Detected VMWare Player version: %s", matches[1]) + + return compareVersions(matches[1], version) +}