From 58ecb820031c15484338842dd8893edf8943d579 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Thu, 18 Jul 2024 18:04:10 -0400 Subject: [PATCH] refactor: consolidate player driver Consolidates `Player5Driver` and `Player6Driver` to `PlayerDriver` within `driver_player.go`. Signed-off-by: Ryan Johnson --- builder/vmware/common/driver.go | 14 +- .../{driver_player5.go => driver_player.go} | 148 ++++++++----- .../vmware/common/driver_player5_windows.go | 204 ------------------ builder/vmware/common/driver_player6.go | 59 ----- .../vmware/common/driver_player6_windows.go | 38 ---- builder/vmware/common/driver_player_unix.go | 86 +++----- .../vmware/common/driver_player_windows.go | 191 ++++++++++++++++ 7 files changed, 329 insertions(+), 411 deletions(-) rename builder/vmware/common/{driver_player5.go => driver_player.go} (51%) delete mode 100644 builder/vmware/common/driver_player5_windows.go delete mode 100644 builder/vmware/common/driver_player6.go delete mode 100644 builder/vmware/common/driver_player6_windows.go create mode 100644 builder/vmware/common/driver_player_windows.go diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index c0a8a5d4..190e9f36 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -33,6 +33,7 @@ const ( // Operating systems. osWindows = "windows" + osLinux = "linux" // Clone types. cloneTypeLinked = "linked" @@ -160,8 +161,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, drivers = []Driver{ NewWorkstation10Driver(config), NewWorkstation9Driver(config), - NewPlayer6Driver(config), - NewPlayer5Driver(config), + NewPlayerDriver(config), } default: return nil, fmt.Errorf("error finding a driver for %s", runtime.GOOS) @@ -683,6 +683,16 @@ func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) { return "", fmt.Errorf("unable to find host IP from devices %v, last error: %s", devices, lastError) } +// GetDhcpLeasesPaths returns a copy of the DHCP leases paths. +func GetDhcpLeasesPaths() []string { + return append([]string(nil), dhcpLeasesPaths...) +} + +// GetDhcpConfPaths returns a copy of the DHCP configuration paths. +func GetDhcpConfPaths() []string { + return append([]string(nil), dhcpConfPaths...) +} + func GetOvfTool() string { ovftool := "ovftool" if runtime.GOOS == "windows" { diff --git a/builder/vmware/common/driver_player5.go b/builder/vmware/common/driver_player.go similarity index 51% rename from builder/vmware/common/driver_player5.go rename to builder/vmware/common/driver_player.go index f0ec8cd0..c3cd80cf 100644 --- a/builder/vmware/common/driver_player5.go +++ b/builder/vmware/common/driver_player.go @@ -4,7 +4,6 @@ package common import ( - "errors" "fmt" "log" "os" @@ -15,30 +14,32 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" ) -// Player5Driver is a driver that can run VMware Player 5 on Linux. -type Player5Driver struct { +const ( + // VMware Workstation Player application name. + playerProductName = "VMware Workstation Player" +) + +// PlayerDriver is a driver for VMware Workstation Player. +type PlayerDriver struct { VmwareDriver - AppPath string + // The path to VMware Workstation Player. + AppPath string + VdiskManagerPath string QemuImgPath string VmrunPath string - // SSHConfig are the SSH settings for the Fusion VM SSHConfig *SSHConfig } -func NewPlayer5Driver(config *SSHConfig) Driver { - return &Player5Driver{ +func NewPlayerDriver(config *SSHConfig) Driver { + return &PlayerDriver{ SSHConfig: config, } } -func (d *Player5Driver) Clone(dst, src string, linked bool, snapshot string) error { - return errors.New("linked clones are not supported on this version of VMware Player, please upgrade") -} - -func (d *Player5Driver) CompactDisk(diskPath string) error { +func (d *PlayerDriver) CompactDisk(diskPath string) error { if d.QemuImgPath != "" { return d.qemuCompactDisk(diskPath) } @@ -56,7 +57,7 @@ func (d *Player5Driver) CompactDisk(diskPath string) error { return nil } -func (d *Player5Driver) qemuCompactDisk(diskPath string) error { +func (d *PlayerDriver) 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 @@ -73,7 +74,7 @@ func (d *Player5Driver) qemuCompactDisk(diskPath string) error { return nil } -func (d *Player5Driver) CreateDisk(output string, size string, adapter_type string, type_id string) error { +func (d *PlayerDriver) CreateDisk(output string, size string, adapter_type string, type_id string) error { var cmd *exec.Cmd if d.QemuImgPath != "" { cmd = exec.Command(d.QemuImgPath, "create", "-f", "vmdk", "-o", "compat6", output, size) @@ -87,13 +88,13 @@ func (d *Player5Driver) CreateDisk(output string, size string, adapter_type stri return nil } -func (d *Player5Driver) CreateSnapshot(vmxPath string, snapshotName string) error { +func (d *PlayerDriver) CreateSnapshot(vmxPath string, snapshotName string) error { cmd := exec.Command(d.VmrunPath, "-T", "player", "snapshot", vmxPath, snapshotName) _, _, err := runAndLog(cmd) return err } -func (d *Player5Driver) IsRunning(vmxPath string) (bool, error) { +func (d *PlayerDriver) IsRunning(vmxPath string) (bool, error) { vmxPath, err := filepath.Abs(vmxPath) if err != nil { return false, err @@ -114,14 +115,14 @@ func (d *Player5Driver) IsRunning(vmxPath string) (bool, error) { return false, nil } -func (d *Player5Driver) CommHost(state multistep.StateBag) (string, error) { +func (d *PlayerDriver) CommHost(state multistep.StateBag) (string, error) { return CommHost(d.SSHConfig)(state) } -func (d *Player5Driver) Start(vmxPath string, headless bool) error { - guiArgument := "gui" - if headless { - guiArgument = "nogui" +func (d *PlayerDriver) Start(vmxPath string, headless bool) error { + guiArgument := guiArgumentNoGUI + if !headless { + guiArgument = guiArgumentGUI } cmd := exec.Command(d.VmrunPath, "-T", "player", "start", vmxPath, guiArgument) @@ -132,32 +133,43 @@ func (d *Player5Driver) Start(vmxPath string, headless bool) error { return nil } -func (d *Player5Driver) Stop(vmxPath string) error { +func (d *PlayerDriver) Stop(vmxPath string) error { cmd := exec.Command(d.VmrunPath, "-T", "player", "stop", vmxPath, "hard") if _, _, err := runAndLog(cmd); err != nil { + // Check if the virtual machine is running. If not, it is stopped. + running, runningErr := d.IsRunning(vmxPath) + if runningErr == nil && !running { + return nil + } + return err } return nil } -func (d *Player5Driver) SuppressMessages(vmxPath string) error { +func (d *PlayerDriver) SuppressMessages(vmxPath string) error { return nil } -func (d *Player5Driver) Verify() error { +func (d *PlayerDriver) Verify() error { var err error + + log.Printf("Checking %s paths...", playerProductName) + if d.AppPath == "" { - if d.AppPath, err = playerFindVMware(); err != nil { - return err + if d.AppPath, err = playerFindVmplayer(); err != nil { + return fmt.Errorf("%s not found: %s", playerProductName, err) } } + log.Printf("- %s app path: %s", playerProductName, d.AppPath) if d.VmrunPath == "" { if d.VmrunPath, err = playerFindVmrun(); err != nil { - return err + return fmt.Errorf("%s not found: %s", appVmrun, err) } } + log.Printf("- %s found at: %s", appVmrun, d.VmrunPath) if d.VdiskManagerPath == "" { d.VdiskManagerPath, err = playerFindVdiskManager() @@ -168,30 +180,44 @@ func (d *Player5Driver) Verify() error { } if err != nil { - return fmt.Errorf("error finding either 'vmware-vdiskmanager' or 'qemu-img' in path") + return fmt.Errorf("error finding either %s or %s: %s", appVdiskManager, appQemuImg, err) } - 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) + log.Printf("- %s found at: %s", appVdiskManager, d.VdiskManagerPath) + log.Printf("- %s found at: %s", appQemuImg, d.QemuImgPath) if _, err := os.Stat(d.AppPath); err != nil { - return fmt.Errorf("player not found in path: %s", d.AppPath) + if os.IsNotExist(err) { + return fmt.Errorf("%s not found at: %s", playerProductName, d.AppPath) + } + return err } + log.Printf("- %s found at: %s", playerProductName, d.AppPath) if _, err := os.Stat(d.VmrunPath); err != nil { - return fmt.Errorf("'vmrun' not found in path: %s", d.VmrunPath) + if os.IsNotExist(err) { + return fmt.Errorf("%s not found at: %s", appVmrun, d.VmrunPath) + } + return err } + log.Printf("- %s found at: %s", appVmrun, d.VmrunPath) if d.VdiskManagerPath != "" { - _, err = os.Stat(d.VdiskManagerPath) + if _, err := os.Stat(d.VdiskManagerPath); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("%s not found at: %s", appVdiskManager, d.VdiskManagerPath) + } + return err + } + log.Printf("- %s found at: %s", appVdiskManager, d.VdiskManagerPath) } else { - _, err = os.Stat(d.QemuImgPath) - } - - if err != nil { - return fmt.Errorf("error finding either 'vmware-vdiskmanager' or 'qemu-img' in path: %s", d.VdiskManagerPath) + if _, err := os.Stat(d.QemuImgPath); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("%s not found at: %s", appQemuImg, d.QemuImgPath) + } + return err + } + log.Printf("- %s found at: %s", appQemuImg, d.QemuImgPath) } // Assigning the path callbacks to VmwareDriver @@ -210,43 +236,59 @@ func (d *Player5Driver) Verify() error { d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) { pathNetmap := playerNetmapConfPath() - // If we were able to find the file (no error), then we can proceed with reading - // the networkmapper configuration. if _, err := os.Stat(pathNetmap); err == nil { - log.Printf("Located networkmapper configuration file: %s", pathNetmap) + log.Printf("Found: %s", pathNetmap) return ReadNetmapConfig(pathNetmap) } - // If we weren't able to find the networkmapper configuration file, then fall back - // to the networking file which might also be in the configuration directory. - libpath, _ := playerVMwareRoot() + libpath, _ := playerInstallationPath() pathNetworking := filepath.Join(libpath, "networking") if _, err := os.Stat(pathNetworking); err != nil { - return nil, fmt.Errorf("error determining network mappings from files in path: %s", libpath) + return nil, fmt.Errorf("not found: %s", pathNetworking) } - // We were able to successfully stat the file.. So, now we can open a handle to it. - log.Printf("Located networking configuration file: %s", pathNetworking) + log.Printf("Found: %s", pathNetworking) fd, err := os.Open(pathNetworking) if err != nil { return nil, err } defer fd.Close() - // Then we pass the handle to the networking configuration parser. return ReadNetworkingConfig(fd) } + return nil } -func (d *Player5Driver) ToolsIsoPath(flavor string) string { +func (d *PlayerDriver) ToolsIsoPath(flavor string) string { return playerToolsIsoPath(flavor) } -func (d *Player5Driver) ToolsInstall() error { +func (d *PlayerDriver) ToolsInstall() error { + return nil +} + +func (d *PlayerDriver) Clone(dst, src string, linked bool, snapshot string) error { + var cloneType string + + if linked { + cloneType = cloneTypeLinked + } else { + cloneType = cloneTypeFull + } + + args := []string{"-T", "ws", "clone", src, dst, cloneType} + if snapshot != "" { + args = append(args, "-snapshot", snapshot) + } + cmd := exec.Command(d.VmrunPath, args...) + if _, _, err := runAndLog(cmd); err != nil { + return err + } + return nil } -func (d *Player5Driver) GetVmwareDriver() VmwareDriver { +func (d *PlayerDriver) GetVmwareDriver() VmwareDriver { return d.VmwareDriver } diff --git a/builder/vmware/common/driver_player5_windows.go b/builder/vmware/common/driver_player5_windows.go deleted file mode 100644 index b3dd3d55..00000000 --- a/builder/vmware/common/driver_player5_windows.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go: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 playerVmDhcpConfPath(device string) string { - // the device isn't actually used on windows hosts - path, err := playerDhcpConfigPathRegistry() - if err != nil { - log.Printf("Error finding configuration in registry: %s", err) - } else if _, err := os.Stat(path); err == nil { - return path - } - return findFile("vmnetdhcp.conf", playerDataFilePaths()) -} - -func playerVmnetnatConfPath(device string) string { - // the device isn't actually used on windows hosts - return findFile("vmnetnat.conf", playerDataFilePaths()) -} - -func playerNetmapConfPath() string { - return findFile("netmap.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 -} - -// This reads the VMware DHCP configuration path from the Windows registry. -func playerDhcpConfigPathRegistry() (s string, err error) { - key := "SYSTEM\\CurrentControlSet\\services\\VMnetDHCP\\Parameters" - subkey := "ConfFile" - 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 deleted file mode 100644 index 4d2af0f9..00000000 --- a/builder/vmware/common/driver_player6.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -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 NewPlayer6Driver(config *SSHConfig) Driver { - return &Player6Driver{ - Player5Driver: Player5Driver{ - SSHConfig: config, - }, - } -} - -func (d *Player6Driver) Clone(dst, src string, linked bool, snapshot string) error { - // TODO(rasa) check if running player+, not just player - - var cloneType string - if linked { - cloneType = "linked" - } else { - cloneType = "full" - } - - args := []string{"-T", "ws", "clone", src, dst, cloneType} - if snapshot != "" { - args = append(args, "-snapshot", snapshot) - } - cmd := exec.Command(d.Player5Driver.VmrunPath, args...) - 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) -} - -func (d *Player6Driver) GetVmwareDriver() VmwareDriver { - return d.Player5Driver.VmwareDriver -} diff --git a/builder/vmware/common/driver_player6_windows.go b/builder/vmware/common/driver_player6_windows.go deleted file mode 100644 index bfacb2df..00000000 --- a/builder/vmware/common/driver_player6_windows.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go: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, "Player") -} diff --git a/builder/vmware/common/driver_player_unix.go b/builder/vmware/common/driver_player_unix.go index f8bba40c..da8d742e 100644 --- a/builder/vmware/common/driver_player_unix.go +++ b/builder/vmware/common/driver_player_unix.go @@ -3,7 +3,6 @@ //go:build !windows -// These functions are compatible with WS 9 and 10 on *NIX package common import ( @@ -17,46 +16,42 @@ import ( "runtime" ) -func playerFindVdiskManager() (string, error) { - return exec.LookPath("vmware-vdiskmanager") +// VMware Workstation Player for Linux. + +func playerFindVmplayer() (string, error) { + return exec.LookPath(appPlayer) } -func playerFindQemuImg() (string, error) { - return exec.LookPath("qemu-img") +func playerFindVmrun() (string, error) { + return exec.LookPath(appVmrun) } -func playerFindVMware() (string, error) { - return exec.LookPath("vmplayer") +func playerFindVdiskManager() (string, error) { + return exec.LookPath(appVdiskManager) } -func playerFindVmrun() (string, error) { - return exec.LookPath("vmrun") +func playerFindQemuImg() (string, error) { + return exec.LookPath(appQemuImg) } func playerToolsIsoPath(flavor string) string { return "/usr/lib/vmware/isoimages/" + flavor + ".iso" } -// return the base path to vmware's config on the host -func playerVMwareRoot() (s string, err error) { +// Return the base path to configuration files. +func playerInstallationPath() (s string, err error) { return "/etc/vmware", nil } -func playerDhcpLeasesPath(device string) string { - base, err := playerVMwareRoot() +// Helper function to find configuration paths +func playerFindConfigPath(device string, paths []string) string { + base, err := playerInstallationPath() if err != nil { - log.Printf("Error finding VMware root: %s", err) + log.Printf("Error finding configuration root path: %s", err) return "" } - // Build the base path to VMware configuration for specified device: `/etc/vmware/${device}` devicebase := filepath.Join(base, device) - - // Walk through a list of paths searching for the correct permutation... - // ...as it appears that in >= WS14 and < WS14, the leases file may be labelled differently. - - // Docs say we should expect: dhcpd/dhcpd.leases - paths := []string{"dhcpd/dhcpd.leases", "dhcpd/dhcp.leases", "dhcp/dhcpd.leases", "dhcp/dhcp.leases"} for _, p := range paths { fp := filepath.Join(devicebase, p) if _, err := os.Stat(fp); !os.IsNotExist(err) { @@ -64,62 +59,43 @@ func playerDhcpLeasesPath(device string) string { } } - log.Printf("Error finding VMware DHCP Server Leases (dhcpd.leases) under device path: %s", devicebase) + log.Printf("Error finding configuration file in device path: %s", devicebase) return "" } -func playerVmDhcpConfPath(device string) string { - base, err := playerVMwareRoot() - if err != nil { - log.Printf("Error finding VMware root: %s", err) - return "" - } - - // Build the base path to VMware configuration for specified device: `/etc/vmware/${device}` - devicebase := filepath.Join(base, device) - - // Walk through a list of paths searching for the correct permutation... - // ...as it appears that in >= WS14 and < WS14, the dhcp config may be labelled differently. - - // Docs say we should expect: dhcp/dhcp.conf - paths := []string{"dhcp/dhcp.conf", "dhcp/dhcpd.conf", "dhcpd/dhcp.conf", "dhcpd/dhcpd.conf"} - for _, p := range paths { - fp := filepath.Join(devicebase, p) - if _, err := os.Stat(fp); !os.IsNotExist(err) { - return fp - } - } +func playerDhcpLeasesPath(device string) string { + return playerFindConfigPath(device, GetDhcpLeasesPaths()) +} - log.Printf("Error finding VMware DHCP Server Configuration (dhcp.conf) under device path: %s", devicebase) - return "" +func playerVmDhcpConfPath(device string) string { + return playerFindConfigPath(device, GetDhcpConfPaths()) } func playerVmnetnatConfPath(device string) string { - base, err := playerVMwareRoot() + base, err := playerInstallationPath() if err != nil { - log.Printf("Error finding VMware root: %s", err) + log.Printf("Error finding the configuration root path: %s", err) return "" } return filepath.Join(base, device, "nat/nat.conf") } func playerNetmapConfPath() string { - base, err := playerVMwareRoot() + base, err := playerInstallationPath() if err != nil { - log.Printf("Error finding VMware root: %s", err) + log.Printf("Error finding the configuration root path: %s", err) return "" } return filepath.Join(base, "netmap.conf") } func playerVerifyVersion(version string) error { - if runtime.GOOS != "linux" { - return fmt.Errorf("driver is only supported on Linux and Windows, not %s", runtime.GOOS) + if runtime.GOOS != osLinux { + return fmt.Errorf("driver is only supported on linux and windows, not %s", runtime.GOOS) } - //TODO: Is there is a better way to find this? // Using the default. - vmxpath := "/usr/lib/vmware/bin/vmware-vmx" + vmxpath := "/usr/lib/vmware/bin/" + appVmx var stderr bytes.Buffer cmd := exec.Command(vmxpath, "-v") @@ -131,9 +107,9 @@ func playerVerifyVersion(version string) error { versionRe := regexp.MustCompile(`(?i)VMware Player (\d+)\.`) matches := versionRe.FindStringSubmatch(stderr.String()) if matches == nil { - return fmt.Errorf("error parsing version output: %s", stderr.String()) + return fmt.Errorf("error parsing version from output: %s", stderr.String()) } - log.Printf("Detected VMware Player version: %s", matches[1]) + log.Printf("VMware Workstation Player: %s", matches[1]) return compareVersions(matches[1], version, "Player") } diff --git a/builder/vmware/common/driver_player_windows.go b/builder/vmware/common/driver_player_windows.go new file mode 100644 index 00000000..cf1a2068 --- /dev/null +++ b/builder/vmware/common/driver_player_windows.go @@ -0,0 +1,191 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:build windows + +package common + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "syscall" +) + +// VMware Workstation Player for Windows. + +const ( + playerInstallationPathKey = `SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\vmplayer.exe` + playerRegistryKey = "SYSTEM\\CurrentControlSet\\services\\VMnetDHCP\\Parameters" +) + +func playerExecutable(executable string) (string, error) { + path, err := exec.LookPath(executable + ".exe") + if err == nil { + return path, nil + } + return findFile(executable+".exe", playerProgramFilePaths()), nil +} + +func playerFindVmplayer() (string, error) { + return playerExecutable(appPlayer) +} + +func playerFindVmrun() (string, error) { + return playerExecutable(appVmrun) +} + +func playerFindVdiskManager() (string, error) { + return playerExecutable(appVdiskManager) +} + +func playerFindQemuImg() (string, error) { + return playerExecutable(appQemuImg) +} + +func playerToolsIsoPath(flavor string) string { + return findFile(flavor+".iso", playerProgramFilePaths()) +} + +func playerDhcpLeasesPath(device string) string { + // Not used on Windows. + path, err := playerDhcpLeasesPathRegistry() + if err != nil { + log.Printf("Unable to retrieve DHCP leases path from registry: %s", err) + } else if _, err := os.Stat(path); err == nil { + return path + } + return findFile(dhcpVmnetLeasesFile, playerDataFilePaths()) +} + +func playerVmDhcpConfPath(device string) string { + // Not used on Windows. + path, err := playerDhcpConfigPathRegistry() + if err != nil { + log.Printf("Unable to retrieve DHCP configuration path from registry: %s", err) + } else if _, err := os.Stat(path); err == nil { + return path + } + return findFile(dhcpVmnetConfFile, playerDataFilePaths()) +} + +func playerVmnetnatConfPath(device string) string { + // Not used on Windows. + return findFile(natVmnetConfFile, playerDataFilePaths()) +} + +func playerNetmapConfPath() string { + return findFile(netmapConfFile, playerDataFilePaths()) +} + +func playerReadRegistryPath(key, subkey string) (string, error) { + s, err := readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) + if err != nil { + log.Printf("Unable to read registry key %s\\%s", key, subkey) + return "", err + } + return normalizePath(s), nil +} + +// Read the installation path from the registry. +func playerInstallationPath() (string, error) { + return playerReadRegistryPath(playerInstallationPathKey, "Path") +} + +// Read the DHCP leases path from the registry. +func playerDhcpLeasesPathRegistry() (string, error) { + return playerReadRegistryPath(playerRegistryKey, "LeaseFile") +} + +// Read the DHCP configuration path from the registry. +func playerDhcpConfigPathRegistry() (string, error) { + return playerReadRegistryPath(playerRegistryKey, "ConfFile") +} + +// Append path if the environment variable exists. +func appendPlayerPath(paths []string, envVar, suffix string) []string { + if value := os.Getenv(envVar); value != "" { + paths = append(paths, filepath.Join(value, suffix)) + } + return paths +} + +func playerProgramFilePaths() []string { + path, err := playerInstallationPath() + if err != nil { + log.Printf("Unable to retrieve installation path from registry: %s", err) + } + + paths := make([]string, 0, 5) + + if homePath := os.Getenv("VMWARE_HOME"); homePath != "" { + paths = append(paths, homePath) + } + + if path != "" { + paths = append(paths, path) + } + + paths = appendPlayerPath(paths, "ProgramFiles(x86)", "VMware/VMware Player") + paths = appendPlayerPath(paths, "ProgramFiles", "VMware/VMware Player") + paths = appendPlayerPath(paths, "QEMU_HOME", "") + paths = appendPlayerPath(paths, "ProgramFiles(x86)", "QEMU") + paths = appendPlayerPath(paths, "ProgramFiles", "QEMU") + paths = appendPlayerPath(paths, "SystemDrive", "QEMU") + + return paths +} + +// Read a list of possible data paths. +func playerDataFilePaths() []string { + leasesPath, err := playerDhcpLeasesPathRegistry() + if err != nil { + log.Printf("Unable to retrieve DHCP leases path from registry: %s", err) + } + + if leasesPath != "" { + leasesPath = filepath.Dir(leasesPath) + } + + paths := make([]string, 0, 5) + + if dataPath := os.Getenv("VMWARE_DATA"); dataPath != "" { + paths = append(paths, dataPath) + } + + if leasesPath != "" { + paths = append(paths, leasesPath) + } + + paths = appendPlayerPath(paths, "ProgramData", "VMware") + paths = appendPlayerPath(paths, "ALLUSERSPROFILE", "Application Data/VMware") + + return paths +} + +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("error retrieving the version from registry key %s\\%s: '%s'", key, subkey, productVersion) + } + log.Printf("VMware Workstation Player: %s", matches[1]) + + return compareVersions(matches[1], version, "Player") +}