Merge pull request #4243 from kristinn/vsphere-cdrom-support

vSphere: Support mounting ISO images to virtual cdrom drives.
This commit is contained in:
James Nugent 2016-04-21 15:50:12 -07:00
commit 5b1b49e2b7
4 changed files with 205 additions and 2 deletions

View File

@ -53,6 +53,11 @@ type windowsOptConfig struct {
domainUserPassword string
}
type cdrom struct {
datastore string
path string
}
type virtualMachine struct {
name string
folder string
@ -65,6 +70,7 @@ type virtualMachine struct {
template string
networkInterfaces []networkInterface
hardDisks []hardDisk
cdroms []cdrom
gateway string
domain string
timeZone string
@ -328,6 +334,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,
@ -492,6 +519,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 {
@ -749,6 +795,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)
@ -940,6 +1011,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)
@ -1077,6 +1163,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)
@ -1104,6 +1191,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
}
@ -1255,6 +1348,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)
@ -1407,6 +1501,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
@ -1416,7 +1515,7 @@ 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, vm.hardDisks[i].initType)
@ -1424,6 +1523,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
return err
}
}
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
newVM.PowerOn(context.TODO())

View File

@ -388,6 +388,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)
@ -664,7 +729,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" {
@ -682,3 +747,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"
}
}
`

View File

@ -89,6 +89,11 @@ The following environment variables depend on your vSphere environment:
* VSPHERE\_RESOURCE\_POOL
* VSPHERE\_DATASTORE
The following additional environment variables are needed for running the "Mount ISO as CDROM media" acceptance tests.
* VSPHERE\_CDROM\_DATASTORE
* VSPHERE\_CDROM\_PATH
These are used to set and verify attributes on the `vsphere_virtual_machine`
resource in tests.

View File

@ -46,6 +46,7 @@ The following arguments are supported:
* `dns_servers` - (Optional) List of DNS servers for the virtual network adapter; defaults to 8.8.8.8, 8.8.4.4
* `network_interface` - (Required) Configures virtual network interfaces; see [Network Interfaces](#network-interfaces) below for details.
* `disk` - (Required) Configures virtual disks; see [Disks](#disks) below for details
* `cdrom` - (Optional) Configures a CDROM device and mounts an image as its media; see [CDROM](#cdrom) below for more details.
* `boot_delay` - (Optional) Time in seconds to wait for machine network to be ready.
* `windows_opt_config` - (Optional) Extra options for clones of Windows machines.
* `linked_clone` - (Optional) Specifies if the new machine is a [linked clone](https://www.vmware.com/support/ws5/doc/ws_clone_overview.html#wp1036396) of another machine or not.
@ -71,6 +72,9 @@ The `windows_opt_config` block supports:
* `domain_user` - (Optional) User that is a member of the specified domain.
* `domain_user_password` - (Optional) Password for domain user, in plain text.
<a id="disks"></a>
## Disks
The `disk` block supports:
* `template` - (Required if size not provided) Template for this disk.
@ -79,6 +83,14 @@ The `disk` block supports:
* `iops` - (Optional) Number of virtual iops to allocate for this disk.
* `type` - (Optional) 'eager_zeroed' (the default), or 'thin' are supported options.
<a id="cdrom"></a>
## CDROM
The `cdrom` block supports:
* `datastore` - (Required) The name of the datastore where the disk image is stored.
* `path` - (Required) The absolute path to the image within the datastore.
## Attributes Reference
The following attributes are exported: