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 }