From 74c8e9db534f3ea1d6814190da01887b73277929 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Thu, 13 Aug 2015 08:00:25 -0500 Subject: [PATCH 01/24] adding vmware builder convert to ova to beter control the final disk type before uploading to vsphere --- post-processor.go | 933 ++++++++++++++++++++++++++-------------------- 1 file changed, 527 insertions(+), 406 deletions(-) diff --git a/post-processor.go b/post-processor.go index fb78821..5464447 100644 --- a/post-processor.go +++ b/post-processor.go @@ -1,481 +1,602 @@ package main import ( - "bytes" - "crypto/tls" - "fmt" - "github.com/mitchellh/packer/common" - "github.com/mitchellh/packer/packer" - "github.com/vmware/govmomi" - "github.com/vmware/govmomi/find" - "github.com/vmware/govmomi/vim25/types" - "golang.org/x/net/context" - "io/ioutil" - "net/http" - "net/url" - "os" - "os/exec" - "strings" - "time" - "github.com/mitchellh/packer/helper/config" - "github.com/mitchellh/packer/template/interpolate" - vmwarecommon "github.com/mitchellh/packer/builder/vmware/common" + "bytes" + "crypto/tls" + "fmt" + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/packer" + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/find" + "github.com/vmware/govmomi/vim25/types" + "golang.org/x/net/context" + "io/ioutil" + "net/http" + "net/url" + "os" + "os/exec" + "strings" + "time" + "log" + "path/filepath" + "github.com/mitchellh/packer/helper/config" + "github.com/mitchellh/packer/template/interpolate" + vmwarecommon "github.com/mitchellh/packer/builder/vmware/common" ) var builtins = map[string]string{ - "mitchellh.virtualbox": "virtualbox", - "mitchellh.vmware": "vmware", + "mitchellh.virtualbox": "virtualbox", + "mitchellh.vmware": "vmware", } type Config struct { - common.PackerConfig `mapstructure:",squash"` - - Datacenter string `mapstructure:"datacenter"` - Datastore string `mapstructure:"datastore"` - Host string `mapstructure:"host"` - Password string `mapstructure:"password"` - Username string `mapstructure:"username"` - VMFolder string `mapstructure:"vm_folder"` - VMNetwork string `mapstructure:"vm_network"` - RemoveEthernet string `mapstructure:"remove_ethernet"` - RemoveFloppy string `mapstructure:"remove_floppy"` - RemoveOpticalDrive string `mapstructure:"remove_optical_drive"` - VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` - ctx interpolate.Context + common.PackerConfig `mapstructure:",squash"` + + Datacenter string `mapstructure:"datacenter"` + Datastore string `mapstructure:"datastore"` + Host string `mapstructure:"host"` + Password string `mapstructure:"password"` + Username string `mapstructure:"username"` + VMFolder string `mapstructure:"vm_folder"` + VMNetwork string `mapstructure:"vm_network"` + RemoveEthernet string `mapstructure:"remove_ethernet"` + RemoveFloppy string `mapstructure:"remove_floppy"` + RemoveOpticalDrive string `mapstructure:"remove_optical_drive"` + VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` + DiskMode string `mapstructure:"disk_mode"` + UploadCommand string `mapstructure:"upload_command"` + UploadArgs string `mapstructure:"upload_args"` + Compression uint `mapstructure:"compression"` + ctx interpolate.Context } type PostProcessor struct { - config Config + config Config } func (p *PostProcessor) Configure(raws ...interface{}) error { - err := config.Decode(&p.config, &config.DecodeOpts{ - Interpolate: true, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{}, - }, - }, raws...) - - if err != nil { - return err - } - - // Defaults - if p.config.RemoveEthernet == "" { - p.config.RemoveEthernet = "false" - } - - if p.config.RemoveFloppy == "" { - p.config.RemoveFloppy = "false" - } - - if p.config.RemoveOpticalDrive == "" { - p.config.RemoveOpticalDrive = "false" - } - - if p.config.VirtualHardwareVer == "" { - p.config.VirtualHardwareVer = "10" - } - - // Accumulate any errors - errs := new(packer.MultiError) - - if _, err := exec.LookPath("ovftool"); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("ovftool not found: %s", err)) - } - - // First define all our templatable parameters that are _required_ - templates := map[string]*string{ - "datacenter": &p.config.Datacenter, - "host": &p.config.Host, - "password": &p.config.Password, - "username": &p.config.Username, - "datastore": &p.config.Datastore, - "vm_folder": &p.config.VMFolder, - } - - for key, ptr := range templates { - if *ptr == "" { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("%s must be set", key)) - } - } - - if len(errs.Errors) > 0 { - return errs - } - - return nil + err := config.Decode(&p.config, &config.DecodeOpts{ + Interpolate: true, + InterpolateFilter: &interpolate.RenderFilter{ + Exclude: []string{}, + }, + }, raws...) + + if err != nil { + return err + } + + // Defaults + if p.config.VMNetwork == "" { + p.config.VMNetwork = "VM Network" + } + + if p.config.DiskMode == "" { + p.config.DiskMode = "thin" + } + + if p.config.RemoveEthernet == "" { + p.config.RemoveEthernet = "false" + } + + if p.config.RemoveFloppy == "" { + p.config.RemoveFloppy = "false" + } + + if p.config.RemoveOpticalDrive == "" { + p.config.RemoveOpticalDrive = "false" + } + + if p.config.VirtualHardwareVer == "" { + p.config.VirtualHardwareVer = "10" + } + + // Accumulate any errors + errs := new(packer.MultiError) + + if _, err := exec.LookPath("ovftool"); err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("ovftool not found: %s", err)) + } + + if !(p.config.Compression >= 0 && p.config.Compression <= 9) { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Invalid compression level. Must be between 1 and 9, or 0 for no compression.")) + } + + if !(p.config.DiskMode == "thick" || + p.config.DiskMode == "thin" || + p.config.DiskMode == "monolithicSparse" || + p.config.DiskMode == "monolithicFlat" || + p.config.DiskMode == "twoGbMaxExtentSparse" || + p.config.DiskMode == "twoGbMaxExtentFlat" || + p.config.DiskMode == "seSparse" || + p.config.DiskMode == "eagerZeroedThick" || + p.config.DiskMode == "sparse" || + p.config.DiskMode == "flat") { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Invalid disk_mode. Only thin(Default), thick, monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, seSparse, eagerZeroedThick, sparse, and flat are allowed.")) + } + + // First define all our templatable parameters that are _required_ + templates := map[string]*string{ + "datacenter": &p.config.Datacenter, + "host": &p.config.Host, + "password": &p.config.Password, + "username": &p.config.Username, + "datastore": &p.config.Datastore, + "vm_folder": &p.config.VMFolder, + } + + for key, ptr := range templates { + if *ptr == "" { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("%s must be set", key)) + } + } + + if len(errs.Errors) > 0 { + return errs + } + + return nil } func (p *PostProcessor) RemoveFloppy(vmx string, ui packer.Ui) error { - ui.Message(fmt.Sprintf("Removing floppy from %s", vmx)) - vmxData, err := vmwarecommon.ReadVMX(vmx) - if err != nil { - return err - } - for k, _ := range vmxData { - if strings.HasPrefix(k, "floppy0.") { - delete(vmxData, k) - } - } - vmxData["floppy0.present"] = "FALSE" - if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { - return err - } - return nil + ui.Message(fmt.Sprintf("Removing floppy from %s", vmx)) + vmxData, err := vmwarecommon.ReadVMX(vmx) + if err != nil { + return err + } + for k, _ := range vmxData { + if strings.HasPrefix(k, "floppy0.") { + delete(vmxData, k) + } + } + vmxData["floppy0.present"] = "FALSE" + if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { + return err + } + return nil } func (p *PostProcessor) RemoveEthernet(vmx string, ui packer.Ui) error { - ui.Message(fmt.Sprintf("Removing ethernet0 intercace from %s", vmx)) - vmxData, err := vmwarecommon.ReadVMX(vmx) - if err != nil { - return err - } - - for k, _ := range vmxData { - if strings.HasPrefix(k, "ethernet0.") { - delete(vmxData, k) - } - } - - vmxData["ethernet0.present"] = "FALSE" - if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { - return err - } - - return nil + ui.Message(fmt.Sprintf("Removing ethernet0 intercace from %s", vmx)) + vmxData, err := vmwarecommon.ReadVMX(vmx) + if err != nil { + return err + } + + for k, _ := range vmxData { + if strings.HasPrefix(k, "ethernet0.") { + delete(vmxData, k) + } + } + + vmxData["ethernet0.present"] = "FALSE" + if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { + return err + } + + return nil } func (p *PostProcessor) SetVHardwareVersion(vmx string, ui packer.Ui, hwversion string) error { - ui.Message(fmt.Sprintf("Setting the hardware version in the vmx to version '%s'", hwversion)) - - vmxContent, err := ioutil.ReadFile(vmx) - lines := strings.Split(string(vmxContent), "\n") - for i, line := range lines { - if strings.Contains(line, "virtualhw.version") { - lines[i] = fmt.Sprintf("virtualhw.version = \"%s\"", hwversion) - } - } - output := strings.Join(lines, "\n") - err = ioutil.WriteFile(vmx, []byte(output), 0644) - if err != nil { - return err - } - - return nil + ui.Message(fmt.Sprintf("Setting the hardware version in the vmx to version '%s'", hwversion)) + + vmxContent, err := ioutil.ReadFile(vmx) + lines := strings.Split(string(vmxContent), "\n") + for i, line := range lines { + if strings.Contains(line, "virtualhw.version") { + lines[i] = fmt.Sprintf("virtualhw.version = \"%s\"", hwversion) + } + } + output := strings.Join(lines, "\n") + err = ioutil.WriteFile(vmx, []byte(output), 0644) + if err != nil { + return err + } + + return nil } func (p *PostProcessor) RemoveOpticalDrive(vmx string, ui packer.Ui) error { - ui.Message(fmt.Sprintf("Removing optical drive from %s", vmx)) - vmxData, err := vmwarecommon.ReadVMX(vmx) - if err != nil { - return err - } - - for k, _ := range vmxData { - if strings.HasPrefix(k, "ide1:0.file") { - delete(vmxData, k) - } - } - - vmxData["ide1:0.present"] = "FALSE" - - if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { - return err - } - return nil + ui.Message(fmt.Sprintf("Removing optical drive from %s", vmx)) + vmxData, err := vmwarecommon.ReadVMX(vmx) + if err != nil { + return err + } + + for k, _ := range vmxData { + if strings.HasPrefix(k, "ide1:0.file") { + delete(vmxData, k) + } + } + + vmxData["ide1:0.present"] = "FALSE" + + if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { + return err + } + return nil +} + +func visit(path string, f os.FileInfo, err error) error { + fmt.Printf("Visited: %s\n", path) + return nil } func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - if _, ok := builtins[artifact.BuilderId()]; !ok { - return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) - } - - ova := "" - vmx := "" - vmdk := "" - for _, path := range artifact.Files() { - if strings.HasSuffix(path, ".ova") { - ova = path - break - } else if strings.HasSuffix(path, ".vmx") { - vmx = path - } else if strings.HasSuffix(path, ".vmdk") { - vmdk = path - } - } - - if ova == "" && ( vmx == "" || vmdk == "" ) { - return nil, false, fmt.Errorf("ERROR: Neither OVA or VMX/VMDK were found!") - } - - if ova != "" { - // Sweet, we've got an OVA, Now it's time to make that baby something we can work with. - command := exec.Command("ovftool", "--lax", "--allowAllExtraConfig", fmt.Sprintf("--extraConfig:ethernet0.networkName=%s", p.config.VMNetwork), ova, fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova"))) - - var ovftoolOut bytes.Buffer - command.Stdout = &ovftoolOut - if err := command.Run(); err != nil { - return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, ovftoolOut.String()) - } - - ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) - - vmdk = fmt.Sprintf("%s-disk1.vmdk", strings.TrimSuffix(ova, ".ova")) - vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) - } - - if p.config.RemoveEthernet == "true" { - if err := p.RemoveEthernet(vmx, ui); err != nil { - return nil, false, fmt.Errorf("Removing ethernet0 interface from VMX failed!") - } - } - - if p.config.RemoveFloppy == "true" { - if err := p.RemoveFloppy(vmx, ui); err != nil { - return nil, false, fmt.Errorf("Removing floppy drive from VMX failed!") - } - } - - if p.config.RemoveOpticalDrive == "true" { - if err := p.RemoveOpticalDrive(vmx, ui); err != nil { - return nil, false, fmt.Errorf("Removing CD/DVD Drive from VMX failed!") - } - } - - if p.config.VirtualHardwareVer != "" { - if err := p.SetVHardwareVersion(vmx, ui, p.config.VirtualHardwareVer); err != nil { - return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") - } - } - - ui.Message(fmt.Sprintf("Uploading %s and %s to Datastore %s on host %s", vmdk, vmx, p.config.Datastore, p.config.Host)) - - clonerequired := false - if p.config.RemoveEthernet == "false" || p.config.RemoveFloppy == "false" || p.config.RemoveOpticalDrive == "false" { - clonerequired = true - } - - splitString := strings.Split(vmdk, "/") - vmdkDestPath := fmt.Sprintf("folder/%s/%s", p.config.VMFolder, splitString[len(splitString)-1]) - - splitString = strings.Split(vmx, "/") - vmxDestPath := fmt.Sprintf("folder/%s/%s", p.config.VMFolder, splitString[len(splitString)-1]) - - err := doUpload(fmt.Sprintf("https://%s:%s@%s/%s?dcPath=%s&dsName=%s", - url.QueryEscape(p.config.Username), - url.QueryEscape(p.config.Password), - p.config.Host, - vmdkDestPath, - p.config.Datacenter, - p.config.Datastore), vmdk) - - if err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - - ui.Message(fmt.Sprintf("Uploaded %s", vmdk)) - - err = doUpload(fmt.Sprintf("https://%s:%s@%s/%s?dcPath=%s&dsName=%s", - url.QueryEscape(p.config.Username), - url.QueryEscape(p.config.Password), - p.config.Host, - vmxDestPath, - p.config.Datacenter, - p.config.Datastore), vmx) - - if err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - - ui.Message(fmt.Sprintf("Uploaded %s", vmx)) - - err = doRegistration(ui, p.config, vmx, clonerequired) - - if err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - ui.Message("Uploaded and registered to VMware") - - return artifact, false, nil + if _, ok := builtins[artifact.BuilderId()]; !ok { + return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) + } + + ova := "" + vmx := "" + vmdk := "" + for _, path := range artifact.Files() { + if strings.HasSuffix(path, ".ova") { + ova = path + break + } else if strings.HasSuffix(path, ".vmx") { + vmx = path + } + } + + if ova == "" && vmx == "" { + return nil, false, fmt.Errorf("ERROR: Neither OVA nor VMX were found!") + } + + // Convert vmware builder artifact to ova so we can specify the hard disk + // type best for use for out purposes. + if artifact.BuilderId() == "mitchellh.vmware" { + if _, err := os.Stat(ova); os.IsNotExist(err) { + os.Mkdir("ova/vmware",0755) + } + + splitString := strings.Split(vmx, "/") + ova = fmt.Sprintf("ova/vmware/%s.ova", strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) + + args := []string{ + "--acceptAllEulas", + "-tt=OVA", + fmt.Sprintf("--diskMode=%s", p.config.DiskMode), + fmt.Sprintf("%s", vmx), + fmt.Sprintf("%s", ova), + } + + ui.Message(fmt.Sprintf("Exporting %s to %s", vmx, ova)) + var out bytes.Buffer + command := exec.Command("ovftool", args...) + log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) + command.Stdout = &out + if err := command.Run(); err != nil { + return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) + } + + // if err := os.Remove(vmx) ; err != nil { + // fmt.Println(err) + // return nil, false, fmt.Errorf("Failed: Deleting %s", err, vmx) + // } + + // if err := os.Remove(vmdk) ; err != nil { + // fmt.Println(err) + // return nil, false, fmt.Errorf("Failed: Deleting %s", err, vmdk) + // } + + ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) + } + + if _, err := os.Stat(ova); os.IsNotExist(err) { + return nil, false, fmt.Errorf("Failed: No such OVA file '%s'", ova) + } else { + // Sweet, we've got an OVA, Now it's time to make that baby something we can work with. + args := []string{ + "--acceptAllEulas", + "--lax", + "--allowAllExtraConfig", + fmt.Sprintf("--extraConfig:ethernet0.networkName=%s", p.config.VMNetwork), + fmt.Sprintf("--diskMode=%s", p.config.DiskMode), + fmt.Sprintf("%s", ova), + fmt.Sprintf("%s", fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova"))), + } + + command := exec.Command("ovftool", args...) + + var ovftoolOut bytes.Buffer + command.Stdout = &ovftoolOut + if err := command.Run(); err != nil { + return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, ovftoolOut.String()) + } + + ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) + + err := filepath.Walk("ova/vmware", visit) + fmt.Printf("filepath.Walk() returned %v\n", err) + + for _, path := range artifact.Files() { + if strings.HasSuffix(path, ".ova") { + ova = path + break + } else if strings.HasSuffix(path, ".vmx") { + vmx = path + } + } + + + vmdk = fmt.Sprintf("%s-disk1.vmdk", strings.TrimSuffix(ova, ".ova")) + vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) + } + + if p.config.RemoveEthernet == "true" { + if err := p.RemoveEthernet(vmx, ui); err != nil { + return nil, false, fmt.Errorf("Removing ethernet0 interface from VMX failed!") + } + } + + if p.config.RemoveFloppy == "true" { + if err := p.RemoveFloppy(vmx, ui); err != nil { + return nil, false, fmt.Errorf("Removing floppy drive from VMX failed!") + } + } + + if p.config.RemoveOpticalDrive == "true" { + if err := p.RemoveOpticalDrive(vmx, ui); err != nil { + return nil, false, fmt.Errorf("Removing CD/DVD Drive from VMX failed!") + } + } + + if p.config.VirtualHardwareVer != "" { + if err := p.SetVHardwareVersion(vmx, ui, p.config.VirtualHardwareVer); err != nil { + return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") + } + } + + ui.Message(fmt.Sprintf("Uploading %s and %s to Datastore %s on host %s", vmdk, vmx, p.config.Datastore, p.config.Host)) + + clonerequired := false + if p.config.RemoveEthernet == "false" || p.config.RemoveFloppy == "false" || p.config.RemoveOpticalDrive == "false" { + clonerequired = true + } + + splitString := strings.Split(vmdk, "/") + vmdkDestPath := fmt.Sprintf("folder/%s/%s", p.config.VMFolder, splitString[len(splitString)-1]) + + splitString = strings.Split(vmx, "/") + vmxDestPath := fmt.Sprintf("folder/%s/%s", p.config.VMFolder, splitString[len(splitString)-1]) + + err := doUpload(fmt.Sprintf("https://%s:%s@%s/%s?dcPath=%s&dsName=%s", + url.QueryEscape(p.config.Username), + url.QueryEscape(p.config.Password), + p.config.Host, + vmdkDestPath, + p.config.Datacenter, + p.config.Datastore), vmdk) + + if err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } + + ui.Message(fmt.Sprintf("Uploaded %s", vmdk)) + + err = doUpload(fmt.Sprintf("https://%s:%s@%s/%s?dcPath=%s&dsName=%s", + url.QueryEscape(p.config.Username), + url.QueryEscape(p.config.Password), + p.config.Host, + vmxDestPath, + p.config.Datacenter, + p.config.Datastore), vmx) + + if err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } + + ui.Message(fmt.Sprintf("Uploaded %s", vmx)) + + err = doRegistration(ui, p.config, vmx, clonerequired) + + if err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } + ui.Message("Uploaded and registered to VMware") + + if p.config.UploadCommand != "" && p.config.UploadArgs != "" { + bitsupload(ui, p.config, ova) + } + + return artifact, false, nil } +func bitsupload(ui packer.Ui, config Config, ova string) (err error) { + ui.Message(fmt.Sprintf("Please wait, Uploading %s to the bits repo.", ova)) + var out bytes.Buffer + log.Printf("Starting upload with parameters: %s %s", config.UploadCommand, config.UploadArgs) + cmd := exec.Command(config.UploadCommand, config.UploadArgs) + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) + } + + ui.Message(fmt.Sprintf("%s", out.String())) + + return nil +} + + func doUpload(url string, file string) (err error) { - data, err := os.Open(file) - if err != nil { - return err - } - defer data.Close() - - fileInfo, err := data.Stat() - if err != nil { - return err - } - req, err := http.NewRequest("PUT", url, data) - - if err != nil { - return err - } - - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.ContentLength = fileInfo.Size() - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - - client := &http.Client{Transport: tr} - res, err := client.Do(req) - if err != nil { - return err - } - - defer res.Body.Close() - - return nil + data, err := os.Open(file) + if err != nil { + return err + } + defer data.Close() + + fileInfo, err := data.Stat() + if err != nil { + return err + } + req, err := http.NewRequest("PUT", url, data) + + if err != nil { + return err + } + + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.ContentLength = fileInfo.Size() + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + client := &http.Client{Transport: tr} + res, err := client.Do(req) + if err != nil { + return err + } + + defer res.Body.Close() + + return nil } func doRegistration(ui packer.Ui, config Config, vmx string, clonerequired bool ) (err error) { - sdkURL, err := url.Parse(fmt.Sprintf("https://%s:%s@%s/sdk", - url.QueryEscape(config.Username), - url.QueryEscape(config.Password), - config.Host)) - if err != nil { - return err - } + sdkURL, err := url.Parse(fmt.Sprintf("https://%s:%s@%s/sdk", + url.QueryEscape(config.Username), + url.QueryEscape(config.Password), + config.Host)) + if err != nil { + return err + } - client, err := govmomi.NewClient(context.TODO(), sdkURL, true) + client, err := govmomi.NewClient(context.TODO(), sdkURL, true) - if err != nil { - return err - } + if err != nil { + return err + } - finder := find.NewFinder(client.Client, false) - datacenter, err := finder.DefaultDatacenter(context.TODO()) - finder.SetDatacenter(datacenter) - if err != nil { - return err - } + finder := find.NewFinder(client.Client, false) + datacenter, err := finder.DefaultDatacenter(context.TODO()) + finder.SetDatacenter(datacenter) + if err != nil { + return err + } - folders, err := datacenter.Folders(context.TODO()) - if err != nil { - return err - } + folders, err := datacenter.Folders(context.TODO()) + if err != nil { + return err + } - resourcePool, err := finder.DefaultResourcePool(context.TODO()) + resourcePool, err := finder.DefaultResourcePool(context.TODO()) - if err != nil { - return err - } + if err != nil { + return err + } - splitString := strings.Split(vmx, "/") - last := splitString[len(splitString)-1] - vmName := strings.TrimSuffix(last, ".vmx") + splitString := strings.Split(vmx, "/") + last := splitString[len(splitString)-1] + vmName := strings.TrimSuffix(last, ".vmx") - datastoreString := fmt.Sprintf( "[%s] %s/%s.vmx", config.Datastore, config.VMFolder, vmName ) + datastoreString := fmt.Sprintf( "[%s] %s/%s.vmx", config.Datastore, config.VMFolder, vmName ) - ui.Message(fmt.Sprintf("Registering %s from %s", vmName, datastoreString)) - task, err := folders.VmFolder.RegisterVM(context.TODO(), datastoreString, vmName, false, resourcePool, nil) - if err != nil { - return err - } - _, err = task.WaitForResult(context.TODO(), nil) - if err != nil { - return err - } - ui.Message(fmt.Sprintf("Registererd VM %s", vmName)) + ui.Message(fmt.Sprintf("Registering %s from %s", vmName, datastoreString)) + task, err := folders.VmFolder.RegisterVM(context.TODO(), datastoreString, vmName, false, resourcePool, nil) + if err != nil { + return err + } + _, err = task.WaitForResult(context.TODO(), nil) + if err != nil { + return err + } + ui.Message(fmt.Sprintf("Registererd VM %s", vmName)) - vm, err := finder.VirtualMachine(context.TODO(), vmName) + vm, err := finder.VirtualMachine(context.TODO(), vmName) - rpRef := resourcePool.Reference() + rpRef := resourcePool.Reference() - if clonerequired { - cloneSpec := types.VirtualMachineCloneSpec{ - Location: types.VirtualMachineRelocateSpec{ - Pool: &rpRef, - }, - } + if clonerequired { + cloneSpec := types.VirtualMachineCloneSpec{ + Location: types.VirtualMachineRelocateSpec{ + Pool: &rpRef, + }, + } - cloneVmName := fmt.Sprintf("%s-vm", vmName) + cloneVmName := fmt.Sprintf("%s-vm", vmName) - ui.Message(fmt.Sprintf("Cloning VM %s", cloneVmName)) - task, err = vm.Clone(context.TODO(), folders.VmFolder, cloneVmName, cloneSpec) + ui.Message(fmt.Sprintf("Cloning VM %s", cloneVmName)) + task, err = vm.Clone(context.TODO(), folders.VmFolder, cloneVmName, cloneSpec) - if err != nil { - return err - } + if err != nil { + return err + } - _, err = task.WaitForResult(context.TODO(), nil) + _, err = task.WaitForResult(context.TODO(), nil) - if err != nil { - return err - } + if err != nil { + return err + } - clonedVM, err := finder.VirtualMachine(context.TODO(), cloneVmName) + clonedVM, err := finder.VirtualMachine(context.TODO(), cloneVmName) - if err != nil { - return err - } + if err != nil { + return err + } - ui.Message(fmt.Sprintf("Powering on %s", cloneVmName)) - task, err = clonedVM.PowerOn(context.TODO()) + ui.Message(fmt.Sprintf("Powering on %s", cloneVmName)) + task, err = clonedVM.PowerOn(context.TODO()) - if err != nil { - return err - } + if err != nil { + return err + } - _, err = task.WaitForResult(context.TODO(), nil) - if err != nil { - return err - } + _, err = task.WaitForResult(context.TODO(), nil) + if err != nil { + return err + } - ui.Message(fmt.Sprintf("Powered on %s", cloneVmName)) + ui.Message(fmt.Sprintf("Powered on %s", cloneVmName)) - time.Sleep(150000 * time.Millisecond) // This is really dirty, but I need to make sure the VM gets fully powered on before I turn it off, otherwise vmware tools won't register on the cloning side. + time.Sleep(150000 * time.Millisecond) // This is really dirty, but I need to make sure the VM gets fully powered on before I turn it off, otherwise vmware tools won't register on the cloning side. - ui.Message(fmt.Sprintf("Powering off %s", cloneVmName)) - task, err = clonedVM.PowerOff(context.TODO()) + ui.Message(fmt.Sprintf("Powering off %s", cloneVmName)) + task, err = clonedVM.PowerOff(context.TODO()) - if err != nil { - return err - } + if err != nil { + return err + } - _, err = task.WaitForResult(context.TODO(), nil) + _, err = task.WaitForResult(context.TODO(), nil) - if err != nil { - return err - } - ui.Message(fmt.Sprintf("Powered off %s", cloneVmName)) + if err != nil { + return err + } + ui.Message(fmt.Sprintf("Powered off %s", cloneVmName)) - ui.Message(fmt.Sprintf("Marking as template %s", cloneVmName)) - err = clonedVM.MarkAsTemplate(context.TODO()) + ui.Message(fmt.Sprintf("Marking as template %s", cloneVmName)) + err = clonedVM.MarkAsTemplate(context.TODO()) - if err != nil { - return err - } + if err != nil { + return err + } - ui.Message(fmt.Sprintf("Destroying %s", cloneVmName)) - task, err = vm.Destroy(context.TODO()) + ui.Message(fmt.Sprintf("Destroying %s", cloneVmName)) + task, err = vm.Destroy(context.TODO()) - _, err = task.WaitForResult(context.TODO(), nil) + _, err = task.WaitForResult(context.TODO(), nil) - if err != nil { - return err - } - ui.Message(fmt.Sprintf("Destroyed %s", cloneVmName)) - } else { - ui.Message(fmt.Sprintf("Marking as template %s", vmName)) - err = vm.MarkAsTemplate(context.TODO()) + if err != nil { + return err + } + ui.Message(fmt.Sprintf("Destroyed %s", cloneVmName)) + } else { + ui.Message(fmt.Sprintf("Marking as template %s", vmName)) + err = vm.MarkAsTemplate(context.TODO()) - if err != nil { - return err - } - ui.Message(fmt.Sprintf("%s is now a template", vmName)) - } + if err != nil { + return err + } + ui.Message(fmt.Sprintf("%s is now a template", vmName)) + } - return nil + return nil } From 98dac993ae9123f9e5d61e621d807559cd707d5f Mon Sep 17 00:00:00 2001 From: Dax Games Date: Thu, 13 Aug 2015 08:35:02 -0500 Subject: [PATCH 02/24] figuring out vmx->ova->vmx --- post-processor.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/post-processor.go b/post-processor.go index 5464447..ba69a1e 100644 --- a/post-processor.go +++ b/post-processor.go @@ -250,7 +250,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac // Convert vmware builder artifact to ova so we can specify the hard disk // type best for use for out purposes. if artifact.BuilderId() == "mitchellh.vmware" { - if _, err := os.Stat(ova); os.IsNotExist(err) { + if _, err := os.Stat("ova/vmware"); os.IsNotExist(err) { os.Mkdir("ova/vmware",0755) } @@ -287,6 +287,10 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) } + if ova == "" { + return nil, false, fmt.Errorf("OVA not found") + } + if _, err := os.Stat(ova); os.IsNotExist(err) { return nil, false, fmt.Errorf("Failed: No such OVA file '%s'", ova) } else { @@ -314,14 +318,14 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac err := filepath.Walk("ova/vmware", visit) fmt.Printf("filepath.Walk() returned %v\n", err) - for _, path := range artifact.Files() { - if strings.HasSuffix(path, ".ova") { - ova = path - break - } else if strings.HasSuffix(path, ".vmx") { - vmx = path - } - } + // for _, path := range artifact.Files() { + // if strings.HasSuffix(path, ".ova") { + // ova = path + // break + // } else if strings.HasSuffix(path, ".vmx") { + // vmx = path + // } + // } vmdk = fmt.Sprintf("%s-disk1.vmdk", strings.TrimSuffix(ova, ".ova")) From 989990d56e3e6959fa0d50be4834a553cff1bf3e Mon Sep 17 00:00:00 2001 From: Dax Games Date: Thu, 13 Aug 2015 12:26:39 -0500 Subject: [PATCH 03/24] working, but I don't like it. Switching to using ovftool --- post-processor.go | 1 - 1 file changed, 1 deletion(-) diff --git a/post-processor.go b/post-processor.go index ba69a1e..288a02b 100644 --- a/post-processor.go +++ b/post-processor.go @@ -300,7 +300,6 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac "--lax", "--allowAllExtraConfig", fmt.Sprintf("--extraConfig:ethernet0.networkName=%s", p.config.VMNetwork), - fmt.Sprintf("--diskMode=%s", p.config.DiskMode), fmt.Sprintf("%s", ova), fmt.Sprintf("%s", fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova"))), } From 9440ca05dc43fa76d4f9e5bb1c03f61da0185a4f Mon Sep 17 00:00:00 2001 From: Dax Games Date: Mon, 17 Aug 2015 13:04:09 -0500 Subject: [PATCH 04/24] using ovftool to upload and register vm to better control vmdk type of final template --- post-processor.go | 352 +++++++++++++--------------------------------- 1 file changed, 95 insertions(+), 257 deletions(-) diff --git a/post-processor.go b/post-processor.go index 288a02b..e6d1879 100644 --- a/post-processor.go +++ b/post-processor.go @@ -2,23 +2,18 @@ package main import ( "bytes" - "crypto/tls" "fmt" "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" "github.com/vmware/govmomi" "github.com/vmware/govmomi/find" - "github.com/vmware/govmomi/vim25/types" "golang.org/x/net/context" "io/ioutil" - "net/http" "net/url" "os" "os/exec" "strings" - "time" "log" - "path/filepath" "github.com/mitchellh/packer/helper/config" "github.com/mitchellh/packer/template/interpolate" vmwarecommon "github.com/mitchellh/packer/builder/vmware/common" @@ -35,6 +30,8 @@ type Config struct { Datacenter string `mapstructure:"datacenter"` Datastore string `mapstructure:"datastore"` Host string `mapstructure:"host"` + Cluster string `mapstructure:"cluster"` + ResourcePool string `mapstructure:"resource_pool"` Password string `mapstructure:"password"` Username string `mapstructure:"username"` VMFolder string `mapstructure:"vm_folder"` @@ -46,6 +43,7 @@ type Config struct { DiskMode string `mapstructure:"disk_mode"` UploadCommand string `mapstructure:"upload_command"` UploadArgs string `mapstructure:"upload_args"` + Insecure string `mapstructure:"insecure"` Compression uint `mapstructure:"compression"` ctx interpolate.Context } @@ -67,12 +65,8 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // Defaults - if p.config.VMNetwork == "" { - p.config.VMNetwork = "VM Network" - } - if p.config.DiskMode == "" { - p.config.DiskMode = "thin" + p.config.DiskMode = "thick" } if p.config.RemoveEthernet == "" { @@ -87,6 +81,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.RemoveOpticalDrive = "false" } + if p.config.Insecure == "" { + p.config.Insecure = "false" + } + if p.config.VirtualHardwareVer == "" { p.config.VirtualHardwareVer = "10" } @@ -115,12 +113,13 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.DiskMode == "sparse" || p.config.DiskMode == "flat") { errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Invalid disk_mode. Only thin(Default), thick, monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, seSparse, eagerZeroedThick, sparse, and flat are allowed.")) + errs, fmt.Errorf("Invalid disk_mode. Only thick(Default), thin, monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, seSparse, eagerZeroedThick, sparse, and flat are allowed.")) } // First define all our templatable parameters that are _required_ templates := map[string]*string{ "datacenter": &p.config.Datacenter, + "cluster": &p.config.Cluster, "host": &p.config.Host, "password": &p.config.Password, "username": &p.config.Username, @@ -161,7 +160,7 @@ func (p *PostProcessor) RemoveFloppy(vmx string, ui packer.Ui) error { } func (p *PostProcessor) RemoveEthernet(vmx string, ui packer.Ui) error { - ui.Message(fmt.Sprintf("Removing ethernet0 intercace from %s", vmx)) + ui.Message(fmt.Sprintf("Removing ethernet0 interface from %s", vmx)) vmxData, err := vmwarecommon.ReadVMX(vmx) if err != nil { return err @@ -233,13 +232,13 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ova := "" vmx := "" - vmdk := "" for _, path := range artifact.Files() { if strings.HasSuffix(path, ".ova") { ova = path break } else if strings.HasSuffix(path, ".vmx") { vmx = path + break } } @@ -247,53 +246,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("ERROR: Neither OVA nor VMX were found!") } - // Convert vmware builder artifact to ova so we can specify the hard disk - // type best for use for out purposes. - if artifact.BuilderId() == "mitchellh.vmware" { - if _, err := os.Stat("ova/vmware"); os.IsNotExist(err) { - os.Mkdir("ova/vmware",0755) - } - - splitString := strings.Split(vmx, "/") - ova = fmt.Sprintf("ova/vmware/%s.ova", strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) - - args := []string{ - "--acceptAllEulas", - "-tt=OVA", - fmt.Sprintf("--diskMode=%s", p.config.DiskMode), - fmt.Sprintf("%s", vmx), - fmt.Sprintf("%s", ova), - } - - ui.Message(fmt.Sprintf("Exporting %s to %s", vmx, ova)) - var out bytes.Buffer - command := exec.Command("ovftool", args...) - log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) - command.Stdout = &out - if err := command.Run(); err != nil { - return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) - } - - // if err := os.Remove(vmx) ; err != nil { - // fmt.Println(err) - // return nil, false, fmt.Errorf("Failed: Deleting %s", err, vmx) - // } - - // if err := os.Remove(vmdk) ; err != nil { - // fmt.Println(err) - // return nil, false, fmt.Errorf("Failed: Deleting %s", err, vmdk) - // } - - ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) - } - - if ova == "" { - return nil, false, fmt.Errorf("OVA not found") - } - - if _, err := os.Stat(ova); os.IsNotExist(err) { - return nil, false, fmt.Errorf("Failed: No such OVA file '%s'", ova) - } else { + if artifact.BuilderId() == "mitchellh.virtualbox" && ova != "" { // Sweet, we've got an OVA, Now it's time to make that baby something we can work with. args := []string{ "--acceptAllEulas", @@ -310,25 +263,12 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac command.Stdout = &ovftoolOut if err := command.Run(); err != nil { return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, ovftoolOut.String()) - } - - ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) - err := filepath.Walk("ova/vmware", visit) - fmt.Printf("filepath.Walk() returned %v\n", err) + ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) - // for _, path := range artifact.Files() { - // if strings.HasSuffix(path, ".ova") { - // ova = path - // break - // } else if strings.HasSuffix(path, ".vmx") { - // vmx = path - // } - // } - - - vmdk = fmt.Sprintf("%s-disk1.vmdk", strings.TrimSuffix(ova, ".ova")) - vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) + // vmdk = fmt.Sprintf("%s-disk1.vmdk", strings.TrimSuffix(ova, ".ova")) + vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) + } } if p.config.RemoveEthernet == "true" { @@ -355,114 +295,115 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac } } - ui.Message(fmt.Sprintf("Uploading %s and %s to Datastore %s on host %s", vmdk, vmx, p.config.Datastore, p.config.Host)) - - clonerequired := false - if p.config.RemoveEthernet == "false" || p.config.RemoveFloppy == "false" || p.config.RemoveOpticalDrive == "false" { - clonerequired = true + if err := doVmxImport(ui, p.config, vmx) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) } - splitString := strings.Split(vmdk, "/") - vmdkDestPath := fmt.Sprintf("folder/%s/%s", p.config.VMFolder, splitString[len(splitString)-1]) - - splitString = strings.Split(vmx, "/") - vmxDestPath := fmt.Sprintf("folder/%s/%s", p.config.VMFolder, splitString[len(splitString)-1]) - - err := doUpload(fmt.Sprintf("https://%s:%s@%s/%s?dcPath=%s&dsName=%s", - url.QueryEscape(p.config.Username), - url.QueryEscape(p.config.Password), - p.config.Host, - vmdkDestPath, - p.config.Datacenter, - p.config.Datastore), vmdk) + ui.Message(fmt.Sprintf("Uploaded %s", vmx)) - if err != nil { + if err := setAsTemplate(ui, p.config, vmx) ; err != nil { return nil, false, fmt.Errorf("Failed: %s", err) } - ui.Message(fmt.Sprintf("Uploaded %s", vmdk)) + ui.Message("Uploaded and registered to VMware as a template") - err = doUpload(fmt.Sprintf("https://%s:%s@%s/%s?dcPath=%s&dsName=%s", - url.QueryEscape(p.config.Username), - url.QueryEscape(p.config.Password), - p.config.Host, - vmxDestPath, - p.config.Datacenter, - p.config.Datastore), vmx) + if artifact.BuilderId() == "mitchellh.vmware" && (p.config.UploadCommand != "" && p.config.UploadArgs != ""){ + // Convert vmware builder artifact to ova so we can upload to bits if required. + if _, err := os.Stat("ova/vmware"); os.IsNotExist(err) { + os.Mkdir("ova/vmware",0755) + } - if err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } + splitString := strings.Split(vmx, "/") + ova = fmt.Sprintf("ova/vmware/%s.ova", strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) - ui.Message(fmt.Sprintf("Uploaded %s", vmx)) + args := []string{ + "--acceptAllEulas", + "-tt=OVA", + "--diskMode=thin", + fmt.Sprintf("%s", vmx), + fmt.Sprintf("%s", ova), + } - err = doRegistration(ui, p.config, vmx, clonerequired) + ui.Message(fmt.Sprintf("Exporting %s to %s", vmx, ova)) + var out bytes.Buffer + command := exec.Command("ovftool", args...) + log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) + command.Stdout = &out + if err := command.Run(); err != nil { + return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) + } - if err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) + ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) } - ui.Message("Uploaded and registered to VMware") if p.config.UploadCommand != "" && p.config.UploadArgs != "" { - bitsupload(ui, p.config, ova) + if err := doBitsUpload(ui, p.config, ova) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } } return artifact, false, nil } -func bitsupload(ui packer.Ui, config Config, ova string) (err error) { +func doBitsUpload(ui packer.Ui, config Config, ova string) (err error) { + args := strings.Split(config.UploadArgs, " ") + ui.Message(fmt.Sprintf("Please wait, Uploading %s to the bits repo.", ova)) - var out bytes.Buffer - log.Printf("Starting upload with parameters: %s %s", config.UploadCommand, config.UploadArgs) - cmd := exec.Command(config.UploadCommand, config.UploadArgs) - cmd.Stdout = &out - if err := cmd.Run(); err != nil { - return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) - } + var out bytes.Buffer + ui.Message(fmt.Sprintf("Starting '%s' with parameters: %s", config.UploadCommand, config.UploadArgs)) + command := exec.Command(config.UploadCommand, args...) + command.Stdout = &out + if err := command.Run(); err != nil { + return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) + } - ui.Message(fmt.Sprintf("%s", out.String())) + ui.Message(fmt.Sprintf("%s", out.String())) - return nil + return nil } +func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { + splitString := strings.Split(vmx, "/") + last := splitString[len(splitString)-1] + VMName := strings.TrimSuffix(last, ".vmx") -func doUpload(url string, file string) (err error) { - - data, err := os.Open(file) - if err != nil { - return err - } - defer data.Close() + ovftool_uri := fmt.Sprintf("vi://%s:%s@%s/%s/host/%s", + url.QueryEscape(config.Username), + url.QueryEscape(config.Password), + config.Host, + config.Datacenter, + config.Cluster) - fileInfo, err := data.Stat() - if err != nil { - return err + if config.ResourcePool != "" { + ovftool_uri += "/Resources/" + config.ResourcePool } - req, err := http.NewRequest("PUT", url, data) - if err != nil { - return err + args := []string{ + fmt.Sprintf("--noSSLVerify=%s", config.Insecure), + "--acceptAllEulas", + fmt.Sprintf("--name=%s", VMName), + fmt.Sprintf("--datastore=%s", config.Datastore), + fmt.Sprintf("--diskMode=%s", config.DiskMode), + fmt.Sprintf("--network=%s", config.VMNetwork), + fmt.Sprintf("%s", vmx), + fmt.Sprintf("%s", ovftool_uri), } - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.ContentLength = fileInfo.Size() - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + ui.Message(fmt.Sprintf("Uploading %s to vSphere", vmx)) + var out bytes.Buffer + log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) + cmd := exec.Command("ovftool", args...) + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) } - client := &http.Client{Transport: tr} - res, err := client.Do(req) - if err != nil { - return err - } - - defer res.Body.Close() + ui.Message(fmt.Sprintf("%s", out.String())) return nil } -func doRegistration(ui packer.Ui, config Config, vmx string, clonerequired bool ) (err error) { - +func setAsTemplate(ui packer.Ui, config Config, vmx string ) (err error) { sdkURL, err := url.Parse(fmt.Sprintf("https://%s:%s@%s/sdk", url.QueryEscape(config.Username), url.QueryEscape(config.Password), @@ -484,122 +425,19 @@ func doRegistration(ui packer.Ui, config Config, vmx string, clonerequired bool return err } - folders, err := datacenter.Folders(context.TODO()) - if err != nil { - return err - } - - resourcePool, err := finder.DefaultResourcePool(context.TODO()) - - if err != nil { - return err - } - splitString := strings.Split(vmx, "/") last := splitString[len(splitString)-1] vmName := strings.TrimSuffix(last, ".vmx") - datastoreString := fmt.Sprintf( "[%s] %s/%s.vmx", config.Datastore, config.VMFolder, vmName ) - - ui.Message(fmt.Sprintf("Registering %s from %s", vmName, datastoreString)) - task, err := folders.VmFolder.RegisterVM(context.TODO(), datastoreString, vmName, false, resourcePool, nil) - if err != nil { - return err - } - _, err = task.WaitForResult(context.TODO(), nil) - if err != nil { - return err - } - ui.Message(fmt.Sprintf("Registererd VM %s", vmName)) - vm, err := finder.VirtualMachine(context.TODO(), vmName) - rpRef := resourcePool.Reference() - - - if clonerequired { - cloneSpec := types.VirtualMachineCloneSpec{ - Location: types.VirtualMachineRelocateSpec{ - Pool: &rpRef, - }, - } - - cloneVmName := fmt.Sprintf("%s-vm", vmName) - - ui.Message(fmt.Sprintf("Cloning VM %s", cloneVmName)) - task, err = vm.Clone(context.TODO(), folders.VmFolder, cloneVmName, cloneSpec) - - if err != nil { - return err - } - - _, err = task.WaitForResult(context.TODO(), nil) - - if err != nil { - return err - } - - clonedVM, err := finder.VirtualMachine(context.TODO(), cloneVmName) - - if err != nil { - return err - } - - ui.Message(fmt.Sprintf("Powering on %s", cloneVmName)) - task, err = clonedVM.PowerOn(context.TODO()) - - if err != nil { - return err - } - - _, err = task.WaitForResult(context.TODO(), nil) - if err != nil { - return err - } + ui.Message(fmt.Sprintf("Marking as template %s", vmName)) + err = vm.MarkAsTemplate(context.TODO()) - ui.Message(fmt.Sprintf("Powered on %s", cloneVmName)) - - time.Sleep(150000 * time.Millisecond) // This is really dirty, but I need to make sure the VM gets fully powered on before I turn it off, otherwise vmware tools won't register on the cloning side. - - ui.Message(fmt.Sprintf("Powering off %s", cloneVmName)) - task, err = clonedVM.PowerOff(context.TODO()) - - if err != nil { - return err - } - - _, err = task.WaitForResult(context.TODO(), nil) - - if err != nil { - return err - } - ui.Message(fmt.Sprintf("Powered off %s", cloneVmName)) - - ui.Message(fmt.Sprintf("Marking as template %s", cloneVmName)) - err = clonedVM.MarkAsTemplate(context.TODO()) - - if err != nil { - return err - } - - ui.Message(fmt.Sprintf("Destroying %s", cloneVmName)) - task, err = vm.Destroy(context.TODO()) - - _, err = task.WaitForResult(context.TODO(), nil) - - if err != nil { - return err - } - ui.Message(fmt.Sprintf("Destroyed %s", cloneVmName)) - } else { - ui.Message(fmt.Sprintf("Marking as template %s", vmName)) - err = vm.MarkAsTemplate(context.TODO()) - - if err != nil { - return err - } - ui.Message(fmt.Sprintf("%s is now a template", vmName)) + if err != nil { + return err } + ui.Message(fmt.Sprintf("%s is now a template", vmName)) return nil } From d95db3a744fe4b6cbdb4571ddc31ec8a85358607 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Tue, 18 Aug 2015 11:45:17 -0500 Subject: [PATCH 05/24] removed testing bitsupload to another branch --- post-processor.go | 93 ++++++++++------------------------------------- 1 file changed, 20 insertions(+), 73 deletions(-) diff --git a/post-processor.go b/post-processor.go index e6d1879..4dea27e 100644 --- a/post-processor.go +++ b/post-processor.go @@ -10,7 +10,6 @@ import ( "golang.org/x/net/context" "io/ioutil" "net/url" - "os" "os/exec" "strings" "log" @@ -36,15 +35,12 @@ type Config struct { Username string `mapstructure:"username"` VMFolder string `mapstructure:"vm_folder"` VMNetwork string `mapstructure:"vm_network"` - RemoveEthernet string `mapstructure:"remove_ethernet"` - RemoveFloppy string `mapstructure:"remove_floppy"` - RemoveOpticalDrive string `mapstructure:"remove_optical_drive"` + RemoveEthernet bool `mapstructure:"remove_ethernet"` + RemoveFloppy bool `mapstructure:"remove_floppy"` + RemoveOpticalDrive bool `mapstructure:"remove_optical_drive"` VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` DiskMode string `mapstructure:"disk_mode"` - UploadCommand string `mapstructure:"upload_command"` - UploadArgs string `mapstructure:"upload_args"` Insecure string `mapstructure:"insecure"` - Compression uint `mapstructure:"compression"` ctx interpolate.Context } @@ -69,22 +65,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.DiskMode = "thick" } - if p.config.RemoveEthernet == "" { - p.config.RemoveEthernet = "false" - } - - if p.config.RemoveFloppy == "" { - p.config.RemoveFloppy = "false" - } - - if p.config.RemoveOpticalDrive == "" { - p.config.RemoveOpticalDrive = "false" - } - - if p.config.Insecure == "" { - p.config.Insecure = "false" - } - if p.config.VirtualHardwareVer == "" { p.config.VirtualHardwareVer = "10" } @@ -97,11 +77,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { errs, fmt.Errorf("ovftool not found: %s", err)) } - if !(p.config.Compression >= 0 && p.config.Compression <= 9) { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Invalid compression level. Must be between 1 and 9, or 0 for no compression.")) - } - if !(p.config.DiskMode == "thick" || p.config.DiskMode == "thin" || p.config.DiskMode == "monolithicSparse" || @@ -220,11 +195,6 @@ func (p *PostProcessor) RemoveOpticalDrive(vmx string, ui packer.Ui) error { return nil } -func visit(path string, f os.FileInfo, err error) error { - fmt.Printf("Visited: %s\n", path) - return nil -} - func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { if _, ok := builtins[artifact.BuilderId()]; !ok { return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) @@ -266,60 +236,60 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) - // vmdk = fmt.Sprintf("%s-disk1.vmdk", strings.TrimSuffix(ova, ".ova")) vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) } } - if p.config.RemoveEthernet == "true" { + if p.config.RemoveEthernet == true { if err := p.RemoveEthernet(vmx, ui); err != nil { return nil, false, fmt.Errorf("Removing ethernet0 interface from VMX failed!") } } - if p.config.RemoveFloppy == "true" { + if p.config.RemoveFloppy == true { if err := p.RemoveFloppy(vmx, ui); err != nil { return nil, false, fmt.Errorf("Removing floppy drive from VMX failed!") } } - if p.config.RemoveOpticalDrive == "true" { + if p.config.RemoveOpticalDrive == true { if err := p.RemoveOpticalDrive(vmx, ui); err != nil { return nil, false, fmt.Errorf("Removing CD/DVD Drive from VMX failed!") } } - if p.config.VirtualHardwareVer != "" { - if err := p.SetVHardwareVersion(vmx, ui, p.config.VirtualHardwareVer); err != nil { - return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") - } + if err := p.SetVHardwareVersion(vmx, ui, p.config.VirtualHardwareVer); err != nil { + return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") } if err := doVmxImport(ui, p.config, vmx) ; err != nil { return nil, false, fmt.Errorf("Failed: %s", err) } - ui.Message(fmt.Sprintf("Uploaded %s", vmx)) - if err := setAsTemplate(ui, p.config, vmx) ; err != nil { return nil, false, fmt.Errorf("Failed: %s", err) } ui.Message("Uploaded and registered to VMware as a template") - if artifact.BuilderId() == "mitchellh.vmware" && (p.config.UploadCommand != "" && p.config.UploadArgs != ""){ - // Convert vmware builder artifact to ova so we can upload to bits if required. - if _, err := os.Stat("ova/vmware"); os.IsNotExist(err) { - os.Mkdir("ova/vmware",0755) - } + if artifact.BuilderId() == "mitchellh.vmware" { + // ova_dir := "ova/vmware" + + // // Convert vmware builder artifact to ova so we can upload to bits if required. + // if _, err := os.Stat("ova/vmware"); os.IsNotExist(err) { + // os.MkdirAll(ova_dir,0755) + // } - splitString := strings.Split(vmx, "/") - ova = fmt.Sprintf("ova/vmware/%s.ova", strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) + // splitString := strings.Split(vmx, "/") + // ova = fmt.Sprintf("%s/%s.ova", ova_dir, strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) + + ova = fmt.Sprintf("%s.ova", strings.TrimSuffix(vmx, ".vmx")) args := []string{ "--acceptAllEulas", "-tt=OVA", "--diskMode=thin", + "--compress=9", fmt.Sprintf("%s", vmx), fmt.Sprintf("%s", ova), } @@ -336,32 +306,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) } - if p.config.UploadCommand != "" && p.config.UploadArgs != "" { - if err := doBitsUpload(ui, p.config, ova) ; err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - } - return artifact, false, nil } -func doBitsUpload(ui packer.Ui, config Config, ova string) (err error) { - args := strings.Split(config.UploadArgs, " ") - - ui.Message(fmt.Sprintf("Please wait, Uploading %s to the bits repo.", ova)) - var out bytes.Buffer - ui.Message(fmt.Sprintf("Starting '%s' with parameters: %s", config.UploadCommand, config.UploadArgs)) - command := exec.Command(config.UploadCommand, args...) - command.Stdout = &out - if err := command.Run(); err != nil { - return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) - } - - ui.Message(fmt.Sprintf("%s", out.String())) - - return nil -} - func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { splitString := strings.Split(vmx, "/") last := splitString[len(splitString)-1] From f0e0b3dad958c59f057f0b91e7674d9d583cf709 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Tue, 18 Aug 2015 12:24:23 -0500 Subject: [PATCH 06/24] Changed ova_dir pathing --- post-processor.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/post-processor.go b/post-processor.go index 4dea27e..46ed07b 100644 --- a/post-processor.go +++ b/post-processor.go @@ -10,6 +10,7 @@ import ( "golang.org/x/net/context" "io/ioutil" "net/url" + "os" "os/exec" "strings" "log" @@ -273,17 +274,18 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message("Uploaded and registered to VMware as a template") if artifact.BuilderId() == "mitchellh.vmware" { - // ova_dir := "ova/vmware" + // ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) + ova_dir := "ova/vmware" - // // Convert vmware builder artifact to ova so we can upload to bits if required. - // if _, err := os.Stat("ova/vmware"); os.IsNotExist(err) { - // os.MkdirAll(ova_dir,0755) - // } + // Convert vmware builder artifact to ova so we can upload to bits if required. + if _, err := os.Stat(ova_dir); os.IsNotExist(err) { + os.MkdirAll(ova_dir,0755) + } - // splitString := strings.Split(vmx, "/") - // ova = fmt.Sprintf("%s/%s.ova", ova_dir, strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) + splitString := strings.Split(vmx, "/") + ova = fmt.Sprintf("%s/%s.ova", ova_dir, strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) - ova = fmt.Sprintf("%s.ova", strings.TrimSuffix(vmx, ".vmx")) + // ova = fmt.Sprintf("%s.ova", strings.TrimSuffix(vmx, ".vmx")) args := []string{ "--acceptAllEulas", From 88fab533f7ef6793c844b829c2f11970271377f4 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Mon, 24 Aug 2015 15:43:13 -0500 Subject: [PATCH 07/24] updated README.md --- README.md | 15 +++------------ post-processor.go | 9 ++++++++- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 137be4a..8a91c37 100644 --- a/README.md +++ b/README.md @@ -39,17 +39,8 @@ Add the following, filled out correctly to your post-processors and you should e I'm not sure if a release of Packer with SCSI support has been released yet, but you can create a virtualbox with a SCSI drive using Packer for maximum performance on your VMWare setup. -There is some wierdness with how this works: - -1. It uploads a virtual machine -2. It registers a virtual machine -3. It clones the virtual machine (it complains about invalid device backing - without this) -4. It powers on the cloned virtual machine -5. It SLEEPS for 2ish minutes while we wait for power on to complete -6. It powers off the cloned virtual machine -7. It marks the cloned virtual machine as a template. -8. You end up with a registered template of the vm name with "-vm" appended. +1. It uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder +1. It marks the cloned virtual machine as a template. This is the statement you need to add to your packer json file: @@ -59,10 +50,10 @@ This is the statement you need to add to your packer json file: "type": "vsphere-ova", "host":"vcenter_host", "datacenter":"datacenter_name", + "cluster":"cluster", "username":"my_username", "password":"my_password", "datastore": "datastore_name", - "vm_folder":"folder_on_datastore", "vm_network":"vmware_network_name" } ] diff --git a/post-processor.go b/post-processor.go index 46ed07b..517374f 100644 --- a/post-processor.go +++ b/post-processor.go @@ -70,6 +70,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.VirtualHardwareVer = "10" } + if p.config.VMFolder == "" { + p.config.VMFolder = "Templates" + } + // Accumulate any errors errs := new(packer.MultiError) @@ -100,7 +104,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { "password": &p.config.Password, "username": &p.config.Username, "datastore": &p.config.Datastore, - "vm_folder": &p.config.VMFolder, } for key, ptr := range templates { @@ -334,6 +337,7 @@ func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { fmt.Sprintf("--datastore=%s", config.Datastore), fmt.Sprintf("--diskMode=%s", config.DiskMode), fmt.Sprintf("--network=%s", config.VMNetwork), + fmt.Sprintf("--vmFolder=%s", config.VMFolder), fmt.Sprintf("%s", vmx), fmt.Sprintf("%s", ovftool_uri), } @@ -377,6 +381,9 @@ func setAsTemplate(ui packer.Ui, config Config, vmx string ) (err error) { splitString := strings.Split(vmx, "/") last := splitString[len(splitString)-1] vmName := strings.TrimSuffix(last, ".vmx") + if config.VMFolder != "" { + vmName = fmt.Sprintf("%s/%s", config.VMFolder, vmName) + } vm, err := finder.VirtualMachine(context.TODO(), vmName) From 89f964d2a9571e00e35cf2ef5840b6eef72cfaab Mon Sep 17 00:00:00 2001 From: Dax Games Date: Mon, 24 Aug 2015 15:54:15 -0500 Subject: [PATCH 08/24] updated readme.md --- README.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 0f25c60..3c39463 100644 --- a/README.md +++ b/README.md @@ -20,25 +20,12 @@ Notes: Pull the repository and compile the code with ```go build``` -Add - -``` -{ - "post-processors": { - "vsphere-ova": "packer-post-processor-vsphere-ova" - } -} -``` - -to your packer configuration (see: http://www.packer.io/docs/other/core-configuration.html -> Core Configuration) Make sure that the directory which contains the packer-post-processor-vsphere-ova executable is your PATH environmental variable (see http://www.packer.io/docs/extend/plugins.html -> Installing Plugins) ## Usage Add the following, filled out correctly to your post-processors and you should end up with `packer-virutalbox-timestamp-vm` registered on your cluster as a template. -I'm not sure if a release of Packer with SCSI support has been released yet, but you can create a virtualbox with a SCSI drive using Packer for maximum performance on your VMWare setup. - 1. It uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder 1. It marks the cloned virtual machine as a template. @@ -54,15 +41,22 @@ This is the statement you need to add to your packer json file: "username":"my_username", "password":"my_password", "datastore": "datastore_name", - "vm_network":"vmware_network_name" } ] ``` -You also will need ```"format": "ova"``` in your virtualbox-iso builder for this to function. +You also will need ```"format": "ova"``` in your virtualbox-iso builder isection of your packer template, this is not required if using VMware builders. (see: http://www.packer.io/docs/other/core-configuration.html -> Core Configuration) NOTE: This will produce the default behavior described above, you can avoid steps 3-6 if you remove the Floppy, Optical Drive, and Ethernet devices prior to upload. See below for how to do this. +### Specifying an alternate folder to hold the Template + +Add ```"vm_folder":"folder_name"``` to the post-processor config in your packer template. 'folder_name' is realative to the Datacenter name. Default: "Templates" + +### Specifying a specific virtual network to connect to + +Add ```"vm_network":"vmware_network_name"``` to the post-processor config in your packer template. 'vmware_network_name' Default: "VM Network" + ### Specifying a Virtual Hardware Version Before Uploading to Vsphere Add ```"virtual_hardware_version": "n"``` to the post-processor config in your packer template. Where 'n' is the desired version. Default: 10 From 0ab2dd59f2e97907fdd0b23c922dd73c4a8e1e19 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Tue, 25 Aug 2015 09:07:43 -0500 Subject: [PATCH 09/24] added ability to control ova and vsphere template actions in packer json --- README.md | 62 ++++++++++++++++++++++++++++++++++++++--------- post-processor.go | 58 +++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 3c39463..18963cf 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,17 @@ Pull the repository and compile the code with ```go build``` Make sure that the directory which contains the packer-post-processor-vsphere-ova executable is your PATH environmental variable (see http://www.packer.io/docs/extend/plugins.html -> Installing Plugins) ## Usage -Add the following, filled out correctly to your post-processors and you should end up with `packer-virutalbox-timestamp-vm` registered on your cluster as a template. -1. It uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder +NOTE: For Virtualbox builders only, you also will need ```"format": "ova"``` in your virtualbox-iso builder section of your packer template. + +### Make a vSphere Template and an Local OVA file +Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster and an ova file in ./ova/[builder_type]. + +1. It uploads and registers the virtual machine using 'ovftool' in the 'Templates' folder 1. It marks the cloned virtual machine as a template. +1. Export an OVA file in ./ova/[builder_type]. -This is the statement you need to add to your packer json file: +Add to your packer json file: ``` "post-processors": [ @@ -41,13 +46,51 @@ This is the statement you need to add to your packer json file: "username":"my_username", "password":"my_password", "datastore": "datastore_name", + "export_ova": true + } +] +``` + +### Make a vSphere Template +Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. + +1. It uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder +1. It marks the cloned virtual machine as a template. + +Add to your packer json file: + +``` +"post-processors": [ + { + "type": "vsphere-ova", + "host":"vcenter_host", + "datacenter":"datacenter_name", + "cluster":"cluster", + "username":"my_username", + "password":"my_password", + "datastore": "datastore_name" } ] ``` -You also will need ```"format": "ova"``` in your virtualbox-iso builder isection of your packer template, this is not required if using VMware builders. (see: http://www.packer.io/docs/other/core-configuration.html -> Core Configuration) +### Make a Local OVA File +Add the following, filled out correctly to your post-processors and you should end up with an ova file in ./ova/[builder_type]. + +1. Export an OVA file in ./ova/[builder_type]. -NOTE: This will produce the default behavior described above, you can avoid steps 3-6 if you remove the Floppy, Optical Drive, and Ethernet devices prior to upload. See below for how to do this. +Add to your packer json file: + +``` +"post-processors": [ + { + "type": "vsphere-ova", + "import_template": false, + "export_ova": true + } +] +``` + +NOTE: This will produce the default behavior described above. ### Specifying an alternate folder to hold the Template @@ -66,15 +109,10 @@ Add ```"virtual_hardware_version": "n"``` to the post-processor config in your p Add ```"remove_floppy": "true"``` to the post-processor config in your packer template. ### Removing the Ethernet0 Interface Before Uploading to Vsphere +NOTE: Do not use with 'vm_network'. -Add ```"remove_ethernet": "true"``` to the post-processor config in your packer template. +Add ```"remove_ethernet": "true"``` to the post-processor config in your packer template. ### Removing the Optical Drive Before Uploading to Vsphere Add ```"remove_optical_drive": "true"``` to the post-processor config in your packer template. - -### Avoiding Post-Processing Steps 3-6 -Add ```"remove_floppy": "true", "remove_ethernet": "true", "remove_optical_drive": "true"``` to the post-processor config in your packer template. - -NOTE: This makes the ```"vm_network": "vmware_network_name"``` parameter optional. - diff --git a/post-processor.go b/post-processor.go index 517374f..f51eb27 100644 --- a/post-processor.go +++ b/post-processor.go @@ -42,6 +42,8 @@ type Config struct { VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` DiskMode string `mapstructure:"disk_mode"` Insecure string `mapstructure:"insecure"` + ImportTemplate bool `mapstructure:"import_template"` + ExportOVA bool `mapstructure:"export_ova"` ctx interpolate.Context } @@ -62,6 +64,14 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // Defaults + if p.config.ImportTemplate != false { + p.config.ImportTemplate = true + } + + if p.config.ExportOVA != true { + p.config.ExportOVA = false + } + if p.config.DiskMode == "" { p.config.DiskMode = "thick" } @@ -97,19 +107,21 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // First define all our templatable parameters that are _required_ - templates := map[string]*string{ - "datacenter": &p.config.Datacenter, - "cluster": &p.config.Cluster, - "host": &p.config.Host, - "password": &p.config.Password, - "username": &p.config.Username, - "datastore": &p.config.Datastore, - } - - for key, ptr := range templates { - if *ptr == "" { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("%s must be set", key)) + if p.config.ImportTemplate == true { + templates := map[string]*string{ + "datacenter": &p.config.Datacenter, + "cluster": &p.config.Cluster, + "host": &p.config.Host, + "password": &p.config.Password, + "username": &p.config.Username, + "datastore": &p.config.Datastore, + } + + for key, ptr := range templates { + if *ptr == "" { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("%s must be set", key)) + } } } @@ -266,19 +278,21 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") } - if err := doVmxImport(ui, p.config, vmx) ; err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } + if p.config.ImportTemplate { + if err := doVmxImport(ui, p.config, vmx) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } - if err := setAsTemplate(ui, p.config, vmx) ; err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } + if err := setAsTemplate(ui, p.config, vmx) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } - ui.Message("Uploaded and registered to VMware as a template") + ui.Message("Uploaded and registered to VMware as a template") + } - if artifact.BuilderId() == "mitchellh.vmware" { + if p.config.ExportOVA { // ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) - ova_dir := "ova/vmware" + ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) // Convert vmware builder artifact to ova so we can upload to bits if required. if _, err := os.Stat(ova_dir); os.IsNotExist(err) { From db4f41eda37a8c99d4b070c030fedf282859948c Mon Sep 17 00:00:00 2001 From: Dax Games Date: Tue, 25 Aug 2015 09:25:47 -0500 Subject: [PATCH 10/24] readme.md updates --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 18963cf..29dbd23 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Make sure that the directory which contains the packer-post-processor-vsphere-ov NOTE: For Virtualbox builders only, you also will need ```"format": "ova"``` in your virtualbox-iso builder section of your packer template. -### Make a vSphere Template and an Local OVA file +### Make a both vSphere Template and a Local OVA file Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster and an ova file in ./ova/[builder_type]. 1. It uploads and registers the virtual machine using 'ovftool' in the 'Templates' folder @@ -90,8 +90,6 @@ Add to your packer json file: ] ``` -NOTE: This will produce the default behavior described above. - ### Specifying an alternate folder to hold the Template Add ```"vm_folder":"folder_name"``` to the post-processor config in your packer template. 'folder_name' is realative to the Datacenter name. Default: "Templates" From da859887144b8911cb6dea030b15e0f114381e0c Mon Sep 17 00:00:00 2001 From: Dax Games Date: Wed, 26 Aug 2015 15:38:03 -0500 Subject: [PATCH 11/24] README.md updates. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29dbd23..ad6540e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Notes: ## Installation -Pull the repository and compile the code with ```go build``` +Pull the repository and compile the code with ```go build``` then copy it to your Packer install directory. You can also just type ```go install .``` if your Packer binaries are in '$GOPATH/bin'. Make sure that the directory which contains the packer-post-processor-vsphere-ova executable is your PATH environmental variable (see http://www.packer.io/docs/extend/plugins.html -> Installing Plugins) From 35d8b2d973bd055704622cf51b89211d688342f3 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Mon, 31 Aug 2015 15:23:13 -0500 Subject: [PATCH 12/24] fixed virtualbox template upload, prepend tempalate- to templates --- post-processor.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/post-processor.go b/post-processor.go index f51eb27..347ec57 100644 --- a/post-processor.go +++ b/post-processor.go @@ -64,14 +64,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // Defaults - if p.config.ImportTemplate != false { - p.config.ImportTemplate = true - } - - if p.config.ExportOVA != true { - p.config.ExportOVA = false - } - if p.config.DiskMode == "" { p.config.DiskMode = "thick" } @@ -251,9 +243,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, ovftoolOut.String()) ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) - - vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) } + + vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) } if p.config.RemoveEthernet == true { @@ -333,6 +325,8 @@ func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { last := splitString[len(splitString)-1] VMName := strings.TrimSuffix(last, ".vmx") + VMName = fmt.Sprintf("Template-%s", VMName) + ovftool_uri := fmt.Sprintf("vi://%s:%s@%s/%s/host/%s", url.QueryEscape(config.Username), url.QueryEscape(config.Password), From f6bffbe2e17197bb42ceb49b73105fb076538626 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Thu, 3 Sep 2015 12:23:44 -0500 Subject: [PATCH 13/24] output_artifact_type --- README.md | 22 +++++++++++++++------- post-processor.go | 34 +++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ad6540e..58298b0 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ Add the following, filled out correctly to your post-processors and you should e 1. It uploads and registers the virtual machine using 'ovftool' in the 'Templates' folder 1. It marks the cloned virtual machine as a template. -1. Export an OVA file in ./ova/[builder_type]. Add to your packer json file: @@ -45,8 +44,7 @@ Add to your packer json file: "cluster":"cluster", "username":"my_username", "password":"my_password", - "datastore": "datastore_name", - "export_ova": true + "datastore": "datastore_name" } ] ``` @@ -54,8 +52,9 @@ Add to your packer json file: ### Make a vSphere Template Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. -1. It uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder -1. It marks the cloned virtual machine as a template. +1. Uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder +1. Marks the cloned virtual machine as a template. +1. Export an OVA file in ./ova/[builder_type]. Add to your packer json file: @@ -68,7 +67,8 @@ Add to your packer json file: "cluster":"cluster", "username":"my_username", "password":"my_password", - "datastore": "datastore_name" + "datastore": "datastore_name", + "output_artifact_type:" "ova_template" } ] ``` @@ -85,11 +85,19 @@ Add to your packer json file: { "type": "vsphere-ova", "import_template": false, - "export_ova": true + "output_artifact_type:" "ova" } ] ``` +### Specifying an the output artifact type + +Add ```"output_artifact_type":"ova|template|ova_template"``` to the post-processor config in your packer template. 'output_artifact_type' Default: "template" + +* ova Produces an OVA file in ./ova/[builder_type]. +* template Uploads a template to the specified vSphere. +* ova_template Produces an OVA file in ./ova/[builder_type] and uploads a template to the specified vSphere. + ### Specifying an alternate folder to hold the Template Add ```"vm_folder":"folder_name"``` to the post-processor config in your packer template. 'folder_name' is realative to the Datacenter name. Default: "Templates" diff --git a/post-processor.go b/post-processor.go index 347ec57..1734207 100644 --- a/post-processor.go +++ b/post-processor.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "net/url" "os" + "regexp" "os/exec" "strings" "log" @@ -41,9 +42,8 @@ type Config struct { RemoveOpticalDrive bool `mapstructure:"remove_optical_drive"` VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` DiskMode string `mapstructure:"disk_mode"` + OutputArtifactType string `mapstructure:"output_artifact_type"` Insecure string `mapstructure:"insecure"` - ImportTemplate bool `mapstructure:"import_template"` - ExportOVA bool `mapstructure:"export_ova"` ctx interpolate.Context } @@ -64,6 +64,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // Defaults + if p.config.OutputArtifactType == "" { + p.config.DiskMode = "template" + } + if p.config.DiskMode == "" { p.config.DiskMode = "thick" } @@ -99,8 +103,9 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // First define all our templatable parameters that are _required_ - if p.config.ImportTemplate == true { - templates := map[string]*string{ + matched, err := regexp.MatchString("template",p.config.OutputArtifactType) + if matched && err == nil { + templates := map[string]*string { "datacenter": &p.config.Datacenter, "cluster": &p.config.Cluster, "host": &p.config.Host, @@ -270,7 +275,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") } - if p.config.ImportTemplate { + matched, err := regexp.MatchString("template",p.config.OutputArtifactType) + ui.Message(fmt.Sprintf("Output Template: %t", matched)) + if matched && err == nil { if err := doVmxImport(ui, p.config, vmx) ; err != nil { return nil, false, fmt.Errorf("Failed: %s", err) } @@ -282,7 +289,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message("Uploaded and registered to VMware as a template") } - if p.config.ExportOVA { + matched, err = regexp.MatchString("ova",p.config.OutputArtifactType) + ui.Message(fmt.Sprintf("Output OVA: %t", matched)) + if matched && err == nil { // ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) @@ -388,20 +397,23 @@ func setAsTemplate(ui packer.Ui, config Config, vmx string ) (err error) { splitString := strings.Split(vmx, "/") last := splitString[len(splitString)-1] - vmName := strings.TrimSuffix(last, ".vmx") + + VMName := strings.TrimSuffix(last, ".vmx") + VMName = fmt.Sprintf("Template-%s", VMName) + if config.VMFolder != "" { - vmName = fmt.Sprintf("%s/%s", config.VMFolder, vmName) + VMName = fmt.Sprintf("%s/%s", config.VMFolder, VMName) } - vm, err := finder.VirtualMachine(context.TODO(), vmName) + vm, err := finder.VirtualMachine(context.TODO(), VMName) - ui.Message(fmt.Sprintf("Marking as template %s", vmName)) + ui.Message(fmt.Sprintf("Marking as template %s", VMName)) err = vm.MarkAsTemplate(context.TODO()) if err != nil { return err } - ui.Message(fmt.Sprintf("%s is now a template", vmName)) + ui.Message(fmt.Sprintf("%s is now a template", VMName)) return nil } From 2d18ce2566a0b0f1bcea3689d878ccd326a1246f Mon Sep 17 00:00:00 2001 From: Dax Games Date: Thu, 3 Sep 2015 13:03:10 -0500 Subject: [PATCH 14/24] fixed output_artifact_type --- README.md | 22 +++++++++++++++------- post-processor.go | 34 +++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ad6540e..58298b0 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ Add the following, filled out correctly to your post-processors and you should e 1. It uploads and registers the virtual machine using 'ovftool' in the 'Templates' folder 1. It marks the cloned virtual machine as a template. -1. Export an OVA file in ./ova/[builder_type]. Add to your packer json file: @@ -45,8 +44,7 @@ Add to your packer json file: "cluster":"cluster", "username":"my_username", "password":"my_password", - "datastore": "datastore_name", - "export_ova": true + "datastore": "datastore_name" } ] ``` @@ -54,8 +52,9 @@ Add to your packer json file: ### Make a vSphere Template Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. -1. It uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder -1. It marks the cloned virtual machine as a template. +1. Uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder +1. Marks the cloned virtual machine as a template. +1. Export an OVA file in ./ova/[builder_type]. Add to your packer json file: @@ -68,7 +67,8 @@ Add to your packer json file: "cluster":"cluster", "username":"my_username", "password":"my_password", - "datastore": "datastore_name" + "datastore": "datastore_name", + "output_artifact_type:" "ova_template" } ] ``` @@ -85,11 +85,19 @@ Add to your packer json file: { "type": "vsphere-ova", "import_template": false, - "export_ova": true + "output_artifact_type:" "ova" } ] ``` +### Specifying an the output artifact type + +Add ```"output_artifact_type":"ova|template|ova_template"``` to the post-processor config in your packer template. 'output_artifact_type' Default: "template" + +* ova Produces an OVA file in ./ova/[builder_type]. +* template Uploads a template to the specified vSphere. +* ova_template Produces an OVA file in ./ova/[builder_type] and uploads a template to the specified vSphere. + ### Specifying an alternate folder to hold the Template Add ```"vm_folder":"folder_name"``` to the post-processor config in your packer template. 'folder_name' is realative to the Datacenter name. Default: "Templates" diff --git a/post-processor.go b/post-processor.go index 347ec57..06dadf8 100644 --- a/post-processor.go +++ b/post-processor.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "net/url" "os" + "regexp" "os/exec" "strings" "log" @@ -41,9 +42,8 @@ type Config struct { RemoveOpticalDrive bool `mapstructure:"remove_optical_drive"` VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` DiskMode string `mapstructure:"disk_mode"` + OutputArtifactType string `mapstructure:"output_artifact_type"` Insecure string `mapstructure:"insecure"` - ImportTemplate bool `mapstructure:"import_template"` - ExportOVA bool `mapstructure:"export_ova"` ctx interpolate.Context } @@ -64,6 +64,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // Defaults + if p.config.OutputArtifactType == "" { + p.config.DiskMode = "template" + } + if p.config.DiskMode == "" { p.config.DiskMode = "thick" } @@ -99,8 +103,9 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } // First define all our templatable parameters that are _required_ - if p.config.ImportTemplate == true { - templates := map[string]*string{ + matched, err := regexp.MatchString("template",p.config.OutputArtifactType) + if matched && err == nil { + templates := map[string]*string { "datacenter": &p.config.Datacenter, "cluster": &p.config.Cluster, "host": &p.config.Host, @@ -270,7 +275,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") } - if p.config.ImportTemplate { + matched, err := regexp.MatchString("template",p.config.OutputArtifactType) + ui.Message(fmt.Sprintf("Output template: %t", matched)) + if matched && err == nil { if err := doVmxImport(ui, p.config, vmx) ; err != nil { return nil, false, fmt.Errorf("Failed: %s", err) } @@ -282,7 +289,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message("Uploaded and registered to VMware as a template") } - if p.config.ExportOVA { + matched, err = regexp.MatchString("ova",p.config.OutputArtifactType) + ui.Message(fmt.Sprintf("Output ova: %t", matched)) + if matched && err == nil { // ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) @@ -388,20 +397,23 @@ func setAsTemplate(ui packer.Ui, config Config, vmx string ) (err error) { splitString := strings.Split(vmx, "/") last := splitString[len(splitString)-1] - vmName := strings.TrimSuffix(last, ".vmx") + + VMName := strings.TrimSuffix(last, ".vmx") + VMName = fmt.Sprintf("Template-%s", VMName) + if config.VMFolder != "" { - vmName = fmt.Sprintf("%s/%s", config.VMFolder, vmName) + VMName = fmt.Sprintf("%s/%s", config.VMFolder, VMName) } - vm, err := finder.VirtualMachine(context.TODO(), vmName) + vm, err := finder.VirtualMachine(context.TODO(), VMName) - ui.Message(fmt.Sprintf("Marking as template %s", vmName)) + ui.Message(fmt.Sprintf("Marking as template %s", VMName)) err = vm.MarkAsTemplate(context.TODO()) if err != nil { return err } - ui.Message(fmt.Sprintf("%s is now a template", vmName)) + ui.Message(fmt.Sprintf("%s is now a template", VMName)) return nil } From 917bd464b3401a66e90b622e9200c0ea7693e984 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Thu, 3 Sep 2015 13:11:05 -0500 Subject: [PATCH 15/24] readme.md updates --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 58298b0..457f372 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Make sure that the directory which contains the packer-post-processor-vsphere-ov NOTE: For Virtualbox builders only, you also will need ```"format": "ova"``` in your virtualbox-iso builder section of your packer template. -### Make a both vSphere Template and a Local OVA file +### Make a vSphere Template - The default behavior. Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster and an ova file in ./ova/[builder_type]. 1. It uploads and registers the virtual machine using 'ovftool' in the 'Templates' folder @@ -49,11 +49,9 @@ Add to your packer json file: ] ``` -### Make a vSphere Template -Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. +### Make a Local OVA File +Add the following, filled out correctly to your post-processors and you should end up with an ova file in ./ova/[builder_type]. -1. Uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder -1. Marks the cloned virtual machine as a template. 1. Export an OVA file in ./ova/[builder_type]. Add to your packer json file: @@ -62,20 +60,17 @@ Add to your packer json file: "post-processors": [ { "type": "vsphere-ova", - "host":"vcenter_host", - "datacenter":"datacenter_name", - "cluster":"cluster", - "username":"my_username", - "password":"my_password", - "datastore": "datastore_name", - "output_artifact_type:" "ova_template" + "import_template": false, + "output_artifact_type:" "ova" } ] ``` -### Make a Local OVA File -Add the following, filled out correctly to your post-processors and you should end up with an ova file in ./ova/[builder_type]. +### Make a both vSphere Template and a Local OVA file +Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. +1. Uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder +1. Marks the cloned virtual machine as a template. 1. Export an OVA file in ./ova/[builder_type]. Add to your packer json file: @@ -84,8 +79,13 @@ Add to your packer json file: "post-processors": [ { "type": "vsphere-ova", - "import_template": false, - "output_artifact_type:" "ova" + "host":"vcenter_host", + "datacenter":"datacenter_name", + "cluster":"cluster", + "username":"my_username", + "password":"my_password", + "datastore": "datastore_name", + "output_artifact_type:" "ova_template" } ] ``` From 2fd0a9051341ffa22b89d9a092359eb0e25bad99 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Thu, 3 Sep 2015 13:12:56 -0500 Subject: [PATCH 16/24] readme.md updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 457f372..8e38789 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # packer-post-processor-vsphere-ova -This post-processor will upload a VMDK and vmware template to a datastore through VSphere 5.5 +This post-processor will upload vmware template to a datastore through VSphere 5.5 and/or optionally create an ova file locally. ## Prerequisites From 69127a3fce267b804b1455228ba4033ebfdb83c8 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Fri, 4 Sep 2015 11:52:53 -0500 Subject: [PATCH 17/24] readme.md updates --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8e38789..86b2e17 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ NOTE: For Virtualbox builders only, you also will need ```"format": "ova"``` in Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster and an ova file in ./ova/[builder_type]. 1. It uploads and registers the virtual machine using 'ovftool' in the 'Templates' folder -1. It marks the cloned virtual machine as a template. +1. It marks the uploaded virtual machine as a template. Add to your packer json file: @@ -70,7 +70,7 @@ Add to your packer json file: Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. 1. Uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder -1. Marks the cloned virtual machine as a template. +1. Marks the uploaded virtual machine as a template. 1. Export an OVA file in ./ova/[builder_type]. Add to your packer json file: @@ -90,7 +90,7 @@ Add to your packer json file: ] ``` -### Specifying an the output artifact type +### Specifying the output artifact type Add ```"output_artifact_type":"ova|template|ova_template"``` to the post-processor config in your packer template. 'output_artifact_type' Default: "template" From 68553338ed5274ae0b2948a4dd8c4c2c03ffd52e Mon Sep 17 00:00:00 2001 From: Dax Games Date: Fri, 4 Sep 2015 12:00:44 -0500 Subject: [PATCH 18/24] readme.md updates --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 86b2e17..2555a1a 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,6 @@ Add to your packer json file: "post-processors": [ { "type": "vsphere-ova", - "import_template": false, "output_artifact_type:" "ova" } ] From 488558bd7b8d0a60fddb3da14d8e4fa1742a1e10 Mon Sep 17 00:00:00 2001 From: "Dax T. Games" Date: Mon, 4 Jan 2016 18:32:28 -0600 Subject: [PATCH 19/24] fix tabs --- post-processor.go | 744 +++++++++++++++++++++++----------------------- 1 file changed, 372 insertions(+), 372 deletions(-) diff --git a/post-processor.go b/post-processor.go index 1734207..a941ccb 100644 --- a/post-processor.go +++ b/post-processor.go @@ -1,419 +1,419 @@ package main import ( - "bytes" - "fmt" - "github.com/mitchellh/packer/common" - "github.com/mitchellh/packer/packer" - "github.com/vmware/govmomi" - "github.com/vmware/govmomi/find" - "golang.org/x/net/context" - "io/ioutil" - "net/url" - "os" - "regexp" - "os/exec" - "strings" - "log" - "github.com/mitchellh/packer/helper/config" - "github.com/mitchellh/packer/template/interpolate" - vmwarecommon "github.com/mitchellh/packer/builder/vmware/common" + "bytes" + "fmt" + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/packer" + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/find" + "golang.org/x/net/context" + "io/ioutil" + "net/url" + "os" + "regexp" + "os/exec" + "strings" + "log" + "github.com/mitchellh/packer/helper/config" + "github.com/mitchellh/packer/template/interpolate" + vmwarecommon "github.com/mitchellh/packer/builder/vmware/common" ) var builtins = map[string]string{ - "mitchellh.virtualbox": "virtualbox", - "mitchellh.vmware": "vmware", + "mitchellh.virtualbox": "virtualbox", + "mitchellh.vmware": "vmware", } type Config struct { - common.PackerConfig `mapstructure:",squash"` - - Datacenter string `mapstructure:"datacenter"` - Datastore string `mapstructure:"datastore"` - Host string `mapstructure:"host"` - Cluster string `mapstructure:"cluster"` - ResourcePool string `mapstructure:"resource_pool"` - Password string `mapstructure:"password"` - Username string `mapstructure:"username"` - VMFolder string `mapstructure:"vm_folder"` - VMNetwork string `mapstructure:"vm_network"` - RemoveEthernet bool `mapstructure:"remove_ethernet"` - RemoveFloppy bool `mapstructure:"remove_floppy"` - RemoveOpticalDrive bool `mapstructure:"remove_optical_drive"` - VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` - DiskMode string `mapstructure:"disk_mode"` - OutputArtifactType string `mapstructure:"output_artifact_type"` - Insecure string `mapstructure:"insecure"` - ctx interpolate.Context + common.PackerConfig `mapstructure:",squash"` + + Datacenter string `mapstructure:"datacenter"` + Datastore string `mapstructure:"datastore"` + Host string `mapstructure:"host"` + Cluster string `mapstructure:"cluster"` + ResourcePool string `mapstructure:"resource_pool"` + Password string `mapstructure:"password"` + Username string `mapstructure:"username"` + VMFolder string `mapstructure:"vm_folder"` + VMNetwork string `mapstructure:"vm_network"` + RemoveEthernet bool `mapstructure:"remove_ethernet"` + RemoveFloppy bool `mapstructure:"remove_floppy"` + RemoveOpticalDrive bool `mapstructure:"remove_optical_drive"` + VirtualHardwareVer string `mapstructure:"virtual_hardware_version"` + DiskMode string `mapstructure:"disk_mode"` + OutputArtifactType string `mapstructure:"output_artifact_type"` + Insecure string `mapstructure:"insecure"` + ctx interpolate.Context } type PostProcessor struct { - config Config + config Config } func (p *PostProcessor) Configure(raws ...interface{}) error { - err := config.Decode(&p.config, &config.DecodeOpts{ - Interpolate: true, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{}, - }, - }, raws...) - - if err != nil { - return err - } - - // Defaults - if p.config.OutputArtifactType == "" { - p.config.DiskMode = "template" - } - - if p.config.DiskMode == "" { - p.config.DiskMode = "thick" - } - - if p.config.VirtualHardwareVer == "" { - p.config.VirtualHardwareVer = "10" - } - - if p.config.VMFolder == "" { - p.config.VMFolder = "Templates" - } - - // Accumulate any errors - errs := new(packer.MultiError) - - if _, err := exec.LookPath("ovftool"); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("ovftool not found: %s", err)) - } - - if !(p.config.DiskMode == "thick" || - p.config.DiskMode == "thin" || - p.config.DiskMode == "monolithicSparse" || - p.config.DiskMode == "monolithicFlat" || - p.config.DiskMode == "twoGbMaxExtentSparse" || - p.config.DiskMode == "twoGbMaxExtentFlat" || - p.config.DiskMode == "seSparse" || - p.config.DiskMode == "eagerZeroedThick" || - p.config.DiskMode == "sparse" || - p.config.DiskMode == "flat") { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Invalid disk_mode. Only thick(Default), thin, monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, seSparse, eagerZeroedThick, sparse, and flat are allowed.")) - } - - // First define all our templatable parameters that are _required_ - matched, err := regexp.MatchString("template",p.config.OutputArtifactType) - if matched && err == nil { - templates := map[string]*string { - "datacenter": &p.config.Datacenter, - "cluster": &p.config.Cluster, - "host": &p.config.Host, - "password": &p.config.Password, - "username": &p.config.Username, - "datastore": &p.config.Datastore, - } - - for key, ptr := range templates { - if *ptr == "" { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("%s must be set", key)) - } - } - } - - if len(errs.Errors) > 0 { - return errs - } - - return nil + err := config.Decode(&p.config, &config.DecodeOpts{ + Interpolate: true, + InterpolateFilter: &interpolate.RenderFilter{ + Exclude: []string{}, + }, + }, raws...) + + if err != nil { + return err + } + + // Defaults + if p.config.OutputArtifactType == "" { + p.config.DiskMode = "template" + } + + if p.config.DiskMode == "" { + p.config.DiskMode = "thick" + } + + if p.config.VirtualHardwareVer == "" { + p.config.VirtualHardwareVer = "10" + } + + if p.config.VMFolder == "" { + p.config.VMFolder = "Templates" + } + + // Accumulate any errors + errs := new(packer.MultiError) + + if _, err := exec.LookPath("ovftool"); err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("ovftool not found: %s", err)) + } + + if !(p.config.DiskMode == "thick" || + p.config.DiskMode == "thin" || + p.config.DiskMode == "monolithicSparse" || + p.config.DiskMode == "monolithicFlat" || + p.config.DiskMode == "twoGbMaxExtentSparse" || + p.config.DiskMode == "twoGbMaxExtentFlat" || + p.config.DiskMode == "seSparse" || + p.config.DiskMode == "eagerZeroedThick" || + p.config.DiskMode == "sparse" || + p.config.DiskMode == "flat") { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Invalid disk_mode. Only thick(Default), thin, monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, seSparse, eagerZeroedThick, sparse, and flat are allowed.")) + } + + // First define all our templatable parameters that are _required_ + matched, err := regexp.MatchString("template",p.config.OutputArtifactType) + if matched && err == nil { + templates := map[string]*string { + "datacenter": &p.config.Datacenter, + "cluster": &p.config.Cluster, + "host": &p.config.Host, + "password": &p.config.Password, + "username": &p.config.Username, + "datastore": &p.config.Datastore, + } + + for key, ptr := range templates { + if *ptr == "" { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("%s must be set", key)) + } + } + } + + if len(errs.Errors) > 0 { + return errs + } + + return nil } func (p *PostProcessor) RemoveFloppy(vmx string, ui packer.Ui) error { - ui.Message(fmt.Sprintf("Removing floppy from %s", vmx)) - vmxData, err := vmwarecommon.ReadVMX(vmx) - if err != nil { - return err - } - for k, _ := range vmxData { - if strings.HasPrefix(k, "floppy0.") { - delete(vmxData, k) - } - } - vmxData["floppy0.present"] = "FALSE" - if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { - return err - } - return nil + ui.Message(fmt.Sprintf("Removing floppy from %s", vmx)) + vmxData, err := vmwarecommon.ReadVMX(vmx) + if err != nil { + return err + } + for k, _ := range vmxData { + if strings.HasPrefix(k, "floppy0.") { + delete(vmxData, k) + } + } + vmxData["floppy0.present"] = "FALSE" + if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { + return err + } + return nil } func (p *PostProcessor) RemoveEthernet(vmx string, ui packer.Ui) error { - ui.Message(fmt.Sprintf("Removing ethernet0 interface from %s", vmx)) - vmxData, err := vmwarecommon.ReadVMX(vmx) - if err != nil { - return err - } - - for k, _ := range vmxData { - if strings.HasPrefix(k, "ethernet0.") { - delete(vmxData, k) - } - } - - vmxData["ethernet0.present"] = "FALSE" - if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { - return err - } - - return nil + ui.Message(fmt.Sprintf("Removing ethernet0 interface from %s", vmx)) + vmxData, err := vmwarecommon.ReadVMX(vmx) + if err != nil { + return err + } + + for k, _ := range vmxData { + if strings.HasPrefix(k, "ethernet0.") { + delete(vmxData, k) + } + } + + vmxData["ethernet0.present"] = "FALSE" + if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { + return err + } + + return nil } func (p *PostProcessor) SetVHardwareVersion(vmx string, ui packer.Ui, hwversion string) error { - ui.Message(fmt.Sprintf("Setting the hardware version in the vmx to version '%s'", hwversion)) - - vmxContent, err := ioutil.ReadFile(vmx) - lines := strings.Split(string(vmxContent), "\n") - for i, line := range lines { - if strings.Contains(line, "virtualhw.version") { - lines[i] = fmt.Sprintf("virtualhw.version = \"%s\"", hwversion) - } - } - output := strings.Join(lines, "\n") - err = ioutil.WriteFile(vmx, []byte(output), 0644) - if err != nil { - return err - } - - return nil + ui.Message(fmt.Sprintf("Setting the hardware version in the vmx to version '%s'", hwversion)) + + vmxContent, err := ioutil.ReadFile(vmx) + lines := strings.Split(string(vmxContent), "\n") + for i, line := range lines { + if strings.Contains(line, "virtualhw.version") { + lines[i] = fmt.Sprintf("virtualhw.version = \"%s\"", hwversion) + } + } + output := strings.Join(lines, "\n") + err = ioutil.WriteFile(vmx, []byte(output), 0644) + if err != nil { + return err + } + + return nil } func (p *PostProcessor) RemoveOpticalDrive(vmx string, ui packer.Ui) error { - ui.Message(fmt.Sprintf("Removing optical drive from %s", vmx)) - vmxData, err := vmwarecommon.ReadVMX(vmx) - if err != nil { - return err - } - - for k, _ := range vmxData { - if strings.HasPrefix(k, "ide1:0.file") { - delete(vmxData, k) - } - } - - vmxData["ide1:0.present"] = "FALSE" - - if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { - return err - } - return nil + ui.Message(fmt.Sprintf("Removing optical drive from %s", vmx)) + vmxData, err := vmwarecommon.ReadVMX(vmx) + if err != nil { + return err + } + + for k, _ := range vmxData { + if strings.HasPrefix(k, "ide1:0.file") { + delete(vmxData, k) + } + } + + vmxData["ide1:0.present"] = "FALSE" + + if err := vmwarecommon.WriteVMX(vmx, vmxData); err != nil { + return err + } + return nil } func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - if _, ok := builtins[artifact.BuilderId()]; !ok { - return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) - } - - ova := "" - vmx := "" - for _, path := range artifact.Files() { - if strings.HasSuffix(path, ".ova") { - ova = path - break - } else if strings.HasSuffix(path, ".vmx") { - vmx = path - break - } - } - - if ova == "" && vmx == "" { - return nil, false, fmt.Errorf("ERROR: Neither OVA nor VMX were found!") - } - - if artifact.BuilderId() == "mitchellh.virtualbox" && ova != "" { - // Sweet, we've got an OVA, Now it's time to make that baby something we can work with. - args := []string{ - "--acceptAllEulas", - "--lax", - "--allowAllExtraConfig", - fmt.Sprintf("--extraConfig:ethernet0.networkName=%s", p.config.VMNetwork), - fmt.Sprintf("%s", ova), - fmt.Sprintf("%s", fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova"))), - } - - command := exec.Command("ovftool", args...) - - var ovftoolOut bytes.Buffer - command.Stdout = &ovftoolOut - if err := command.Run(); err != nil { - return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, ovftoolOut.String()) - - ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) - } - - vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) - } - - if p.config.RemoveEthernet == true { - if err := p.RemoveEthernet(vmx, ui); err != nil { - return nil, false, fmt.Errorf("Removing ethernet0 interface from VMX failed!") - } - } - - if p.config.RemoveFloppy == true { - if err := p.RemoveFloppy(vmx, ui); err != nil { - return nil, false, fmt.Errorf("Removing floppy drive from VMX failed!") - } - } - - if p.config.RemoveOpticalDrive == true { - if err := p.RemoveOpticalDrive(vmx, ui); err != nil { - return nil, false, fmt.Errorf("Removing CD/DVD Drive from VMX failed!") - } - } - - if err := p.SetVHardwareVersion(vmx, ui, p.config.VirtualHardwareVer); err != nil { - return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") - } - - matched, err := regexp.MatchString("template",p.config.OutputArtifactType) - ui.Message(fmt.Sprintf("Output Template: %t", matched)) - if matched && err == nil { - if err := doVmxImport(ui, p.config, vmx) ; err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - - if err := setAsTemplate(ui, p.config, vmx) ; err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - - ui.Message("Uploaded and registered to VMware as a template") - } - - matched, err = regexp.MatchString("ova",p.config.OutputArtifactType) - ui.Message(fmt.Sprintf("Output OVA: %t", matched)) - if matched && err == nil { - // ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) - ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) - - // Convert vmware builder artifact to ova so we can upload to bits if required. - if _, err := os.Stat(ova_dir); os.IsNotExist(err) { - os.MkdirAll(ova_dir,0755) - } - - splitString := strings.Split(vmx, "/") - ova = fmt.Sprintf("%s/%s.ova", ova_dir, strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) - - // ova = fmt.Sprintf("%s.ova", strings.TrimSuffix(vmx, ".vmx")) - - args := []string{ - "--acceptAllEulas", - "-tt=OVA", - "--diskMode=thin", - "--compress=9", - fmt.Sprintf("%s", vmx), - fmt.Sprintf("%s", ova), - } - - ui.Message(fmt.Sprintf("Exporting %s to %s", vmx, ova)) - var out bytes.Buffer - command := exec.Command("ovftool", args...) - log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) - command.Stdout = &out - if err := command.Run(); err != nil { - return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) - } - - ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) - } - - return artifact, false, nil + if _, ok := builtins[artifact.BuilderId()]; !ok { + return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) + } + + ova := "" + vmx := "" + for _, path := range artifact.Files() { + if strings.HasSuffix(path, ".ova") { + ova = path + break + } else if strings.HasSuffix(path, ".vmx") { + vmx = path + break + } + } + + if ova == "" && vmx == "" { + return nil, false, fmt.Errorf("ERROR: Neither OVA nor VMX were found!") + } + + if artifact.BuilderId() == "mitchellh.virtualbox" && ova != "" { + // Sweet, we've got an OVA, Now it's time to make that baby something we can work with. + args := []string{ + "--acceptAllEulas", + "--lax", + "--allowAllExtraConfig", + fmt.Sprintf("--extraConfig:ethernet0.networkName=%s", p.config.VMNetwork), + fmt.Sprintf("%s", ova), + fmt.Sprintf("%s", fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova"))), + } + + command := exec.Command("ovftool", args...) + + var ovftoolOut bytes.Buffer + command.Stdout = &ovftoolOut + if err := command.Run(); err != nil { + return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, ovftoolOut.String()) + + ui.Message(fmt.Sprintf("%s", ovftoolOut.String())) + } + + vmx = fmt.Sprintf("%s.vmx", strings.TrimSuffix(ova, ".ova")) + } + + if p.config.RemoveEthernet == true { + if err := p.RemoveEthernet(vmx, ui); err != nil { + return nil, false, fmt.Errorf("Removing ethernet0 interface from VMX failed!") + } + } + + if p.config.RemoveFloppy == true { + if err := p.RemoveFloppy(vmx, ui); err != nil { + return nil, false, fmt.Errorf("Removing floppy drive from VMX failed!") + } + } + + if p.config.RemoveOpticalDrive == true { + if err := p.RemoveOpticalDrive(vmx, ui); err != nil { + return nil, false, fmt.Errorf("Removing CD/DVD Drive from VMX failed!") + } + } + + if err := p.SetVHardwareVersion(vmx, ui, p.config.VirtualHardwareVer); err != nil { + return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") + } + + matched, err := regexp.MatchString("template",p.config.OutputArtifactType) + ui.Message(fmt.Sprintf("Output Template: %t", matched)) + if matched && err == nil { + if err := doVmxImport(ui, p.config, vmx) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } + + if err := setAsTemplate(ui, p.config, vmx) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } + + ui.Message("Uploaded and registered to VMware as a template") + } + + matched, err = regexp.MatchString("ova",p.config.OutputArtifactType) + ui.Message(fmt.Sprintf("Output OVA: %t", matched)) + if matched && err == nil { + // ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) + ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) + + // Convert vmware builder artifact to ova so we can upload to bits if required. + if _, err := os.Stat(ova_dir); os.IsNotExist(err) { + os.MkdirAll(ova_dir,0755) + } + + splitString := strings.Split(vmx, "/") + ova = fmt.Sprintf("%s/%s.ova", ova_dir, strings.TrimSuffix(splitString[len(splitString)-1], ".vmx")) + + // ova = fmt.Sprintf("%s.ova", strings.TrimSuffix(vmx, ".vmx")) + + args := []string{ + "--acceptAllEulas", + "-tt=OVA", + "--diskMode=thin", + "--compress=9", + fmt.Sprintf("%s", vmx), + fmt.Sprintf("%s", ova), + } + + ui.Message(fmt.Sprintf("Exporting %s to %s", vmx, ova)) + var out bytes.Buffer + command := exec.Command("ovftool", args...) + log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) + command.Stdout = &out + if err := command.Run(); err != nil { + return nil, false, fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) + } + + ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) + } + + return artifact, false, nil } func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { - splitString := strings.Split(vmx, "/") - last := splitString[len(splitString)-1] - VMName := strings.TrimSuffix(last, ".vmx") - - VMName = fmt.Sprintf("Template-%s", VMName) - - ovftool_uri := fmt.Sprintf("vi://%s:%s@%s/%s/host/%s", - url.QueryEscape(config.Username), - url.QueryEscape(config.Password), - config.Host, - config.Datacenter, - config.Cluster) - - if config.ResourcePool != "" { - ovftool_uri += "/Resources/" + config.ResourcePool - } - - args := []string{ - fmt.Sprintf("--noSSLVerify=%s", config.Insecure), - "--acceptAllEulas", - fmt.Sprintf("--name=%s", VMName), - fmt.Sprintf("--datastore=%s", config.Datastore), - fmt.Sprintf("--diskMode=%s", config.DiskMode), - fmt.Sprintf("--network=%s", config.VMNetwork), - fmt.Sprintf("--vmFolder=%s", config.VMFolder), - fmt.Sprintf("%s", vmx), - fmt.Sprintf("%s", ovftool_uri), - } - - ui.Message(fmt.Sprintf("Uploading %s to vSphere", vmx)) - var out bytes.Buffer - log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) - cmd := exec.Command("ovftool", args...) - cmd.Stdout = &out - if err := cmd.Run(); err != nil { - return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) - } - - ui.Message(fmt.Sprintf("%s", out.String())) - - return nil + splitString := strings.Split(vmx, "/") + last := splitString[len(splitString)-1] + VMName := strings.TrimSuffix(last, ".vmx") + + VMName = fmt.Sprintf("Template-%s", VMName) + + ovftool_uri := fmt.Sprintf("vi://%s:%s@%s/%s/host/%s", + url.QueryEscape(config.Username), + url.QueryEscape(config.Password), + config.Host, + config.Datacenter, + config.Cluster) + + if config.ResourcePool != "" { + ovftool_uri += "/Resources/" + config.ResourcePool + } + + args := []string{ + fmt.Sprintf("--noSSLVerify=%s", config.Insecure), + "--acceptAllEulas", + fmt.Sprintf("--name=%s", VMName), + fmt.Sprintf("--datastore=%s", config.Datastore), + fmt.Sprintf("--diskMode=%s", config.DiskMode), + fmt.Sprintf("--network=%s", config.VMNetwork), + fmt.Sprintf("--vmFolder=%s", config.VMFolder), + fmt.Sprintf("%s", vmx), + fmt.Sprintf("%s", ovftool_uri), + } + + ui.Message(fmt.Sprintf("Uploading %s to vSphere", vmx)) + var out bytes.Buffer + log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) + cmd := exec.Command("ovftool", args...) + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) + } + + ui.Message(fmt.Sprintf("%s", out.String())) + + return nil } func setAsTemplate(ui packer.Ui, config Config, vmx string ) (err error) { - sdkURL, err := url.Parse(fmt.Sprintf("https://%s:%s@%s/sdk", - url.QueryEscape(config.Username), - url.QueryEscape(config.Password), - config.Host)) - if err != nil { - return err - } + sdkURL, err := url.Parse(fmt.Sprintf("https://%s:%s@%s/sdk", + url.QueryEscape(config.Username), + url.QueryEscape(config.Password), + config.Host)) + if err != nil { + return err + } - client, err := govmomi.NewClient(context.TODO(), sdkURL, true) + client, err := govmomi.NewClient(context.TODO(), sdkURL, true) - if err != nil { - return err - } + if err != nil { + return err + } - finder := find.NewFinder(client.Client, false) - datacenter, err := finder.DefaultDatacenter(context.TODO()) - finder.SetDatacenter(datacenter) - if err != nil { - return err - } + finder := find.NewFinder(client.Client, false) + datacenter, err := finder.DefaultDatacenter(context.TODO()) + finder.SetDatacenter(datacenter) + if err != nil { + return err + } - splitString := strings.Split(vmx, "/") - last := splitString[len(splitString)-1] + splitString := strings.Split(vmx, "/") + last := splitString[len(splitString)-1] - VMName := strings.TrimSuffix(last, ".vmx") - VMName = fmt.Sprintf("Template-%s", VMName) + VMName := strings.TrimSuffix(last, ".vmx") + VMName = fmt.Sprintf("Template-%s", VMName) - if config.VMFolder != "" { - VMName = fmt.Sprintf("%s/%s", config.VMFolder, VMName) - } + if config.VMFolder != "" { + VMName = fmt.Sprintf("%s/%s", config.VMFolder, VMName) + } - vm, err := finder.VirtualMachine(context.TODO(), VMName) + vm, err := finder.VirtualMachine(context.TODO(), VMName) - ui.Message(fmt.Sprintf("Marking as template %s", VMName)) - err = vm.MarkAsTemplate(context.TODO()) + ui.Message(fmt.Sprintf("Marking as template %s", VMName)) + err = vm.MarkAsTemplate(context.TODO()) - if err != nil { - return err - } - ui.Message(fmt.Sprintf("%s is now a template", VMName)) + if err != nil { + return err + } + ui.Message(fmt.Sprintf("%s is now a template", VMName)) - return nil + return nil } From 363892075d811412330a763bbbb5810804a6c24a Mon Sep 17 00:00:00 2001 From: Dax T Games Date: Sat, 5 Mar 2016 10:25:13 -0600 Subject: [PATCH 20/24] Update README.md Fixed output type stuff in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2555a1a..7a53f9d 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Make sure that the directory which contains the packer-post-processor-vsphere-ov NOTE: For Virtualbox builders only, you also will need ```"format": "ova"``` in your virtualbox-iso builder section of your packer template. ### Make a vSphere Template - The default behavior. -Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster and an ova file in ./ova/[builder_type]. +Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. 1. It uploads and registers the virtual machine using 'ovftool' in the 'Templates' folder 1. It marks the uploaded virtual machine as a template. @@ -66,7 +66,7 @@ Add to your packer json file: ``` ### Make a both vSphere Template and a Local OVA file -Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster. +Add the following, filled out correctly to your post-processors and you should end up with a new template registered on your cluster and an ova file in ./ova/[builder_type]. 1. Uploads and registers the virtual maching using 'ovftool' in the 'Templates' folder 1. Marks the uploaded virtual machine as a template. From 30915fdaa442b757fedcd0acfd52520b60ebe787 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Mon, 20 Jun 2016 13:39:51 -0500 Subject: [PATCH 21/24] Fixed dual setting of p.config.DiskMode defaults that caused build failure on invalid disk_mode --- post-processor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/post-processor.go b/post-processor.go index 1734207..4acb6ee 100644 --- a/post-processor.go +++ b/post-processor.go @@ -65,7 +65,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { // Defaults if p.config.OutputArtifactType == "" { - p.config.DiskMode = "template" + p.config.OutputArtifactType = "template" } if p.config.DiskMode == "" { @@ -99,7 +99,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.DiskMode == "sparse" || p.config.DiskMode == "flat") { errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Invalid disk_mode. Only thick(Default), thin, monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, seSparse, eagerZeroedThick, sparse, and flat are allowed.")) + errs, fmt.Errorf("Invalid disk_mode '" + p.config.DiskMode + "'. Only thick(Default), thin, monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, seSparse, eagerZeroedThick, sparse, and flat are allowed.")) } // First define all our templatable parameters that are _required_ From e9bb6e3d327488ee208f990792688604245aa0e8 Mon Sep 17 00:00:00 2001 From: Dax Games Date: Mon, 20 Jun 2016 15:02:09 -0500 Subject: [PATCH 22/24] made ui message more verbose --- post-processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor.go b/post-processor.go index 4acb6ee..9ff3d3b 100644 --- a/post-processor.go +++ b/post-processor.go @@ -359,7 +359,7 @@ func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { fmt.Sprintf("%s", ovftool_uri), } - ui.Message(fmt.Sprintf("Uploading %s to vSphere", vmx)) + ui.Message(fmt.Sprintf("Uploading %s to vSphere with command: %s", vmx, strings.Join(args, " "))) var out bytes.Buffer log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) cmd := exec.Command("ovftool", args...) From f55c232fe2b62ca30481b125eda5216219f3622d Mon Sep 17 00:00:00 2001 From: Dax Games Date: Mon, 20 Jun 2016 15:57:03 -0500 Subject: [PATCH 23/24] made ui message more verbose --- post-processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor.go b/post-processor.go index 3903678..473911b 100644 --- a/post-processor.go +++ b/post-processor.go @@ -359,7 +359,7 @@ func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { fmt.Sprintf("%s", ovftool_uri), } - ui.Message(fmt.Sprintf("Uploading %s to vSphere with command: %s", vmx, strings.Join(args, " "))) + ui.Message(fmt.Sprintf("Uploading %s to vSphere with command: ovftool %s", vmx, strings.Join(args, " "))) var out bytes.Buffer log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " ")) cmd := exec.Command("ovftool", args...) From 6a467fe14cb47167d203dafbb754c407c0677d7d Mon Sep 17 00:00:00 2001 From: Dax Games Date: Tue, 21 Jun 2016 14:30:39 -0500 Subject: [PATCH 24/24] moved ova creation ahead of template upload, not forcing prepend of 'Template-' --- post-processor.go | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/post-processor.go b/post-processor.go index 473911b..b3da7bb 100644 --- a/post-processor.go +++ b/post-processor.go @@ -275,21 +275,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Setting the Virtual Hardware Version in VMX failed!") } - matched, err := regexp.MatchString("template",p.config.OutputArtifactType) - ui.Message(fmt.Sprintf("Output Template: %t", matched)) - if matched && err == nil { - if err := doVmxImport(ui, p.config, vmx) ; err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - - if err := setAsTemplate(ui, p.config, vmx) ; err != nil { - return nil, false, fmt.Errorf("Failed: %s", err) - } - - ui.Message("Uploaded and registered to VMware as a template") - } - - matched, err = regexp.MatchString("ova",p.config.OutputArtifactType) + matched, err := regexp.MatchString("ova",p.config.OutputArtifactType) ui.Message(fmt.Sprintf("Output OVA: %t", matched)) if matched && err == nil { // ova_dir := fmt.Sprintf("ova/%s", builtins[artifact.BuilderId()]) @@ -326,6 +312,20 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message(fmt.Sprintf("Conversion of VMX to OVA: %s", out.String())) } + matched, err = regexp.MatchString("template",p.config.OutputArtifactType) + ui.Message(fmt.Sprintf("Output Template: %t", matched)) + if matched && err == nil { + if err := doVmxImport(ui, p.config, vmx) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } + + if err := setAsTemplate(ui, p.config, vmx) ; err != nil { + return nil, false, fmt.Errorf("Failed: %s", err) + } + + ui.Message("Uploaded and registered to VMware as a template") + } + return artifact, false, nil } @@ -334,7 +334,7 @@ func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { last := splitString[len(splitString)-1] VMName := strings.TrimSuffix(last, ".vmx") - VMName = fmt.Sprintf("Template-%s", VMName) + // VMName = fmt.Sprintf("Template-%s", VMName) ovftool_uri := fmt.Sprintf("vi://%s:%s@%s/%s/host/%s", url.QueryEscape(config.Username), @@ -365,10 +365,10 @@ func doVmxImport(ui packer.Ui, config Config, vmx string) (err error) { cmd := exec.Command("ovftool", args...) cmd.Stdout = &out if err := cmd.Run(); err != nil { - return fmt.Errorf("Failed: %s\nStdout: %s", err, out.String()) + return fmt.Errorf("VMX Import Failed: %s\nStdout: %s", err, out.String()) } - ui.Message(fmt.Sprintf("%s", out.String())) + ui.Message(fmt.Sprintf("VMX Import %s", out.String())) return nil } @@ -378,7 +378,8 @@ func setAsTemplate(ui packer.Ui, config Config, vmx string ) (err error) { url.QueryEscape(config.Username), url.QueryEscape(config.Password), config.Host)) - if err != nil { + + if err != nil { return err } @@ -399,7 +400,7 @@ func setAsTemplate(ui packer.Ui, config Config, vmx string ) (err error) { last := splitString[len(splitString)-1] VMName := strings.TrimSuffix(last, ".vmx") - VMName = fmt.Sprintf("Template-%s", VMName) + // VMName = fmt.Sprintf("Template-%s", VMName) if config.VMFolder != "" { VMName = fmt.Sprintf("%s/%s", config.VMFolder, VMName)