/* * Copyright (c) 2012-2015 iWave Software LLC * All Rights Reserved */ package com.emc.sa.service.linux; import java.net.URI; import java.util.List; import java.util.Set; import org.apache.commons.lang.StringUtils; import com.emc.sa.engine.ExecutionUtils; import com.emc.sa.engine.bind.BindingUtils; import com.emc.sa.service.vipr.block.BlockStorageUtils; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.model.block.BlockObjectRestRep; import com.emc.storageos.model.block.VolumeRestRep; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.iwave.ext.linux.LinuxSystemCLI; import com.iwave.ext.linux.model.MountPoint; import com.iwave.ext.linux.model.MultiPathEntry; import com.iwave.ext.linux.model.PowerPathDevice; import com.iwave.ext.linux.util.VolumeWWNUtils; public class UnmountBlockVolumeHelper { private final LinuxSupport linux; public static UnmountBlockVolumeHelper createHelper(LinuxSystemCLI linuxSystem, List<Initiator> hostPorts) { LinuxSupport linuxSupport = new LinuxSupport(linuxSystem, hostPorts); UnmountBlockVolumeHelper unmountBlockVolumeHelper = new UnmountBlockVolumeHelper(linuxSupport); BindingUtils.bind(unmountBlockVolumeHelper, ExecutionUtils.currentContext().getParameters()); return unmountBlockVolumeHelper; } private UnmountBlockVolumeHelper(LinuxSupport linuxSupport) { this.linux = linuxSupport; } /** * container class to hold all the specifications of a particular volume, including its mountpoint, * multipath/powerpath entries and related volumes */ public class VolumeSpec { public BlockObjectRestRep viprVolume; public MountPoint mountPoint; public List<MultiPathEntry> multipathEntries; public List<PowerPathDevice> powerpathDevices; public List<BlockObjectRestRep> relatedVolumes; public VolumeSpec(BlockObjectRestRep volume) { this.viprVolume = volume; } } /** The list of VolumeSpec objects which represents the volumes to unmount and their associated metadata. */ private List<VolumeSpec> volumes; /** The flag which indicates whether we're using EMC PowerPath for multipathing or not. */ private boolean usePowerPath; public void setVolumes(List<? extends BlockObjectRestRep> volumes) { this.volumes = Lists.newArrayList(); for (BlockObjectRestRep volume : volumes) { this.volumes.add(new VolumeSpec(volume)); } } /** search through the volumes list to find the {@link VolumeSpec} which has the given wwn. */ private VolumeSpec getVolumeSpecByWwn(String wwn) { for (VolumeSpec volume : volumes) { if (VolumeWWNUtils.wwnMatches(wwn, volume.viprVolume.getWwn())) { return volume; } } return null; } /** * search through the volumes list to find the {@link VolumeSpec} which has the given wwn and return its * 'viprVolume' which is a {@link VolumeRestRep} */ private BlockObjectRestRep findVolumeRestRepByWwn(String relatedWwn) { VolumeSpec relatedVolume = getVolumeSpecByWwn(relatedWwn); if (relatedVolume != null) { return relatedVolume.viprVolume; } return null; } public void precheck() { linux.findMountPoints(volumes); usePowerPath = linux.checkForMultipathingSoftware(); if (usePowerPath) { linux.findPowerPathDevices(volumes); } else { linux.findMultipathEntries(volumes); } linux.ensureVolumesAreMounted(volumes); findRelatedVolumes(); } /** * Finds the volumes related to the mount points selected. When a mount point is removed, all related volumes have * to have their mount point tags removed. */ private void findRelatedVolumes() { for (VolumeSpec volume : volumes) { volume.relatedVolumes = Lists.newArrayList(); Set<String> volumeWwns = getVolumeWwns(volume); for (String relatedWwn : volumeWwns) { BlockObjectRestRep related = findVolumeRestRepByWwn(relatedWwn); if (related == null) { related = BlockStorageUtils.getVolumeByWWN(relatedWwn); } if (related != null) { volume.relatedVolumes.add(related); } } } } public void unmountVolumes() { Set<URI> untaggedVolumeIds = Sets.newHashSet(); for (VolumeSpec volume : volumes) { // unmount the volume linux.unmountPath(volume.mountPoint.getPath()); // remove from fstab linux.removeFromFSTab(volume.mountPoint.getPath()); // remove mount point tag from all volumes for this mount point for (BlockObjectRestRep mountedVolume : volume.relatedVolumes) { linux.removeVolumeMountPointTag(mountedVolume); untaggedVolumeIds.add(mountedVolume.getId()); } // remove multipath/powerpath entries if (usePowerPath) { linux.removePowerPathDevices(volume.powerpathDevices); } else { linux.removeMultipathEntries(volume.multipathEntries); } // delete the directory entry if it's empty if (linux.isDirectoryEmpty(volume.mountPoint.getPath())) { linux.deleteDirectory(volume.mountPoint.getPath()); } } // Ensure all volumes have had their mount point tag removed for (VolumeSpec volume : volumes) { if (untaggedVolumeIds.add(volume.viprVolume.getId())) { linux.removeVolumeMountPointTag(volume.viprVolume); } } } private Set<String> getVolumeWwns(VolumeSpec volume) { Set<String> volumeWwns = Sets.newHashSet(); if (usePowerPath) { for (PowerPathDevice device : volume.powerpathDevices) { volumeWwns.add(getVolumeWwn(device)); } } else { for (MultiPathEntry entry : volume.multipathEntries) { volumeWwns.add(getVolumeWwn(entry)); } } return volumeWwns; } private String getVolumeWwn(PowerPathDevice device) { return StringUtils.upperCase(device.getWwn()); } private String getVolumeWwn(MultiPathEntry entry) { String wwid = entry.getWwid(); // Multipath WWIDs start with an extra digit ('3') String wwn = StringUtils.substring(wwid, 1); return StringUtils.upperCase(wwn); } }