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 }