/*
* Copyright (c) 2012-2015 iWave Software LLC
* All Rights Reserved
*/
package com.emc.sa.service.windows;
import static com.emc.sa.service.ServiceParams.BLOCK_SIZE;
import static com.emc.sa.service.ServiceParams.DO_FORMAT;
import static com.emc.sa.service.ServiceParams.PARTITION_TYPE;
import static com.emc.sa.service.ServiceParams.FILE_SYSTEM_TYPE;
import static com.emc.sa.service.ServiceParams.LABEL;
import static com.emc.sa.service.ServiceParams.MOUNT_POINT;
import static com.emc.sa.service.vipr.ViPRExecutionUtils.logInfo;
import static com.iwave.ext.windows.WindowsUtils.isFat32;
import static com.iwave.ext.windows.WindowsUtils.isFat32CapacityInBytesTooLarge;
import static com.iwave.ext.windows.WindowsUtils.isMBR;
import static com.iwave.ext.windows.WindowsUtils.isMBRCapacityInBytesTooLarge;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
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.engine.bind.Param;
import com.emc.sa.service.vipr.block.BlockStorageUtils;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.model.block.BlockObjectRestRep;
import com.google.common.collect.Lists;
import com.iwave.ext.windows.WindowsSystemWinRM;
import com.iwave.ext.windows.model.Disk;
import com.iwave.ext.windows.model.Volume;
import com.iwave.ext.windows.model.wmi.DiskDrive;
public class MountBlockVolumeHelper {
private static final short MAX_FAT32_LABEL_LENGTH = 11;
private static final short MAX_NTFS_LABEL_LENGTH = 32;
private final WindowsSupport windows;
private final String hostname;
@Param(MOUNT_POINT)
protected String mountPoint;
@Param(value = FILE_SYSTEM_TYPE, required = false)
protected String fileSystemType;
@Param(value = BLOCK_SIZE, required = false)
protected String blockSize;
@Param(value = DO_FORMAT, required = false)
protected boolean doFormat = true;
@Param(value = PARTITION_TYPE, required = false)
protected String partitionType;
@Param(value = LABEL, required = false)
protected String label;
/** The amount of time to wait between attempts to refresh the storage. */
private long refreshDelay = 15 * 1000;
/** The number of attempts to try refreshing the storage waiting for a disk to appear. */
private int refreshAttempts = 60;
private long volumeCapacityInBytes = 0;
private Set<String> assignedMountpoints;
public static List<MountBlockVolumeHelper> createHelpers(List<WindowsSystemWinRM> windowsSystems, long volumeCapacityInBytes) {
List<MountBlockVolumeHelper> helpers = Lists.newArrayList();
for (WindowsSystemWinRM windowsSystem : windowsSystems) {
WindowsSupport windowsSupport = new WindowsSupport(windowsSystem);
MountBlockVolumeHelper mountBlockVolumeHelper = new MountBlockVolumeHelper(windowsSupport, volumeCapacityInBytes);
BindingUtils.bind(mountBlockVolumeHelper, ExecutionUtils.currentContext().getParameters());
helpers.add(mountBlockVolumeHelper);
}
return helpers;
}
private MountBlockVolumeHelper(WindowsSupport windowsSupport, long volumeCapacityInBytes) {
this.windows = windowsSupport;
this.hostname = windowsSupport.getHostName();
this.volumeCapacityInBytes = volumeCapacityInBytes;
}
public long getRefreshDelay() {
return refreshDelay;
}
public void setRefreshDelay(long refreshDelay) {
this.refreshDelay = refreshDelay;
}
public int getRefreshAttempts() {
return refreshAttempts;
}
public void setRefreshAttempts(int refreshAttempts) {
this.refreshAttempts = refreshAttempts;
}
public void precheck() {
verifyCapacity();
verifyDriveLabel();
windows.verifyWinRM();
windows.rescanDisks();
if (windows.isClustered()) {
logInfo("win.mount.block.volume.mount.share");
windows.verifyClusterSupport();
windows.verifyMountPointIsDriveLetter(mountPoint);
}
assignedMountpoints = windows.getAssignedDriveLetters();
if (WindowsUtils.isMountPointDriveLetterOnly(mountPoint)) {
windows.verifyMountPointLetterIsAvailable(mountPoint, assignedMountpoints);
}
else {
logInfo("win.mount.block.volume.mount.path");
windows.verifyMountPointHostDriveIsMounted(mountPoint, assignedMountpoints);
windows.verifyMountPointIsNotShared(mountPoint);
}
}
public void verifyMountConfiguration(BlockObjectRestRep volume) {
if (BlockStorageUtils.isVolumeMounted(volume) && doFormat) {
ExecutionUtils.fail("failTask.verifyMountConfiguration", volume.getName(), volume.getName());
}
}
public void verifyDriveLabel() {
if (isFat32(fileSystemType) && label.length() > MAX_FAT32_LABEL_LENGTH) {
ExecutionUtils.fail("failTask.MountBlockVolumeHelper.fat32.driveLabelTooLarge", label);
} else if (label.length() > MAX_NTFS_LABEL_LENGTH) {
ExecutionUtils.fail("failTask.MountBlockVolumeHelper.ntfs.driveLabelTooLarge", label);
}
}
public void verifyCapacity() {
if (this.doFormat && isFat32(fileSystemType)) {
if (isFat32CapacityInBytesTooLarge(volumeCapacityInBytes)) {
ExecutionUtils.fail("failTask.MountBlockVolumeHelper.fat32.tooLarge", fileSystemType);
}
}
if (this.doFormat && isMBR(this.partitionType)) {
if (isMBRCapacityInBytesTooLarge(volumeCapacityInBytes)) {
ExecutionUtils.fail("failTask.MountBlockVolumeHelper.MBR.tooLarge", partitionType);
}
}
}
public void verifyClusterHosts(List<Host> hosts) {
if (windows.isClustered()) {
windows.verifyClusterHosts(hosts);
}
}
/**
* Mounts the given volumes. If {@link #doFormat} is true, the volume is formatted first.
*
* @param volume
* the volumes to mount.
*/
public DiskDrive mountVolume(BlockObjectRestRep volume) {
windows.rescanDisks();
Map<? extends BlockObjectRestRep, DiskDrive> volume2disk =
windows.discoverDisks(Collections.singleton(volume), refreshAttempts, refreshDelay);
DiskDrive disk = volume2disk.get(volume);
mount(volume, disk, getMountPoint());
return disk;
}
private String getMountPoint() {
String mountpoint = WindowsUtils.normalizeMountPath(mountPoint);
if (!WindowsUtils.isMountPointDriveLetterOnly(mountpoint)) {
logInfo("win.mount.block.volume.mkdir.mount", mountpoint);
windows.makeDirectory(mountpoint);
}
return mountpoint;
}
/**
* Mounts the volume. If {@link #doFormat} is true, the volume is formatted first.
*
* @param volume
* the volume to mount.
* @param mountPoint
* the mount point to assign the volume. An empty mount point will cause the system to auto-assign a
* drive letter.
*/
public void mount(BlockObjectRestRep volume, DiskDrive disk, String mountPoint) {
Disk diskDetail = detailDisk(disk);
boolean isOnline = diskDetail.isOnline();
if (!isOnline) {
logInfo("win.mount.block.volume.disk.online", hostname, disk.getNumber(), volume.getWwn());
windows.onlineDisk(diskDetail);
}
if (doFormat) {
logInfo("win.mount.block.volume.format", hostname, disk.getNumber(), fileSystemType, volume.getWwn());
windows.formatAndMount(disk, fileSystemType, getBlockSize(), getActualLabel(volume), mountPoint, partitionType);
}
else {
// If the disk was not online, detail it again since no volume information would have been available
if (!isOnline) {
diskDetail = detailDisk(disk);
}
if (diskDetail.getVolumes() == null || diskDetail.getVolumes().isEmpty()) {
ExecutionUtils
.fail("failTask.MountBlockVolumeHelper.noVolumes", disk.getName(), hostname, disk.getName(), disk.getNumber());
}
// Mount the first volume only
Volume diskVolume = diskDetail.getVolumes().get(0);
int volumeNumber = diskVolume.getNumber();
String label = StringUtils
.defaultIfBlank(diskVolume.getLabel(), ExecutionUtils.getMessage("MountBlockVolumeHelper.label.none"));
String fs = diskVolume.getFileSystem();
logInfo("win.mount.block.volume.mount", hostname, volumeNumber, label, fs, volume.getWwn());
windows.mountVolume(volumeNumber, mountPoint);
}
// Refresh the disk details
diskDetail = detailDisk(disk);
String assignedMountpoint = windows.getAssignedMountPoint(disk, diskDetail);
assignedMountpoints.add(assignedMountpoint);
logInfo(ExecutionUtils.getMessage("MountBlockVolumeHelper.log.mountpointToVolume", hostname, assignedMountpoint, volume.getWwn()));
// Check to see if the the desired volume label is different than the actual label if it wasn't formatted
if (!doFormat) {
Volume diskVolume = diskDetail.getVolumes().get(0);
String desiredLabel = getActualLabel(volume);
if (!StringUtils.defaultString(diskVolume.getLabel()).equals(desiredLabel)) {
windows.assignLabel(diskVolume, label);
}
}
windows.addVolumeMountPoint(volume, assignedMountpoint);
}
public Disk detailDisk(DiskDrive disk) {
Disk diskDetail;
diskDetail = windows.getDiskDetail(disk);
if (diskDetail == null) {
ExecutionUtils.fail("failTask.MountBlockVolumeHelper.couldNotDetailDisk", disk.getName(), hostname, disk.getNumber());
}
return diskDetail;
}
private String getActualLabel(BlockObjectRestRep volume) {
String actualLabel = label;
if (StringUtils.isBlank(actualLabel)) {
actualLabel = volume.getName();
}
return actualLabel;
}
private String getBlockSize() {
if (StringUtils.equalsIgnoreCase("DEFAULT", blockSize)) {
return StringUtils.EMPTY;
}
return blockSize;
}
public void rescanDisks() {
windows.rescanDisks();
}
public void addDisksToCluster(Collection<DiskDrive> diskDrives) {
for (DiskDrive diskDrive : diskDrives) {
addDiskToCluster(diskDrive);
}
}
public void addDiskToCluster(DiskDrive diskDrive) {
Disk diskDetail = windows.getDiskDetail(diskDrive);
String signature = "";
if (windows.isGuid(diskDetail.getDiskId())) {
signature = diskDetail.getDiskId();
} else {
signature = "" + Long.decode("0x" + diskDetail.getDiskId());
}
String resourceName = windows.addDiskToCluster(signature);
logInfo("win.mount.block.volume.added.disk.cluster", hostname, signature, resourceName);
}
}