/* * Copyright (c) 2012-2015 iWave Software LLC * All Rights Reserved */ package com.iwave.ext.windows; import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.Map; import com.google.common.collect.Lists; import com.iwave.ext.windows.model.wmi.*; import com.iwave.ext.windows.scsi.DeviceIdentification; import com.iwave.ext.windows.winrm.WinRMSoapException; import com.iwave.ext.windows.winrm.wmi.*; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrBuilder; import org.apache.log4j.Logger; import com.google.common.collect.Maps; import com.iwave.ext.command.CommandOutput; import com.iwave.ext.command.HostRescanAdapter; import com.iwave.ext.windows.model.Disk; import com.iwave.ext.windows.parser.DiskParser; import com.iwave.ext.windows.winrm.WinRMException; import com.iwave.ext.windows.winrm.WinRMTarget; import com.iwave.ext.windows.winrm.winrs.WinRS; public class WindowsSystemWinRM implements HostRescanAdapter { private static final String LUN_KEY_FORMAT = "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d"; private static final String DEVICE_ID_PAGE = "DeviceIdentifierPage"; private static final Logger LOG = Logger.getLogger(WindowsSystemWinRM.class); private static final int MAX_SCAN_RETRIES = 2; private static final int SLEEP_BETWEEN_SCAN_ATTEMPTS = 10; private WinRMTarget target; private URI hostId; private URI clusterId; public WindowsSystemWinRM(WinRMTarget target) { this.target = target; } public WindowsSystemWinRM(String host, int port, boolean secure, String username, String password) { this.target = new WinRMTarget(host, port, secure, username, password); } public WinRMTarget getTarget() { return target; } public CommandOutput executeCommand(String commandLine) throws WinRMException { WinRS winrs = new WinRS(target); debug("Running: %s", commandLine); CommandOutput output = winrs.executeCommandLine(commandLine); if (output.getExitValue() != 0) { debug("Exit Value: %d", output.getExitValue()); } if (StringUtils.isNotBlank(output.getStdout())) { debug("STDOUT: \n%s", output.getStdout()); } if (StringUtils.isNotBlank(output.getStderr())) { debug("STDERR: \n%s", output.getStderr()); } return output; } public WindowsVersion getVersion() throws WinRMException { List<WindowsVersion> versions = new GetWindowsVersionQuery(target).execute(); return !versions.isEmpty() ? versions.get(0) : null; } public String rescanDisks() throws WinRMException { String output = ""; int scanAttempt = 1; while (scanAttempt <= MAX_SCAN_RETRIES) { try { info(String.format("Rescan attempt %s/%s", scanAttempt, MAX_SCAN_RETRIES)); output = diskPart(WindowsUtils.getRescanCommands()); break; } catch (WinRMException wrme) { if (scanAttempt == MAX_SCAN_RETRIES) { throw wrme; } else { scanAttempt++; error(String.format("Encountered exception during rescan. " + "Another rescan attempt will be made in %s seconds. Exception: %s", SLEEP_BETWEEN_SCAN_ATTEMPTS, wrme.getMessage())); try { // Sleep between rescan attempts Thread.sleep(SLEEP_BETWEEN_SCAN_ATTEMPTS * 1000); } catch (InterruptedException e) { throw new WinRMException(e); } } } } info("Rescan complete."); return output; } @Override public void rescan() throws WinRMException { rescanDisks(); } public String formatAndMountDisk(int diskNumber, String fsType, String allocationUnitSize, String label, String mountpoint, String partitionType) throws WinRMException { return diskPart(WindowsUtils .getFormatAndMountDiskCommands(diskNumber, fsType, allocationUnitSize, label, mountpoint, partitionType)); } public String mountVolume(int volumeNumber, String mountpoint) throws WinRMException { return diskPart(WindowsUtils.getMountVolumeCommands(volumeNumber, mountpoint)); } public String unmountVolume(int volumeNumber, String mountpoint) throws WinRMException { return diskPart(WindowsUtils.getUnmountVolumeCommands(volumeNumber, mountpoint)); } public Disk detailDisk(int diskNumber) throws WinRMException { String output = diskPart(WindowsUtils.getDetailDiskCommands(diskNumber)); DiskParser parser = new DiskParser(); List<Disk> disks = parser.parseDisks(output); if (disks.isEmpty()) { return null; } else { return disks.get(0); } } public String onlineDisk(int diskNumber, boolean currentReadOnlyState) throws WinRMException { return diskPart(WindowsUtils.getOnlineDiskCommands(diskNumber, currentReadOnlyState)); } public String offlineDisk(int diskNumber) throws WinRMException { return diskPart(WindowsUtils.getOfflineDiskCommands(diskNumber)); } public String extendVolume(String mountpoint) throws WinRMException { return diskPart(WindowsUtils.getExtendVolumeCommands(mountpoint)); } public String listDisk() throws WinRMException { return diskPart(WindowsUtils.getListDiskCommands()); } public String diskPart(String... commands) throws WinRMException { return diskPart(Arrays.asList(commands)); } public String diskPart(List<String> commands) throws WinRMException { StrBuilder sb = new StrBuilder(); sb.append("("); for (int i = 0; i < commands.size(); i++) { sb.appendSeparator(" && ", i); sb.append("echo ").append(commands.get(i)); } sb.append(" && echo EXIT) | (CHCP 437 & DISKPART)"); CommandOutput output = executeCommand(sb.toString()); String error = WindowsUtils.getDiskPartError(output); if (StringUtils.isNotBlank(error)) { error("DiskPart Error: %s", error); throw new WinRMException(error); } return output.getStdout(); } public void makeDirectory(String directory) throws WinRMException { executeCommand("mkdir " + directory); } public CommandOutput getDirectoryContents(String directory) throws WinRMException { return executeCommand("dir " + directory); } public void deleteDirectory(String directory) throws WinRMException { executeCommand("rmdir " + directory + " /s /q"); } public void assignLabel(String disk, String label) throws WinRMException { if (StringUtils.isNotBlank(disk)) { if (disk.length() == 1 && Character.isLetter(disk.charAt(0))) { executeCommand("label " + disk + ": " + label); } else { // Windows mount points reported in diskpart have a trailing slash, but you can't provide it // in that form to the label command String mountPoint = StringUtils.removeEnd(disk, "\\"); executeCommand("label /mp " + mountPoint + " " + label); } } } public List<FibreChannelHBA> listFibreChannelHBAs() throws WinRMException { return new ListFibreChannelHBAsQuery(target).execute(); } public List<NetworkAdapter> listNetworkAdapters() throws WinRMException { return new ListNetworkAdaptersQuery(target).execute(); } public List<String> listIScsiInitiators() throws WinRMException { return new ListIScsiInitiatorsQuery(target).execute(); } public List<IScsiSession> listIScsiSessions() throws WinRMException { return new ListIScsiSessionsQuery(target).execute(); } public List<Win32Service> listServices() throws WinRMException { return new ListWin32ServicesQuery(target).execute(); } public List<DiskDrive> listDiskDrives() throws WinRMException { return new ListDiskDrivesQuery(target).execute(); } public List<Volume> listVolumes() throws WinRMException { return new ListVolumesQuery(target).execute(); } public List<MSCluster> listClusters() throws WinRMException { return new ListClustersQuery(target).execute(); } public boolean hasActiveClusters() throws WinRMException { try { listClusters(); return true; } catch (WinRMSoapException e) { if (e.getMessage().equals("There are no more endpoints available from the endpoint mapper.")) { return false; } else { throw e; } } } public boolean isClustered() throws WinRMException { Win32Service clusterService = WindowsClusterUtils.findClusterService(listServices()); if (clusterService != null) { if (clusterService.isStarted()) { if (hasActiveClusters()) { return true; } } } return false; } public List<MSClusterNetworkInterface> listClusterNetworkInterfaces() throws WinRMException { return new ListClusterNetworkInterfaceQuery(target).execute(); } public List<FibreChannelTargetMapping> getTargetMapping(FibreChannelHBA hba) throws WinRMException { return getTargetMapping(hba.getInstanceName(), hba.getPortWWN()); } public List<FibreChannelTargetMapping> getTargetMapping(String instanceName, String portWWN) throws WinRMException { return new GetFcpTargetMappingMethod(target, instanceName, portWWN).execute(); } public List<String> getRegistryKeys(String subKey) throws WinRMException { return new EnumerateRegistryKeysMethod(target, subKey).execute(); } public List<RegistryValueDef> getRegistryValues(String subKey) throws WinRMException { return new EnumerateRegistryValuesMethod(target, subKey).execute(); } public Object getRegistryValue(String subKey, RegistryValueDef valueDef) throws WinRMException { return getRegistryValue(subKey, valueDef.getName(), valueDef.getType()); } public Object getRegistryValue(String subKey, String valueName, RegistryValueType valueType) throws WinRMException { debug("Getting registry value: %s{%s} [%s]", subKey, valueName, valueType); GetRegistryValueMethod method = new GetRegistryValueMethod(target); method.setSubKeyName(subKey); method.setValueName(valueName); method.setValueType(valueType); Object result = method.execute(); debug("%s {%s} = %s", subKey, valueName, result); return result; } public String getRegistryValueAsString(String subKey, String valueName) throws WinRMException { return (String) getRegistryValue(subKey, valueName, RegistryValueType.STRING); } public String getRegistryValueAsExpandedString(String subKey, String valueName) throws WinRMException { return (String) getRegistryValue(subKey, valueName, RegistryValueType.EXPANDED_STRING); } public String[] getRegistryValueAsMultiString(String subKey, String valueName) throws WinRMException { return (String[]) getRegistryValue(subKey, valueName, RegistryValueType.MULTI_STRING); } public byte[] getRegistryValueAsBinary(String subKey, String valueName) throws WinRMException { return (byte[]) getRegistryValue(subKey, valueName, RegistryValueType.BINARY); } public int getRegistryValueAsDWord(String subKey, String valueName) throws WinRMException { return (Integer) getRegistryValue(subKey, valueName, RegistryValueType.DWORD); } public byte[] getDeviceIdentifierPage(DiskDrive disk) throws WinRMException { String regKey = String.format(LUN_KEY_FORMAT, disk.getScsiPort(), disk.getScsiBus(), disk.getScsiTarget(), disk.getScsiLun()); return getRegistryValueAsBinary(regKey, DEVICE_ID_PAGE); } public String getWwid(DiskDrive disk) throws WinRMException { byte[] deviceIdentifierPage = getDeviceIdentifierPage(disk); if (deviceIdentifierPage != null) { try { String wwid = DeviceIdentification.getWwid(deviceIdentifierPage); debug("Disk: %s, WWID: %s", disk.getDeviceId(), wwid); return wwid; } catch (IllegalArgumentException e) { info("Could not retrieve WWID from Disk: %s", disk.getDeviceId()); return null; } } else { return null; } } public String addDiskToCluster(String diskId) throws WinRMException { AddDiskToClusterMethod method = new AddDiskToClusterMethod(target, diskId); return method.execute(); } public void deleteClusterResource(String resourceName) throws WinRMException { DeleteClusterResourceMethod method = new DeleteClusterResourceMethod(target, resourceName); method.execute(); } public void offlineClusterResource(String resourceName) throws WinRMException { OfflineClusteredResourceMethod method = new OfflineClusteredResourceMethod(target, resourceName); method.execute(); } public Map<String, String> getDiskToClusterResourceMap() throws WinRMException { GetDiskToResourceMethod method = new GetDiskToResourceMethod(target); List<ResourceToDisk> resourceToDisks = method.execute(); Map<String, String> diskToResource = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER); for (ResourceToDisk resourceToDisk : resourceToDisks) { diskToResource.put(resourceToDisk.getDiskId(), resourceToDisk.getResourceName()); } return diskToResource; } public Map<String, List<MSClusterNetworkInterface>> getClusterToNetworkInterfaces() throws WinRMException { List<MSClusterToNetworkInterface> clusterToNetworkInterfaces = new ListClusterToNetworkInterfaceQuery(target).execute(); List<MSClusterNetworkInterface> networkInterfaces = new ListClusterNetworkInterfaceQuery(target).execute(); Map<String, List<MSClusterNetworkInterface>> clusterNetworkInterfacesMap = Maps.newHashMap(); for (MSClusterToNetworkInterface networkInterface : clusterToNetworkInterfaces) { if (!clusterNetworkInterfacesMap.containsKey(networkInterface.getClusterName())) { clusterNetworkInterfacesMap.put(networkInterface.getClusterName(), Lists.<MSClusterNetworkInterface> newArrayList()); } clusterNetworkInterfacesMap.get(networkInterface.getClusterName()).add( findNetworkInterface(networkInterface.getNetworkInterface(), networkInterfaces)); } return clusterNetworkInterfacesMap; } public URI getHostId() { return hostId; } public void setHostId(URI hostId) { this.hostId = hostId; } public URI getClusterId() { return clusterId; } public void setClusterId(URI clusterId) { this.clusterId = clusterId; } protected void error(String message, Object... args) { if (args.length > 0) { LOG.error(String.format(message, args)); } else { LOG.error(message); } } protected void info(String message, Object... args) { if (LOG.isInfoEnabled()) { if (args.length > 0) { LOG.info(String.format(message, args)); } else { LOG.info(message); } } } protected void debug(String message, Object... args) { if (LOG.isDebugEnabled()) { if (args.length > 0) { LOG.debug(String.format(message, args)); } else { LOG.debug(message); } } } private MSClusterNetworkInterface findNetworkInterface(String interfaceName, List<MSClusterNetworkInterface> networkInterfaces) { for (MSClusterNetworkInterface networkInterface : networkInterfaces) { if (networkInterface.getName().equals(interfaceName)) { return networkInterface; } } throw new IllegalStateException("Cluster Network Interface " + interfaceName + " Not Found"); } }