Add support for dynamically create floppy images
This commit is contained in:
parent
9139a6029b
commit
cf198539e8
@ -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
|
||||
}
|
||||
}
|
||||
|
31
driver/vm.go
31
driver/vm.go
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user