vSphere: Support mounting ISO images to virtual cdrom drives.
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.
This commit is contained in:
parent
c682dece84
commit
a67fa662bf
|
@ -53,6 +53,11 @@ type windowsOptConfig struct {
|
||||||
domainUserPassword string
|
domainUserPassword string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type cdrom struct {
|
||||||
|
datastore string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
type virtualMachine struct {
|
type virtualMachine struct {
|
||||||
name string
|
name string
|
||||||
folder string
|
folder string
|
||||||
|
@ -65,6 +70,7 @@ type virtualMachine struct {
|
||||||
template string
|
template string
|
||||||
networkInterfaces []networkInterface
|
networkInterfaces []networkInterface
|
||||||
hardDisks []hardDisk
|
hardDisks []hardDisk
|
||||||
|
cdroms []cdrom
|
||||||
gateway string
|
gateway string
|
||||||
domain string
|
domain string
|
||||||
timeZone 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{
|
"boot_delay": &schema.Schema{
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
@ -492,6 +519,25 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
|
||||||
log.Printf("[DEBUG] disk init: %v", disks)
|
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 != "" {
|
if vm.template != "" {
|
||||||
err := vm.deployVirtualMachine(client)
|
err := vm.deployVirtualMachine(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -743,6 +789,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.
|
// buildNetworkDevice builds VirtualDeviceConfigSpec for Network Device.
|
||||||
func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.VirtualDeviceConfigSpec, error) {
|
func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.VirtualDeviceConfigSpec, error) {
|
||||||
network, err := f.Network(context.TODO(), "*"+label)
|
network, err := f.Network(context.TODO(), "*"+label)
|
||||||
|
@ -934,6 +1005,21 @@ func findDatastore(c *govmomi.Client, sps types.StoragePlacementSpec) (*object.D
|
||||||
return datastore, nil
|
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.
|
// createVirtualMachine creates a new VirtualMachine.
|
||||||
func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
|
func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
|
||||||
dc, err := getDatacenter(c, vm.datacenter)
|
dc, err := getDatacenter(c, vm.datacenter)
|
||||||
|
@ -1071,6 +1157,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
|
||||||
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
||||||
Device: scsi,
|
Device: scsi,
|
||||||
})
|
})
|
||||||
|
|
||||||
configSpec.Files = &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", mds.Name)}
|
configSpec.Files = &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", mds.Name)}
|
||||||
|
|
||||||
task, err := folder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
|
task, err := folder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
|
||||||
|
@ -1098,6 +1185,12 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the cdroms if needed.
|
||||||
|
if err := createCdroms(newVM, vm.cdroms); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1249,6 +1342,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
|
||||||
NumCoresPerSocket: 1,
|
NumCoresPerSocket: 1,
|
||||||
MemoryMB: vm.memoryMb,
|
MemoryMB: vm.memoryMb,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
|
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
|
||||||
|
|
||||||
log.Printf("[DEBUG] starting extra custom config spec: %v", vm.customConfigurations)
|
log.Printf("[DEBUG] starting extra custom config spec: %v", vm.customConfigurations)
|
||||||
|
@ -1401,6 +1495,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)
|
taskb, err := newVM.Customize(context.TODO(), customSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1410,7 +1509,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("[DEBUG]VM customization finished")
|
log.Printf("[DEBUG] VM customization finished")
|
||||||
|
|
||||||
for i := 1; i < len(vm.hardDisks); i++ {
|
for i := 1; i < len(vm.hardDisks); i++ {
|
||||||
err = addHardDisk(newVM, vm.hardDisks[i].size, vm.hardDisks[i].iops, vm.hardDisks[i].initType)
|
err = addHardDisk(newVM, vm.hardDisks[i].size, vm.hardDisks[i].iops, vm.hardDisks[i].initType)
|
||||||
|
@ -1418,6 +1517,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
|
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
|
||||||
|
|
||||||
newVM.PowerOn(context.TODO())
|
newVM.PowerOn(context.TODO())
|
||||||
|
|
|
@ -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 {
|
func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
|
||||||
client := testAccProvider.Meta().(*govmomi.Client)
|
client := testAccProvider.Meta().(*govmomi.Client)
|
||||||
finder := find.NewFinder(client.Client, true)
|
finder := find.NewFinder(client.Client, true)
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
@ -89,6 +89,11 @@ The following environment variables depend on your vSphere environment:
|
||||||
* VSPHERE\_RESOURCE\_POOL
|
* VSPHERE\_RESOURCE\_POOL
|
||||||
* VSPHERE\_DATASTORE
|
* 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`
|
These are used to set and verify attributes on the `vsphere_virtual_machine`
|
||||||
resource in tests.
|
resource in tests.
|
||||||
|
|
|
@ -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
|
* `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.
|
* `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
|
* `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.
|
* `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.
|
* `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.
|
* `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` - (Optional) User that is a member of the specified domain.
|
||||||
* `domain_user_password` - (Optional) Password for domain user, in plain text.
|
* `domain_user_password` - (Optional) Password for domain user, in plain text.
|
||||||
|
|
||||||
|
<a id="disks"></a>
|
||||||
|
## Disks
|
||||||
|
|
||||||
The `disk` block supports:
|
The `disk` block supports:
|
||||||
|
|
||||||
* `template` - (Required if size not provided) Template for this disk.
|
* `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.
|
* `iops` - (Optional) Number of virtual iops to allocate for this disk.
|
||||||
* `type` - (Optional) 'eager_zeroed' (the default), or 'thin' are supported options.
|
* `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
|
## Attributes Reference
|
||||||
|
|
||||||
The following attributes are exported:
|
The following attributes are exported:
|
||||||
|
|
Loading…
Reference in New Issue