Skip to content

Commit

Permalink
vSphere: Support mounting ISO images to virtual cdrom drives.
Browse files Browse the repository at this point in the history
It can come in handy to be able to mount ISOs programmatically.
For instance if you're developing a custom appliance (that automatically installs itself on the hard drive volume)
that you want to automatically test on every successful build (given the ISO is uploaded to the vmware datastore).

There are probably lots of other reasons for using this functionality.
  • Loading branch information
kristinn committed Dec 22, 2015
1 parent 7d6b980 commit 9fa7e5e
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 2 deletions.
102 changes: 101 additions & 1 deletion builtin/providers/vsphere/resource_vsphere_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ type hardDisk struct {
iops int64
}

type cdrom struct {
datastore string
path string
}

type virtualMachine struct {
name string
folder string
Expand All @@ -54,6 +59,7 @@ type virtualMachine struct {
template string
networkInterfaces []networkInterface
hardDisks []hardDisk
cdroms []cdrom
gateway string
domain string
timeZone string
Expand Down Expand Up @@ -256,6 +262,27 @@ func resourceVSphereVirtualMachine() *schema.Resource {
},
},

"cdrom": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"datastore": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"path": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
},
},

"boot_delay": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -390,6 +417,25 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
log.Printf("[DEBUG] disk init: %v", disks)
}

if vL, ok := d.GetOk("cdrom"); ok {
cdroms := make([]cdrom, len(vL.([]interface{})))
for i, v := range vL.([]interface{}) {
c := v.(map[string]interface{})
if v, ok := c["datastore"].(string); ok && v != "" {
cdroms[i].datastore = v
} else {
return fmt.Errorf("Datastore argument must be specified when attaching a cdrom image.")
}
if v, ok := c["path"].(string); ok && v != "" {
cdroms[i].path = v
} else {
return fmt.Errorf("Path argument must be specified when attaching a cdrom image.")
}
}
vm.cdroms = cdroms
log.Printf("[DEBUG] cdrom init: %v", cdroms)
}

if vm.template != "" {
err := vm.deployVirtualMachine(client)
if err != nil {
Expand Down Expand Up @@ -632,6 +678,31 @@ func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string) e
}
}

// addCdrom adds a new virtual cdrom drive to the VirtualMachine and attaches an image (ISO) to it from a datastore path.
func addCdrom(vm *object.VirtualMachine, datastore, path string) error {
devices, err := vm.Device(context.TODO())
if err != nil {
return err
}
log.Printf("[DEBUG] vm devices: %#v", devices)

controller, err := devices.FindIDEController("")
if err != nil {
return err
}
log.Printf("[DEBUG] ide controller: %#v", controller)

c, err := devices.CreateCdrom(controller)
if err != nil {
return err
}

c = devices.InsertIso(c, fmt.Sprintf("[%s] %s", datastore, path))
log.Printf("[DEBUG] addCdrom: %#v", c)

return vm.AddDevice(context.TODO(), c)
}

// buildNetworkDevice builds VirtualDeviceConfigSpec for Network Device.
func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.VirtualDeviceConfigSpec, error) {
network, err := f.Network(context.TODO(), "*"+label)
Expand Down Expand Up @@ -814,6 +885,21 @@ func findDatastore(c *govmomi.Client, sps types.StoragePlacementSpec) (*object.D
return datastore, nil
}

// createCdroms is a helper function to attach virtual cdrom devices (and their attached disk images) to a virtual IDE controller.
func createCdroms(vm *object.VirtualMachine, cdroms []cdrom) error {
log.Printf("[DEBUG] add cdroms: %v", cdroms)
for _, cd := range cdroms {
log.Printf("[DEBUG] add cdrom (datastore): %v", cd.datastore)
log.Printf("[DEBUG] add cdrom (cd path): %v", cd.path)
err := addCdrom(vm, cd.datastore, cd.path)
if err != nil {
return err
}
}

return nil
}

// createVirtualMachine creates a new VirtualMachine.
func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
dc, err := getDatacenter(c, vm.datacenter)
Expand Down Expand Up @@ -951,6 +1037,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
Operation: types.VirtualDeviceConfigSpecOperationAdd,
Device: scsi,
})

configSpec.Files = &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", mds.Name)}

task, err := folder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
Expand Down Expand Up @@ -978,6 +1065,12 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
return err
}
}

// Create the cdroms if needed.
if err := createCdroms(newVM, vm.cdroms); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -1127,6 +1220,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
NumCoresPerSocket: 1,
MemoryMB: vm.memoryMb,
}

log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)

log.Printf("[DEBUG] starting extra custom config spec: %v", vm.customConfigurations)
Expand Down Expand Up @@ -1214,6 +1308,11 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
}
}

// Create the cdroms if needed.
if err := createCdroms(newVM, vm.cdroms); err != nil {
return err
}

taskb, err := newVM.Customize(context.TODO(), customSpec)
if err != nil {
return err
Expand All @@ -1223,14 +1322,15 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
if err != nil {
return err
}
log.Printf("[DEBUG]VM customization finished")
log.Printf("[DEBUG] VM customization finished")

for i := 1; i < len(vm.hardDisks); i++ {
err = addHardDisk(newVM, vm.hardDisks[i].size, vm.hardDisks[i].iops, "eager_zeroed")
if err != nil {
return err
}
}

log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)

newVM.PowerOn(context.TODO())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,71 @@ func TestAccVSphereVirtualMachine_createWithFolder(t *testing.T) {
})
}

func TestAccVSphereVirtualMachine_createWithCdrom(t *testing.T) {
var vm virtualMachine
var locationOpt string
var datastoreOpt string

if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
cdromDatastore := os.Getenv("VSPHERE_CDROM_DATASTORE")
cdromPath := os.Getenv("VSPHERE_CDROM_PATH")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVsphereVirtualMachineConfig_cdrom,
locationOpt,
label,
datastoreOpt,
template,
cdromDatastore,
cdromPath,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.with_cdrom", &vm),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "name", "terraform-test-with-cdrom"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "vcpu", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "memory", "4096"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "disk.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "disk.0.template", template),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "cdrom.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "cdrom.0.datastore", cdromDatastore),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "cdrom.0.path", cdromPath),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "network_interface.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_cdrom", "network_interface.0.label", label),
),
},
},
})
}

func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
Expand Down Expand Up @@ -577,7 +642,7 @@ resource "vsphere_virtual_machine" "folder" {

const testAccCheckVSphereVirtualMachineConfig_createWithFolder = `
resource "vsphere_folder" "with_folder" {
path = "%s"
path = "%s"
%s
}
resource "vsphere_virtual_machine" "with_folder" {
Expand All @@ -595,3 +660,24 @@ resource "vsphere_virtual_machine" "with_folder" {
}
}
`

const testAccCheckVsphereVirtualMachineConfig_cdrom = `
resource "vsphere_virtual_machine" "with_cdrom" {
name = "terraform-test-with-cdrom"
%s
vcpu = 2
memory = 4096
network_interface {
label = "%s"
}
disk {
%s
template = "%s"
}
cdrom {
datastore = "%s"
path = "%s"
}
}
`

0 comments on commit 9fa7e5e

Please sign in to comment.