/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.computesystemcontroller.impl.adapter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.emc.storageos.computesystemcontroller.exceptions.CompatibilityException;
import com.emc.storageos.computesystemcontroller.exceptions.ComputeSystemControllerException;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.DiscoveredDataObject.CompatibilityStatus;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.Host.HostType;
import com.emc.storageos.db.client.model.HostInterface.Protocol;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.IpInterface;
import com.emc.storageos.db.client.util.CommonTransformerFunctions;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.util.SanUtils;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.iwave.ext.command.CommandException;
import com.iwave.ext.linux.LinuxSystemCLI;
import com.iwave.ext.linux.command.MultipathCommand;
import com.iwave.ext.linux.command.MultipathException;
import com.iwave.ext.linux.command.powerpath.PowerPathException;
import com.iwave.ext.linux.command.powerpath.PowermtCheckRegistrationCommand;
import com.iwave.ext.linux.command.version.GetLinuxVersionLSBReleaseCommand;
import com.iwave.ext.linux.command.version.GetRedhatVersionCommandOne;
import com.iwave.ext.linux.command.version.GetRedhatVersionCommandTwo;
import com.iwave.ext.linux.command.version.GetRedhatVersionCommandThree;
import com.iwave.ext.linux.command.version.GetSuSEVersionCommand;
import com.iwave.ext.linux.command.version.LinuxVersionCommand;
import com.iwave.ext.linux.model.HBAInfo;
import com.iwave.ext.linux.model.IPInterface;
import com.iwave.ext.linux.model.LinuxVersion;
import com.iwave.ext.linux.model.LinuxVersion.LinuxDistribution;
import com.jcraft.jsch.JSchException;
@Component
public class LinuxHostDiscoveryAdapter extends AbstractHostDiscoveryAdapter {
private static final Logger LOG = LoggerFactory.getLogger(LinuxHostDiscoveryAdapter.class);
private static final String ETH0 = "eth0";
@Override
protected String getSupportedType() {
return HostType.Linux.name();
}
@Override
protected void discoverHost(Host host, HostStateChange changes) {
validateHost(host);
List<LinuxVersion> versions = getVersions(host);
LinuxVersion version = findVersion(versions);
host.setOsVersion(version.toString());
if (getVersionValidator().isValidLinuxVersion(version)) {
host.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.name());
save(host);
super.discoverHost(host, changes);
try {
checkMultipathSoftwareCompatibility(host);
} catch (CompatibilityException e) {
host.setCompatibilityStatus(CompatibilityStatus.INCOMPATIBLE.name());
save(host);
}
}
else {
host.setCompatibilityStatus(CompatibilityStatus.INCOMPATIBLE.name());
save(host);
throw ComputeSystemControllerException.exceptions.incompatibleLinuxHostVersion(
getSupportedType(), version.toString(), getVersionValidator().getSuSELinuxMinimumVersion(false).toString(),
getVersionValidator().getRedhatLinuxMinimumVersion(false).toString());
}
}
/**
* Returns the first valid version from list of versions.
* If no valid version is found, returns the first version or unknown version if list is empty.
*
* @param versions list of versions
* @return valid version or first version if no valid version found
*/
private LinuxVersion findVersion(List<LinuxVersion> versions) {
for (LinuxVersion version : versions) {
if (getVersionValidator().isValidLinuxVersion(version)) {
return version;
}
}
return !versions.isEmpty() ? versions.get(0) : new LinuxVersion(LinuxDistribution.UNKNOWN, "");
}
private void checkMultipathSoftwareCompatibility(Host host) {
LinuxSystemCLI cli = createLinuxCLI(host);
String powerpathMessage = null;
String multipathMessage = null;
try {
PowermtCheckRegistrationCommand command = new PowermtCheckRegistrationCommand();
cli.executeCommand(command);
// powerpath is installed
LOG.info("PowerPath is installed");
return;
} catch (PowerPathException e) {
powerpathMessage = e.getMessage();
LOG.info("PowerPath is unavailable: " + powerpathMessage);
}
try {
MultipathCommand command = new MultipathCommand();
command.addArgument("-l");
cli.executeCommand(command);
// multipath is installed
LOG.info("Multipath is installed");
return;
} catch (MultipathException e) {
multipathMessage = e.getMessage();
LOG.info("Multipath is unavailable: " + multipathMessage);
}
throw new CompatibilityException("No multipath software available: \n" + powerpathMessage + "\n"
+ multipathMessage);
}
@Override
public String getErrorMessage(Throwable t) {
Throwable rootCause = getRootCause(t);
if (rootCause instanceof JSchException) {
if (StringUtils.equals("Auth fail", rootCause.getMessage())) {
return "Login failed, invalid username or password";
}
}
return super.getErrorMessage(t);
}
protected void validateHost(Host host) {
createLinuxCLI(host).executeCommand("pwd");
}
protected List<LinuxVersion> getVersions(Host host) {
LinuxSystemCLI cli = createLinuxCLI(host);
List<LinuxVersion> versions = new ArrayList<LinuxVersion>();
LinuxVersionCommand[] commands = { new GetSuSEVersionCommand(), new GetRedhatVersionCommandOne(),
new GetRedhatVersionCommandTwo(), new GetRedhatVersionCommandThree(), new GetLinuxVersionLSBReleaseCommand() };
for (LinuxVersionCommand command : commands) {
try {
cli.executeCommand(command);
LinuxVersion version = command.getResults();
if (version != null) {
versions.add(version);
}
} catch (CommandException e) {
warn("Could not retrieve linux version", e);
}
}
if (versions.isEmpty()) {
error("Could not determine version of linux host %s", host.getLabel());
versions.add(new LinuxVersion(LinuxDistribution.UNKNOWN, ""));
}
return versions;
}
@Override
protected void discoverInitiators(Host host, List<Initiator> oldInitiators, HostStateChange changes) {
LinuxSystemCLI linux = createLinuxCLI(host);
List<Initiator> addedInitiators = Lists.newArrayList();
try {
for (HBAInfo hba : linux.listHBAs()) {
Initiator initiator;
String wwpn = SanUtils.normalizeWWN(hba.getWwpn());
if (findInitiatorByPort(oldInitiators, wwpn) == null) {
initiator = getOrCreateInitiator(host.getId(), oldInitiators, wwpn);
addedInitiators.add(initiator);
} else {
initiator = getOrCreateInitiator(host.getId(), oldInitiators, wwpn);
}
discoverFCInitiator(host, initiator, hba);
}
} catch (DeviceControllerException e) {
throw e;
} catch (Exception e) {
LOG.error("Failed to list FC Ports, skipping");
}
try {
for (String iqn : linux.listIQNs()) {
Initiator initiator;
if (findInitiatorByPort(oldInitiators, iqn) == null) {
initiator = getOrCreateInitiator(host.getId(), oldInitiators, iqn);
addedInitiators.add(initiator);
} else {
initiator = getOrCreateInitiator(host.getId(), oldInitiators, iqn);
}
discoverISCSIInitiator(host, initiator, iqn);
}
} catch (DeviceControllerException e) {
throw e;
} catch (Exception e) {
LOG.error("Failed to list iSCSI Ports, skipping");
}
try {
String cephPseudoPort = String.format("rbd:%s", linux.getMachineId());
Initiator initiator;
if (findInitiatorByPort(oldInitiators, cephPseudoPort) == null) {
initiator = getOrCreateInitiator(host.getId(), oldInitiators, cephPseudoPort);
addedInitiators.add(initiator);
} else {
initiator = getOrCreateInitiator(host.getId(), oldInitiators, cephPseudoPort);
}
discoverRBDInitiator(host, initiator, cephPseudoPort);
} catch (DeviceControllerException e) {
throw e;
} catch (Exception e) {
LOG.error("Failed to create RBD pseudo port, skipping");
}
// update export groups with new initiators if host is in use.
if (!addedInitiators.isEmpty()) {
Collection<URI> addedInitiatorIds = Lists.newArrayList(Collections2.transform(addedInitiators,
CommonTransformerFunctions.fctnDataObjectToID()));
changes.setNewInitiators(addedInitiatorIds);
}
}
private void discoverFCInitiator(Host host, Initiator initiator, HBAInfo hba) {
setInitiator(initiator, host);
initiator.setProtocol(Protocol.FC.name());
initiator.setInitiatorNode(SanUtils.normalizeWWN(hba.getWwnn()));
initiator.setInitiatorPort(SanUtils.normalizeWWN(hba.getWwpn()));
setHostInterfaceRegistrationStatus(initiator, host);
save(initiator);
}
private void discoverISCSIInitiator(Host host, Initiator initiator, String iqn) {
setInitiator(initiator, host);
initiator.setInitiatorNode("");
initiator.setInitiatorPort(iqn);
initiator.setProtocol(Protocol.iSCSI.name());
setHostInterfaceRegistrationStatus(initiator, host);
save(initiator);
}
private void discoverRBDInitiator(Host host, Initiator initiator, String port) {
setInitiator(initiator, host);
initiator.setInitiatorNode("");
initiator.setInitiatorPort(port);
initiator.setProtocol(Protocol.RBD.name());
setHostInterfaceRegistrationStatus(initiator, host);
save(initiator);
}
@Override
protected void discoverIpInterfaces(Host host, List<IpInterface> oldIpInterfaces) {
LinuxSystemCLI linux = createLinuxCLI(host);
for (IPInterface nic : linux.listIPInterfaces()) {
if (StringUtils.isNotBlank(nic.getIpAddress())) {
IpInterface ipInterface = getOrCreateIpInterface(oldIpInterfaces, nic.getIpAddress());
discoverIp4Interface(host, ipInterface, nic);
}
if (StringUtils.isNotBlank(nic.getIP6Address())) {
IpInterface ipInterface = getOrCreateIpInterface(oldIpInterfaces,
StringUtils.substringBefore(nic.getIP6Address(), "/"));
discoverIp6Interface(host, ipInterface, nic);
}
}
}
private void discoverIp4Interface(Host host, IpInterface ipInterface, IPInterface nic) {
ipInterface.setHost(host.getId());
ipInterface.setProtocol(Protocol.IPV4.name());
ipInterface.setIpAddress(nic.getIpAddress());
ipInterface.setNetmask(nic.getNetMask());
ipInterface.setIsManualCreation(false);
setHostInterfaceRegistrationStatus(ipInterface, host);
save(ipInterface);
}
private void discoverIp6Interface(Host host, IpInterface ipInterface, IPInterface nic) {
String ipAddress = StringUtils.substringBefore(nic.getIP6Address(), "/");
String prefixLength = StringUtils.substringAfter(nic.getIP6Address(), "/");
ipInterface.setHost(host.getId());
ipInterface.setProtocol(Protocol.IPV6.name());
ipInterface.setIpAddress(ipAddress);
ipInterface.setPrefixLength(NumberUtils.toInt(prefixLength));
ipInterface.setIsManualCreation(false);
setHostInterfaceRegistrationStatus(ipInterface, host);
save(ipInterface);
}
public static LinuxSystemCLI createLinuxCLI(Host host) {
if ((host.getPortNumber() != null) && (host.getPortNumber() > 0)) {
return new LinuxSystemCLI(host.getHostName(), host.getPortNumber(), host.getUsername(), host.getPassword());
}
else {
return new LinuxSystemCLI(host.getHostName(), host.getUsername(), host.getPassword());
}
}
public void setDbCLient(DbClient dbClient) {
super.setDbClient(dbClient);
}
@Override
protected void setNativeGuid(Host host) {
LinuxSystemCLI linux = createLinuxCLI(host);
for (IPInterface nic : linux.listIPInterfaces()) {
if (nic.getInterfaceName().equalsIgnoreCase(ETH0)) {
if (!host.getNativeGuid().equalsIgnoreCase(nic.getMacAddress())) {
checkDuplicateHost(host, nic.getMacAddress());
info("Setting nativeGuid for " + host.getId() + " as " + nic.getMacAddress());
host.setNativeGuid(nic.getMacAddress());
save(host);
}
break;
}
}
}
}