github.com/tonnydourado/packer@v0.6.1-0.20140701134019-5d0cd9676a37/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  	log.Printf("Checking VM state: %s\n", strings.TrimSpace(stdout.String()))
    83  
    84  	for _, line := range strings.Split(stdout.String(), "\n") {
    85  		if line == "running" {
    86  			return true, nil
    87  		}
    88  
    89  		if line == "suspended" {
    90  			return true, nil
    91  		}
    92  		if line == "paused" {
    93  			return true, nil
    94  		}
    95  		if line == "stopping" {
    96  			return true, nil
    97  		}
    98  	}
    99  
   100  	return false, nil
   101  }
   102  
   103  func (d *Parallels9Driver) Stop(name string) error {
   104  	if err := d.Prlctl("stop", name); err != nil {
   105  		return err
   106  	}
   107  
   108  	// We sleep here for a little bit to let the session "unlock"
   109  	time.Sleep(2 * time.Second)
   110  
   111  	return nil
   112  }
   113  
   114  func (d *Parallels9Driver) Prlctl(args ...string) error {
   115  	var stdout, stderr bytes.Buffer
   116  
   117  	log.Printf("Executing prlctl: %#v", args)
   118  	cmd := exec.Command(d.PrlctlPath, args...)
   119  	cmd.Stdout = &stdout
   120  	cmd.Stderr = &stderr
   121  	err := cmd.Run()
   122  
   123  	stdoutString := strings.TrimSpace(stdout.String())
   124  	stderrString := strings.TrimSpace(stderr.String())
   125  
   126  	if _, ok := err.(*exec.ExitError); ok {
   127  		err = fmt.Errorf("prlctl error: %s", stderrString)
   128  	}
   129  
   130  	log.Printf("stdout: %s", stdoutString)
   131  	log.Printf("stderr: %s", stderrString)
   132  
   133  	return err
   134  }
   135  
   136  func (d *Parallels9Driver) Verify() error {
   137  	version, _ := d.Version()
   138  	if !strings.HasPrefix(version, "9.") {
   139  		return fmt.Errorf("The packer-parallels builder plugin only supports Parallels Desktop v. 9. You have: %s!\n", version)
   140  	}
   141  	return nil
   142  }
   143  
   144  func (d *Parallels9Driver) Version() (string, error) {
   145  	var stdout bytes.Buffer
   146  
   147  	cmd := exec.Command(d.PrlctlPath, "--version")
   148  	cmd.Stdout = &stdout
   149  	if err := cmd.Run(); err != nil {
   150  		return "", err
   151  	}
   152  
   153  	versionOutput := strings.TrimSpace(stdout.String())
   154  	re := regexp.MustCompile("prlctl version ([0-9\\.]+)")
   155  	verMatch := re.FindAllStringSubmatch(versionOutput, 1)
   156  
   157  	if len(verMatch) != 1 {
   158  		return "", fmt.Errorf("prlctl version not found!\n")
   159  	}
   160  
   161  	version := verMatch[0][1]
   162  	log.Printf("prlctl version: %s\n", version)
   163  	return version, nil
   164  }
   165  
   166  func (d *Parallels9Driver) SendKeyScanCodes(vmName string, codes ...string) error {
   167  	var stdout, stderr bytes.Buffer
   168  
   169  	args := prepend(vmName, codes)
   170  	cmd := exec.Command("prltype", args...)
   171  	cmd.Stdout = &stdout
   172  	cmd.Stderr = &stderr
   173  	err := cmd.Run()
   174  
   175  	stdoutString := strings.TrimSpace(stdout.String())
   176  	stderrString := strings.TrimSpace(stderr.String())
   177  
   178  	if _, ok := err.(*exec.ExitError); ok {
   179  		err = fmt.Errorf("prltype error: %s", stderrString)
   180  	}
   181  
   182  	log.Printf("stdout: %s", stdoutString)
   183  	log.Printf("stderr: %s", stderrString)
   184  
   185  	return err
   186  }
   187  
   188  func prepend(head string, tail []string) []string {
   189  	tmp := make([]string, len(tail)+1)
   190  	for i := 0; i < len(tail); i++ {
   191  		tmp[i+1] = tail[i]
   192  	}
   193  	tmp[0] = head
   194  	return tmp
   195  }
   196  
   197  func (d *Parallels9Driver) Mac(vmName string) (string, error) {
   198  	var stdout bytes.Buffer
   199  
   200  	cmd := exec.Command(d.PrlctlPath, "list", "-i", vmName)
   201  	cmd.Stdout = &stdout
   202  	if err := cmd.Run(); err != nil {
   203  		log.Printf("MAC address for NIC: nic0 on Virtual Machine: %s not found!\n", vmName)
   204  		return "", err
   205  	}
   206  
   207  	stdoutString := strings.TrimSpace(stdout.String())
   208  	re := regexp.MustCompile("net0.* mac=([0-9A-F]{12}) card=.*")
   209  	macMatch := re.FindAllStringSubmatch(stdoutString, 1)
   210  
   211  	if len(macMatch) != 1 {
   212  		return "", fmt.Errorf("MAC address for NIC: nic0 on Virtual Machine: %s not found!\n", vmName)
   213  	}
   214  
   215  	mac := macMatch[0][1]
   216  	log.Printf("Found MAC address for NIC: net0 - %s\n", mac)
   217  	return mac, nil
   218  }
   219  
   220  // Finds the IP address of a VM connected that uses DHCP by its MAC address
   221  func (d *Parallels9Driver) IpAddress(mac string) (string, error) {
   222  	var stdout bytes.Buffer
   223  	dhcp_lease_file := "/Library/Preferences/Parallels/parallels_dhcp_leases"
   224  
   225  	if len(mac) != 12 {
   226  		return "", fmt.Errorf("Not a valid MAC address: %s. It should be exactly 12 digits.", mac)
   227  	}
   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  
   235  	stdoutString := strings.TrimSpace(stdout.String())
   236  	re := regexp.MustCompile("(.*)=.*")
   237  	ipMatch := re.FindAllStringSubmatch(stdoutString, 1)
   238  
   239  	if len(ipMatch) != 1 {
   240  		return "", fmt.Errorf("IP lease not found for MAC address %s in: %s\n", mac, dhcp_lease_file)
   241  	}
   242  
   243  	ip := ipMatch[0][1]
   244  	log.Printf("Found IP lease: %s for MAC address %s\n", ip, mac)
   245  	return ip, nil
   246  }