PageRenderTime 158ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/builder/parallels/common/driver_9.go

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