/* * Copyright (c) 2012-2015 iWave Software LLC * All Rights Reserved */ package com.emc.sa.service.vmware.block.tasks; import java.util.List; import org.apache.commons.lang.StringUtils; import com.emc.sa.engine.ExecutionTask; import com.emc.sa.engine.ExecutionUtils; import com.emc.storageos.model.block.BlockObjectRestRep; import com.iwave.ext.linux.util.VolumeWWNUtils; import com.iwave.ext.vmware.HostStorageAPI; import com.iwave.ext.vmware.VMwareUtils; import com.vmware.vim25.HostScsiDisk; import com.vmware.vim25.ScsiLunState; import com.vmware.vim25.mo.HostSystem; public class FindHostScsiDiskForLun extends ExecutionTask<HostScsiDisk> { private static long SECONDS = 1000; private static long MINUTES = 60 * SECONDS; private static long FIND_DISK_TIMEOUT = 10 * MINUTES; // 10 Minutes private static long VALID_STATE_TIMEOUT = 20 * MINUTES; // 20 Minutes private static long FIND_DISK_DELAY = 15 * SECONDS; // 15 Seconds private static long VALID_STATE_DELAY = 5 * SECONDS; // 5 Seconds private HostSystem host; private String lunDiskName; private HostStorageAPI storageAPI; private BlockObjectRestRep volume; private boolean availableDiskOnly = false; // By default, fail if the lun isn't found. Some other operations (like validate boot volume before delete) // would like to control the failure handling. private boolean throwIfNotFound = true; /** * Finds the SCSI disk on the host system that matches the volume. * * @param host the host system * @param volume the volume to find */ public FindHostScsiDiskForLun(HostSystem host, BlockObjectRestRep volume) { this.host = host; this.volume = volume; this.storageAPI = new HostStorageAPI(host); this.lunDiskName = VMwareUtils.CANONICAL_NAME_PREFIX + StringUtils.lowerCase(volume.getWwn()); provideDetailArgs(host.getName(), lunDiskName); } /** * Finds the SCSI disk on the host system that matches the volume. * * @param host the host system * @param volume the volume to find * @param availableDiskOnly if true, only find available disk for VMFS. if false, find disk even if it's not available for VMFS. */ public FindHostScsiDiskForLun(HostSystem host, BlockObjectRestRep volume, boolean availableDiskOnly) { this(host, volume); this.availableDiskOnly = availableDiskOnly; } /** * Finds the SCSI disk on the host system that matches the volume. * * @param host * the host system * @param volume * the volume to find * @param availableDiskOnly * if true, only find available disk for VMFS. if false, find disk even if it's not available for VMFS. * @param throwIfNotFound * throws an exception if the lun is not found. (defaults to true) */ public FindHostScsiDiskForLun(HostSystem host, BlockObjectRestRep volume, boolean availableDiskOnly, boolean throwIfNotFound) { this(host, volume, availableDiskOnly); this.throwIfNotFound = throwIfNotFound; } @Override public HostScsiDisk executeTask() throws Exception { HostScsiDisk lun = findLun(); lun = waitForValidState(lun); return lun; } private boolean canRetry(long start, long length) { long timeout = start + length; return System.currentTimeMillis() < timeout; } private HostScsiDisk findLun() { HostScsiDisk lun = getLunDisk(); long startTime = System.currentTimeMillis(); while ((lun == null) && canRetry(startTime, FIND_DISK_TIMEOUT)) { rescan(); lun = getLunDisk(); } if (lun == null) { diskNotFound(true); } logInfo("find.host.scsi.lun", lunDiskName); return lun; } private HostScsiDisk getLunDisk() { List<HostScsiDisk> scsiDisks = storageAPI.listScsiDisks(); // List all disks and attach the disk if it is found for (HostScsiDisk entry : scsiDisks) { if (VolumeWWNUtils.wwnMatches(VMwareUtils.getDiskWwn(entry), volume.getWwn()) && VMwareUtils.isDiskOff(entry)) { attachDisk(entry); } } if (availableDiskOnly) { scsiDisks = storageAPI.queryAvailableDisksForVmfs(null); } else { scsiDisks = storageAPI.listScsiDisks(); } for (HostScsiDisk entry : scsiDisks) { if (VolumeWWNUtils.wwnMatches(VMwareUtils.getDiskWwn(entry), volume.getWwn())) { return entry; } } return null; } /** * Attaches the scsi disk to the host * * @param disk the scsi disk to attach */ private void attachDisk(HostScsiDisk disk) { logInfo("find.host.scsi.lun.esx.attach", lunDiskName, host.getName()); new HostStorageAPI(host).attachScsiLun(disk); } private void rescan() { pause(FIND_DISK_DELAY); logInfo("find.host.scsi.lun.esx.rescan", host.getName()); new HostStorageAPI(host).rescanHBAs(); } /** * Waits for a valid state of the given scsi disk. * If the disk is in an 'off' state, the disk is attached to the host. * * @param disk the scsi disk to monitor * @return the scsi disk once it is in a valid state */ private HostScsiDisk waitForValidState(HostScsiDisk disk) { logInfo("find.host.scsi.lun.esx.wait.valid", lunDiskName, host.getName()); long startTime = System.currentTimeMillis(); while ((disk == null || !isValidState(disk)) && canRetry(startTime, VALID_STATE_TIMEOUT)) { pause(VALID_STATE_DELAY); disk = getLunDisk(); if (disk == null) { diskNotFound(false); } else if (VMwareUtils.isDiskOff(disk)) { attachDisk(disk); } } if (disk == null) { diskNotFound(true); } else if (!isValidState(disk)) { diskInvalid(disk); } return disk; } private boolean isValidState(HostScsiDisk disk) { String[] state = disk.getOperationalState(); if (state == null || state.length == 0) { return false; } String primaryState = state[0]; if (StringUtils.equals(primaryState, ScsiLunState.ok.name()) || StringUtils.equals(primaryState, ScsiLunState.degraded.name())) { logInfo("find.host.scsi.lun.esx.valid", lunDiskName, host.getName(), StringUtils.join(state, ", ")); return true; } else { logInfo("find.host.scsi.lun.esx.invalid", lunDiskName, host.getName(), StringUtils.join(state, ", ")); return false; } } private void pause(long delay) { try { Thread.sleep(delay); } catch (InterruptedException e) { warn(e); Thread.currentThread().interrupt(); } } private void diskNotFound(boolean fail) { if (fail && this.throwIfNotFound) { if (availableDiskOnly) { throw stateException("FindHostScsiDiskForLun.illegalState.diskNotFoundCheckDatastoreRDM", lunDiskName, host.getName()); } else { throw stateException("FindHostScsiDiskForLun.illegalState.diskNotFound", lunDiskName, host.getName()); } } else { logInfo("FindHostScsiDiskForLun.illegalState.diskNotFound", lunDiskName, host.getName()); } } private void diskInvalid(HostScsiDisk disk) { String name = StringUtils.defaultIfBlank(disk.getDisplayName(), lunDiskName); String state = displayState(disk); throw stateException("FindHostScsiDiskForLun.illegalState.invalidState", name, host.getName(), state); } private String displayState(HostScsiDisk disk) { try { String[] state = disk.getOperationalState(); if (state == null || state.length == 0) { return ExecutionUtils.getMessage("FindHostScsiDiskForLun.unknownState"); } return StringUtils.join(state, ", "); } catch (RuntimeException e) { return ExecutionUtils.getMessage("FindHostScsiDiskForLun.unknownState"); } } }