github.com/homburg/packer@v0.6.1-0.20140528012651-1dcaf1716848/builder/parallels/common/driver_9.go (about) 1 package common 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/going/toolkit/xmlpath" 7 "log" 8 "os" 9 "os/exec" 10 "regexp" 11 "strings" 12 "time" 13 ) 14 15 type Parallels9Driver struct { 16 // This is the path to the "prlctl" application. 17 PrlctlPath string 18 } 19 20 func (d *Parallels9Driver) Import(name, srcPath, dstDir string) error { 21 22 err := d.Prlctl("register", srcPath, "--preserve-uuid") 23 if err != nil { 24 return err 25 } 26 27 srcId, err := getVmId(srcPath) 28 if err != nil { 29 return err 30 } 31 32 srcMac, err := getFirtsMacAddress(srcPath) 33 if err != nil { 34 return err 35 } 36 37 err = d.Prlctl("clone", srcId, "--name", name, "--dst", dstDir) 38 if err != nil { 39 return err 40 } 41 42 err = d.Prlctl("unregister", srcId) 43 if err != nil { 44 return err 45 } 46 47 err = d.Prlctl("set", name, "--device-set", "net0", "--mac", srcMac) 48 return nil 49 } 50 51 func getVmId(path string) (string, error) { 52 return getConfigValueFromXpath(path, "/ParallelsVirtualMachine/Identification/VmUuid") 53 } 54 55 func getFirtsMacAddress(path string) (string, error) { 56 return getConfigValueFromXpath(path, "/ParallelsVirtualMachine/Hardware/NetworkAdapter[@id='0']/MAC") 57 } 58 59 func getConfigValueFromXpath(path, xpath string) (string, error) { 60 file, err := os.Open(path + "/config.pvs") 61 if err != nil { 62 return "", err 63 } 64 xpathComp := xmlpath.MustCompile(xpath) 65 root, err := xmlpath.Parse(file) 66 if err != nil { 67 return "", err 68 } 69 value, _ := xpathComp.String(root) 70 return value, nil 71 } 72 73 func (d *Parallels9Driver) IsRunning(name string) (bool, error) { 74 var stdout bytes.Buffer 75 76 cmd := exec.Command(d.PrlctlPath, "list", name, "--no-header", "--output", "status") 77 cmd.Stdout = &stdout 78 if err := cmd.Run(); err != nil { 79 return false, err 80 } 81 82 for _, line := range strings.Split(stdout.String(), "\n") { 83 if line == "running" { 84 return true, nil 85 } 86 87 if line == "suspended" { 88 return true, nil 89 } 90 if line == "paused" { 91 return true, nil 92 } 93 } 94 95 return false, nil 96 } 97 98 func (d *Parallels9Driver) Stop(name string) error { 99 if err := d.Prlctl("stop", name); err != nil { 100 return err 101 } 102 103 // We sleep here for a little bit to let the session "unlock" 104 time.Sleep(2 * time.Second) 105 106 return nil 107 } 108 109 func (d *Parallels9Driver) Prlctl(args ...string) error { 110 var stdout, stderr bytes.Buffer 111 112 log.Printf("Executing prlctl: %#v", args) 113 cmd := exec.Command(d.PrlctlPath, args...) 114 cmd.Stdout = &stdout 115 cmd.Stderr = &stderr 116 err := cmd.Run() 117 118 stdoutString := strings.TrimSpace(stdout.String()) 119 stderrString := strings.TrimSpace(stderr.String()) 120 121 if _, ok := err.(*exec.ExitError); ok { 122 err = fmt.Errorf("prlctl error: %s", stderrString) 123 } 124 125 log.Printf("stdout: %s", stdoutString) 126 log.Printf("stderr: %s", stderrString) 127 128 return err 129 } 130 131 func (d *Parallels9Driver) Verify() error { 132 version, _ := d.Version() 133 if !strings.HasPrefix(version, "9.") { 134 return fmt.Errorf("The packer-parallels builder plugin only supports Parallels Desktop v. 9. You have: %s!\n", version) 135 } 136 return nil 137 } 138 139 func (d *Parallels9Driver) Version() (string, error) { 140 var stdout bytes.Buffer 141 142 cmd := exec.Command(d.PrlctlPath, "--version") 143 cmd.Stdout = &stdout 144 if err := cmd.Run(); err != nil { 145 return "", err 146 } 147 148 versionOutput := strings.TrimSpace(stdout.String()) 149 re := regexp.MustCompile("prlctl version ([0-9\\.]+)") 150 verMatch := re.FindAllStringSubmatch(versionOutput, 1) 151 152 if len(verMatch) != 1 { 153 return "", fmt.Errorf("prlctl version not found!\n") 154 } 155 156 version := verMatch[0][1] 157 log.Printf("prlctl version: %s\n", version) 158 return version, nil 159 } 160 161 func (d *Parallels9Driver) SendKeyScanCodes(vmName string, codes ...string) error { 162 var stdout, stderr bytes.Buffer 163 164 args := prepend(vmName, codes) 165 cmd := exec.Command("prltype", args...) 166 cmd.Stdout = &stdout 167 cmd.Stderr = &stderr 168 err := cmd.Run() 169 170 stdoutString := strings.TrimSpace(stdout.String()) 171 stderrString := strings.TrimSpace(stderr.String()) 172 173 if _, ok := err.(*exec.ExitError); ok { 174 err = fmt.Errorf("prltype error: %s", stderrString) 175 } 176 177 log.Printf("stdout: %s", stdoutString) 178 log.Printf("stderr: %s", stderrString) 179 180 return err 181 } 182 183 func prepend(head string, tail []string) []string { 184 tmp := make([]string, len(tail)+1) 185 for i := 0; i < len(tail); i++ { 186 tmp[i+1] = tail[i] 187 } 188 tmp[0] = head 189 return tmp 190 } 191 192 func (d *Parallels9Driver) Mac(vmName string) (string, error) { 193 var stdout bytes.Buffer 194 195 cmd := exec.Command(d.PrlctlPath, "list", "-i", vmName) 196 cmd.Stdout = &stdout 197 if err := cmd.Run(); err != nil { 198 log.Printf("MAC address for NIC: nic0 on Virtual Machine: %s not found!\n", vmName) 199 return "", err 200 } 201 202 stdoutString := strings.TrimSpace(stdout.String()) 203 re := regexp.MustCompile("net0.* mac=([0-9A-F]{12}) card=.*") 204 macMatch := re.FindAllStringSubmatch(stdoutString, 1) 205 206 if len(macMatch) != 1 { 207 return "", fmt.Errorf("MAC address for NIC: nic0 on Virtual Machine: %s not found!\n", vmName) 208 } 209 210 mac := macMatch[0][1] 211 log.Printf("Found MAC address for NIC: net0 - %s\n", mac) 212 return mac, nil 213 } 214 215 // Finds the IP address of a VM connected that uses DHCP by its MAC address 216 func (d *Parallels9Driver) IpAddress(mac string) (string, error) { 217 var stdout bytes.Buffer 218 dhcp_lease_file := "/Library/Preferences/Parallels/parallels_dhcp_leases" 219 220 if len(mac) != 12 { 221 return "", fmt.Errorf("Not a valid MAC address: %s. It should be exactly 12 digits.", mac) 222 } 223 224 cmd := exec.Command("grep", "-i", mac, dhcp_lease_file) 225 cmd.Stdout = &stdout 226 if err := cmd.Run(); err != nil { 227 return "", err 228 } 229 230 stdoutString := strings.TrimSpace(stdout.String()) 231 re := regexp.MustCompile("(.*)=.*") 232 ipMatch := re.FindAllStringSubmatch(stdoutString, 1) 233 234 if len(ipMatch) != 1 { 235 return "", fmt.Errorf("IP lease not found for MAC address %s in: %s\n", mac, dhcp_lease_file) 236 } 237 238 ip := ipMatch[0][1] 239 log.Printf("Found IP lease: %s for MAC address %s\n", ip, mac) 240 return ip, nil 241 }