code: func validateVMForUpdate(v *crimson.VM, mask *field_mask.FieldMask) error {
  	switch err := validateUpdateMask(mask); {
  	case v == nil:
  		return status.Error(codes.InvalidArgument, "VM specification is required")
  	case v.Name == "":
  		return status.Error(codes.InvalidArgument, "hostname is required and must be non-empty")
  	case err != nil:
  		return err
  	}
  	for _, path := range mask.Paths {
  		// TODO(smut): Allow IPv4 address to be updated.
  		switch path {
  		case "name":
  			return status.Error(codes.InvalidArgument, "hostname cannot be updated, delete and create a new VM instead")
  		case "vlan":
  			return status.Error(codes.InvalidArgument, "VLAN cannot be updated, delete and create a new VM instead")
  		case "host":
  			if v.Host == "" {
  				return status.Error(codes.InvalidArgument, "physical hostname is required and must be non-empty")
  			}
  		case "host_vlan":
  			return status.Error(codes.InvalidArgument, "host VLAN cannot be updated, update the host instead")
  		case "os":
  			if v.Os == "" {
  				return status.Error(codes.InvalidArgument, "operating system is required and must be non-empty")
  			}
  		case "state":
  			if v.State == states.State_STATE_UNSPECIFIED {
  				return status.Error(codes.InvalidArgument, "state is required")
  			}
  		case "description":
  			// Empty description is allowed, nothing to validate.
  		case "deployment_ticket":
  			// Empty deployment ticket is allowed, nothing to validate.
  		default:
  			return status.Errorf(codes.InvalidArgument, "unsupported update mask path %q", path)
  		}
  	}
  	return nil
  }
docs: // validateVMForUpdate validates a VM for update.