// DiskIsAttached implements Volumes.DiskIsAttached
972439
func (c *Cloud) DiskIsAttached(diskName KubernetesVolumeID, nodeName types.NodeName) (bool, error) {
	_, attached, err := c.checkIfAttachedToNode(diskName, nodeName)
	if err != nil {
		if isAWSErrorVolumeNotFound(err) {
			// The disk doesn't exist, can't be attached
			klog.Warningf("DiskIsAttached called for volume %s on node %s but the volume does not exist", diskName, nodeName)
			return false, nil
		}

		return true, err
	}

	return attached, nil
}
1000616
func (c *ConfigLocal) DiskMDCache() DiskMDCache {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.diskMDCache
}
1172375
func (d *driver) VolumeDetach(
	ctx types.Context,
	volumeID string,
	opts *types.VolumeDetachOpts) (*types.Volume, error) {

	// TODO(kasisnu): Think about what to do here?
	// It is safe to remove the mount target
	// when it is no longer being used anywhere
	return nil, nil
}
1091949
func (os *OpenStack) DisksAreAttached(instanceID string, volumeIDs []string) (map[string]bool, error) {
	attached := make(map[string]bool)
	for _, volumeID := range volumeIDs {
		isAttached, err := os.DiskIsAttached(instanceID, volumeID)
		if err != nil && err != ErrNotFound {
			attached[volumeID] = true
			continue
		}
		attached[volumeID] = isAttached
	}
	return attached, nil
}
1089176
func (d *driver) VolumeRemove(
	ctx types.Context,
	volumeID string,
	opts *types.VolumeRemoveOpts) error {

	zone, err := d.validZone(ctx)
	if err != nil {
		return err
	}

	if zone == nil || *zone == "" {
		return goof.New("Zone is required for VolumeRemove")
	}

	// TODO: check if disk is still attached first
	asyncOp, err := mustSession(ctx).Disks.Delete(
		*d.projectID, *zone, volumeID).Do()
	if err != nil {
		return goof.WithError("Failed to initiate disk deletion", err)
	}

	err = d.waitUntilOperationIsFinished(
		ctx, zone, asyncOp)
	if err != nil {
		return err
	}

	return nil
}
302261
func (gce *Cloud) DiskIsAttached(diskName string, nodeName types.NodeName) (bool, error) {
	instanceName := mapNodeNameToInstanceName(nodeName)
	instance, err := gce.getInstanceByName(instanceName)
	if err != nil {
		if err == cloudprovider.ErrInstanceNotFound {
			// If instance no longer exists, safe to assume volume is not attached.
			glog.Warningf(
				"Instance %q does not exist. DiskIsAttached will assume PD %q is not attached to it.",
				instanceName,
				diskName)
			return false, nil
		}

		return false, err
	}

	for _, disk := range instance.Disks {
		if disk.DeviceName == diskName {
			// Disk is still attached to node
			return true, nil
		}
	}

	return false, nil
}
109323
func (client *Client) DetachDisk(instanceId string, diskId string) error {
	args := DetachDiskArgs{
		InstanceId: instanceId,
		DiskId:     diskId,
	}
	response := DetachDiskResponse{}
	err := client.Invoke("DetachDisk", &args, &response)
	return err
}
1069699
func (client *Client) ReplaceSystemDisk(args *ReplaceSystemDiskArgs) (diskId string, err error) {
	response := ReplaceSystemDiskResponse{}
	err = client.Invoke("ReplaceSystemDisk", args, &response)
	if err != nil {
		return "", err
	}
	return response.DiskId, nil
}
1050443
func (c *controllerCommon) DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error) {
	attached := make(map[string]bool)
	for _, diskName := range diskNames {
		attached[diskName] = false
	}

	disks, err := c.getNodeDataDisks(nodeName)
	if err != nil {
		if err == cloudprovider.InstanceNotFound {
			// if host doesn't exist, no need to detach
			klog.Warningf("azureDisk - Cannot find node %q, DisksAreAttached will assume disks %v are not attached to it.",
				nodeName, diskNames)
			return attached, nil
		}

		return attached, err
	}

	for _, disk := range disks {
		for _, diskName := range diskNames {
			if disk.Name != nil && diskName != "" && *disk.Name == diskName {
				attached[diskName] = true
			}
		}
	}

	return attached, nil
}
523405
func (e *endpoint) IsAttached() bool {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.workerStarted
}
599839
func validateVolumeAttachmentStatus(status *storage.VolumeAttachmentStatus, fldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}
	allErrs = append(allErrs, validateAttachmentMetadata(status.AttachmentMetadata, fldPath.Child("attachmentMetadata"))...)
	allErrs = append(allErrs, validateVolumeError(status.AttachError, fldPath.Child("attachError"))...)
	allErrs = append(allErrs, validateVolumeError(status.DetachError, fldPath.Child("detachError"))...)
	return allErrs
}
698227
func (d *driver) VolumeAttach(
	ctx types.Context,
	volumeID string,
	opts *types.VolumeAttachOpts) (*types.Volume, string, error) {

	zone, err := d.validZone(ctx)
	if err != nil {
		return nil, "", err
	}

	if zone == nil || *zone == "" {
		return nil, "", goof.New("Zone is required for VolumeAttach")
	}

	instanceName := context.MustInstanceID(ctx).ID
	gceInst, err := d.getInstance(ctx, zone, &instanceName)
	if err != nil {
		return nil, "", err
	}
	if gceInst == nil {
		return nil, "", goof.New("Instance to attach to not found")
	}

	// Check if volume is already attached somewhere, if so, force detach?
	gceDisk, err := d.getDisk(ctx, zone, &volumeID)
	if err != nil {
		return nil, "", err
	}
	if gceDisk == nil {
		return nil, "", apiUtils.NewNotFoundError(volumeID)
	}

	if len(gceDisk.Users) > 0 {
		if !opts.Force {
			return nil, "", goof.New(
				"Volume already attached to different host")
		}
		ctx.Info("Automatically detaching volume from other instance")
		err = d.detachVolume(ctx, gceDisk)
		if err != nil {
			return nil, "", goof.WithError(
				"Error detaching volume during force attach",
				err)
		}
	}

	err = d.attachVolume(ctx, &instanceName, zone, &volumeID)
	if err != nil {
		return nil, "", err
	}

	vol, err := d.VolumeInspect(
		ctx, volumeID, &types.VolumeInspectOpts{
			Attachments: types.VolAttReq,
			Opts:        opts.Opts,
		},
	)
	if err != nil {
		return nil, "", goof.WithError("Error getting volume", err)
	}

	return vol, volumeID, nil
}
179195
func (util *RBDUtil) DetachDisk(plugin *rbdPlugin, deviceMountPath string, device string) error {
	if len(device) == 0 {
		return fmt.Errorf("DetachDisk failed , device is empty")
	}

	exec := plugin.host.GetExec(plugin.GetPluginName())

	var rbdCmd string

	// Unlike map, we cannot fallthrough for unmap
	// the tool to unmap is based on device type
	if strings.HasPrefix(device, "/dev/nbd") {
		rbdCmd = "rbd-nbd"
	} else {
		rbdCmd = "rbd"
	}

	// rbd unmap
	output, err := exec.Run(rbdCmd, "unmap", device)
	if err != nil {
		return rbdErrors(err, fmt.Errorf("rbd: failed to unmap device %s, error %v, rbd output: %v", device, err, output))
	}
	klog.V(3).Infof("rbd: successfully unmap device %s", device)

	// Currently, we don't persist rbd info on the disk, but for backward
	// compatbility, we need to clean it if found.
	rbdFile := filepath.Join(deviceMountPath, "rbd.json")
	exists, err := utilpath.Exists(utilpath.CheckFollowSymlink, rbdFile)
	if err != nil {
		return err
	}
	if exists {
		klog.V(3).Infof("rbd: old rbd.json is found under %s, cleaning it", deviceMountPath)
		err = util.cleanOldRBDFile(plugin, rbdFile)
		if err != nil {
			klog.Errorf("rbd: failed to clean %s", rbdFile)
			return err
		}
		klog.V(3).Infof("rbd: successfully remove %s", rbdFile)
	}
	return nil
}
1215063
func (util *DiskUtil) DetachDisk(cd *cinderVolumeUnmounter) error {
	globalPDPath := makeGlobalPDName(cd.plugin.host, cd.pdName)
	if err := cd.mounter.Unmount(globalPDPath); err != nil {
		return err
	}
	if err := os.Remove(globalPDPath); err != nil {
		return err
	}
	klog.V(2).Infof("Successfully unmounted main device: %s\n", globalPDPath)

	cloud, err := cd.plugin.getCloudProvider()
	if err != nil {
		return err
	}
	instanceid, err := cloud.InstanceID()
	if err != nil {
		return err
	}
	if err = cloud.DetachDisk(instanceid, cd.pdName); err != nil {
		return err
	}
	klog.V(2).Infof("Successfully detached cinder volume %s", cd.pdName)
	return nil
}
499518
func (dds *Delayed) DiskUsage() (uint64, error) {
	dds.delay.Wait()
	return ds.DiskUsage(dds.ds)
}
1119410
func (d *driver) VolumeAttach(
	ctx types.Context,
	volumeID string,
	opts *types.VolumeAttachOpts) (*types.Volume, string, error) {
	// Initialize for logging
	fields := map[string]interface{}{
		"provider": d.Name(),
		"volumeID": volumeID,
	}
	// review volume with attachments to any host
	ec2vols, err := d.getVolume(ctx, volumeID, "")
	if err != nil {
		return nil, "", goof.WithFieldsE(fields, "error getting volume", err)
	}
	volumes, convErr := d.toTypesVolume(
		ctx, ec2vols, types.VolAttReqTrue)
	if convErr != nil {
		return nil, "", goof.WithError(
			"error converting to types.Volume", convErr)
	}

	// Check if there a volume to attach
	if len(volumes) == 0 {
		return nil, "", goof.New("no volume found")
	}

	// Check if volume is already attached
	if len(volumes[0].Attachments) > 0 {
		// Detach already attached volume if forced
		if !opts.Force {
			return nil, "", errVolAlreadyAttached
		}
		_, err := d.VolumeDetach(
			ctx,
			volumeID,
			&types.VolumeDetachOpts{
				Force: opts.Force,
				Opts:  opts.Opts,
			})
		if err != nil {
			return nil, "", goof.WithError("error detaching volume", err)
		}
	}

	if opts.NextDevice == nil {
		return nil, "", errMissingNextDevice
	}

	// tagName: e.g. "vol1"
	tagName := volumes[0].Name
	if tagName == "" {
		return nil, "", goof.WithFields(fields, "error empty Name tag")
	}
	// get fcvol constituent disks by Name tag
	ec2vols, err = d.getVolume(ctx, "", tagName)
	if err != nil {
		return nil, "", goof.WithError("error getting FittedCloud volumes", err)
	}

	// Make sure all volumes are FittedCloudCreated
	allOK := true
	for _, volume := range ec2vols {
		if d.fittedcloudCreated(volume.Tags) == "" {
			allOK = false
			break
		}
	}
	if !allOK {
		return nil, "", goof.WithFields(fields, "not a FittedCloudCreated volume")
	}

	// Attach constituent disks
	nextDevice := *opts.NextDevice
	for _, volume := range ec2vols {
		if nextDevice == "" {
			return nil, "", errMissingNextDevice
		}
		// Attach volume via helper function which uses EC2 API call
		err = d.attachVolume(ctx, *volume.VolumeId, volumes[0].Name, nextDevice)
		if err != nil {
			return nil, "", goof.WithFieldsE(
				log.Fields{
					"provider": d.Name(),
					"volumeID": volume.VolumeId},
				"error attaching volume",
				err,
			)
		}
		// Wait for volume's status to update
		err = d.waitVolumeComplete(ctx, *volume.VolumeId, waitVolumeAttach)
		if err != nil {
			return nil, "", goof.WithError("error waiting for volume attach", err)
		}
		nextDevice, _ = fcUtils.NextDevice(ctx, apiUtils.NewStore())
	}

	// Check if successful attach
	attachedVol, err := d.VolumeInspect(
		ctx, volumeID, &types.VolumeInspectOpts{
			Attachments: types.VolAttReqTrue,
			Opts:        opts.Opts,
		})
	if err != nil {
		return nil, "", goof.WithError("error getting volume", err)
	}

	// Token is the attachment's device name, which will be matched
	// to the executor's device ID
	return attachedVol, *opts.NextDevice, nil
}
407018
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
	args := []string{"-nd", "-o", "FSTYPE", disk}
	cmd := mounter.Runner.Command("lsblk", args...)
	dataOut, err := cmd.CombinedOutput()
	output := strings.TrimSpace(string(dataOut))

	// TODO (#13212): check if this disk has partitions and return false, and
	// an error if so.

	if err != nil {
		return false, err
	}

	return output == "", nil
}