192 lines
4.9 KiB
Go
192 lines
4.9 KiB
Go
package common
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"os/exec"
|
|
"regexp"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/mitchellh/multistep"
|
|
)
|
|
|
|
// A driver is able to talk to VMware, control virtual machines, etc.
|
|
type Driver interface {
|
|
// Clone clones the VMX and the disk to the destination path. The
|
|
// destination is a path to the VMX file. The disk will be copied
|
|
// to that same directory.
|
|
Clone(dst string, src string) error
|
|
|
|
// CompactDisk compacts a virtual disk.
|
|
CompactDisk(string) error
|
|
|
|
// CreateDisk creates a virtual disk with the given size.
|
|
CreateDisk(string, string, string) error
|
|
|
|
// Checks if the VMX file at the given path is running.
|
|
IsRunning(string) (bool, error)
|
|
|
|
// CommHost returns the host address for the VM that is being
|
|
// managed by this driver.
|
|
CommHost(multistep.StateBag) (string, error)
|
|
|
|
// Start starts a VM specified by the path to the VMX given.
|
|
Start(string, bool) error
|
|
|
|
// Stop stops a VM specified by the path to the VMX given.
|
|
Stop(string) error
|
|
|
|
// SuppressMessages modifies the VMX or surrounding directory so that
|
|
// VMware doesn't show any annoying messages.
|
|
SuppressMessages(string) error
|
|
|
|
// Get the path to the VMware ISO for the given flavor.
|
|
ToolsIsoPath(string) string
|
|
|
|
// Attach the VMware tools ISO
|
|
ToolsInstall() error
|
|
|
|
// 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
|
|
// return an error. Otherwise, this returns an error.
|
|
Verify() error
|
|
}
|
|
|
|
// NewDriver returns a new driver implementation for this operating
|
|
// system, or an error if the driver couldn't be initialized.
|
|
func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) {
|
|
drivers := []Driver{}
|
|
|
|
switch runtime.GOOS {
|
|
case "darwin":
|
|
drivers = []Driver{
|
|
&Fusion6Driver{
|
|
Fusion5Driver: Fusion5Driver{
|
|
AppPath: dconfig.FusionAppPath,
|
|
SSHConfig: config,
|
|
},
|
|
},
|
|
&Fusion5Driver{
|
|
AppPath: dconfig.FusionAppPath,
|
|
SSHConfig: config,
|
|
},
|
|
}
|
|
case "linux":
|
|
fallthrough
|
|
case "windows":
|
|
drivers = []Driver{
|
|
&Workstation10Driver{
|
|
Workstation9Driver: Workstation9Driver{
|
|
SSHConfig: config,
|
|
},
|
|
},
|
|
&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)
|
|
}
|
|
|
|
errs := ""
|
|
for _, driver := range drivers {
|
|
err := driver.Verify()
|
|
if err == nil {
|
|
return driver, nil
|
|
}
|
|
errs += "* " + err.Error() + "\n"
|
|
}
|
|
|
|
return nil, fmt.Errorf(
|
|
"Unable to initialize any driver for this platform. The errors\n"+
|
|
"from each driver are shown below. Please fix at least one driver\n"+
|
|
"to continue:\n%s", errs)
|
|
}
|
|
|
|
func runAndLog(cmd *exec.Cmd) (string, string, error) {
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
log.Printf("Executing: %s %s", cmd.Path, strings.Join(cmd.Args[1:], " "))
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
err := cmd.Run()
|
|
|
|
stdoutString := strings.TrimSpace(stdout.String())
|
|
stderrString := strings.TrimSpace(stderr.String())
|
|
|
|
if _, ok := err.(*exec.ExitError); ok {
|
|
message := stderrString
|
|
if message == "" {
|
|
message = stdoutString
|
|
}
|
|
|
|
err = fmt.Errorf("VMware error: %s", message)
|
|
|
|
// If "unknown error" is in there, add some additional notes
|
|
re := regexp.MustCompile(`(?i)unknown error`)
|
|
if re.MatchString(message) {
|
|
err = fmt.Errorf(
|
|
"%s\n\n%s", err,
|
|
"Packer detected a VMware 'Unknown Error'. Unfortunately VMware\n"+
|
|
"often has extremely vague error messages such as this and Packer\n"+
|
|
"itself can't do much about that. Please check the vmware.log files\n"+
|
|
"created by VMware when a VM is started (in the directory of the\n"+
|
|
"vmx file), which often contains more detailed error information.")
|
|
}
|
|
}
|
|
|
|
log.Printf("stdout: %s", stdoutString)
|
|
log.Printf("stderr: %s", stderrString)
|
|
|
|
// Replace these for Windows, we only want to deal with Unix
|
|
// style line endings.
|
|
returnStdout := strings.Replace(stdout.String(), "\r\n", "\n", -1)
|
|
returnStderr := strings.Replace(stderr.String(), "\r\n", "\n", -1)
|
|
|
|
return returnStdout, returnStderr, err
|
|
}
|
|
|
|
func normalizeVersion(version string) (string, error) {
|
|
i, err := strconv.Atoi(version)
|
|
if err != nil {
|
|
return "", fmt.Errorf(
|
|
"VMware version '%s' is not numeric", version)
|
|
}
|
|
|
|
return fmt.Sprintf("%02d", i), nil
|
|
}
|
|
|
|
func compareVersions(versionFound string, versionWanted string, product string) error {
|
|
found, err := normalizeVersion(versionFound)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
wanted, err := normalizeVersion(versionWanted)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if found < wanted {
|
|
return fmt.Errorf(
|
|
"VMware %s version %s, or greater, is required. Found version: %s", product, versionWanted, versionFound)
|
|
}
|
|
|
|
return nil
|
|
}
|