Merge pull request #2662 from rickard-von-essen/prl_pd11

Parallels Desktop 11: verify correct edition and enable headless mode
This commit is contained in:
Rickard von Essen 2015-09-16 13:34:05 +02:00
commit b528811540
11 changed files with 200 additions and 12 deletions

View File

@ -57,6 +57,7 @@ type Driver interface {
func NewDriver() (Driver, error) { func NewDriver() (Driver, error) {
var drivers map[string]Driver var drivers map[string]Driver
var prlctlPath string var prlctlPath string
var prlsrvctlPath string
var supportedVersions []string var supportedVersions []string
dhcp_lease_file := "/Library/Preferences/Parallels/parallels_dhcp_leases" dhcp_lease_file := "/Library/Preferences/Parallels/parallels_dhcp_leases"
@ -75,21 +76,34 @@ func NewDriver() (Driver, error) {
log.Printf("prlctl path: %s", prlctlPath) log.Printf("prlctl path: %s", prlctlPath)
if prlsrvctlPath == "" {
var err error
prlsrvctlPath, err = exec.LookPath("prlsrvctl")
if err != nil {
return nil, err
}
}
log.Printf("prlsrvctl path: %s", prlsrvctlPath)
drivers = map[string]Driver{ drivers = map[string]Driver{
"11": &Parallels10Driver{ "11": &Parallels11Driver{
Parallels9Driver: Parallels9Driver{ Parallels9Driver: Parallels9Driver{
PrlctlPath: prlctlPath, PrlctlPath: prlctlPath,
PrlsrvctlPath: prlsrvctlPath,
dhcp_lease_file: dhcp_lease_file, dhcp_lease_file: dhcp_lease_file,
}, },
}, },
"10": &Parallels10Driver{ "10": &Parallels10Driver{
Parallels9Driver: Parallels9Driver{ Parallels9Driver: Parallels9Driver{
PrlctlPath: prlctlPath, PrlctlPath: prlctlPath,
PrlsrvctlPath: prlsrvctlPath,
dhcp_lease_file: dhcp_lease_file, dhcp_lease_file: dhcp_lease_file,
}, },
}, },
"9": &Parallels9Driver{ "9": &Parallels9Driver{
PrlctlPath: prlctlPath, PrlctlPath: prlctlPath,
PrlsrvctlPath: prlsrvctlPath,
dhcp_lease_file: dhcp_lease_file, dhcp_lease_file: dhcp_lease_file,
}, },
} }
@ -97,6 +111,9 @@ func NewDriver() (Driver, error) {
for v, d := range drivers { for v, d := range drivers {
version, _ := d.Version() version, _ := d.Version()
if strings.HasPrefix(version, v) { if strings.HasPrefix(version, v) {
if err := d.Verify(); err != nil {
return nil, err
}
return d, nil return d, nil
} }
supportedVersions = append(supportedVersions, v) supportedVersions = append(supportedVersions, v)

View File

@ -0,0 +1,61 @@
package common
import (
"fmt"
"os/exec"
"regexp"
)
// Parallels11Driver are inherited from Parallels9Driver.
// Used for Parallels Desktop 11, requires Pro or Business Edition
type Parallels11Driver struct {
Parallels9Driver
}
func (d *Parallels11Driver) Verify() error {
stdout, err := exec.Command(d.PrlsrvctlPath, "info", "--license").Output()
if err != nil {
return err
}
editionRe := regexp.MustCompile(`edition="(\w+)"`)
matches := editionRe.FindStringSubmatch(string(stdout))
if matches == nil {
return fmt.Errorf(
"Could not determine your Parallels Desktop edition using: %s info --license", d.PrlsrvctlPath)
} else {
switch matches[1] {
case "pro", "business":
break
default:
return fmt.Errorf("Packer can be used only with Parallels Desktop 11 Pro or Business edition. You use: %s edition", matches[1])
}
}
return nil
}
func (d *Parallels11Driver) SetDefaultConfiguration(vmName string) error {
commands := make([][]string, 12)
commands[0] = []string{"set", vmName, "--cpus", "1"}
commands[1] = []string{"set", vmName, "--memsize", "512"}
commands[2] = []string{"set", vmName, "--startup-view", "headless"}
commands[3] = []string{"set", vmName, "--on-shutdown", "close"}
commands[4] = []string{"set", vmName, "--on-window-close", "keep-running"}
commands[5] = []string{"set", vmName, "--auto-share-camera", "off"}
commands[6] = []string{"set", vmName, "--smart-guard", "off"}
commands[7] = []string{"set", vmName, "--shared-cloud", "off"}
commands[8] = []string{"set", vmName, "--shared-profile", "off"}
commands[9] = []string{"set", vmName, "--smart-mount", "off"}
commands[10] = []string{"set", vmName, "--sh-app-guest-to-host", "off"}
commands[11] = []string{"set", vmName, "--sh-app-host-to-guest", "off"}
for _, command := range commands {
err := d.Prlctl(command...)
if err != nil {
return err
}
}
return nil
}

View File

@ -19,6 +19,10 @@ import (
type Parallels9Driver struct { type Parallels9Driver struct {
// This is the path to the "prlctl" application. // This is the path to the "prlctl" application.
PrlctlPath string PrlctlPath string
// This is the path to the "prlsrvctl" application.
PrlsrvctlPath string
// The path to the parallels_dhcp_leases file // The path to the parallels_dhcp_leases file
dhcp_lease_file string dhcp_lease_file string
} }

View File

@ -8,7 +8,6 @@ import (
) )
type RunConfig struct { type RunConfig struct {
Headless bool `mapstructure:"headless"`
RawBootWait string `mapstructure:"boot_wait"` RawBootWait string `mapstructure:"boot_wait"`
BootWait time.Duration `` BootWait time.Duration ``

View File

@ -17,7 +17,6 @@ import (
// Produces: // Produces:
type StepRun struct { type StepRun struct {
BootWait time.Duration BootWait time.Duration
Headless bool
vmName string vmName string
} }
@ -28,13 +27,6 @@ func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
vmName := state.Get("vmName").(string) vmName := state.Get("vmName").(string)
ui.Say("Starting the virtual machine...") ui.Say("Starting the virtual machine...")
//guiArgument := "gui"
if s.Headless == true {
ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" +
"In headless mode, errors during the boot sequence or OS setup\n" +
"won't be easily visible. Use at your own discretion.")
//guiArgument = "headless"
}
command := []string{"start", vmName} command := []string{"start", vmName}
if err := driver.Prlctl(command...); err != nil { if err := driver.Prlctl(command...); err != nil {
err := fmt.Errorf("Error starting VM: %s", err) err := fmt.Errorf("Error starting VM: %s", err)

View File

@ -241,7 +241,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
}, },
&parallelscommon.StepRun{ &parallelscommon.StepRun{
BootWait: b.config.BootWait, BootWait: b.config.BootWait,
Headless: b.config.Headless, // TODO: migth work on Enterprise Ed.
}, },
&parallelscommon.StepTypeBootCommand{ &parallelscommon.StepTypeBootCommand{
BootCommand: b.config.BootCommand, BootCommand: b.config.BootCommand,

View File

@ -74,7 +74,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
}, },
&parallelscommon.StepRun{ &parallelscommon.StepRun{
BootWait: b.config.BootWait, BootWait: b.config.BootWait,
Headless: b.config.Headless,
}, },
&parallelscommon.StepTypeBootCommand{ &parallelscommon.StepTypeBootCommand{
BootCommand: b.config.BootCommand, BootCommand: b.config.BootCommand,

View File

@ -26,6 +26,7 @@ func init() {
"virtualbox-gaattach": new(FixerVirtualBoxGAAttach), "virtualbox-gaattach": new(FixerVirtualBoxGAAttach),
"virtualbox-rename": new(FixerVirtualBoxRename), "virtualbox-rename": new(FixerVirtualBoxRename),
"vmware-rename": new(FixerVMwareRename), "vmware-rename": new(FixerVMwareRename),
"parallels-headless": new(FixerParallelsHeadless),
} }
FixerOrder = []string{ FixerOrder = []string{
@ -35,5 +36,6 @@ func init() {
"pp-vagrant-override", "pp-vagrant-override",
"virtualbox-rename", "virtualbox-rename",
"vmware-rename", "vmware-rename",
"parallels-headless",
} }
} }

View File

@ -0,0 +1,51 @@
package fix
import (
"github.com/mitchellh/mapstructure"
)
// FixerParallelsHeadless removes "headless" from a template in a Parallels builder
type FixerParallelsHeadless struct{}
func (FixerParallelsHeadless) Fix(input map[string]interface{}) (map[string]interface{}, error) {
// The type we'll decode into; we only care about builders
type template struct {
Builders []map[string]interface{}
}
// Decode the input into our structure, if we can
var tpl template
if err := mapstructure.Decode(input, &tpl); err != nil {
return nil, err
}
for _, builder := range tpl.Builders {
builderTypeRaw, ok := builder["type"]
if !ok {
continue
}
builderType, ok := builderTypeRaw.(string)
if !ok {
continue
}
if builderType != "parallels-iso" && builderType != "parallels-pvm" {
continue
}
_, ok = builder["headless"]
if !ok {
continue
}
delete(builder, "headless")
}
input["builders"] = tpl.Builders
return input, nil
}
func (FixerParallelsHeadless) Synopsis() string {
return `Removes unused "headless" from Parallels builders`
}

View File

@ -0,0 +1,61 @@
package fix
import (
"reflect"
"testing"
)
func TestFixerParallelsHeadless_Impl(t *testing.T) {
var _ Fixer = new(FixerParallelsHeadless)
}
func TestFixerParallelsHeadless_Fix(t *testing.T) {
cases := []struct {
Input map[string]interface{}
Expected map[string]interface{}
}{
// No headless field
{
Input: map[string]interface{}{
"type": "parallels-iso",
},
Expected: map[string]interface{}{
"type": "parallels-iso",
},
},
// Headless field
{
Input: map[string]interface{}{
"type": "parallels-iso",
"headless": false,
},
Expected: map[string]interface{}{
"type": "parallels-iso",
},
},
}
for _, tc := range cases {
var f FixerParallelsHeadless
input := map[string]interface{}{
"builders": []map[string]interface{}{tc.Input},
}
expected := map[string]interface{}{
"builders": []map[string]interface{}{tc.Expected},
}
output, err := f.Fix(input)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(output, expected) {
t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected)
}
}
}

View File

@ -35,3 +35,6 @@ Virtualization SDK](http://www.parallels.com/downloads/desktop/).
The SDK can be installed by downloading and following the instructions in the The SDK can be installed by downloading and following the instructions in the
dmg. dmg.
Parallels Desktop for Mac 9 and later is supported, from PD 11 Pro or Business
edition is required.