Remove steps that are windows specific
This commit is contained in:
parent
d632f3b574
commit
1fe4c501e4
|
@ -0,0 +1,65 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
// This is the common builder ID to all of these artifacts.
|
||||
const BuilderId = "mitchellh.hyperv"
|
||||
|
||||
// Artifact is the result of running the hyperv builder, namely a set
|
||||
// of files associated with the resulting machine.
|
||||
type artifact struct {
|
||||
dir string
|
||||
f []string
|
||||
}
|
||||
|
||||
// NewArtifact returns a hyperv artifact containing the files
|
||||
// in the given directory.
|
||||
func NewArtifact(dir string) (packer.Artifact, error) {
|
||||
files := make([]string, 0, 5)
|
||||
visit := func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
files = append(files, path)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := filepath.Walk(dir, visit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &artifact{
|
||||
dir: dir,
|
||||
f: files,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (*artifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
func (a *artifact) Files() []string {
|
||||
return a.f
|
||||
}
|
||||
|
||||
func (*artifact) Id() string {
|
||||
return "VM"
|
||||
}
|
||||
|
||||
func (a *artifact) String() string {
|
||||
return fmt.Sprintf("VM files in directory: %s", a.dir)
|
||||
}
|
||||
|
||||
func (a *artifact) State(name string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *artifact) Destroy() error {
|
||||
return os.RemoveAll(a.dir)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
func TestArtifact_impl(t *testing.T) {
|
||||
var _ packer.Artifact = new(artifact)
|
||||
}
|
||||
|
||||
func TestNewArtifact(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(td, "a"), []byte("foo"), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if err := os.Mkdir(filepath.Join(td, "b"), 0755); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
a, err := NewArtifact(td)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if a.BuilderId() != BuilderId {
|
||||
t.Fatalf("bad: %#v", a.BuilderId())
|
||||
}
|
||||
if len(a.Files()) != 1 {
|
||||
t.Fatalf("should length 1: %d", len(a.Files()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
func testConfigTemplate(t *testing.T) *interpolate.Context {
|
||||
return &interpolate.Context{}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// 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
|
||||
|
||||
// A driver is able to talk to HyperV and perform certain
|
||||
// operations with it. Some of the operations on here may seem overly
|
||||
// specific, but they were built specifically in mind to handle features
|
||||
// of the HyperV builder for Packer, and to abstract differences in
|
||||
// versions out of the builder steps, so sometimes the methods are
|
||||
// extremely specific.
|
||||
type Driver interface {
|
||||
|
||||
// Checks if the VM named is running.
|
||||
IsRunning(string) (bool, error)
|
||||
|
||||
// Start starts a VM specified by the name given.
|
||||
Start(string) error
|
||||
|
||||
// Stop stops a VM specified by the name given.
|
||||
Stop(string) error
|
||||
|
||||
// Verify checks to make sure that this driver should function
|
||||
// properly. If there is any indication the driver can't function,
|
||||
// this will return an error.
|
||||
Verify() error
|
||||
|
||||
// Finds the MAC address of the NIC nic0
|
||||
Mac(string) (string, error)
|
||||
|
||||
// Finds the IP address of a VM connected that uses DHCP by its MAC address
|
||||
IpAddress(string) (string, error)
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
// 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/packer/powershell"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HypervPS4Driver struct {
|
||||
}
|
||||
|
||||
func NewHypervPS4Driver() (Driver, error) {
|
||||
appliesTo := "Applies to Windows 8.1, Windows PowerShell 4.0, Windows Server 2012 R2 only"
|
||||
|
||||
// Check this is Windows
|
||||
if runtime.GOOS != "windows" {
|
||||
err := fmt.Errorf("%s", appliesTo)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ps4Driver := &HypervPS4Driver{}
|
||||
|
||||
if err := ps4Driver.Verify(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ps4Driver, nil
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) IsRunning(vmName string) (bool, error) {
|
||||
return hyperv.IsRunning(vmName)
|
||||
}
|
||||
|
||||
// Start starts a VM specified by the name given.
|
||||
func (d *HypervPS4Driver) Start(vmName string) error {
|
||||
return hyperv.Start(vmName)
|
||||
}
|
||||
|
||||
// Stop stops a VM specified by the name given.
|
||||
func (d *HypervPS4Driver) Stop(vmName string) error {
|
||||
return hyperv.TurnOff(vmName)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) Verify() error {
|
||||
|
||||
if err := d.verifyPSVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.verifyPSHypervModule(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.verifyElevatedMode(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get mac address for VM.
|
||||
func (d *HypervPS4Driver) Mac(vmName string) (string, error) {
|
||||
return hyperv.Mac(vmName)
|
||||
}
|
||||
|
||||
// Get ip address for mac address.
|
||||
func (d *HypervPS4Driver) IpAddress(mac string) (string, error) {
|
||||
return hyperv.IpAddress(mac)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) verifyPSVersion() error {
|
||||
|
||||
log.Printf("Enter method: %s", "verifyPSVersion")
|
||||
// check PS is available and is of proper version
|
||||
versionCmd := "$host.version.Major"
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(versionCmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
versionOutput := strings.TrimSpace(string(cmdOut))
|
||||
log.Printf("%s output: %s", versionCmd, versionOutput)
|
||||
|
||||
ver, err := strconv.ParseInt(versionOutput, 10, 32)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ver < 4 {
|
||||
err := fmt.Errorf("%s", "Windows PowerShell version 4.0 or higher is expected")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) verifyPSHypervModule() error {
|
||||
|
||||
log.Printf("Enter method: %s", "verifyPSHypervModule")
|
||||
|
||||
versionCmd := "function foo(){try{ $commands = Get-Command -Module Hyper-V;if($commands.Length -eq 0){return $false} }catch{return $false}; return $true} foo"
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(versionCmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res := strings.TrimSpace(string(cmdOut))
|
||||
|
||||
if res == "False" {
|
||||
err := fmt.Errorf("%s", "PS Hyper-V module is not loaded. Make sure Hyper-V feature is on.")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) verifyElevatedMode() error {
|
||||
|
||||
log.Printf("Enter method: %s", "verifyElevatedMode")
|
||||
|
||||
isAdmin, _ := powershell.IsCurrentUserAnAdministrator()
|
||||
|
||||
if !isAdmin {
|
||||
err := fmt.Errorf("%s", "Please restart your shell in elevated mode")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type OutputConfig struct {
|
||||
OutputDir string `mapstructure:"output_directory"`
|
||||
}
|
||||
|
||||
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
|
||||
if c.OutputDir == "" {
|
||||
c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
|
||||
}
|
||||
|
||||
var errs []error
|
||||
if !pc.PackerForce {
|
||||
if _, err := os.Stat(c.OutputDir); err == nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"Output directory '%s' already exists. It must not exist.", c.OutputDir))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/common"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOutputConfigPrepare(t *testing.T) {
|
||||
c := new(OutputConfig)
|
||||
if c.OutputDir != "" {
|
||||
t.Fatalf("what: %s", c.OutputDir)
|
||||
}
|
||||
|
||||
pc := &common.PackerConfig{PackerBuildName: "foo"}
|
||||
errs := c.Prepare(testConfigTemplate(t), pc)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("err: %#v", errs)
|
||||
}
|
||||
|
||||
if c.OutputDir == "" {
|
||||
t.Fatal("should have output dir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputConfigPrepare_exists(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
c := new(OutputConfig)
|
||||
c.OutputDir = td
|
||||
|
||||
pc := &common.PackerConfig{
|
||||
PackerBuildName: "foo",
|
||||
PackerForce: false,
|
||||
}
|
||||
errs := c.Prepare(testConfigTemplate(t), pc)
|
||||
if len(errs) != 0 {
|
||||
t.Fatal("should not have errors")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
commonssh "github.com/mitchellh/packer/common/ssh"
|
||||
packerssh "github.com/mitchellh/packer/communicator/ssh"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func CommHost(state multistep.StateBag) (string, error) {
|
||||
vmName := state.Get("vmName").(string)
|
||||
driver := state.Get("driver").(Driver)
|
||||
|
||||
mac, err := driver.Mac(vmName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ip, err := driver.IpAddress(mac)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func SSHConfigFunc(config SSHConfig) func(multistep.StateBag) (*ssh.ClientConfig, error) {
|
||||
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
|
||||
auth := []ssh.AuthMethod{
|
||||
ssh.Password(config.Comm.SSHPassword),
|
||||
ssh.KeyboardInteractive(
|
||||
packerssh.PasswordKeyboardInteractive(config.Comm.SSHPassword)),
|
||||
}
|
||||
|
||||
if config.SSHKeyPath != "" {
|
||||
signer, err := commonssh.FileSigner(config.Comm.SSHPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auth = append(auth, ssh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
return &ssh.ClientConfig{
|
||||
User: config.Comm.SSHUsername,
|
||||
Auth: auth,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/packer/helper/communicator"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type SSHConfig struct {
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
// These are deprecated, but we keep them around for BC
|
||||
// TODO(@mitchellh): remove
|
||||
SSHKeyPath string `mapstructure:"ssh_key_path"`
|
||||
SSHWaitTimeout time.Duration `mapstructure:"ssh_wait_timeout"`
|
||||
}
|
||||
|
||||
func (c *SSHConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
// TODO: backwards compatibility, write fixer instead
|
||||
if c.SSHKeyPath != "" {
|
||||
c.Comm.SSHPrivateKey = c.SSHKeyPath
|
||||
}
|
||||
if c.SSHWaitTimeout != 0 {
|
||||
c.Comm.SSHTimeout = c.SSHWaitTimeout
|
||||
}
|
||||
|
||||
return c.Comm.Prepare(ctx)
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// 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"
|
||||
"strings"
|
||||
"time"
|
||||
"log"
|
||||
powershell "github.com/mitchellh/packer/powershell"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
|
||||
type StepConfigureIp struct {
|
||||
}
|
||||
|
||||
func (s *StepConfigureIp) Run(state multistep.StateBag) multistep.StepAction {
|
||||
// driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error configuring ip address: %s"
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Configuring ip address...")
|
||||
|
||||
count := 60
|
||||
var duration time.Duration = 1
|
||||
sleepTime := time.Minute * duration
|
||||
var ip string
|
||||
|
||||
for count != 0 {
|
||||
cmdOut, err := hyperv.GetVirtualMachineNetworkAdapterAddress(vmName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ip = strings.TrimSpace(string(cmdOut))
|
||||
|
||||
if ip != "False" {
|
||||
break;
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Waiting for another %v minutes...", uint(duration)))
|
||||
time.Sleep(sleepTime)
|
||||
count--
|
||||
}
|
||||
|
||||
if(count == 0){
|
||||
err := fmt.Errorf(errorMsg, "IP address assigned to the adapter is empty")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("ip address is " + ip)
|
||||
|
||||
hostName, err := powershell.GetHostName(ip);
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("hostname is " + hostName)
|
||||
|
||||
state.Put("ip", ip)
|
||||
state.Put("hostname", hostName)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepConfigureIp) Cleanup(state multistep.StateBag) {
|
||||
// do nothing
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// 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"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
|
||||
const(
|
||||
vlanId = "1724"
|
||||
)
|
||||
|
||||
type StepConfigureVlan struct {
|
||||
}
|
||||
|
||||
func (s *StepConfigureVlan) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//config := state.Get("config").(*config)
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error configuring vlan: %s"
|
||||
vmName := state.Get("vmName").(string)
|
||||
switchName := state.Get("SwitchName").(string)
|
||||
|
||||
ui.Say("Configuring vlan...")
|
||||
|
||||
err := hyperv.SetNetworkAdapterVlanId(switchName, vlanId)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = hyperv.SetVirtualMachineVlanId(vmName, vlanId)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepConfigureVlan) Cleanup(state multistep.StateBag) {
|
||||
//do nothing
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
// 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"
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
// This step creates switch for VM.
|
||||
//
|
||||
// Produces:
|
||||
// SwitchName string - The name of the Switch
|
||||
type StepCreateExternalSwitch struct {
|
||||
SwitchName string
|
||||
oldSwitchName string
|
||||
}
|
||||
|
||||
func (s *StepCreateExternalSwitch) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
vmName := state.Get("vmName").(string)
|
||||
errorMsg := "Error createing external switch: %s"
|
||||
var err error
|
||||
|
||||
ui.Say("Creating external switch...")
|
||||
|
||||
packerExternalSwitchName := "paes_" + uuid.New()
|
||||
|
||||
err = hyperv.CreateExternalVirtualSwitch(vmName, packerExternalSwitchName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating switch: %s", err)
|
||||
state.Put(errorMsg, err)
|
||||
ui.Error(err.Error())
|
||||
s.SwitchName = "";
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
switchName, err := hyperv.GetVirtualMachineSwitchName(vmName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if len(switchName) == 0 {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", "Can't get the VM switch name")
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("External switch name is: '" + switchName + "'")
|
||||
|
||||
if(switchName != packerExternalSwitchName){
|
||||
s.SwitchName = ""
|
||||
} else {
|
||||
s.SwitchName = packerExternalSwitchName
|
||||
s.oldSwitchName = state.Get("SwitchName").(string)
|
||||
}
|
||||
|
||||
// Set the final name in the state bag so others can use it
|
||||
state.Put("SwitchName", switchName)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCreateExternalSwitch) Cleanup(state multistep.StateBag) {
|
||||
if s.SwitchName == "" {
|
||||
return
|
||||
}
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Unregistering and deleting external switch...")
|
||||
|
||||
var err error = nil
|
||||
|
||||
errMsg := "Error deleting external switch: %s"
|
||||
|
||||
// connect the vm to the old switch
|
||||
if s.oldSwitchName == "" {
|
||||
ui.Error(fmt.Sprintf(errMsg, "the old switch name is empty"))
|
||||
return
|
||||
}
|
||||
|
||||
err = hyperv.ConnectVirtualMachineNetworkAdapterToSwitch(vmName, s.oldSwitchName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(errMsg, err))
|
||||
return
|
||||
}
|
||||
|
||||
state.Put("SwitchName", s.oldSwitchName)
|
||||
|
||||
err = hyperv.DeleteVirtualSwitch(s.SwitchName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(errMsg, err))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// 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"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
const (
|
||||
SwitchTypeInternal = "Internal"
|
||||
SwitchTypePrivate = "Private"
|
||||
DefaultSwitchType = SwitchTypeInternal
|
||||
)
|
||||
|
||||
// This step creates switch for VM.
|
||||
//
|
||||
// Produces:
|
||||
// SwitchName string - The name of the Switch
|
||||
type StepCreateSwitch struct {
|
||||
// Specifies the name of the switch to be created.
|
||||
SwitchName string
|
||||
// Specifies the type of the switch to be created. Allowed values are Internal and Private. To create an External
|
||||
// virtual switch, specify either the NetAdapterInterfaceDescription or the NetAdapterName parameter, which
|
||||
// implicitly set the type of the virtual switch to External.
|
||||
SwitchType string
|
||||
// Specifies the name of the network adapter to be bound to the switch to be created.
|
||||
NetAdapterName string
|
||||
// Specifies the interface description of the network adapter to be bound to the switch to be created.
|
||||
NetAdapterInterfaceDescription string
|
||||
|
||||
createdSwitch bool
|
||||
}
|
||||
|
||||
func (s *StepCreateSwitch) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if len(s.SwitchType) == 0 {
|
||||
s.SwitchType = DefaultSwitchType
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating switch '%v' if required...", s.SwitchName))
|
||||
|
||||
createdSwitch, err := hyperv.CreateVirtualSwitch(s.SwitchName, s.SwitchType)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating switch: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
s.SwitchName = "";
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.createdSwitch = createdSwitch
|
||||
|
||||
if !s.createdSwitch {
|
||||
ui.Say(fmt.Sprintf(" switch '%v' already exists. Will not delete on cleanup...", s.SwitchName))
|
||||
}
|
||||
|
||||
// Set the final name in the state bag so others can use it
|
||||
state.Put("SwitchName", s.SwitchName)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCreateSwitch) Cleanup(state multistep.StateBag) {
|
||||
if len(s.SwitchName) == 0 || !s.createdSwitch {
|
||||
return
|
||||
}
|
||||
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Unregistering and deleting switch...")
|
||||
|
||||
err := hyperv.DeleteVirtualSwitch(s.SwitchName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting switch: %s", err))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// 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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
type StepCreateTempDir struct {
|
||||
dirPath string
|
||||
}
|
||||
|
||||
func (s *StepCreateTempDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Creating temporary directory...")
|
||||
|
||||
tempDir := os.TempDir()
|
||||
packerTempDir, err := ioutil.TempDir(tempDir, "packerhv")
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary directory: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.dirPath = packerTempDir;
|
||||
state.Put("packerTempDir", packerTempDir)
|
||||
|
||||
// ui.Say("packerTempDir = '" + packerTempDir + "'")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCreateTempDir) Cleanup(state multistep.StateBag) {
|
||||
if s.dirPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Deleting temporary directory...")
|
||||
|
||||
err := os.RemoveAll(s.dirPath)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting temporary directory: %s", err))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// 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"
|
||||
"strconv"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
// This step creates the actual virtual machine.
|
||||
//
|
||||
// Produces:
|
||||
// VMName string - The name of the VM
|
||||
type StepCreateVM struct {
|
||||
VMName string
|
||||
SwitchName string
|
||||
RamSizeMB uint
|
||||
DiskSize uint
|
||||
}
|
||||
|
||||
func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Creating virtual machine...")
|
||||
|
||||
path := state.Get("packerTempDir").(string)
|
||||
|
||||
// convert the MB to bytes
|
||||
ramBytes := int64(s.RamSizeMB * 1024 * 1024)
|
||||
diskSizeBytes := int64(s.DiskSize * 1024 * 1024)
|
||||
|
||||
ram := strconv.FormatInt(ramBytes, 10)
|
||||
diskSize := strconv.FormatInt(diskSizeBytes, 10)
|
||||
switchName := s.SwitchName
|
||||
|
||||
err := hyperv.CreateVirtualMachine(s.VMName, path, ram, diskSize, switchName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating virtual machine: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Set the final name in the state bag so others can use it
|
||||
state.Put("vmName", s.VMName)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCreateVM) Cleanup(state multistep.StateBag) {
|
||||
if s.VMName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Unregistering and deleting virtual machine...")
|
||||
|
||||
err := hyperv.DeleteVirtualMachine(s.VMName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting virtual machine: %s", err))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// 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"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
type StepDisableVlan struct {
|
||||
}
|
||||
|
||||
func (s *StepDisableVlan) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//config := state.Get("config").(*config)
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error disabling vlan: %s"
|
||||
vmName := state.Get("vmName").(string)
|
||||
switchName := state.Get("SwitchName").(string)
|
||||
|
||||
ui.Say("Disabling vlan...")
|
||||
|
||||
err := hyperv.UntagVirtualMachineNetworkAdapterVlan(vmName, switchName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepDisableVlan) Cleanup(state multistep.StateBag) {
|
||||
//do nothing
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// 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"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
type StepEnableIntegrationService struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (s *StepEnableIntegrationService) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Enabling Integration Service...")
|
||||
|
||||
vmName := state.Get("vmName").(string)
|
||||
s.name = "Guest Service Interface"
|
||||
|
||||
err := hyperv.EnableVirtualMachineIntegrationService(vmName, s.name)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error enabling Integration Service: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepEnableIntegrationService) Cleanup(state multistep.StateBag) {
|
||||
// do nothing
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// 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
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
// 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
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// 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"
|
||||
"path/filepath"
|
||||
"io/ioutil"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
const(
|
||||
vhdDir string = "Virtual Hard Disks"
|
||||
vmDir string = "Virtual Machines"
|
||||
)
|
||||
|
||||
type StepExportVm struct {
|
||||
OutputDir string
|
||||
}
|
||||
|
||||
func (s *StepExportVm) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var err error
|
||||
var errorMsg string
|
||||
|
||||
vmName := state.Get("vmName").(string)
|
||||
tmpPath := state.Get("packerTempDir").(string)
|
||||
outputPath := s.OutputDir
|
||||
|
||||
// create temp path to export vm
|
||||
errorMsg = "Error creating temp export path: %s"
|
||||
vmExportPath , err := ioutil.TempDir(tmpPath, "export")
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Exporting vm...")
|
||||
|
||||
err = hyperv.ExportVirtualMachine(vmName, vmExportPath)
|
||||
if err != nil {
|
||||
errorMsg = "Error exporting vm: %s"
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// copy to output dir
|
||||
expPath := filepath.Join(vmExportPath,vmName)
|
||||
|
||||
ui.Say("Coping to output dir...")
|
||||
err = hyperv.CopyExportedVirtualMachine(expPath, outputPath, vhdDir, vmDir)
|
||||
if err != nil {
|
||||
errorMsg = "Error exporting vm: %s"
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepExportVm) Cleanup(state multistep.StateBag) {
|
||||
// do nothing
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// 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"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
|
||||
type StepMountDvdDrive struct {
|
||||
RawSingleISOUrl string
|
||||
path string
|
||||
}
|
||||
|
||||
func (s *StepMountDvdDrive) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error mounting dvd drive: %s"
|
||||
vmName := state.Get("vmName").(string)
|
||||
isoPath := s.RawSingleISOUrl
|
||||
|
||||
ui.Say("Mounting dvd drive...")
|
||||
|
||||
err := hyperv.MountDvdDrive(vmName, isoPath)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.path = isoPath
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepMountDvdDrive) Cleanup(state multistep.StateBag) {
|
||||
if s.path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
errorMsg := "Error unmounting dvd drive: %s"
|
||||
|
||||
vmName := state.Get("vmName").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Unmounting dvd drive...")
|
||||
|
||||
err := hyperv.UnmountDvdDrive(vmName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(errorMsg, err))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
// 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"
|
||||
"strings"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/powershell"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
"log"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
|
||||
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 {
|
||||
floppyPath string
|
||||
}
|
||||
|
||||
func (s *StepMountFloppydrive) Run(state multistep.StateBag) multistep.StepAction {
|
||||
// Determine if we even have a floppy disk to attach
|
||||
var floppyPath string
|
||||
if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
|
||||
floppyPath = floppyPathRaw.(string)
|
||||
} else {
|
||||
log.Println("No floppy disk, not attaching.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Hyper-V is really dumb and can't figure out the format of the file
|
||||
// without an extension, so we need to add the "vfd" extension to the
|
||||
// floppy.
|
||||
floppyPath, err := s.copyFloppy(floppyPath)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error preparing floppy: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Mounting floppy drive...")
|
||||
|
||||
err = hyperv.MountFloppyDrive(vmName, floppyPath)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error mounting floppy drive: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Track the path so that we can unregister it from Hyper-V later
|
||||
s.floppyPath = floppyPath
|
||||
|
||||
return multistep.ActionContinue}
|
||||
|
||||
func (s *StepMountFloppydrive) Cleanup(state multistep.StateBag) {
|
||||
if s.floppyPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
errorMsg := "Error unmounting floppy drive: %s"
|
||||
|
||||
vmName := state.Get("vmName").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Unmounting floppy drive (cleanup)...")
|
||||
|
||||
err := hyperv.UnmountFloppyDrive(vmName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(errorMsg, err))
|
||||
}
|
||||
|
||||
err = os.Remove(s.floppyPath)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(errorMsg, err))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepMountFloppydrive) copyFloppy(path string) (string, error) {
|
||||
tempdir, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
floppyPath := filepath.Join(tempdir, "floppy.vfd")
|
||||
f, err := os.Create(floppyPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sourceF, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer sourceF.Close()
|
||||
|
||||
log.Printf("Copying floppy to temp location: %s", floppyPath)
|
||||
if _, err := io.Copy(f, sourceF); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return floppyPath, nil
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
// 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"
|
||||
"log"
|
||||
"os"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
powershell "github.com/mitchellh/packer/powershell"
|
||||
)
|
||||
|
||||
type StepMountSecondaryDvdImages struct {
|
||||
Files [] string
|
||||
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 %s 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
|
||||
var script powershell.ScriptBuilder
|
||||
powershell := new(powershell.PowerShellCmd)
|
||||
|
||||
// get the controller number that the OS install disk is mounted on
|
||||
script.Reset()
|
||||
script.WriteLine("param([string]$vmName)")
|
||||
script.WriteLine("(Get-VMDvdDrive -VMName $vmName).ControllerNumber")
|
||||
controllerNumber, err := powershell.Output(script.String(), vmName)
|
||||
if err != nil {
|
||||
return properties, err
|
||||
}
|
||||
|
||||
script.Reset()
|
||||
script.WriteLine("param([string]$vmName,[int]$controllerNumber)")
|
||||
script.WriteLine("Add-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber")
|
||||
err = powershell.Run(script.String(), vmName, controllerNumber)
|
||||
if err != nil {
|
||||
return properties, err
|
||||
}
|
||||
|
||||
// we could try to get the controller location and number in one call, but this way we do not
|
||||
// need to parse the output
|
||||
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 properties, err
|
||||
}
|
||||
|
||||
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 properties, err
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v",isoPath, controllerNumber, controllerLocation))
|
||||
|
||||
properties.ControllerNumber = controllerNumber
|
||||
properties.ControllerLocation = controllerLocation
|
||||
|
||||
return properties, nil
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// 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"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
// StepOutputDir sets up the output directory by creating it if it does
|
||||
// not exist, deleting it if it does exist and we're forcing, and cleaning
|
||||
// it up when we're done with it.
|
||||
type StepOutputDir struct {
|
||||
Force bool
|
||||
Path string
|
||||
}
|
||||
|
||||
func (s *StepOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if _, err := os.Stat(s.Path); err == nil && s.Force {
|
||||
ui.Say("Deleting previous output directory...")
|
||||
os.RemoveAll(s.Path)
|
||||
}
|
||||
|
||||
// Create the directory
|
||||
if err := os.MkdirAll(s.Path, 0755); err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Make sure we can write in the directory
|
||||
f, err := os.Create(filepath.Join(s.Path, "_packer_perm_check"))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Couldn't write to output directory: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepOutputDir) Cleanup(state multistep.StateBag) {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
|
||||
if cancelled || halted {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Deleting output directory...")
|
||||
for i := 0; i < 5; i++ {
|
||||
err := os.RemoveAll(s.Path)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
log.Printf("Error removing output dir: %s", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
// 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"
|
||||
"time"
|
||||
// "net"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
const port string = "13000"
|
||||
|
||||
type StepPollingInstalation struct {
|
||||
step int
|
||||
}
|
||||
|
||||
func (s *StepPollingInstalation) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error polling VM: %s"
|
||||
vmIp := state.Get("ip").(string)
|
||||
|
||||
ui.Say("Start polling VM to check the installation is complete...")
|
||||
/*
|
||||
count := 30
|
||||
var minutes time.Duration = 1
|
||||
sleepMin := time.Minute * minutes
|
||||
host := vmIp + ":" + port
|
||||
|
||||
timeoutSec := time.Second * 15
|
||||
|
||||
for count > 0 {
|
||||
ui.Say(fmt.Sprintf("Connecting vm (%s)...", host ))
|
||||
conn, err := net.DialTimeout("tcp", host, timeoutSec)
|
||||
if err == nil {
|
||||
ui.Say("Done!")
|
||||
conn.Close()
|
||||
break;
|
||||
}
|
||||
|
||||
log.Println(err)
|
||||
ui.Say(fmt.Sprintf("Waiting more %v minutes...", uint(minutes)))
|
||||
time.Sleep(sleepMin)
|
||||
count--
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
err := fmt.Errorf(errorMsg, "a signal from vm was not received in a given time period ")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
*/
|
||||
host := "'" + vmIp + "'," + port
|
||||
|
||||
var blockBuffer bytes.Buffer
|
||||
blockBuffer.WriteString("Invoke-Command -scriptblock {function foo(){try{$client=New-Object System.Net.Sockets.TcpClient(")
|
||||
blockBuffer.WriteString(host)
|
||||
blockBuffer.WriteString(") -ErrorAction SilentlyContinue;if($client -eq $null){return $false}}catch{return $false}return $true} foo}")
|
||||
|
||||
count := 60
|
||||
var duration time.Duration = 20
|
||||
sleepTime := time.Second * duration
|
||||
|
||||
var res string
|
||||
|
||||
for count > 0 {
|
||||
log.Println(fmt.Sprintf("Connecting vm (%s)...", host ))
|
||||
cmd := exec.Command("powershell", blockBuffer.String())
|
||||
cmdOut, err := cmd.Output()
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
res = strings.TrimSpace(string(cmdOut))
|
||||
|
||||
if res != "False" {
|
||||
ui.Say("Signal was received from the VM")
|
||||
// Sleep before starting provision
|
||||
time.Sleep(time.Second*30)
|
||||
break;
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Slipping for more %v seconds...", uint(duration)))
|
||||
time.Sleep(sleepTime)
|
||||
count--
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
err := fmt.Errorf(errorMsg, "a signal from vm was not received in a given time period ")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("The installation complete")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepPollingInstalation) Cleanup(state multistep.StateBag) {
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// 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"
|
||||
"time"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
type StepRebootVm struct {
|
||||
}
|
||||
|
||||
func (s *StepRebootVm) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error rebooting vm: %s"
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Rebooting vm...")
|
||||
|
||||
err := hyperv.RestartVirtualMachine(vmName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Waiting the VM to complete rebooting (2 minutes)...")
|
||||
|
||||
sleepTime := time.Minute * 2
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepRebootVm) Cleanup(state multistep.StateBag) {
|
||||
// do nothing
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ShutdownConfig struct {
|
||||
ShutdownCommand string `mapstructure:"shutdown_command"`
|
||||
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
|
||||
|
||||
ShutdownTimeout time.Duration ``
|
||||
}
|
||||
|
||||
func (c *ShutdownConfig) Prepare(t *packer.ConfigTemplate) []error {
|
||||
if c.RawShutdownTimeout == "" {
|
||||
c.RawShutdownTimeout = "5m"
|
||||
}
|
||||
|
||||
templates := map[string]*string{
|
||||
"shutdown_command": &c.ShutdownCommand,
|
||||
"shutdown_timeout": &c.RawShutdownTimeout,
|
||||
}
|
||||
|
||||
errs := make([]error, 0)
|
||||
for n, ptr := range templates {
|
||||
var err error
|
||||
*ptr, err = t.Process(*ptr, nil)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err))
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
c.ShutdownTimeout, err = time.ParseDuration(c.RawShutdownTimeout)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// This step shuts down the machine. It first attempts to do so gracefully,
|
||||
// but ultimately forcefully shuts it down if that fails.
|
||||
//
|
||||
// Uses:
|
||||
// communicator packer.Communicator
|
||||
// dir OutputDir
|
||||
// driver Driver
|
||||
// ui packer.Ui
|
||||
// vmx_path string
|
||||
//
|
||||
// Produces:
|
||||
// <nothing>
|
||||
type StepShutdown struct {
|
||||
Command string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
comm := state.Get("communicator").(packer.Communicator)
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
if s.Command != "" {
|
||||
ui.Say("Gracefully halting virtual machine...")
|
||||
log.Printf("Executing shutdown command: %s", s.Command)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd := &packer.RemoteCmd{
|
||||
Command: s.Command,
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
}
|
||||
if err := comm.Start(cmd); err != nil {
|
||||
err := fmt.Errorf("Failed to send shutdown command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Wait for the command to run
|
||||
cmd.Wait()
|
||||
|
||||
// If the command failed to run, notify the user in some way.
|
||||
if cmd.ExitStatus != 0 {
|
||||
state.Put("error", fmt.Errorf(
|
||||
"Shutdown command has non-zero exit status.\n\nStdout: %s\n\nStderr: %s",
|
||||
stdout.String(), stderr.String()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if stdout.Len() > 0 {
|
||||
log.Printf("Shutdown stdout: %s", stdout.String())
|
||||
}
|
||||
|
||||
if stderr.Len() > 0 {
|
||||
log.Printf("Shutdown stderr: %s", stderr.String())
|
||||
}
|
||||
|
||||
// Wait for the machine to actually shut down
|
||||
log.Printf("Waiting max %s for shutdown to complete", s.Timeout)
|
||||
shutdownTimer := time.After(s.Timeout)
|
||||
for {
|
||||
running, _ := driver.IsRunning(vmName)
|
||||
if !running {
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case <-shutdownTimer:
|
||||
err := errors.New("Timeout while waiting for machine to shut down.")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
default:
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui.Say("Forcibly halting virtual machine...")
|
||||
if err := driver.Stop(vmName); err != nil {
|
||||
err := fmt.Errorf("Error stopping VM: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("VM shut down.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepShutdown) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,32 @@
|
|||
// 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"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StepSleep struct {
|
||||
Minutes time.Duration
|
||||
ActionName string
|
||||
}
|
||||
|
||||
func (s *StepSleep) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if(len(s.ActionName)>0){
|
||||
ui.Say(s.ActionName + "! Waiting for "+ fmt.Sprintf("%v",uint(s.Minutes)) + " minutes to let the action to complete...")
|
||||
}
|
||||
time.Sleep(time.Minute*s.Minutes);
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepSleep) Cleanup(state multistep.StateBag) {
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// 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"
|
||||
"time"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
type StepStartVm struct {
|
||||
Reason string
|
||||
StartUpDelay int
|
||||
}
|
||||
|
||||
func (s *StepStartVm) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error starting vm: %s"
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Starting vm for " + s.Reason + "...")
|
||||
|
||||
err := hyperv.StartVirtualMachine(vmName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if s.StartUpDelay != 0 {
|
||||
//sleepTime := s.StartUpDelay * time.Second
|
||||
sleepTime := 60 * time.Second
|
||||
|
||||
ui.Say(fmt.Sprintf(" Waiting %v for vm to start...", sleepTime))
|
||||
time.Sleep(sleepTime);
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepStartVm) Cleanup(state multistep.StateBag) {
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// 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"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
|
||||
type StepUnmountDvdDrive struct {
|
||||
}
|
||||
|
||||
func (s *StepUnmountDvdDrive) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Unmounting dvd drive...")
|
||||
|
||||
err := hyperv.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
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepUnmountDvdDrive) Cleanup(state multistep.StateBag) {
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// 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"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
)
|
||||
|
||||
|
||||
type StepUnmountFloppyDrive struct {
|
||||
}
|
||||
|
||||
func (s *StepUnmountFloppyDrive) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
errorMsg := "Error Unmounting floppy drive: %s"
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Unmounting floppy drive (Run)...")
|
||||
|
||||
err := hyperv.UnmountFloppyDrive(vmName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepUnmountFloppyDrive) Cleanup(state multistep.StateBag) {
|
||||
// do nothing
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// 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"
|
||||
"log"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
powershell "github.com/mitchellh/packer/powershell"
|
||||
)
|
||||
|
||||
type StepUnmountSecondaryDvdImages struct {
|
||||
}
|
||||
|
||||
func (s *StepUnmountSecondaryDvdImages) Run(state multistep.StateBag) multistep.StepAction {
|
||||
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 %s", len(dvdProperties)))
|
||||
|
||||
for _, dvdProperty := range dvdProperties {
|
||||
controllerNumber := dvdProperty.ControllerNumber
|
||||
controllerLocation := dvdProperty.ControllerLocation
|
||||
|
||||
var script powershell.ScriptBuilder
|
||||
powershell := new(powershell.PowerShellCmd)
|
||||
|
||||
script.WriteLine("param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)")
|
||||
script.WriteLine("Remove-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation")
|
||||
err := powershell.Run(script.String(), vmName, controllerNumber, 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) {
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
// 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
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// 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"
|
||||
"strings"
|
||||
"strconv"
|
||||
"time"
|
||||
powershell "github.com/mitchellh/packer/powershell"
|
||||
)
|
||||
|
||||
const (
|
||||
SleepSeconds = 10
|
||||
)
|
||||
|
||||
type StepWaitForPowerOff struct {
|
||||
}
|
||||
|
||||
func (s *StepWaitForPowerOff) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
ui.Say("Waiting for vm to be powered down...")
|
||||
|
||||
// unless the person has a super fast disk, it should take at least 5 minutes
|
||||
// for the install and post-install operations to take. Wait 5 minutes to
|
||||
// avoid hammering on getting VM status via PowerShell
|
||||
time.Sleep(time.Second * 300);
|
||||
|
||||
var script powershell.ScriptBuilder
|
||||
script.WriteLine("param([string]$vmName)")
|
||||
script.WriteLine("(Get-VM -Name $vmName).State -eq [Microsoft.HyperV.PowerShell.VMState]::Off")
|
||||
isOffScript := script.String()
|
||||
|
||||
for {
|
||||
powershell := new(powershell.PowerShellCmd)
|
||||
cmdOut, err := powershell.Output(isOffScript, vmName);
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error checking VM's state: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if cmdOut == "True" {
|
||||
break
|
||||
} else {
|
||||
time.Sleep(time.Second * SleepSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepWaitForPowerOff) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
|
||||
type StepWaitForInstallToComplete struct {
|
||||
ExpectedRebootCount uint
|
||||
ActionName string
|
||||
}
|
||||
|
||||
func (s *StepWaitForInstallToComplete) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
if(len(s.ActionName)>0){
|
||||
ui.Say(fmt.Sprintf("%v ! Waiting for VM to reboot %v times...",s.ActionName, s.ExpectedRebootCount))
|
||||
}
|
||||
|
||||
var rebootCount uint
|
||||
var lastUptime uint64
|
||||
|
||||
var script powershell.ScriptBuilder
|
||||
script.WriteLine("param([string]$vmName)")
|
||||
script.WriteLine("(Get-VM -Name $vmName).Uptime.TotalSeconds")
|
||||
|
||||
uptimeScript := script.String()
|
||||
|
||||
for rebootCount < s.ExpectedRebootCount {
|
||||
powershell := new(powershell.PowerShellCmd)
|
||||
cmdOut, err := powershell.Output(uptimeScript, vmName);
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error checking uptime: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
uptime, _ := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 64)
|
||||
if uint64(uptime) < lastUptime {
|
||||
rebootCount++
|
||||
ui.Say(fmt.Sprintf("%v -> Detected reboot %v after %v seconds...", s.ActionName, rebootCount, lastUptime))
|
||||
}
|
||||
|
||||
lastUptime = uptime
|
||||
|
||||
if (rebootCount < s.ExpectedRebootCount) {
|
||||
time.Sleep(time.Second * SleepSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepWaitForInstallToComplete) Cleanup(state multistep.StateBag) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
type StepWaitForWinRm struct {
|
||||
}
|
||||
|
||||
func (s *StepWaitForWinRm) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
//vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Waiting for WinRM to be ready...")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepWaitForWinRm) Cleanup(state multistep.StateBag) {
|
||||
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
// 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 iso
|
||||
|
||||
import (
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
hypervcommon "github.com/mitchellh/packer/builder/hyperv/common"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/helper/communicator"
|
||||
powershell "github.com/mitchellh/packer/powershell"
|
||||
"github.com/mitchellh/packer/powershell/hyperv"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultDiskSize = 127 * 1024 // 127GB
|
||||
MinDiskSize = 10 * 1024 // 10GB
|
||||
MaxDiskSize = 65536 * 1024 // 64TB
|
||||
|
||||
DefaultRamSize = 1024 // 1GB
|
||||
MinRamSize = 512 // 512MB
|
||||
MaxRamSize = 32768 // 32GB
|
||||
|
||||
LowRam = 512 // 512MB
|
||||
|
||||
DefaultUsername = "vagrant"
|
||||
DefaultPassword = "vagrant"
|
||||
)
|
||||
|
||||
// Builder implements packer.Builder and builds the actual Hyperv
|
||||
// images.
|
||||
type Builder struct {
|
||||
config config
|
||||
runner multistep.Runner
|
||||
}
|
||||
|
||||
type config struct {
|
||||
// The size, in megabytes, of the hard disk to create for the VM.
|
||||
// By default, this is 130048 (about 127 GB).
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
// The size, in megabytes, of the computer memory in the VM.
|
||||
// By default, this is 1024 (about 1 GB).
|
||||
RamSizeMB uint `mapstructure:"ram_size_mb"`
|
||||
// A list of files to place onto a floppy disk that is attached when the
|
||||
// VM is booted. This is most useful for unattended Windows installs,
|
||||
// which look for an Autounattend.xml file on removable media. By default,
|
||||
// no floppy will be attached. All files listed in this setting get
|
||||
// placed into the root directory of the floppy and the floppy is attached
|
||||
// as the first floppy device. Currently, no support exists for creating
|
||||
// sub-directories on the floppy. Wildcard characters (*, ?, and [])
|
||||
// are allowed. Directory names are also allowed, which will add all
|
||||
// the files found in the directory to the floppy.
|
||||
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||
//
|
||||
SecondaryDvdImages []string `mapstructure:"secondary_iso_images"`
|
||||
// The checksum for the OS ISO file. Because ISO files are so large,
|
||||
// this is required and Packer will verify it prior to booting a virtual
|
||||
// machine with the ISO attached. The type of the checksum is specified
|
||||
// with iso_checksum_type, documented below.
|
||||
ISOChecksum string `mapstructure:"iso_checksum"`
|
||||
// The type of the checksum specified in iso_checksum. Valid values are
|
||||
// "none", "md5", "sha1", "sha256", or "sha512" currently. While "none"
|
||||
// will skip checksumming, this is not recommended since ISO files are
|
||||
// generally large and corruption does happen from time to time.
|
||||
ISOChecksumType string `mapstructure:"iso_checksum_type"`
|
||||
// A URL to the ISO containing the installation image. This URL can be
|
||||
// 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"`
|
||||
// This is the name of the new virtual machine.
|
||||
// By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build.
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
hypervcommon.OutputConfig `mapstructure:",squash"`
|
||||
hypervcommon.SSHConfig `mapstructure:",squash"`
|
||||
hypervcommon.ShutdownConfig `mapstructure:",squash"`
|
||||
|
||||
SwitchName string `mapstructure:"switch_name"`
|
||||
|
||||
Communicator string `mapstructure:"communicator"`
|
||||
|
||||
// The time in seconds to wait for the virtual machine to report an IP address.
|
||||
// This defaults to 120 seconds. This may have to be increased if your VM takes longer to boot.
|
||||
IPAddressTimeout time.Duration `mapstructure:"ip_address_timeout"`
|
||||
|
||||
SSHWaitTimeout time.Duration
|
||||
|
||||
tpl *packer.ConfigTemplate
|
||||
}
|
||||
|
||||
// Prepare processes the build configuration parameters.
|
||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||
|
||||
md, err := common.DecodeConfig(&b.config, raws...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.config.tpl, err = packer.NewConfigTemplate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("%s: %v", "PackerUserVars", b.config.PackerUserVars))
|
||||
|
||||
b.config.tpl.UserVars = b.config.PackerUserVars
|
||||
|
||||
// Accumulate any errors and warnings
|
||||
errs := common.CheckUnusedConfig(md)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(b.config.tpl)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(b.config.tpl)...)
|
||||
|
||||
warnings := make([]string, 0)
|
||||
|
||||
err = b.checkDiskSize()
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
err = b.checkRamSize()
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
if b.config.VMName == "" {
|
||||
b.config.VMName = fmt.Sprintf("pvm_%s", uuid.New())
|
||||
}
|
||||
|
||||
if b.config.SwitchName == "" {
|
||||
// no switch name, try to get one attached to a online network adapter
|
||||
onlineSwitchName, err := hyperv.GetExternalOnlineVirtualSwitch()
|
||||
if onlineSwitchName == "" || err != nil {
|
||||
b.config.SwitchName = fmt.Sprintf("pis_%s", uuid.New())
|
||||
} else {
|
||||
b.config.SwitchName = onlineSwitchName
|
||||
}
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Using switch %s", b.config.SwitchName))
|
||||
|
||||
if b.config.Communicator == "" {
|
||||
b.config.Communicator = "ssh"
|
||||
} else if b.config.Communicator == "ssh" || b.config.Communicator == "winrm" {
|
||||
// good
|
||||
} else {
|
||||
err = errors.New("communicator must be either ssh or winrm")
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
// Errors
|
||||
templates := map[string]*string{
|
||||
"iso_url": &b.config.RawSingleISOUrl
|
||||
}
|
||||
|
||||
for n, ptr := range templates {
|
||||
var err error
|
||||
*ptr, err = b.config.tpl.Process(*ptr, nil)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("%s: %v", "VMName", b.config.VMName))
|
||||
log.Println(fmt.Sprintf("%s: %v", "SwitchName", b.config.SwitchName))
|
||||
log.Println(fmt.Sprintf("%s: %v", "Communicator", b.config.Communicator))
|
||||
|
||||
if b.config.RawSingleISOUrl == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("iso_url: The option can't be missed and a path must be specified."))
|
||||
} else if _, err := os.Stat(b.config.RawSingleISOUrl); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("iso_url: Check the path is correct"))
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("%s: %v", "RawSingleISOUrl", b.config.RawSingleISOUrl))
|
||||
|
||||
b.config.SSHWaitTimeout, err = time.ParseDuration(b.config.RawSSHWaitTimeout)
|
||||
|
||||
// Warnings
|
||||
warning := b.checkHostAvailableMemory()
|
||||
if warning != "" {
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
|
||||
if b.config.ShutdownCommand == "" {
|
||||
warnings = append(warnings,
|
||||
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
|
||||
"will forcibly halt the virtual machine, which may result in data loss.")
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
return warnings, nil
|
||||
}
|
||||
|
||||
// Run executes a Packer build and returns a packer.Artifact representing
|
||||
// a Hyperv appliance.
|
||||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||
// Create the driver that we'll use to communicate with Hyperv
|
||||
driver, err := hypervcommon.NewHypervPS4Driver()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed creating Hyper-V driver: %s", err)
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
state.Put("driver", driver)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
steps := []multistep.Step{
|
||||
&hypervcommon.StepCreateTempDir{},
|
||||
&hypervcommon.StepOutputDir{
|
||||
Force: b.config.PackerForce,
|
||||
Path: b.config.OutputDir,
|
||||
},
|
||||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyFiles,
|
||||
},
|
||||
&hypervcommon.StepCreateSwitch{
|
||||
SwitchName: b.config.SwitchName,
|
||||
},
|
||||
&hypervcommon.StepCreateVM{
|
||||
VMName: b.config.VMName,
|
||||
SwitchName: b.config.SwitchName,
|
||||
RamSizeMB: b.config.RamSizeMB,
|
||||
DiskSize: b.config.DiskSize,
|
||||
},
|
||||
&hypervcommon.StepEnableIntegrationService{},
|
||||
|
||||
&hypervcommon.StepMountDvdDrive{
|
||||
RawSingleISOUrl: b.config.RawSingleISOUrl,
|
||||
},
|
||||
&hypervcommon.StepMountFloppydrive{},
|
||||
|
||||
&hypervcommon.StepMountSecondaryDvdImages{},
|
||||
|
||||
|
||||
&hypervcommon.StepStartVm{
|
||||
Reason: "OS installation",
|
||||
},
|
||||
|
||||
// wait for the vm to be powered off
|
||||
&hypervcommon.StepWaitForPowerOff{},
|
||||
|
||||
// remove the integration services dvd drive
|
||||
// after we power down
|
||||
&hypervcommon.StepUnmountSecondaryDvdImages{},
|
||||
|
||||
//
|
||||
&hypervcommon.StepStartVm{
|
||||
Reason: "provisioning",
|
||||
StartUpDelay: 60,
|
||||
},
|
||||
|
||||
// configure the communicator ssh, winrm
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: hypervcommon.CommHost,
|
||||
SSHConfig: hypervcommon.SSHConfigFunc(b.config.SSHConfig),
|
||||
SSHPort: hypervcommon.SSHPort,
|
||||
},
|
||||
|
||||
// provision requires communicator to be setup
|
||||
&common.StepProvision{},
|
||||
|
||||
&hypervcommon.StepUnmountFloppyDrive{},
|
||||
&hypervcommon.StepUnmountDvdDrive{},
|
||||
|
||||
&hypervcommon.StepShutdown{
|
||||
Command: b.config.ShutdownCommand,
|
||||
Timeout: b.config.ShutdownTimeout,
|
||||
},
|
||||
|
||||
&hypervcommon.StepExportVm{
|
||||
OutputDir: b.config.OutputDir,
|
||||
},
|
||||
|
||||
// the clean up actions for each step will be executed reverse order
|
||||
}
|
||||
|
||||
// Run the steps.
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
b.runner.Run(state)
|
||||
|
||||
// Report any errors.
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
// If we were interrupted or cancelled, then just exit.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return nil, errors.New("Build was cancelled.")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
||||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
return hypervcommon.NewArtifact(b.config.OutputDir)
|
||||
}
|
||||
|
||||
// Cancel.
|
||||
func (b *Builder) Cancel() {
|
||||
if b.runner != nil {
|
||||
log.Println("Cancelling the step runner...")
|
||||
b.runner.Cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func appendWarnings(slice []string, data ...string) []string {
|
||||
m := len(slice)
|
||||
n := m + len(data)
|
||||
if n > cap(slice) { // if necessary, reallocate
|
||||
// allocate double what's needed, for future growth.
|
||||
newSlice := make([]string, (n+1)*2)
|
||||
copy(newSlice, slice)
|
||||
slice = newSlice
|
||||
}
|
||||
slice = slice[0:n]
|
||||
copy(slice[m:n], data)
|
||||
return slice
|
||||
}
|
||||
|
||||
func (b *Builder) checkDiskSize() error {
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = DefaultDiskSize
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("%s: %v", "DiskSize", b.config.DiskSize))
|
||||
|
||||
if b.config.DiskSize < MinDiskSize {
|
||||
return fmt.Errorf("disk_size_gb: Windows server requires disk space >= %v GB, but defined: %v", MinDiskSize, b.config.DiskSize/1024)
|
||||
} else if b.config.DiskSize > MaxDiskSize {
|
||||
return fmt.Errorf("disk_size_gb: Windows server requires disk space <= %v GB, but defined: %v", MaxDiskSize, b.config.DiskSize/1024)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) checkRamSize() error {
|
||||
if b.config.RamSizeMB == 0 {
|
||||
b.config.RamSizeMB = DefaultRamSize
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("%s: %v", "RamSize", b.config.RamSizeMB))
|
||||
|
||||
if b.config.RamSizeMB < MinRamSize {
|
||||
return fmt.Errorf("ram_size_mb: Windows server requires memory size >= %v MB, but defined: %v", MinRamSize, b.config.RamSizeMB)
|
||||
} else if b.config.RamSizeMB > MaxRamSize {
|
||||
return fmt.Errorf("ram_size_mb: Windows server requires memory size <= %v MB, but defined: %v", MaxRamSize, b.config.RamSizeMB)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) checkHostAvailableMemory() string {
|
||||
freeMB := powershell.GetHostAvailableMemory()
|
||||
|
||||
if (freeMB - float64(b.config.RamSizeMB)) < LowRam {
|
||||
return fmt.Sprintf("Hyper-V might fail to create a VM if there is not enough free memory in the system.")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
go build
|
||||
cp packer-builder-hyperv-iso.exe ../../../bin/
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/builder/hyperv/iso"
|
||||
"github.com/mitchellh/packer/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server, err := plugin.Server()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.RegisterBuilder(new(iso.Builder))
|
||||
server.Serve()
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
// 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 main
|
|
@ -0,0 +1,433 @@
|
|||
package hyperv
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/powershell"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetVirtualMachineNetworkAdapterAddress(vmName string) (string, error) {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName, [int]$addressIndex)
|
||||
try {
|
||||
$adapter = Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue
|
||||
$ip = $adapter.IPAddresses[$addressIndex]
|
||||
if($ip -eq $null) {
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
$ip
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(script, vmName, "0")
|
||||
|
||||
return cmdOut, err
|
||||
}
|
||||
|
||||
func MountDvdDrive(vmName string, path string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName,[string]$path)
|
||||
Set-VMDvdDrive -VMName $vmName -Path $path
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, path)
|
||||
return err
|
||||
}
|
||||
|
||||
func UnmountDvdDrive(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
Set-VMDvdDrive -VMName $vmName -Path $null
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func MountFloppyDrive(vmName string, path string) error {
|
||||
var script = `
|
||||
param([string]$vmName, [string]$path)
|
||||
Set-VMFloppyDiskDrive -VMName $vmName -Path $path
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, path)
|
||||
return err
|
||||
}
|
||||
|
||||
func UnmountFloppyDrive(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
Set-VMFloppyDiskDrive -VMName $vmName -Path $null
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func CreateVirtualMachine(vmName string, path string, ram string, diskSize string, switchName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName, [string]$path, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName)
|
||||
$vhdx = $vmName + '.vhdx'
|
||||
$vhdPath = Join-Path -Path $path -ChildPath $vhdx
|
||||
New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, path, ram, diskSize, switchName)
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteVirtualMachine(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
Remove-VM -Name $vmName -Force
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func ExportVirtualMachine(vmName string, path string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName, [string]$path)
|
||||
Export-VM -Name $vmName -Path $path
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, path)
|
||||
return err
|
||||
}
|
||||
|
||||
func CopyExportedVirtualMachine(expPath string, outputPath string, vhdDir string, vmDir string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$srcPath, [string]$dstPath, [string]$vhdDirName, [string]$vmDir)
|
||||
Copy-Item -Path $srcPath/$vhdDirName -Destination $dstPath -recurse
|
||||
Copy-Item -Path $srcPath/$vmDir -Destination $dstPath
|
||||
Copy-Item -Path $srcPath/$vmDir/*.xml -Destination $dstPath/$vmDir
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, expPath, outputPath, vhdDir, vmDir)
|
||||
return err
|
||||
}
|
||||
|
||||
func CreateVirtualSwitch(switchName string, switchType string) (bool, error) {
|
||||
|
||||
var script = `
|
||||
param([string]$switchName,[string]$switchType)
|
||||
$switches = Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
|
||||
if ($switches.Count -eq 0) {
|
||||
New-VMSwitch -Name $switchName -SwitchType $switchType
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(script, switchName, switchType)
|
||||
var created = strings.TrimSpace(cmdOut) == "True"
|
||||
return created, err
|
||||
}
|
||||
|
||||
func DeleteVirtualSwitch(switchName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$switchName)
|
||||
$switch = Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
|
||||
if ($switch -ne $null) {
|
||||
$switch | Remove-VMSwitch -Force
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, switchName)
|
||||
return err
|
||||
}
|
||||
|
||||
func StartVirtualMachine(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
Start-VM -Name $vmName
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func RestartVirtualMachine(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
Restart-VM $vmName -Force
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func StopVirtualMachine(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
$vm = Get-VM -Name $vmName
|
||||
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
|
||||
Stop-VM -VM $vm
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName,[string]$integrationServiceName)
|
||||
Enable-VMIntegrationService -VMName $vmName -Name $integrationServiceName
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, integrationServiceName)
|
||||
return err
|
||||
}
|
||||
|
||||
func SetNetworkAdapterVlanId(switchName string, vlanId string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$networkAdapterName,[string]$vlanId)
|
||||
Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $networkAdapterName -Access -VlanId $vlanId
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, switchName, vlanId)
|
||||
return err
|
||||
}
|
||||
|
||||
func SetVirtualMachineVlanId(vmName string, vlanId string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName,[string]$vlanId)
|
||||
Set-VMNetworkAdapterVlan -VMName $vmName -Access -VlanId $vlanId
|
||||
`
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, vlanId)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetExternalOnlineVirtualSwitch() (string, error) {
|
||||
|
||||
var script = `
|
||||
$adapters = Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq 'Up' } | Sort-Object -Descending -Property Speed
|
||||
foreach ($adapter in $adapters) {
|
||||
$switch = Get-VMSwitch -SwitchType External | Where-Object { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
|
||||
|
||||
if ($switch -ne $null) {
|
||||
$switch.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(script)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var switchName = strings.TrimSpace(cmdOut)
|
||||
return switchName, nil
|
||||
}
|
||||
|
||||
func CreateExternalVirtualSwitch(vmName string, switchName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName,[string]$switchName)
|
||||
$switch = $null
|
||||
$names = @('ethernet','wi-fi','lan')
|
||||
$adapters = foreach ($name in $names) {
|
||||
Get-NetAdapter -Physical -Name $name -ErrorAction SilentlyContinue | where status -eq 'up'
|
||||
}
|
||||
|
||||
foreach ($adapter in $adapters) {
|
||||
$switch = Get-VMSwitch -SwitchType External | where { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
|
||||
|
||||
if ($switch -eq $null) {
|
||||
$switch = New-VMSwitch -Name $switchName -NetAdapterName $adapter.Name -AllowManagementOS $true -Notes 'Parent OS, VMs, WiFi'
|
||||
}
|
||||
|
||||
if ($switch -ne $null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if($switch -ne $null) {
|
||||
Get-VMNetworkAdapter –VMName $vmName | Connect-VMNetworkAdapter -VMSwitch $switch
|
||||
} else {
|
||||
Write-Error 'No internet adapters found'
|
||||
}
|
||||
`
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, switchName)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetVirtualMachineSwitchName(vmName string) (string, error) {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
(Get-VMNetworkAdapter -VMName $vmName).SwitchName
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(script, vmName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(cmdOut), nil
|
||||
}
|
||||
|
||||
func ConnectVirtualMachineNetworkAdapterToSwitch(vmName string, switchName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName,[string]$switchName)
|
||||
Get-VMNetworkAdapter –VMName $vmName | Connect-VMNetworkAdapter –SwitchName $switchName
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, switchName)
|
||||
return err
|
||||
}
|
||||
|
||||
func UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName,[string]$switchName)
|
||||
Set-VMNetworkAdapterVlan -VMName $vmName -Untagged
|
||||
Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $switchName -Untagged
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, switchName)
|
||||
return err
|
||||
}
|
||||
|
||||
func IsRunning(vmName string) (bool, error) {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
$vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
|
||||
$vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(script, vmName)
|
||||
var isRunning = strings.TrimSpace(cmdOut) == "True"
|
||||
return isRunning, err
|
||||
}
|
||||
|
||||
func Mac(vmName string) (string, error) {
|
||||
var script = `
|
||||
param([string]$vmName, [int]$addressIndex)
|
||||
try {
|
||||
$adapter = Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue
|
||||
$mac = $adapter.MacAddress[$addressIndex]
|
||||
if($mac -eq $null) {
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
$mac
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(script, vmName, "0")
|
||||
|
||||
return cmdOut, err
|
||||
}
|
||||
|
||||
func IpAddress(mac string) (string, error) {
|
||||
var script = `
|
||||
param([string]$mac, [int]$addressIndex)
|
||||
try {
|
||||
$ip = Get-Vm | %{$_.NetworkAdapters} | ?{$_.MacAddress -eq $mac} | %{$_.IpAddresses[$addressIndex]}
|
||||
|
||||
if($ip -eq $null) {
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
$ip
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
cmdOut, err := ps.Output(script, mac, "0")
|
||||
|
||||
return cmdOut, err
|
||||
}
|
||||
|
||||
func Start(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
$vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
|
||||
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off) {
|
||||
Start-VM –Name $vmName
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func TurnOff(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
$vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
|
||||
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
|
||||
Stop-VM -Name $vmName -TurnOff
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
||||
|
||||
func ShutDown(vmName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName)
|
||||
$vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
|
||||
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
|
||||
Stop-VM –Name $vmName
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
// 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 powershell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
powerShellFalse = "False"
|
||||
powerShellTrue = "True"
|
||||
)
|
||||
|
||||
type PowerShellCmd struct {
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
}
|
||||
|
||||
func (ps *PowerShellCmd) Run(fileContents string, params ...string) error {
|
||||
_, err := ps.Output(fileContents, params...)
|
||||
return err
|
||||
}
|
||||
|
||||
// Output runs the PowerShell command and returns its standard output.
|
||||
func (ps *PowerShellCmd) Output(fileContents string, params ...string) (string, error) {
|
||||
path, err := ps.getPowerShellPath();
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
filename, err := saveScript(fileContents);
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
debug := os.Getenv("PACKER_POWERSHELL_DEBUG") != ""
|
||||
verbose := debug || os.Getenv("PACKER_POWERSHELL_VERBOSE") != ""
|
||||
|
||||
if !debug {
|
||||
defer os.Remove(filename)
|
||||
}
|
||||
|
||||
args := createArgs(filename, params...)
|
||||
|
||||
if verbose {
|
||||
log.Printf("Run: %s %s", path, args)
|
||||
}
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
command := exec.Command(path, args...)
|
||||
command.Stdout = &stdout
|
||||
command.Stderr = &stderr
|
||||
|
||||
err = command.Run()
|
||||
|
||||
if ps.Stdout != nil {
|
||||
stdout.WriteTo(ps.Stdout)
|
||||
}
|
||||
|
||||
if ps.Stderr != nil {
|
||||
stderr.WriteTo(ps.Stderr)
|
||||
}
|
||||
|
||||
stderrString := strings.TrimSpace(stderr.String())
|
||||
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
err = fmt.Errorf("PowerShell error: %s", stderrString)
|
||||
}
|
||||
|
||||
if len(stderrString) > 0 {
|
||||
err = fmt.Errorf("PowerShell error: %s", stderrString)
|
||||
}
|
||||
|
||||
stdoutString := strings.TrimSpace(stdout.String())
|
||||
|
||||
if verbose && stdoutString != "" {
|
||||
log.Printf("stdout: %s", stdoutString)
|
||||
}
|
||||
|
||||
// only write the stderr string if verbose because
|
||||
// the error string will already be in the err return value.
|
||||
if verbose && stderrString != "" {
|
||||
log.Printf("stderr: %s", stderrString)
|
||||
}
|
||||
|
||||
return stdoutString, err;
|
||||
}
|
||||
|
||||
func (ps *PowerShellCmd) getPowerShellPath() (string, error) {
|
||||
path, err := exec.LookPath("powershell")
|
||||
if err != nil {
|
||||
log.Fatal("Cannot find PowerShell in the path", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func saveScript(fileContents string) (string, error) {
|
||||
file, err := ioutil.TempFile(os.TempDir(), "ps")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = file.Write([]byte(fileContents))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
newFilename := file.Name() + ".ps1"
|
||||
err = os.Rename(file.Name(), newFilename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return newFilename, nil
|
||||
}
|
||||
|
||||
func createArgs(filename string, params ...string) []string {
|
||||
args := make([]string,len(params)+4)
|
||||
args[0] = "-ExecutionPolicy"
|
||||
args[1] = "Bypass"
|
||||
|
||||
args[2] = "-File"
|
||||
args[3] = filename
|
||||
|
||||
for key, value := range params {
|
||||
args[key+4] = value
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
func GetHostAvailableMemory() float64 {
|
||||
|
||||
var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024"
|
||||
|
||||
var ps PowerShellCmd
|
||||
output, _ := ps.Output(script)
|
||||
|
||||
freeMB, _ := strconv.ParseFloat(output, 64)
|
||||
|
||||
return freeMB
|
||||
}
|
||||
|
||||
|
||||
func GetHostName(ip string) (string, error) {
|
||||
|
||||
var script = `
|
||||
param([string]$ip)
|
||||
try {
|
||||
$HostName = [System.Net.Dns]::GetHostEntry($ip).HostName
|
||||
if ($HostName -ne $null) {
|
||||
$HostName = $HostName.Split('.')[0]
|
||||
}
|
||||
$HostName
|
||||
} catch { }
|
||||
`
|
||||
|
||||
//
|
||||
var ps PowerShellCmd
|
||||
cmdOut, err := ps.Output(script, ip);
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cmdOut, nil
|
||||
}
|
||||
|
||||
func IsCurrentUserAnAdministrator() (bool, error) {
|
||||
var script = `
|
||||
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = new-object System.Security.Principal.WindowsPrincipal($identity)
|
||||
$administratorRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
|
||||
return $principal.IsInRole($administratorRole)
|
||||
`
|
||||
|
||||
var ps PowerShellCmd
|
||||
cmdOut, err := ps.Output(script);
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
res := strings.TrimSpace(cmdOut)
|
||||
return res == powerShellTrue, nil
|
||||
}
|
||||
|
||||
|
||||
func ModuleExists(moduleName string) (bool, error) {
|
||||
|
||||
var script = `
|
||||
param([string]$moduleName)
|
||||
(Get-Module -Name $moduleName) -ne $null
|
||||
`
|
||||
var ps PowerShellCmd
|
||||
cmdOut, err := ps.Output(script)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
res := strings.TrimSpace(string(cmdOut))
|
||||
|
||||
if(res == powerShellFalse){
|
||||
err := fmt.Errorf("PowerShell %s module is not loaded. Make sure %s feature is on.", moduleName, moduleName)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func SetUnattendedProductKey(path string, productKey string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$path,[string]$productKey)
|
||||
|
||||
$unattend = [xml](Get-Content -Path $path)
|
||||
$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
|
||||
|
||||
$setupNode = $unattend |
|
||||
Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
|
||||
Select-Object -ExpandProperty Node
|
||||
|
||||
$productKeyNode = $setupNode |
|
||||
Select-Xml -XPath '//un:ProductKey' -Namespace $ns |
|
||||
Select-Object -ExpandProperty Node
|
||||
|
||||
if ($productKeyNode -eq $null) {
|
||||
$productKeyNode = $unattend.CreateElement('ProductKey', $ns.un)
|
||||
[Void]$setupNode.AppendChild($productKeyNode)
|
||||
}
|
||||
|
||||
$productKeyNode.InnerText = $productKey
|
||||
|
||||
$unattend.Save($path)
|
||||
`
|
||||
|
||||
var ps PowerShellCmd
|
||||
err := ps.Run(script, path, productKey)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
|
||||
package powershell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOutputScriptBlock(t *testing.T) {
|
||||
|
||||
ps, err := powershell.Command()
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
trueOutput, err := powershell.OutputScriptBlock("$True")
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if trueOutput != "True" {
|
||||
t.Fatalf("output '%v' is not 'True'", trueOutput)
|
||||
}
|
||||
|
||||
falseOutput, err := powershell.OutputScriptBlock("$False")
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if falseOutput != "False" {
|
||||
t.Fatalf("output '%v' is not 'False'", falseOutput)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunScriptBlock(t *testing.T) {
|
||||
powershell, err := powershell.Command()
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
err = powershell.RunScriptBlock("$True")
|
||||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
powershell, err := powershell.Command()
|
||||
version, err := powershell.Version();
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if (version != 4) {
|
||||
t.Fatalf("expected version 4")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunFile(t *testing.T) {
|
||||
powershell, err := powershell.Command()
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
var blockBuffer bytes.Buffer
|
||||
blockBuffer.WriteString("param([string]$a, [string]$b, [int]$x, [int]$y) $n = $x + $y; Write-Host $a, $b, $n")
|
||||
|
||||
err = powershell.Run(blockBuffer.String(), "a", "b", "5", "10")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package powershell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type ScriptBuilder struct {
|
||||
buffer bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *ScriptBuilder) WriteLine(s string) (n int, err error) {
|
||||
n, err = b.buffer.WriteString(s);
|
||||
b.buffer.WriteString("\n")
|
||||
|
||||
return n+1, err
|
||||
}
|
||||
|
||||
func (b *ScriptBuilder) WriteString(s string) (n int, err error) {
|
||||
n, err = b.buffer.WriteString(s);
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (b *ScriptBuilder) String() string {
|
||||
return b.buffer.String()
|
||||
}
|
||||
|
||||
func (b *ScriptBuilder) Reset() {
|
||||
b.buffer.Reset()
|
||||
}
|
||||
|
Loading…
Reference in New Issue