github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/builder/vmware/common/driver.go (about)

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