Add support for dynamically create floppy images

This commit is contained in:
Andrei Tonkikh 2018-02-01 14:47:09 +03:00
parent 9139a6029b
commit cf198539e8
7 changed files with 176 additions and 67 deletions

View File

@ -4,32 +4,23 @@ import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
)
type Datastore struct {
ds *object.Datastore
ds *object.Datastore
driver *Driver
}
func (d *Driver) NewDatastore(ref *types.ManagedObjectReference) *Datastore {
return &Datastore{
ds: object.NewDatastore(d.client.Client, *ref),
ds: object.NewDatastore(d.client.Client, *ref),
driver: d,
}
}
// If name is an empty string, returns the default datastore (is exists)
func (d *Driver) FindDatastore(name string) (*Datastore, error) {
ds, err := d.finder.Datastore(d.ctx, name)
if err != nil {
return nil, err
}
return &Datastore{
ds: ds,
driver: d,
}, nil
}
func (d *Driver) FindDatastoreOrDefault(name string) (*Datastore, error) {
ds, err := d.finder.DatastoreOrDefault(d.ctx, name)
if err != nil {
return nil, err
@ -67,3 +58,28 @@ func (ds *Datastore) Name() string {
func (ds *Datastore) ResolvePath(path string) string {
return ds.ds.Path(path)
}
func (ds *Datastore) UploadFile(src, dst string) error {
p := soap.DefaultUpload
return ds.ds.UploadFile(ds.driver.ctx, src, dst, &p)
}
func (ds *Datastore) Delete(path string) error {
dc, err := ds.driver.finder.Datacenter(ds.driver.ctx, ds.ds.DatacenterPath)
if err != nil {
return err
}
fm := ds.ds.NewFileManager(dc, false)
return fm.Delete(ds.driver.ctx, path)
}
// Cuts out the datastore prefix
// Example: "[datastore1] file.ext" --> "file.ext"
func RemoveDatastorePrefix(path string) string {
res := object.DatastorePath{}
if hadPrefix := res.FromString(path); hadPrefix {
return res.Path
} else {
return path
}
}

View File

@ -7,6 +7,7 @@ import (
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
"time"
"strings"
)
type VirtualMachine struct {
@ -48,7 +49,6 @@ type CreateConfig struct {
Datastore string
GuestOS string // example: otherGuest
Network string // "" for default network
Overwrite bool
NetworkCard string // example: vmxnet3
}
@ -88,19 +88,11 @@ func (d *Driver) CreateVM(config *CreateConfig) (*VirtualMachine, error) {
return nil, err
}
datastore, err := d.FindDatastoreOrDefault(config.Datastore)
datastore, err := d.FindDatastore(config.Datastore)
if err != nil {
return nil, err
}
if !config.Overwrite {
vmxPath := fmt.Sprintf("%s/%s.vmx", config.Name, config.Name)
if datastore.FileExists(vmxPath) {
dsPath := datastore.ResolvePath(vmxPath)
return nil, fmt.Errorf("File '%s' already exists", dsPath)
}
}
devices := object.VirtualDeviceList{}
devices, err = addIDE(devices)
@ -178,7 +170,7 @@ func (template *VirtualMachine) Clone(config *CloneConfig) (*VirtualMachine, err
poolRef := pool.pool.Reference()
relocateSpec.Pool = &poolRef
datastore, err := template.driver.FindDatastoreOrDefault(config.Datastore)
datastore, err := template.driver.FindDatastore(config.Datastore)
if err != nil {
return nil, err
}
@ -349,6 +341,21 @@ func (vm *VirtualMachine) ConvertToTemplate() error {
return vm.vm.MarkAsTemplate(vm.driver.ctx)
}
func (vm *VirtualMachine) GetDir() (string, error) {
vmInfo, err := vm.Info("name", "layoutEx.file")
if err != nil {
return "", err
}
vmxName := fmt.Sprintf("/%s.vmx", vmInfo.Name)
for _, file := range vmInfo.LayoutEx.File {
if strings.HasSuffix(file.Name, vmxName) {
return file.Name[:len(file.Name)-len(vmxName)], nil
}
}
return "", fmt.Errorf("cannot find '%s'", vmxName)
}
func (config HardwareConfig) toConfigSpec() types.VirtualMachineConfigSpec {
var confSpec types.VirtualMachineConfigSpec
confSpec.NumCPUs = config.CPUs
@ -377,7 +384,7 @@ func (config CreateConfig) toConfigSpec() types.VirtualMachineConfigSpec {
return confSpec
}
func addDisk(d *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
func addDisk(_ *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
device, err := devices.CreateSCSIController(config.DiskControllerType)
if err != nil {
return nil, err

View File

@ -30,20 +30,25 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("hook", hook)
state.Put("ui", ui)
steps := []multistep.Step{}
var steps []multistep.Step
steps = append(steps,
&common.StepConnect{
Config: &b.config.ConnectConfig,
},
&StepCreateVM{
config: &b.config.CreateConfig,
Config: &b.config.CreateConfig,
},
&StepAddCDRom{
config: &b.config.CDRomConfig,
Config: &b.config.CDRomConfig,
},
&packerCommon.StepCreateFloppy{
Files: b.config.FloppyFiles,
Directories: b.config.FloppyDirectories,
},
&StepAddFloppy{
config: &b.config.FloppyConfig,
Config: &b.config.FloppyConfig,
Datastore: b.config.Datastore,
},
)

View File

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/vim25/types"
"fmt"
"io/ioutil"
)
func TestISOBuilderAcc_default(t *testing.T) {
@ -234,3 +235,38 @@ func checkNetworkCard(t *testing.T) builderT.TestCheckFunc {
return nil
}
}
func TestISOBuilderAcc_createFloppy(t *testing.T) {
tmpFile, err := ioutil.TempFile("", "packer-vsphere-iso-test")
if err != nil {
t.Fatalf("Error creating temp file ")
}
content := "Hello, World!"
fmt.Fprint(tmpFile, content)
tmpFile.Close()
builderT.Test(t, builderT.TestCase{
Builder: &Builder{},
Template: createFloppyConfig(tmpFile.Name()),
Check: checkCreateFloppy(t, content),
})
}
func createFloppyConfig(filePath string) string {
config := defaultConfig()
config["floppy_files"] = []string{filePath}
return commonT.RenderConfig(config)
}
func checkCreateFloppy(t *testing.T, content string) builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error {
d := commonT.TestConn(t)
vm := commonT.GetVM(t, d, artifacts)
_, err := vm.GetDir()
if err != nil {
t.Fatalf(err.Error())
}
return nil
}
}

View File

@ -16,7 +16,7 @@ func (c *CDRomConfig) Prepare() []error {
}
type StepAddCDRom struct {
config *CDRomConfig
Config *CDRomConfig
}
func (s *StepAddCDRom) Run(state multistep.StateBag) multistep.StepAction {
@ -25,7 +25,7 @@ func (s *StepAddCDRom) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Adding CDRoms...")
vm := state.Get("vm").(*driver.VirtualMachine)
for _, path := range s.config.ISOPaths {
for _, path := range s.Config.ISOPaths {
if err := vm.AddCdrom(path); err != nil {
state.Put("error", fmt.Errorf("error adding a cdrom: %v", err))
return multistep.ActionHalt

View File

@ -26,38 +26,83 @@ func (c *FloppyConfig) Prepare() []error {
}
type StepAddFloppy struct {
config *FloppyConfig
Config *FloppyConfig
Datastore string
uploadedFloppyPath string
}
func (s *StepAddFloppy) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("Adding Floppy...")
floppyIMGPath := s.config.FloppyIMGPath
if s.config.FloppyFiles != nil || s.config.FloppyDirectories != nil {
var err error
floppyIMGPath, err = s.createFloppy()
if err != nil {
state.Put("error", fmt.Errorf("Error creating floppy image: %v", err))
}
}
vm := state.Get("vm").(*driver.VirtualMachine)
err := vm.AddFloppy(floppyIMGPath)
err := s.runImpl(state)
if err != nil {
state.Put("error", err)
state.Put("error", fmt.Errorf("error adding floppy: %v", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepAddFloppy) Cleanup(state multistep.StateBag) {
// nothing
func (s *StepAddFloppy) runImpl(state multistep.StateBag) error {
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*driver.VirtualMachine)
d := state.Get("driver").(*driver.Driver)
tmpFloppy := state.Get("floppy_path")
if s.Config.FloppyIMGPath != "" && tmpFloppy != nil {
return fmt.Errorf("'floppy_img_path' cannot be used together with 'floppy_files' and 'floppy_dirs'")
}
var floppyIMGPath string
if tmpFloppy != nil {
ui.Say("Uploading created floppy image")
ds, err := d.FindDatastore(s.Datastore)
if err != nil {
return err
}
vmDir, err := vm.GetDir()
if err != nil {
return err
}
vmDir = driver.RemoveDatastorePrefix(vmDir)
uploadPath := fmt.Sprintf("%v/packer-tmp-created-floppy.img", vmDir)
if err := ds.UploadFile(tmpFloppy.(string), uploadPath); err != nil {
return fmt.Errorf("error uploading floppy image: %v", err)
}
// remember the path to the temporary floppy image to remove it after the build is finished
s.uploadedFloppyPath = uploadPath
floppyIMGPath = ds.ResolvePath(uploadPath)
} else {
floppyIMGPath = s.Config.FloppyIMGPath
}
ui.Say("Adding Floppy...")
err := vm.AddFloppy(floppyIMGPath)
if err != nil {
return err
}
return nil
}
func (s *StepAddFloppy) createFloppy() (string, error) {
return "", fmt.Errorf("Not implemented")
// TODO
func (s *StepAddFloppy) Cleanup(state multistep.StateBag) {
if s.uploadedFloppyPath == "" {
return
}
ui := state.Get("ui").(packer.Ui)
d := state.Get("driver").(*driver.Driver)
ds, err := d.FindDatastore(s.Datastore)
if err != nil {
ui.Error(err.Error())
return
}
if err := ds.Delete(s.uploadedFloppyPath); err != nil {
ui.Error(fmt.Sprintf("Error deleting floppy image '%v': %v", s.uploadedFloppyPath, err.Error()))
return
}
// TODO: remove Floppy device, or eject the img
}

View File

@ -57,7 +57,7 @@ func (c *CreateConfig) Prepare() []error {
}
type StepCreateVM struct {
config *CreateConfig
Config *CreateConfig
}
func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
@ -67,22 +67,22 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Creating VM...")
vm, err := d.CreateVM(&driver.CreateConfig{
HardwareConfig: s.config.HardwareConfig.ToDriverHardwareConfig(),
HardwareConfig: s.Config.HardwareConfig.ToDriverHardwareConfig(),
DiskThinProvisioned: s.config.DiskThinProvisioned,
DiskControllerType: s.config.DiskControllerType,
Name: s.config.VMName,
Folder: s.config.Folder,
Host: s.config.Host,
ResourcePool: s.config.ResourcePool,
Datastore: s.config.Datastore,
GuestOS: s.config.GuestOSType,
Network: s.config.Network,
NetworkCard: s.config.NetworkCard,
DiskThinProvisioned: s.Config.DiskThinProvisioned,
DiskControllerType: s.Config.DiskControllerType,
Name: s.Config.VMName,
Folder: s.Config.Folder,
Host: s.Config.Host,
ResourcePool: s.Config.ResourcePool,
Datastore: s.Config.Datastore,
GuestOS: s.Config.GuestOSType,
Network: s.Config.Network,
NetworkCard: s.Config.NetworkCard,
})
if err != nil {
state.Put("error", err)
state.Put("error", fmt.Errorf("error creating vm: %v", err))
return multistep.ActionHalt
}