code: func (os *OpenStack) AttachDisk(instanceID, volumeID string) (string, error) {
  	volume, err := os.getVolume(volumeID)
  	if err != nil {
  		return "", err
  	}
  
  	cClient, err := os.NewComputeV2()
  	if err != nil {
  		return "", err
  	}
  
  	if volume.AttachedServerID != "" {
  		if instanceID == volume.AttachedServerID {
  			klog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, instanceID)
  			return volume.ID, nil
  		}
  		nodeName, err := os.GetNodeNameByID(volume.AttachedServerID)
  		attachErr := fmt.Sprintf("disk %s path %s is attached to a different instance (%s)", volumeID, volume.AttachedDevice, volume.AttachedServerID)
  		if err != nil {
  			klog.Error(attachErr)
  			return "", errors.New(attachErr)
  		}
  		// using volume.AttachedDevice may cause problems because cinder does not report device path correctly see issue #33128
  		devicePath := volume.AttachedDevice
  		danglingErr := volerr.NewDanglingError(attachErr, nodeName, devicePath)
  		klog.V(2).Infof("Found dangling volume %s attached to node %s", volumeID, nodeName)
  		return "", danglingErr
  	}
  
  	startTime := time.Now()
  	// add read only flag here if possible spothanis
  	_, err = volumeattach.Create(cClient, instanceID, &volumeattach.CreateOpts{
  		VolumeID: volume.ID,
  	}).Extract()
  	timeTaken := time.Since(startTime).Seconds()
  	recordOpenstackOperationMetric("attach_disk", timeTaken, err)
  	if err != nil {
  		return "", fmt.Errorf("failed to attach %s volume to %s compute: %v", volumeID, instanceID, err)
  	}
  	klog.V(2).Infof("Successfully attached %s volume to %s compute", volumeID, instanceID)
  	return volume.ID, nil
  }
docs: // AttachDisk attaches given cinder volume to the compute running kubelet