github.com/rothwerx/packer@v0.9.0/builder/vmware/common/driver.go (about)

     1  package common
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"os/exec"
     8  	"regexp"
     9  	"runtime"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/mitchellh/multistep"
    14  )
    15  
    16  // A driver is able to talk to VMware, control virtual machines, etc.
    17  type Driver interface {
    18  	// Clone clones the VMX and the disk to the destination path. The
    19  	// destination is a path to the VMX file. The disk will be copied
    20  	// to that same directory.
    21  	Clone(dst string, src string) error
    22  
    23  	// CompactDisk compacts a virtual disk.
    24  	CompactDisk(string) error
    25  
    26  	// CreateDisk creates a virtual disk with the given size.
    27  	CreateDisk(string, string, string) error
    28  
    29  	// Checks if the VMX file at the given path is running.
    30  	IsRunning(string) (bool, error)
    31  
    32  	// CommHost returns the host address for the VM that is being
    33  	// managed by this driver.
    34  	CommHost(multistep.StateBag) (string, error)
    35  
    36  	// Start starts a VM specified by the path to the VMX given.
    37  	Start(string, bool) error
    38  
    39  	// Stop stops a VM specified by the path to the VMX given.
    40  	Stop(string) error
    41  
    42  	// SuppressMessages modifies the VMX or surrounding directory so that
    43  	// VMware doesn't show any annoying messages.
    44  	SuppressMessages(string) error
    45  
    46  	// Get the path to the VMware ISO for the given flavor.
    47  	ToolsIsoPath(string) string
    48  
    49  	// Attach the VMware tools ISO
    50  	ToolsInstall() error
    51  
    52  	// Get the path to the DHCP leases file for the given device.
    53  	DhcpLeasesPath(string) string
    54  
    55  	// Verify checks to make sure that this driver should function
    56  	// properly. This should check that all the files it will use
    57  	// appear to exist and so on. If everything is okay, this doesn't
    58  	// return an error. Otherwise, this returns an error.
    59  	Verify() error
    60  }
    61  
    62  // NewDriver returns a new driver implementation for this operating
    63  // system, or an error if the driver couldn't be initialized.
    64  func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) {
    65  	drivers := []Driver{}
    66  
    67  	switch runtime.GOOS {
    68  	case "darwin":
    69  		drivers = []Driver{
    70  			&Fusion6Driver{
    71  				Fusion5Driver: Fusion5Driver{
    72  					AppPath:   dconfig.FusionAppPath,
    73  					SSHConfig: config,
    74  				},
    75  			},
    76  			&Fusion5Driver{
    77  				AppPath:   dconfig.FusionAppPath,
    78  				SSHConfig: config,
    79  			},
    80  		}
    81  	case "linux":
    82  		fallthrough
    83  	case "windows":
    84  		drivers = []Driver{
    85  			&Workstation10Driver{
    86  				Workstation9Driver: Workstation9Driver{
    87  					SSHConfig: config,
    88  				},
    89  			},
    90  			&Workstation9Driver{
    91  				SSHConfig: config,
    92  			},
    93  			&Player6Driver{
    94  				Player5Driver: Player5Driver{
    95  					SSHConfig: config,
    96  				},
    97  			},
    98  			&Player5Driver{
    99  				SSHConfig: config,
   100  			},
   101  		}
   102  	default:
   103  		return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS)
   104  	}
   105  
   106  	errs := ""
   107  	for _, driver := range drivers {
   108  		err := driver.Verify()
   109  		if err == nil {
   110  			return driver, nil
   111  		}
   112  		errs += "* " + err.Error() + "\n"
   113  	}
   114  
   115  	return nil, fmt.Errorf(
   116  		"Unable to initialize any driver for this platform. The errors\n"+
   117  			"from each driver are shown below. Please fix at least one driver\n"+
   118  			"to continue:\n%s", errs)
   119  }
   120  
   121  func runAndLog(cmd *exec.Cmd) (string, string, error) {
   122  	var stdout, stderr bytes.Buffer
   123  
   124  	log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:])
   125  	cmd.Stdout = &stdout
   126  	cmd.Stderr = &stderr
   127  	err := cmd.Run()
   128  
   129  	stdoutString := strings.TrimSpace(stdout.String())
   130  	stderrString := strings.TrimSpace(stderr.String())
   131  
   132  	if _, ok := err.(*exec.ExitError); ok {
   133  		message := stderrString
   134  		if message == "" {
   135  			message = stdoutString
   136  		}
   137  
   138  		err = fmt.Errorf("VMware error: %s", message)
   139  
   140  		// If "unknown error" is in there, add some additional notes
   141  		re := regexp.MustCompile(`(?i)unknown error`)
   142  		if re.MatchString(message) {
   143  			err = fmt.Errorf(
   144  				"%s\n\n%s", err,
   145  				"Packer detected a VMware 'Unknown Error'. Unfortunately VMware\n"+
   146  					"often has extremely vague error messages such as this and Packer\n"+
   147  					"itself can't do much about that. Please check the vmware.log files\n"+
   148  					"created by VMware when a VM is started (in the directory of the\n"+
   149  					"vmx file), which often contains more detailed error information.")
   150  		}
   151  	}
   152  
   153  	log.Printf("stdout: %s", stdoutString)
   154  	log.Printf("stderr: %s", stderrString)
   155  
   156  	// Replace these for Windows, we only want to deal with Unix
   157  	// style line endings.
   158  	returnStdout := strings.Replace(stdout.String(), "\r\n", "\n", -1)
   159  	returnStderr := strings.Replace(stderr.String(), "\r\n", "\n", -1)
   160  
   161  	return returnStdout, returnStderr, err
   162  }
   163  
   164  func normalizeVersion(version string) (string, error) {
   165  	i, err := strconv.Atoi(version)
   166  	if err != nil {
   167  		return "", fmt.Errorf(
   168  			"VMware version '%s' is not numeric", version)
   169  	}
   170  
   171  	return fmt.Sprintf("%02d", i), nil
   172  }
   173  
   174  func compareVersions(versionFound string, versionWanted string, product string) error {
   175  	found, err := normalizeVersion(versionFound)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	wanted, err := normalizeVersion(versionWanted)
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	if found < wanted {
   186  		return fmt.Errorf(
   187  			"VMware %s version %s, or greater, is required. Found version: %s", product, versionWanted, versionFound)
   188  	}
   189  
   190  	return nil
   191  }