func CdromRefreshOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) error {
	log.Printf("[DEBUG] CdromRefreshOperation: Beginning refresh")
	// While we are currently only restricting CD devices to one device, we have
	// to actually account for the fact that someone could add multiple CD drives
	// out of band. So this workflow is similar to the multi-device workflow that
	// exists for network devices.
	devices := l.Select(func(device types.BaseVirtualDevice) bool {
		if _, ok := device.(*types.VirtualCdrom); ok {
			return true
		}
		return false
	})
	log.Printf("[DEBUG] CdromRefreshOperation: CDROM devices located: %s", DeviceListString(devices))
	curSet := d.Get(subresourceTypeCdrom).([]interface{})
	log.Printf("[DEBUG] CdromRefreshOperation: Current resource set from state: %s", subresourceListString(curSet))
	var newSet []interface{}
	// First check for negative keys. These are freshly added devices that are
	// usually coming into read post-create.
	//
	// If we find what we are looking for, we remove the device from the working
	// set so that we don't try and process it in the next few passes.
	log.Printf("[DEBUG] CdromRefreshOperation: Looking for freshly-created resources to read in")
	for n, item := range curSet {
		m := item.(map[string]interface{})
		if m["key"].(int) < 1 {
			r := NewCdromSubresource(c, d, m, nil, n)
			if err := r.Read(l); err != nil {
				return fmt.Errorf("%s: %s", r.Addr(), err)
			}
			if r.Get("key").(int) < 1 {
				// This should not have happened - if it did, our device
				// creation/update logic failed somehow that we were not able to track.
				return fmt.Errorf("device %d with address %s still unaccounted for after update/read", r.Get("key").(int), r.Get("device_address").(string))
			}
			newSet = append(newSet, r.Data())
			for i := 0; i < len(devices); i++ {
				device := devices[i]
				if device.GetVirtualDevice().Key == int32(r.Get("key").(int)) {
					devices = append(devices[:i], devices[i+1:]...)
					i--
				}
			}
		}
	}
	log.Printf("[DEBUG] CdromRefreshOperation: CDROM devices after freshly-created device search: %s", DeviceListString(devices))
	log.Printf("[DEBUG] CdromRefreshOperation: Resource set to write after freshly-created device search: %s", subresourceListString(newSet))

	// Go over the remaining devices, refresh via key, and then remove their
	// entries as well.
	log.Printf("[DEBUG] CdromRefreshOperation: Looking for devices known in state")
	for i := 0; i < len(devices); i++ {
		device := devices[i]
		for n, item := range curSet {
			m := item.(map[string]interface{})
			if m["key"].(int) < 0 {
				// Skip any of these keys as we won't be matching any of those anyway here
				continue
			}
			if device.GetVirtualDevice().Key != int32(m["key"].(int)) {
				// Skip any device that doesn't match key as well
				continue
			}
			// We should have our device -> resource match, so read now.
			r := NewCdromSubresource(c, d, m, nil, n)
			vApp, err := verifyVAppCdromIso(d, device.(*types.VirtualCdrom), l, c)
			if err != nil {
				return err
			}
			if vApp == true && r.Get("client_device") == true {
				log.Printf("[DEBUG] CdromRefreshOperation: %s: Skipping read since CDROM is in use for vApp ISO transport", r)
				// Set the CDROM properties to match a client device so there won't be a diff.
				r.Set("client_device", true)
				r.Set("datastore_id", "")
				r.Set("path", "")
			} else {
				if err := r.Read(l); err != nil {
					return fmt.Errorf("%s: %s", r.Addr(), err)
				}
			}
			// Done reading, push this onto our new set and remove the device from
			// the list
			newSet = append(newSet, r.Data())
			devices = append(devices[:i], devices[i+1:]...)
			i--
		}
	}
	log.Printf("[DEBUG] CdromRefreshOperation: Resource set to write after known device search: %s", subresourceListString(newSet))
	log.Printf("[DEBUG] CdromRefreshOperation: Probable orphaned CDROM devices: %s", DeviceListString(devices))

	// Finally, any device that is still here is orphaned. They should be added
	// as new devices.
	for n, device := range devices {
		m := make(map[string]interface{})
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		m["key"] = int(vd.Key)
		var err error
		m["device_address"], err = computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return fmt.Errorf("error computing device address: %s", err)
		}
		r := NewCdromSubresource(c, d, m, nil, n)
		if err := r.Read(l); err != nil {
			return fmt.Errorf("%s: %s", r.Addr(), err)
		}
		newSet = append(newSet, r.Data())
	}

	log.Printf("[DEBUG] CdromRefreshOperation: Resource set to write after adding orphaned devices: %s", subresourceListString(newSet))
	log.Printf("[DEBUG] CdromRefreshOperation: Refresh operation complete, sending new resource set")
	return d.Set(subresourceTypeCdrom, newSet)
}
func CdromRefreshOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) error {
	log.Printf("[DEBUG] CdromRefreshOperation: Beginning refresh")
	// While we are currently only restricting CD devices to one device, we have
	// to actually account for the fact that someone could add multiple CD drives
	// out of band. So this workflow is similar to the multi-device workflow that
	// exists for network devices.
	devices := l.Select(func(device types.BaseVirtualDevice) bool {
		if _, ok := device.(*types.VirtualCdrom); ok {
			return true
		}
		return false
	})
	log.Printf("[DEBUG] CdromRefreshOperation: CDROM devices located: %s", DeviceListString(devices))
	curSet := d.Get(subresourceTypeCdrom).([]interface{})
	log.Printf("[DEBUG] CdromRefreshOperation: Current resource set from state: %s", subresourceListString(curSet))
	var newSet []interface{}
	// First check for negative keys. These are freshly added devices that are
	// usually coming into read post-create.
	//
	// If we find what we are looking for, we remove the device from the working
	// set so that we don't try and process it in the next few passes.
	log.Printf("[DEBUG] CdromRefreshOperation: Looking for freshly-created resources to read in")
	for n, item := range curSet {
		m := item.(map[string]interface{})
		if m["key"].(int) < 1 {
			r := NewCdromSubresource(c, d, m, nil, n)
			if err := r.Read(l); err != nil {
				return fmt.Errorf("%s: %s", r.Addr(), err)
			}
			if r.Get("key").(int) < 1 {
				// This should not have happened - if it did, our device
				// creation/update logic failed somehow that we were not able to track.
				return fmt.Errorf("device %d with address %s still unaccounted for after update/read", r.Get("key").(int), r.Get("device_address").(string))
			}
			newSet = append(newSet, r.Data())
			for i := 0; i < len(devices); i++ {
				device := devices[i]
				if device.GetVirtualDevice().Key == int32(r.Get("key").(int)) {
					devices = append(devices[:i], devices[i+1:]...)
					i--
				}
			}
		}
	}
	log.Printf("[DEBUG] CdromRefreshOperation: CDROM devices after freshly-created device search: %s", DeviceListString(devices))
	log.Printf("[DEBUG] CdromRefreshOperation: Resource set to write after freshly-created device search: %s", subresourceListString(newSet))

	// Go over the remaining devices, refresh via key, and then remove their
	// entries as well.
	log.Printf("[DEBUG] CdromRefreshOperation: Looking for devices known in state")
	for i := 0; i < len(devices); i++ {
		device := devices[i]
		for n, item := range curSet {
			m := item.(map[string]interface{})
			if m["key"].(int) < 0 {
				// Skip any of these keys as we won't be matching any of those anyway here
				continue
			}
			if device.GetVirtualDevice().Key != int32(m["key"].(int)) {
				// Skip any device that doesn't match key as well
				continue
			}
			// We should have our device -> resource match, so read now.
			r := NewCdromSubresource(c, d, m, nil, n)
			vApp, err := verifyVAppCdromIso(d, device.(*types.VirtualCdrom), l, c)
			if err != nil {
				return err
			}
			if vApp == true && r.Get("client_device") == true {
				log.Printf("[DEBUG] CdromRefreshOperation: %s: Skipping read since CDROM is in use for vApp ISO transport", r)
				// Set the CDROM properties to match a client device so there won't be a diff.
				r.Set("client_device", true)
				r.Set("datastore_id", "")
				r.Set("path", "")
			} else {
				if err := r.Read(l); err != nil {
					return fmt.Errorf("%s: %s", r.Addr(), err)
				}
			}
			// Done reading, push this onto our new set and remove the device from
			// the list
			newSet = append(newSet, r.Data())
			devices = append(devices[:i], devices[i+1:]...)
			i--
		}
	}
	log.Printf("[DEBUG] CdromRefreshOperation: Resource set to write after known device search: %s", subresourceListString(newSet))
	log.Printf("[DEBUG] CdromRefreshOperation: Probable orphaned CDROM devices: %s", DeviceListString(devices))

	// Finally, any device that is still here is orphaned. They should be added
	// as new devices.
	for n, device := range devices {
		m := make(map[string]interface{})
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		m["key"] = int(vd.Key)
		var err error
		m["device_address"], err = computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return fmt.Errorf("error computing device address: %s", err)
		}
		r := NewCdromSubresource(c, d, m, nil, n)
		if err := r.Read(l); err != nil {
			return fmt.Errorf("%s: %s", r.Addr(), err)
		}
		newSet = append(newSet, r.Data())
	}

	log.Printf("[DEBUG] CdromRefreshOperation: Resource set to write after adding orphaned devices: %s", subresourceListString(newSet))
	log.Printf("[DEBUG] CdromRefreshOperation: Refresh operation complete, sending new resource set")
	return d.Set(subresourceTypeCdrom, newSet)
}
// CdromRefreshOperation processes a refresh operation for all of the disks in
// the resource.
//
// This functions similar to CdromApplyOperation, but nothing to change is
// returned, all necessary values are just set and committed to state.