From 010d171bec8e1c926406a89457f208be392bb6a3 Mon Sep 17 00:00:00 2001 From: Taliesin Sisson Date: Fri, 30 Oct 2015 17:19:25 +0000 Subject: [PATCH] GuestAdditionsMode and GuestAdditionsPath can be set in config. If GuestAdditionsMode == "attach" it will mount the HyperV Integration Services ISO. If GuestAdditionsPath is set, then it will be used as an alternative to where the HyperV Integration Service ISO is. Included the build step to download ISO, so iso_urls works properly now. Online activation should be done via provisioner Installation of integration services should be done via provisioner Cleaned up the way dvd drives are mounted and unmounted (still need to implement feature to find unused drives before adding a new one) Cleaned up the way floppies are mounted and unmounted --- builder/hyperv/common/driver.go | 8 +- builder/hyperv/common/driver_ps_4.go | 10 +- .../common/step_execute_online_activation.go | 62 ---------- .../step_execute_online_activation_full.go | 90 -------------- builder/hyperv/common/step_mount_dvddrive.go | 84 +++++++------ .../hyperv/common/step_mount_floppydrive.go | 92 +++------------ .../common/step_mount_integration_services.go | 110 ------------------ .../hyperv/common/step_unmount_dvddrive.go | 33 ++++-- .../hyperv/common/step_unmount_floppydrive.go | 4 +- .../step_unmount_integration_services.go | 43 ------- .../step_upgrade_integration_services.go | 110 ------------------ builder/hyperv/iso/builder.go | 87 ++++++++++++-- powershell/hyperv/hyperv.go | 17 ++- 13 files changed, 196 insertions(+), 554 deletions(-) delete mode 100644 builder/hyperv/common/step_execute_online_activation.go delete mode 100644 builder/hyperv/common/step_execute_online_activation_full.go delete mode 100644 builder/hyperv/common/step_mount_integration_services.go delete mode 100644 builder/hyperv/common/step_unmount_integration_services.go delete mode 100644 builder/hyperv/common/step_upgrade_integration_services.go diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 5afa15d77..6ceb37c0f 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -91,10 +91,14 @@ type Driver interface { MountDvdDrive(string, string) error MountDvdDriveByLocation(string, string, uint, uint) error + + SetBootDvdDrive(string, uint, uint) error UnmountDvdDrive(string) error - DeleteDvdDrive(string, string, string) error + DeleteDvdDrive(string, uint, uint) error - UnmountFloppyDrive(vmName string) error + MountFloppyDrive(string, string) error + + UnmountFloppyDrive(string) error } diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index 5662ce0d8..c6d6d4e65 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -217,14 +217,22 @@ func (d *HypervPS4Driver) MountDvdDriveByLocation(vmName string, path string, co return hyperv.MountDvdDriveByLocation(vmName, path, controllerNumber, controllerLocation) } +func (d *HypervPS4Driver) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { + return hyperv.SetBootDvdDrive(vmName, controllerNumber, controllerLocation) +} + func (d *HypervPS4Driver) UnmountDvdDrive(vmName string) error { return hyperv.UnmountDvdDrive(vmName) } -func (d *HypervPS4Driver) DeleteDvdDrive(vmName string, controllerNumber string, controllerLocation string) error { +func (d *HypervPS4Driver) DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { return hyperv.DeleteDvdDrive(vmName, controllerNumber, controllerLocation) } +func (d *HypervPS4Driver) MountFloppyDrive(vmName string, path string) error { + return hyperv.MountFloppyDrive(vmName, path) +} + func (d *HypervPS4Driver) UnmountFloppyDrive(vmName string) error { return hyperv.UnmountFloppyDrive(vmName) } diff --git a/builder/hyperv/common/step_execute_online_activation.go b/builder/hyperv/common/step_execute_online_activation.go deleted file mode 100644 index b369934e1..000000000 --- a/builder/hyperv/common/step_execute_online_activation.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. -// All Rights Reserved. -// Licensed under the Apache License, Version 2.0. -// See License.txt in the project root for license information. -package common - -import ( - "fmt" - "bytes" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - "strings" - "log" -) - -type StepExecuteOnlineActivation struct { -} - -func (s *StepExecuteOnlineActivation) Run(state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - comm := state.Get("communicator").(packer.Communicator) - - errorMsg := "Error Executing Online Activation: %s" - - var remoteCmd packer.RemoteCmd - stdout := new(bytes.Buffer) - stderr := new(bytes.Buffer) - var err error - - ui.Say("Executing Online Activation...") - - var blockBuffer bytes.Buffer - blockBuffer.WriteString("{ cscript \"$env:SystemRoot/system32/slmgr.vbs\" -ato //nologo }") - - remoteCmd.Command = "-ScriptBlock " + blockBuffer.String() - - remoteCmd.Stdout = stdout - remoteCmd.Stderr = stderr - - err = comm.Start(&remoteCmd) - - stderrString := strings.TrimSpace(stderr.String()) - stdoutString := strings.TrimSpace(stdout.String()) - - log.Printf("stdout: %s", stdoutString) - log.Printf("stderr: %s", stderrString) - - if len(stderrString) > 0 { - err = fmt.Errorf(errorMsg, stderrString) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - ui.Say(stdoutString) - - return multistep.ActionContinue -} - -func (s *StepExecuteOnlineActivation) Cleanup(state multistep.StateBag) { - // do nothing -} diff --git a/builder/hyperv/common/step_execute_online_activation_full.go b/builder/hyperv/common/step_execute_online_activation_full.go deleted file mode 100644 index 6eb91936c..000000000 --- a/builder/hyperv/common/step_execute_online_activation_full.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. -// All Rights Reserved. -// Licensed under the Apache License, Version 2.0. -// See License.txt in the project root for license information. -package common - -import ( - "fmt" - "bytes" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - "strings" - "log" -) - -type StepExecuteOnlineActivationFull struct { - Pk string -} - -func (s *StepExecuteOnlineActivationFull) Run(state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - comm := state.Get("communicator").(packer.Communicator) - - errorMsg := "Error Executing Online Activation: %s" - - var remoteCmd packer.RemoteCmd - stdout := new(bytes.Buffer) - stderr := new(bytes.Buffer) - var err error - var stderrString string - var stdoutString string - - ui.Say("Executing Online Activation Full version...") - - var blockBuffer bytes.Buffer - blockBuffer.WriteString("{ cscript \"$env:SystemRoot/system32/slmgr.vbs\" /ipk "+ s.Pk +" //nologo }") - - log.Printf("cmd: %s", blockBuffer.String()) - remoteCmd.Command = "-ScriptBlock " + blockBuffer.String() - - remoteCmd.Stdout = stdout - remoteCmd.Stderr = stderr - - err = comm.Start(&remoteCmd) - - stderrString = strings.TrimSpace(stderr.String()) - stdoutString = strings.TrimSpace(stdout.String()) - - log.Printf("stdout: %s", stdoutString) - log.Printf("stderr: %s", stderrString) - - if len(stderrString) > 0 { - err = fmt.Errorf(errorMsg, stderrString) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - -// ui.Say(stdoutString) - -/* - blockBuffer.Reset() - blockBuffer.WriteString("{ cscript \"$env:SystemRoot/system32/slmgr.vbs\" -ato //nologo }") - - log.Printf("cmd: %s", blockBuffer.String()) - remoteCmd.Command = "-ScriptBlock " + blockBuffer.String() - - err = comm.Start(&remoteCmd) - - stderrString = strings.TrimSpace(stderr.String()) - stdoutString = strings.TrimSpace(stdout.String()) - - log.Printf("stdout: %s", stdoutString) - log.Printf("stderr: %s", stderrString) - - if len(stderrString) > 0 { - err = fmt.Errorf(errorMsg, stderrString) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - ui.Say(stdoutString) -*/ - return multistep.ActionContinue -} - -func (s *StepExecuteOnlineActivationFull) Cleanup(state multistep.StateBag) { - // do nothing -} diff --git a/builder/hyperv/common/step_mount_dvddrive.go b/builder/hyperv/common/step_mount_dvddrive.go index a9129a378..f31ef4c2c 100644 --- a/builder/hyperv/common/step_mount_dvddrive.go +++ b/builder/hyperv/common/step_mount_dvddrive.go @@ -8,12 +8,13 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - powershell "github.com/mitchellh/packer/powershell" + "log" ) type StepMountDvdDrive struct { - RawSingleISOUrl string - path string + Generation uint + cleanup bool + dvdProperties DvdControllerProperties } func (s *StepMountDvdDrive) Run(state multistep.StateBag) multistep.StepAction { @@ -22,39 +23,37 @@ func (s *StepMountDvdDrive) Run(state multistep.StateBag) multistep.StepAction { errorMsg := "Error mounting dvd drive: %s" vmName := state.Get("vmName").(string) - isoPath := s.RawSingleISOUrl + isoPath := state.Get("iso_path").(string) + + // should be able to mount up to 60 additional iso images using SCSI + // but Windows would only allow a max of 22 due to available drive letters + // Will Windows assign DVD drives to A: and B: ? - // Check that there is a virtual dvd drive - var script powershell.ScriptBuilder - powershell := new(powershell.PowerShellCmd) + // For IDE, there are only 2 controllers (0,1) with 2 locations each (0,1) - script.Reset() - script.WriteLine("param([string]$vmName)") - script.WriteLine("(Get-VMDvdDrive -VMName $vmName).ControllerNumber") - controllerNumber, err := powershell.Output(script.String(), vmName) + var dvdControllerProperties DvdControllerProperties + controllerNumber, controllerLocation, err := driver.CreateDvdDrive(vmName, s.Generation) if err != nil { state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - if controllerNumber == "" { - // Add a virtual dvd drive as there is none - script.Reset() - script.WriteLine("param([string]$vmName)") - script.WriteLine("Add-VMDvdDrive -VMName $vmName") - script.WriteLine("$dvdDrive = Get-VMDvdDrive -VMName $vmName | Select-Object -first 1") - script.WriteLine("Set-VMFirmware -VMName $vmName -FirstBootDevice $dvdDrive") - err = powershell.Run(script.String(), vmName) - if err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - ui.Say("Mounting dvd drive...") - + dvdControllerProperties.ControllerNumber = controllerNumber + dvdControllerProperties.ControllerLocation = controllerLocation + s.cleanup = true + s.dvdProperties = dvdControllerProperties + + ui.Say("Setting boot drive to os dvd drive %s ...") + err = driver.SetBootDvdDrive(vmName, controllerNumber, controllerLocation) + if err != nil { + err := fmt.Errorf(errorMsg, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + ui.Say(fmt.Sprintf("Mounting os dvd drive %s ...", isoPath)) err = driver.MountDvdDrive(vmName, isoPath) if err != nil { err := fmt.Errorf(errorMsg, err) @@ -62,28 +61,39 @@ func (s *StepMountDvdDrive) Run(state multistep.StateBag) multistep.StepAction { ui.Error(err.Error()) return multistep.ActionHalt } - - s.path = isoPath + + state.Put("os.dvd.properties", dvdControllerProperties) return multistep.ActionContinue } func (s *StepMountDvdDrive) Cleanup(state multistep.StateBag) { - if s.path == "" { + if !s.cleanup { return } driver := state.Get("driver").(Driver) - errorMsg := "Error unmounting dvd drive: %s" + errorMsg := "Error unmounting os dvd drive: %s" vmName := state.Get("vmName").(string) ui := state.Get("ui").(packer.Ui) - ui.Say("Unmounting dvd drive...") - - err := driver.UnmountDvdDrive(vmName) - if err != nil { - ui.Error(fmt.Sprintf(errorMsg, err)) + ui.Say("Clean up os dvd drive...") + + dvdControllerProperties := s.dvdProperties + + if dvdControllerProperties.Existing { + err := driver.UnmountDvdDrive(vmName) + if err != nil { + err := fmt.Errorf("Error unmounting dvd drive: %s", err) + log.Print(fmt.Sprintf(errorMsg, err)) + } + } else { + err := driver.DeleteDvdDrive(vmName, dvdControllerProperties.ControllerNumber, dvdControllerProperties.ControllerLocation) + if err != nil { + err := fmt.Errorf("Error deleting dvd drive: %s", err) + log.Print(fmt.Sprintf(errorMsg, err)) + } } } diff --git a/builder/hyperv/common/step_mount_floppydrive.go b/builder/hyperv/common/step_mount_floppydrive.go index 78f9f0914..8e8254083 100644 --- a/builder/hyperv/common/step_mount_floppydrive.go +++ b/builder/hyperv/common/step_mount_floppydrive.go @@ -8,93 +8,29 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/mitchellh/packer/powershell" - "github.com/mitchellh/packer/powershell/hyperv" "io" "io/ioutil" "log" "os" "path/filepath" - "strings" ) const ( FloppyFileName = "assets.vfd" ) -type StepSetUnattendedProductKey struct { - Files []string - ProductKey string -} - -func (s *StepSetUnattendedProductKey) Run(state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - - if s.ProductKey == "" { - ui.Say("No product key specified...") - return multistep.ActionContinue - } - - index := -1 - for i, value := range s.Files { - if s.caseInsensitiveContains(value, "Autounattend.xml") { - index = i - break - } - } - - ui.Say("Setting product key in Autounattend.xml...") - copyOfAutounattend, err := s.copyAutounattend(s.Files[index]) - if err != nil { - state.Put("error", fmt.Errorf("Error copying Autounattend.xml: %s", err)) - return multistep.ActionHalt - } - - powershell.SetUnattendedProductKey(copyOfAutounattend, s.ProductKey) - s.Files[index] = copyOfAutounattend - return multistep.ActionContinue -} - -func (s *StepSetUnattendedProductKey) caseInsensitiveContains(str, substr string) bool { - str, substr = strings.ToUpper(str), strings.ToUpper(substr) - return strings.Contains(str, substr) -} - -func (s *StepSetUnattendedProductKey) copyAutounattend(path string) (string, error) { - tempdir, err := ioutil.TempDir("", "packer") - if err != nil { - return "", err - } - - autounattend := filepath.Join(tempdir, "Autounattend.xml") - f, err := os.Create(autounattend) - if err != nil { - return "", err - } - defer f.Close() - - sourceF, err := os.Open(path) - if err != nil { - return "", err - } - defer sourceF.Close() - - log.Printf("Copying %s to temp location: %s", path, autounattend) - if _, err := io.Copy(f, sourceF); err != nil { - return "", err - } - - return autounattend, nil -} - -func (s *StepSetUnattendedProductKey) Cleanup(state multistep.StateBag) { -} - type StepMountFloppydrive struct { + Generation uint floppyPath string } func (s *StepMountFloppydrive) Run(state multistep.StateBag) multistep.StepAction { + if s.Generation > 1 { + return multistep.ActionContinue + } + + driver := state.Get("driver").(Driver) + // Determine if we even have a floppy disk to attach var floppyPath string if floppyPathRaw, ok := state.GetOk("floppy_path"); ok { @@ -118,7 +54,7 @@ func (s *StepMountFloppydrive) Run(state multistep.StateBag) multistep.StepActio ui.Say("Mounting floppy drive...") - err = hyperv.MountFloppyDrive(vmName, floppyPath) + err = driver.MountFloppyDrive(vmName, floppyPath) if err != nil { state.Put("error", fmt.Errorf("Error mounting floppy drive: %s", err)) return multistep.ActionHalt @@ -131,6 +67,10 @@ func (s *StepMountFloppydrive) Run(state multistep.StateBag) multistep.StepActio } func (s *StepMountFloppydrive) Cleanup(state multistep.StateBag) { + if s.Generation > 1 { + return + } + driver := state.Get("driver").(Driver) if s.floppyPath == "" { return } @@ -140,17 +80,17 @@ func (s *StepMountFloppydrive) Cleanup(state multistep.StateBag) { vmName := state.Get("vmName").(string) ui := state.Get("ui").(packer.Ui) - ui.Say("Unmounting floppy drive (cleanup)...") + ui.Say("Cleanup floppy drive...") - err := hyperv.UnmountFloppyDrive(vmName) + err := driver.UnmountFloppyDrive(vmName) if err != nil { - ui.Error(fmt.Sprintf(errorMsg, err)) + log.Print(fmt.Sprintf(errorMsg, err)) } err = os.Remove(s.floppyPath) if err != nil { - ui.Error(fmt.Sprintf(errorMsg, err)) + log.Print(fmt.Sprintf(errorMsg, err)) } } diff --git a/builder/hyperv/common/step_mount_integration_services.go b/builder/hyperv/common/step_mount_integration_services.go deleted file mode 100644 index 317296916..000000000 --- a/builder/hyperv/common/step_mount_integration_services.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. -// All Rights Reserved. -// Licensed under the Apache License, Version 2.0. -// See License.txt in the project root for license information. -package common - -import ( - "fmt" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - hyperv "github.com/mitchellh/packer/powershell/hyperv" - "log" - "os" - "strconv" -) - -type StepMountSecondaryDvdImages struct { - Files []string - Generation uint - dvdProperties []DvdControllerProperties -} - -type DvdControllerProperties struct { - ControllerNumber string - ControllerLocation string -} - -func (s *StepMountSecondaryDvdImages) Run(state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - ui.Say("Mounting secondary DVD images...") - - vmName := state.Get("vmName").(string) - - // should be able to mount up to 60 additional iso images using SCSI - // but Windows would only allow a max of 22 due to available drive letters - // Will Windows assign DVD drives to A: and B: ? - - // For IDE, there are only 2 controllers (0,1) with 2 locations each (0,1) - dvdProperties, err := s.mountFiles(vmName) - if err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - log.Println(fmt.Sprintf("Saving DVD properties %d DVDs", len(dvdProperties))) - - state.Put("secondary.dvd.properties", dvdProperties) - - return multistep.ActionContinue -} - -func (s *StepMountSecondaryDvdImages) Cleanup(state multistep.StateBag) { - -} - -func (s *StepMountSecondaryDvdImages) mountFiles(vmName string) ([]DvdControllerProperties, error) { - - var dvdProperties []DvdControllerProperties - - properties, err := s.addAndMountIntegrationServicesSetupDisk(vmName) - if err != nil { - return dvdProperties, err - } - - dvdProperties = append(dvdProperties, properties) - - for _, value := range s.Files { - properties, err := s.addAndMountDvdDisk(vmName, value) - if err != nil { - return dvdProperties, err - } - - dvdProperties = append(dvdProperties, properties) - } - - return dvdProperties, nil -} - -func (s *StepMountSecondaryDvdImages) addAndMountIntegrationServicesSetupDisk(vmName string) (DvdControllerProperties, error) { - - isoPath := os.Getenv("WINDIR") + "\\system32\\vmguest.iso" - properties, err := s.addAndMountDvdDisk(vmName, isoPath) - if err != nil { - return properties, err - } - - return properties, nil -} - -func (s *StepMountSecondaryDvdImages) addAndMountDvdDisk(vmName string, isoPath string) (DvdControllerProperties, error) { - var properties DvdControllerProperties - - controllerNumber, controllerLocation, err := hyperv.CreateDvdDrive(vmName, s.Generation) - if err != nil { - return properties, err - } - - properties.ControllerNumber = strconv.FormatInt(int64(controllerNumber), 10) - properties.ControllerLocation = strconv.FormatInt(int64(controllerLocation), 10) - - err = hyperv.MountDvdDriveByLocation(vmName, isoPath, controllerNumber, controllerLocation) - if err != nil { - return properties, err - } - - log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v", isoPath, controllerNumber, controllerLocation)) - - return properties, nil -} diff --git a/builder/hyperv/common/step_unmount_dvddrive.go b/builder/hyperv/common/step_unmount_dvddrive.go index c5a4f2f84..c0f4d3e1c 100644 --- a/builder/hyperv/common/step_unmount_dvddrive.go +++ b/builder/hyperv/common/step_unmount_dvddrive.go @@ -15,20 +15,31 @@ type StepUnmountDvdDrive struct { func (s *StepUnmountDvdDrive) Run(state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) + vmName := state.Get("vmName").(string) ui := state.Get("ui").(packer.Ui) - vmName := state.Get("vmName").(string) - - ui.Say("Unmounting dvd drive...") - - err := driver.UnmountDvdDrive(vmName) - if err != nil { - err := fmt.Errorf("Error unmounting dvd drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt + ui.Say("Unmounting os dvd drive...") + + dvdController := state.Get("os.dvd.properties").(DvdControllerProperties) + + if dvdController.Existing { + err := driver.UnmountDvdDrive(vmName) + if err != nil { + err := fmt.Errorf("Error unmounting os dvd drive: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } else { + err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation) + if err != nil { + err := fmt.Errorf("Error deleting os dvd drive: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } } - + return multistep.ActionContinue } diff --git a/builder/hyperv/common/step_unmount_floppydrive.go b/builder/hyperv/common/step_unmount_floppydrive.go index 21ae1dcc1..45546e1d4 100644 --- a/builder/hyperv/common/step_unmount_floppydrive.go +++ b/builder/hyperv/common/step_unmount_floppydrive.go @@ -22,10 +22,10 @@ func (s *StepUnmountFloppyDrive) Run(state multistep.StateBag) multistep.StepAct return multistep.ActionContinue } - errorMsg := "Error Unmounting floppy drive: %s" vmName := state.Get("vmName").(string) - ui.Say("Unmounting floppy drive (Run)...") + + errorMsg := "Error Unmounting floppy drive: %s" err := driver.UnmountFloppyDrive(vmName) if err != nil { diff --git a/builder/hyperv/common/step_unmount_integration_services.go b/builder/hyperv/common/step_unmount_integration_services.go deleted file mode 100644 index d2719744b..000000000 --- a/builder/hyperv/common/step_unmount_integration_services.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. -// All Rights Reserved. -// Licensed under the Apache License, Version 2.0. -// See License.txt in the project root for license information. -package common - -import ( - "fmt" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - "log" -) - -type StepUnmountSecondaryDvdImages struct { -} - -func (s *StepUnmountSecondaryDvdImages) Run(state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packer.Ui) - ui.Say("Unmounting Integration Services Setup Disk...") - - vmName := state.Get("vmName").(string) - - // todo: should this message say removing the dvd? - - dvdProperties := state.Get("secondary.dvd.properties").([]DvdControllerProperties) - - log.Println(fmt.Sprintf("Found DVD properties %d", len(dvdProperties))) - - for _, dvdProperty := range dvdProperties { - err := driver.DeleteDvdDrive(vmName, dvdProperty.ControllerNumber, dvdProperty.ControllerLocation) - if err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - return multistep.ActionContinue -} - -func (s *StepUnmountSecondaryDvdImages) Cleanup(state multistep.StateBag) { -} diff --git a/builder/hyperv/common/step_upgrade_integration_services.go b/builder/hyperv/common/step_upgrade_integration_services.go deleted file mode 100644 index ab076e7ed..000000000 --- a/builder/hyperv/common/step_upgrade_integration_services.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. -// All Rights Reserved. -// Licensed under the Apache License, Version 2.0. -// See License.txt in the project root for license information. -package common - -import ( - //"fmt" - "os" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - powershell "github.com/mitchellh/packer/powershell" -) - -type StepUpdateIntegrationServices struct { - Username string - Password string - - newDvdDriveProperties dvdDriveProperties -} - -type dvdDriveProperties struct { - ControllerNumber string - ControllerLocation string -} - -func (s *StepUpdateIntegrationServices) Run(state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - vmName := state.Get("vmName").(string) - - ui.Say("Mounting Integration Services Setup Disk...") - - _, err := s.mountIntegrationServicesSetupDisk(vmName); - if err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // dvdDriveLetter, err := s.getDvdDriveLetter(vmName) - // if err != nil { - // state.Put("error", err) - // ui.Error(err.Error()) - // return multistep.ActionHalt - // } - - // setup := dvdDriveLetter + ":\\support\\"+osArchitecture+"\\setup.exe /quiet /norestart" - - // ui.Say("Run: " + setup) - - return multistep.ActionContinue -} - -func (s *StepUpdateIntegrationServices) Cleanup(state multistep.StateBag) { - vmName := state.Get("vmName").(string) - - var script powershell.ScriptBuilder - script.WriteLine("param([string]$vmName)") - script.WriteLine("Set-VMDvdDrive -VMName $vmName -Path $null") - - powershell := new(powershell.PowerShellCmd) - _ = powershell.Run(script.String(), vmName) -} - -func (s *StepUpdateIntegrationServices) mountIntegrationServicesSetupDisk(vmName string) (dvdDriveProperties, error) { - - var dvdProperties dvdDriveProperties - - var script powershell.ScriptBuilder - script.WriteLine("param([string]$vmName)") - script.WriteLine("Add-VMDvdDrive -VMName $vmName") - - powershell := new(powershell.PowerShellCmd) - err := powershell.Run(script.String(), vmName) - if err != nil { - return dvdProperties, err - } - - script.Reset() - script.WriteLine("param([string]$vmName)") - script.WriteLine("(Get-VMDvdDrive -VMName $vmName | Where-Object {$_.Path -eq $null}).ControllerLocation") - controllerLocation, err := powershell.Output(script.String(), vmName) - if err != nil { - return dvdProperties, err - } - - script.Reset() - script.WriteLine("param([string]$vmName)") - script.WriteLine("(Get-VMDvdDrive -VMName $vmName | Where-Object {$_.Path -eq $null}).ControllerNumber") - controllerNumber, err := powershell.Output(script.String(), vmName) - if err != nil { - return dvdProperties, err - } - - isoPath := os.Getenv("WINDIR") + "\\system32\\vmguest.iso" - - script.Reset() - script.WriteLine("param([string]$vmName,[string]$path,[string]$controllerNumber,[string]$controllerLocation)") - script.WriteLine("Set-VMDvdDrive -VMName $vmName -Path $path -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation") - - err = powershell.Run(script.String(), vmName, isoPath, controllerNumber, controllerLocation) - if err != nil { - return dvdProperties, err - } - - dvdProperties.ControllerNumber = controllerNumber - dvdProperties.ControllerLocation = controllerLocation - - return dvdProperties, err -} diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 9a18c133f..40b1a36a7 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -19,6 +19,7 @@ import ( "log" "strings" "time" + "os" ) const ( @@ -84,12 +85,21 @@ type Config struct { // either an HTTP URL or a file URL (or path to a file). If this is an // HTTP URL, Packer will download it and cache it between runs. RawSingleISOUrl string `mapstructure:"iso_url"` + // Multiple URLs for the ISO to download. Packer will try these in order. // If anything goes wrong attempting to download or while downloading a // single URL, it will move on to the next. All URLs must point to the // same file (same checksum). By default this is empty and iso_url is // used. Only one of iso_url or iso_urls can be specified. ISOUrls []string `mapstructure:"iso_urls"` + + TargetPath string `mapstructure:"iso_target_path"` + + // Should integration services iso be mounted + GuestAdditionsMode string `mapstructure:"guest_additions_mode"` + + // The path to the integration services iso + GuestAdditionsPath string `mapstructure:"guest_additions_path"` // This is the name of the new virtual machine. // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. @@ -234,6 +244,50 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { log.Println(fmt.Sprintf("%s: %v", "RawSingleISOUrl", b.config.RawSingleISOUrl)) + if b.config.GuestAdditionsMode == "" { + b.config.GuestAdditionsMode = "attach" + } + + if b.config.GuestAdditionsPath == "" { + b.config.GuestAdditionsPath = os.Getenv("WINDIR") + "\\system32\\vmguest.iso" + } + + for _, isoPath := range b.config.SecondaryDvdImages { + if _, err := os.Stat(isoPath); os.IsNotExist(err) { + if err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Secondary Dvd image does not exist: %s", err)) + } + } + } + + numberOfIsos := len(b.config.SecondaryDvdImages) + + if b.config.GuestAdditionsMode == "attach" { + if _, err := os.Stat(b.config.GuestAdditionsPath); os.IsNotExist(err) { + if err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Guest additions iso does not exist: %s", err)) + } + } + + numberOfIsos = numberOfIsos + 1 + } + + if b.config.Generation < 2 && numberOfIsos > 2 { + if b.config.GuestAdditionsMode == "attach" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are only 2 ide controllers available, so we can't support guest additions and these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", "))) + } else { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are only 2 ide controllers available, so we can't support these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", "))) + } + } else if b.config.Generation > 1 && len(b.config.SecondaryDvdImages) > 16 { + if b.config.GuestAdditionsMode == "attach" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are not enough drive letters available for scsi (limited to 16), so we can't support guest additions and these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", "))) + } else { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are not enough drive letters available for scsi (limited to 16), so we can't support these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", "))) + } + } + // Warnings if b.config.ISOChecksumType == "none" { warnings = append(warnings, @@ -275,12 +329,21 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("hook", hook) state.Put("ui", ui) - steps := []multistep.Step{ + steps := []multistep.Step{ &hypervcommon.StepCreateTempDir{}, &hypervcommon.StepOutputDir{ Force: b.config.PackerForce, Path: b.config.OutputDir, }, + &common.StepDownload{ + Checksum: b.config.ISOChecksum, + ChecksumType: b.config.ISOChecksumType, + Description: "ISO", + ResultKey: "iso_path", + Url: b.config.ISOUrls, + Extension: "iso", + TargetPath: b.config.TargetPath, + }, &common.StepCreateFloppy{ Files: b.config.FloppyFiles, }, @@ -304,12 +367,20 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &hypervcommon.StepEnableIntegrationService{}, &hypervcommon.StepMountDvdDrive{ - RawSingleISOUrl: b.config.RawSingleISOUrl, + Generation: b.config.Generation, + }, + &hypervcommon.StepMountFloppydrive{ + Generation: b.config.Generation, + }, + + &hypervcommon.StepMountGuestAdditions{ + GuestAdditionsMode: b.config.GuestAdditionsMode, + GuestAdditionsPath: b.config.GuestAdditionsPath, + Generation: b.config.Generation, }, - &hypervcommon.StepMountFloppydrive{}, &hypervcommon.StepMountSecondaryDvdImages{ - Files: b.config.SecondaryDvdImages, + IsoPaths: b.config.SecondaryDvdImages, Generation: b.config.Generation, }, @@ -341,15 +412,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // wait for the vm to be powered off &hypervcommon.StepWaitForPowerOff{}, - - // remove the integration services dvd drive + + // remove the secondary dvd images // after we power down &hypervcommon.StepUnmountSecondaryDvdImages{}, + &hypervcommon.StepUnmountGuestAdditions{}, + &hypervcommon.StepUnmountDvdDrive{}, &hypervcommon.StepUnmountFloppyDrive{ Generation: b.config.Generation, }, - &hypervcommon.StepUnmountDvdDrive{}, - &hypervcommon.StepExportVm{ OutputDir: b.config.OutputDir, SkipCompaction: b.config.SkipCompaction, diff --git a/powershell/hyperv/hyperv.go b/powershell/hyperv/hyperv.go index 162b048fa..bcba3edf5 100644 --- a/powershell/hyperv/hyperv.go +++ b/powershell/hyperv/hyperv.go @@ -153,7 +153,20 @@ Get-VMDvdDrive -VMName $vmName | Set-VMDvdDrive -Path $null return err } -func DeleteDvdDrive(vmName string, controllerNumber string, controllerLocation string) error { +func SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { + var script = ` +param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation) +$vmDvdDrive = Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation +if (!$vmDvdDrive) {throw 'unable to find dvd drive'} +Set-VMFirmware -VMName $vmName -FirstBootDevice $vmDvdDrive +` + + var ps powershell.PowerShellCmd + err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10)) + return err +} + +func DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { var script = ` param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation) $vmDvdDrive = Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation @@ -162,7 +175,7 @@ Remove-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -Controlle ` var ps powershell.PowerShellCmd - err := ps.Run(script, vmName, controllerNumber, controllerLocation) + err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10)) return err }