github.com/tonnydourado/packer@v0.6.1-0.20140701134019-5d0cd9676a37/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  		drivers = []Driver{
    82  			&Workstation10Driver{
    83  				Workstation9Driver: Workstation9Driver{
    84  					SSHConfig: config,
    85  				},
    86  			},
    87  			&Workstation9Driver{
    88  				SSHConfig: config,
    89  			},
    90  			&Player5LinuxDriver{
    91  				SSHConfig: config,
    92  			},
    93  		}
    94  	case "windows":
    95  		drivers = []Driver{
    96  			&Workstation10Driver{
    97  				Workstation9Driver: Workstation9Driver{
    98  					SSHConfig: config,
    99  				},
   100  			},
   101  			&Workstation9Driver{
   102  				SSHConfig: config,
   103  			},
   104  		}
   105  	default:
   106  		return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS)
   107  	}
   108  
   109  	errs := ""
   110  	for _, driver := range drivers {
   111  		err := driver.Verify()
   112  		if err == nil {
   113  			return driver, nil
   114  		}
   115  		errs += "* " + err.Error() + "\n"
   116  	}
   117  
   118  	return nil, fmt.Errorf(
   119  		"Unable to initialize any driver for this platform. The errors\n"+
   120  			"from each driver are shown below. Please fix at least one driver\n"+
   121  			"to continue:\n%s", errs)
   122  }
   123  
   124  func runAndLog(cmd *exec.Cmd) (string, string, error) {
   125  	var stdout, stderr bytes.Buffer
   126  
   127  	log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:])
   128  	cmd.Stdout = &stdout
   129  	cmd.Stderr = &stderr
   130  	err := cmd.Run()
   131  
   132  	stdoutString := strings.TrimSpace(stdout.String())
   133  	stderrString := strings.TrimSpace(stderr.String())
   134  
   135  	if _, ok := err.(*exec.ExitError); ok {
   136  		err = fmt.Errorf("VMware error: %s", stderrString)
   137  	}
   138  
   139  	log.Printf("stdout: %s", stdoutString)
   140  	log.Printf("stderr: %s", stderrString)
   141  
   142  	// Replace these for Windows, we only want to deal with Unix
   143  	// style line endings.
   144  	returnStdout := strings.Replace(stdout.String(), "\r\n", "\n", -1)
   145  	returnStderr := strings.Replace(stderr.String(), "\r\n", "\n", -1)
   146  
   147  	return returnStdout, returnStderr, err
   148  }
   149  
   150  func normalizeVersion(version string) (string, error) {
   151  	i, err := strconv.Atoi(version)
   152  	if err != nil {
   153  		return "", fmt.Errorf(
   154  			"VMWare version '%s' is not numeric", version)
   155  	}
   156  
   157  	return fmt.Sprintf("%02d", i), nil
   158  }
   159  
   160  func compareVersions(versionFound string, versionWanted string) error {
   161  	found, err := normalizeVersion(versionFound)
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	wanted, err := normalizeVersion(versionWanted)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	if found < wanted {
   172  		return fmt.Errorf(
   173  			"VMWare WS version %s, or greater, is required. Found version: %s", versionWanted, versionFound)
   174  	}
   175  
   176  	return nil
   177  }