code: func (os *OpenStack) DetachDisk(instanceID, volumeID string) error {
  	volume, err := os.getVolume(volumeID)
  	if err != nil {
  		return err
  	}
  	if volume.Status == volumeAvailableStatus {
  		// "available" is fine since that means the volume is detached from instance already.
  		klog.V(2).Infof("volume: %s has been detached from compute: %s ", volume.ID, instanceID)
  		return nil
  	}
  
  	if volume.Status != volumeInUseStatus {
  		return fmt.Errorf("can not detach volume %s, its status is %s", volume.Name, volume.Status)
  	}
  	cClient, err := os.NewComputeV2()
  	if err != nil {
  		return err
  	}
  	if volume.AttachedServerID != instanceID {
  		return fmt.Errorf("disk: %s has no attachments or is not attached to compute: %s", volume.Name, instanceID)
  	}
  
  	startTime := time.Now()
  	// This is a blocking call and effects kubelet's performance directly.
  	// We should consider kicking it out into a separate routine, if it is bad.
  	err = volumeattach.Delete(cClient, instanceID, volume.ID).ExtractErr()
  	timeTaken := time.Since(startTime).Seconds()
  	recordOpenstackOperationMetric("detach_disk", timeTaken, err)
  	if err != nil {
  		return fmt.Errorf("failed to delete volume %s from compute %s attached %v", volume.ID, instanceID, err)
  	}
  	klog.V(2).Infof("Successfully detached volume: %s from compute: %s", volume.ID, instanceID)
  
  	return nil
  }
docs: // DetachDisk detaches given cinder volume from the compute running kubelet