vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go
func DiskDiffOperation(d *schema.ResourceDiff, c *govmomi.Client) error {
	log.Printf("[DEBUG] DiskDiffOperation: Beginning disk diff customization")
	o, n := d.GetChange(subresourceTypeDisk)
	// Some global validation first. We handle individual validation later.
	log.Printf("[DEBUG] DiskDiffOperation: Beginning collective diff validation (indexes aligned to new config)")
	names := make(map[string]struct{})
	attachments := make(map[string]struct{})
	units := make(map[int]struct{})
	if len(n.([]interface{})) < 1 {
		return errors.New("there must be at least one disk specified")
	}
	for ni, ne := range n.([]interface{}) {
		nm := ne.(map[string]interface{})
		name, err := diskLabelOrName(nm)
		if err != nil {
			return fmt.Errorf("disk.%d: %s", ni, err)
		}
		if _, ok := names[name]; ok {
			return fmt.Errorf("disk: duplicate name %s", name)
		}
		// If attach is set, we need to validate that there's no other duplicate paths.
		if nm["attach"].(bool) {
			path := diskPathOrName(nm)
			if path == "" {
				return fmt.Errorf("disk.%d: path or name cannot be empty when using attach", ni)
			}
			if _, ok := attachments[path]; ok {
				return fmt.Errorf("disk: multiple entries trying to attach external disk %s", path)
			}
			attachments[path] = struct{}{}
		}

		if _, ok := units[nm["unit_number"].(int)]; ok {
			return fmt.Errorf("disk: duplicate unit_number %d", nm["unit_number"].(int))
		}
		names[name] = struct{}{}
		units[nm["unit_number"].(int)] = struct{}{}
		r := NewDiskSubresource(c, d, nm, nil, ni)
		if err := r.DiffGeneral(); err != nil {
			return fmt.Errorf("%s: %s", r.Addr(), err)
		}
	}
	if _, ok := units[0]; !ok {
		return errors.New("at least one disk must have a unit_number of 0")
	}

	// Perform the normalization here.
	log.Printf("[DEBUG] DiskDiffOperation: Beginning diff validation and normalization (indexes aligned to old state)")
	ods := o.([]interface{})
	nds := n.([]interface{})

	normalized := make([]interface{}, len(ods))
nextNew:
	for _, ne := range nds {
		nm := ne.(map[string]interface{})
		for oi, oe := range ods {
			om := oe.(map[string]interface{})
			var oname, nname string
			var err error
			if oname, err = diskLabelOrName(om); err != nil {
				return fmt.Errorf("disk.%d: %s", oi, err)
			}
			if nname, err = diskLabelOrName(nm); err != nil {
				return fmt.Errorf("disk.%d: %s", oi, err)
			}
			// We extrapolate using the label as a "primary key" of sorts.
			if nname == oname {
				r := NewDiskSubresource(c, d, nm, om, oi)
				if err := r.DiffExisting(); err != nil {
					return fmt.Errorf("%s: %s", r.Addr(), err)
				}
				normalized[oi] = r.Data()
				continue nextNew
			}
		}
		// We didn't find a match for this resource, it could be a new resource or
		// significantly altered. Put it back on the list in the same form we got
		// it in, but all computed data first, just in case it was in a position
		// previously occupied by an existing resource.
		nm["key"] = 0
		nm["device_address"] = ""
		nm["uuid"] = ""
		if a, ok := nm["attach"]; !ok || !a.(bool) {
			nm["path"] = ""
		} else {
			_, ok := nm["size"]
			if ok {
				delete(nm, "size")
			}
		}
		if dsID, ok := nm["datastore_id"]; !ok || dsID == "" {
			nm["datastore_id"] = diskDatastoreComputedName
		}
		normalized = append(normalized, nm)
	}

	// Go thru the new list, and replace any nils with a "deleted" copy of the
	// old resource. This is basically a copy of the old entry with <deleted> in
	// place of the label, so it shows up nicely in the diff.
	for ni, ne := range normalized {
		if ne != nil {
			continue
		}
		nv, err := copystructure.Copy(ods[ni])
		if err != nil {
			return fmt.Errorf("disk.%d: error making updated diff of deleted entry: %s", ni, err)
		}
		nm := nv.(map[string]interface{})
		// Clear out the name. We put the message in label now, even if name was
		// the item defined.  TODO: Remove this after 2.0.
		nm["name"] = ""
		switch {
		case nm["keep_on_remove"].(bool):
			fallthrough
		case nm["attach"].(bool):
			nm["label"] = diskDetachedName
		default:
			nm["label"] = diskDeletedName
		}
		normalized[ni] = nm
	}

	// All done. We can end the customization off by setting the new, normalized diff.
	log.Printf("[DEBUG] DiskDiffOperation: New resource set post-normalization: %s", subresourceListString(normalized))
	log.Printf("[DEBUG] DiskDiffOperation: Disk diff customization complete, sending new diff")
	return d.SetNew(subresourceTypeDisk, normalized)
}
func DiskDiffOperation(d *schema.ResourceDiff, c *govmomi.Client) error {
	log.Printf("[DEBUG] DiskDiffOperation: Beginning disk diff customization")
	o, n := d.GetChange(subresourceTypeDisk)
	// Some global validation first. We handle individual validation later.
	log.Printf("[DEBUG] DiskDiffOperation: Beginning collective diff validation (indexes aligned to new config)")
	names := make(map[string]struct{})
	attachments := make(map[string]struct{})
	units := make(map[int]struct{})
	if len(n.([]interface{})) < 1 {
		return errors.New("there must be at least one disk specified")
	}
	for ni, ne := range n.([]interface{}) {
		nm := ne.(map[string]interface{})
		name, err := diskLabelOrName(nm)
		if err != nil {
			return fmt.Errorf("disk.%d: %s", ni, err)
		}
		if _, ok := names[name]; ok {
			return fmt.Errorf("disk: duplicate name %s", name)
		}
		// If attach is set, we need to validate that there's no other duplicate paths.
		if nm["attach"].(bool) {
			path := diskPathOrName(nm)
			if path == "" {
				return fmt.Errorf("disk.%d: path or name cannot be empty when using attach", ni)
			}
			if _, ok := attachments[path]; ok {
				return fmt.Errorf("disk: multiple entries trying to attach external disk %s", path)
			}
			attachments[path] = struct{}{}
		}

		if _, ok := units[nm["unit_number"].(int)]; ok {
			return fmt.Errorf("disk: duplicate unit_number %d", nm["unit_number"].(int))
		}
		names[name] = struct{}{}
		units[nm["unit_number"].(int)] = struct{}{}
		r := NewDiskSubresource(c, d, nm, nil, ni)
		if err := r.DiffGeneral(); err != nil {
			return fmt.Errorf("%s: %s", r.Addr(), err)
		}
	}
	if _, ok := units[0]; !ok {
		return errors.New("at least one disk must have a unit_number of 0")
	}

	// Perform the normalization here.
	log.Printf("[DEBUG] DiskDiffOperation: Beginning diff validation and normalization (indexes aligned to old state)")
	ods := o.([]interface{})
	nds := n.([]interface{})

	normalized := make([]interface{}, len(ods))
nextNew:
	for _, ne := range nds {
		nm := ne.(map[string]interface{})
		for oi, oe := range ods {
			om := oe.(map[string]interface{})
			var oname, nname string
			var err error
			if oname, err = diskLabelOrName(om); err != nil {
				return fmt.Errorf("disk.%d: %s", oi, err)
			}
			if nname, err = diskLabelOrName(nm); err != nil {
				return fmt.Errorf("disk.%d: %s", oi, err)
			}
			// We extrapolate using the label as a "primary key" of sorts.
			if nname == oname {
				r := NewDiskSubresource(c, d, nm, om, oi)
				if err := r.DiffExisting(); err != nil {
					return fmt.Errorf("%s: %s", r.Addr(), err)
				}
				normalized[oi] = r.Data()
				continue nextNew
			}
		}
		// We didn't find a match for this resource, it could be a new resource or
		// significantly altered. Put it back on the list in the same form we got
		// it in, but all computed data first, just in case it was in a position
		// previously occupied by an existing resource.
		nm["key"] = 0
		nm["device_address"] = ""
		nm["uuid"] = ""
		if a, ok := nm["attach"]; !ok || !a.(bool) {
			nm["path"] = ""
		} else {
			_, ok := nm["size"]
			if ok {
				delete(nm, "size")
			}
		}
		if dsID, ok := nm["datastore_id"]; !ok || dsID == "" {
			nm["datastore_id"] = diskDatastoreComputedName
		}
		normalized = append(normalized, nm)
	}

	// Go thru the new list, and replace any nils with a "deleted" copy of the
	// old resource. This is basically a copy of the old entry with <deleted> in
	// place of the label, so it shows up nicely in the diff.
	for ni, ne := range normalized {
		if ne != nil {
			continue
		}
		nv, err := copystructure.Copy(ods[ni])
		if err != nil {
			return fmt.Errorf("disk.%d: error making updated diff of deleted entry: %s", ni, err)
		}
		nm := nv.(map[string]interface{})
		// Clear out the name. We put the message in label now, even if name was
		// the item defined.  TODO: Remove this after 2.0.
		nm["name"] = ""
		switch {
		case nm["keep_on_remove"].(bool):
			fallthrough
		case nm["attach"].(bool):
			nm["label"] = diskDetachedName
		default:
			nm["label"] = diskDeletedName
		}
		normalized[ni] = nm
	}

	// All done. We can end the customization off by setting the new, normalized diff.
	log.Printf("[DEBUG] DiskDiffOperation: New resource set post-normalization: %s", subresourceListString(normalized))
	log.Printf("[DEBUG] DiskDiffOperation: Disk diff customization complete, sending new diff")
	return d.SetNew(subresourceTypeDisk, normalized)
}
func DiskDiffOperation ( d * schema . ResourceDiff , c * govmomi . Client ) error { log . Printf ( " " ) o , n := d . GetChange ( subresourceTypeDisk ) // Some global validation first. We handle individual validation later. log . Printf ( " " ) names := make ( map [ string ] struct { } ) attachments := make ( map [ string ] struct { } ) units := make ( map [ int ] struct { } ) if len ( n . ( [ ] interface { } ) ) < 1 { return errors . New ( " " ) } for ni , ne := range n . ( [ ] interface { } ) { nm := ne . ( map [ string ] interface { } ) name , err := diskLabelOrName ( nm ) if err != nil { return fmt . Errorf ( " " , ni , err ) } if _ , ok := names [ name ] ; ok { return fmt . Errorf ( " " , name ) } // If attach is set, we need to validate that there's no other duplicate paths. if nm [ " " ] . ( bool ) { path := diskPathOrName ( nm ) if path == " " { return fmt . Errorf ( " " , ni ) } if _ , ok := attachments [ path ] ; ok { return fmt . Errorf ( " " , path ) } attachments [ path ] = struct { } { } } if _ , ok := units [ nm [ " " ] . ( int ) ] ; ok { return fmt . Errorf ( " " , nm [ " " ] . ( int ) ) } names [ name ] = struct { } { } units [ nm [ " " ] . ( int ) ] = struct { } { } r := NewDiskSubresource ( c , d , nm , nil , ni ) if err := r . DiffGeneral ( ) ; err != nil { return fmt . Errorf ( " " , r . Addr ( ) , err ) } } if _ , ok := units [ 0 ] ; ! ok { return errors . New ( " " ) } // Perform the normalization here. log . Printf ( " " ) ods := o . ( [ ] interface { } ) nds := n . ( [ ] interface { } ) normalized := make ( [ ] interface { } , len ( ods ) ) nextNew : for _ , ne := range nds { nm := ne . ( map [ string ] interface { } ) for oi , oe := range ods { om := oe . ( map [ string ] interface { } ) var oname , nname string var err error if oname , err = diskLabelOrName ( om ) ; err != nil { return fmt . Errorf ( " " , oi , err ) } if nname , err = diskLabelOrName ( nm ) ; err != nil { return fmt . Errorf ( " " , oi , err ) } // We extrapolate using the label as a "primary key" of sorts. if nname == oname { r := NewDiskSubresource ( c , d , nm , om , oi ) if err := r . DiffExisting ( ) ; err != nil { return fmt . Errorf ( " " , r . Addr ( ) , err ) } normalized [ oi ] = r . Data ( ) continue nextNew } } // We didn't find a match for this resource, it could be a new resource or // significantly altered. Put it back on the list in the same form we got // it in, but all computed data first, just in case it was in a position // previously occupied by an existing resource. nm [ " " ] = 0 nm [ " " ] = " " nm [ " " ] = " " if a , ok := nm [ " " ] ; ! ok || ! a . ( bool ) { nm [ " " ] = " " } else { _ , ok := nm [ " " ] if ok { delete ( nm , " " ) } } if dsID , ok := nm [ " " ] ; ! ok || dsID == " " { nm [ " " ] = diskDatastoreComputedName } normalized = append ( normalized , nm ) } // Go thru the new list, and replace any nils with a "deleted" copy of the // old resource. This is basically a copy of the old entry with <deleted> in // place of the label, so it shows up nicely in the diff. for ni , ne := range normalized { if ne != nil { continue } nv , err := copystructure . Copy ( ods [ ni ] ) if err != nil { return fmt . Errorf ( " " , ni , err ) } nm := nv . ( map [ string ] interface { } ) // Clear out the name. We put the message in label now, even if name was // the item defined.  TODO: Remove this after 2.0. nm [ " " ] = " " switch { case nm [ " " ] . ( bool ) : fallthrough case nm [ " " ] . ( bool ) : nm [ " " ] = diskDetachedName default : nm [ " " ] = diskDeletedName } normalized [ ni ] = nm } // All done. We can end the customization off by setting the new, normalized diff. log . Printf ( " " , subresourceListString ( normalized ) ) log . Printf ( " " ) return d . SetNew ( subresourceTypeDisk , normalized ) }
// DiskDiffOperation performs operations relevant to managing the diff on disk
// sub-resources.
//
// Most importantly, this works to prevent spurious diffs by extrapolating the
// correct correlation between the old and new sets using the name as a primary
// key, and then normalizing the two diffs so that computed data is properly
// set.
//
// The following validation operations are also carried out on the set as a
// whole:
//
// * Ensuring all names are unique across the set.
// * Ensuring that at least one element in the set has a unit_number of 0.
DiskDiffOperation performs operations relevant to managing the diff on disk sub - resources . Most importantly this works to prevent spurious diffs by extrapolating the correct correlation between the old and new sets using the name as a primary key and then normalizing the two diffs so that computed data is properly set . The following validation operations are also carried out on the set as a whole : * Ensuring all names are unique across the set . * Ensuring that at least one element in the set has a unit_number of 0 .