/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.computesystemcontroller.impl.adapter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import com.emc.storageos.computesystemcontroller.exceptions.ComputeSystemControllerException;
import com.emc.storageos.computesystemcontroller.impl.DiscoveryStatusUtils;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.model.Cluster;
import com.emc.storageos.db.client.model.DiscoveredSystemObject;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.IpInterface;
import com.emc.storageos.db.client.model.HostInterface.Protocol;
import com.emc.storageos.db.client.util.CommonTransformerFunctions;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
public abstract class AbstractHostDiscoveryAdapter extends AbstractDiscoveryAdapter {
protected abstract String getSupportedType();
@Override
public boolean isSupportedTarget(String targetId) {
if (URIUtil.isType(URI.create(targetId), Host.class)) {
Host host = getModelClient().hosts().findById(targetId);
return isSupportedHost(host) && isDiscoverable(host);
}
else {
return false;
}
}
@Override
public void discoveryFailure(DiscoveredSystemObject target, String compatibilityStatus, String errorMessage) {
target.setCompatibilityStatus(compatibilityStatus);
target.setLastDiscoveryStatusMessage(errorMessage);
save(target);
}
@Override
public void discoverTarget(String targetId) {
Host host = getModelClient().hosts().findById(targetId);
HostStateChange changes = new HostStateChange(host, host.getCluster());
changes.setNewCluster(host.getCluster());
discoverHost(host, changes);
processHostChanges(changes);
}
protected boolean isSupportedHost(Host host) {
return StringUtils.equals(host.getType(), getSupportedType());
}
protected boolean isDiscoverable(Host host) {
return host.getDiscoverable() == null || host.getDiscoverable();
}
protected void discoverHost(Host host, HostStateChange changes) {
setNativeGuid(host);
info("Discovering IP interfaces for host %s", host.forDisplay());
List<IpInterface> oldIpInterfaces = new ArrayList<IpInterface>();
Iterables.addAll(oldIpInterfaces, getIpInterfaces(host));
discoverIpInterfaces(host, oldIpInterfaces);
removeDiscoveredInterfaces(oldIpInterfaces);
info("Discovering initiators for host %s", host.forDisplay());
List<Initiator> oldInitiators = new ArrayList<Initiator>();
Iterables.addAll(oldInitiators, getInitiators(host));
discoverInitiators(host, oldInitiators, changes);
if (!oldInitiators.isEmpty()) {
clearScaleIOInitiators(oldInitiators);
}
Collection<URI> oldInitiatorIds = Lists.newArrayList(Collections2.transform(oldInitiators,
CommonTransformerFunctions.fctnDataObjectToID()));
changes.setOldInitiators(oldInitiatorIds);
}
/**
* Discovers the IP interfaces for the host. The current IP interfaces are supplied and should be reused where
* appropriate. When an IpInterface is reused, it should be removed from this list. Any remaining in the list after
* this method are assumed to not exist on the host any longer and will be deleted.
*
* @param host
* the host.
* @param oldIpInterfaces
* the old IP interfaces for the host, any remaining after this method will be deleted.
*/
protected abstract void discoverIpInterfaces(Host host, List<IpInterface> oldIpInterfaces);
/**
* Discovers the initiators for the host. The current initiators are supplied and should be reused where
* appropriate. When reusing an initiator, it should be removed from this list. Any remaining in the list after this
* method are assumed to not exist on the host any longer and will be deleted.
*
* @param host
* the host.
* @param oldInitiators
* the old initiators for the host, any remaining after this method will be deleted.
* @param changes
* the state of changes for the host during discovery
*/
protected abstract void discoverInitiators(Host host, List<Initiator> oldInitiators, HostStateChange changes);
protected Iterable<IpInterface> getIpInterfaces(Host host) {
return getModelClient().ipInterfaces().findByHost(host.getId(), true);
}
/**
* Gets or creates an IP interface. If an IP interface is found in the list, it will be removed from the list before
* returning.
*
* @param ipInterfaces
* the IP interfaces.
* @param ip
* the name of the interface.
* @return the IP interface.
*/
protected IpInterface getOrCreateIpInterface(List<IpInterface> ipInterfaces, String ip) {
return getOrCreateInterfaceByIp(ipInterfaces, ip);
}
protected Iterable<Initiator> getInitiators(Host host) {
return getModelClient().initiators().findByHost(host.getId(), true);
}
/**
* Sets the host/cluster values for the initiator.
*
* @param initiator
* the initiator.
* @param host
* the host.
*/
protected void setInitiator(Initiator initiator, Host host) {
initiator.setHost(host.getId());
initiator.setHostName(host.getHostName());
Cluster cluster = getCluster(host);
initiator.setClusterName(cluster != null ? cluster.getLabel() : "");
}
/**
* Gets the cluster for the given host.
*
* @param host
* the host
* @return the cluster.
*/
protected Cluster getCluster(Host host) {
if (host.getCluster() != null) {
return get(Cluster.class, host.getCluster());
}
else {
return null;
}
}
/**
* Finds cluster by IP addresses in the given tenant.
*
* @param discoveredHost
* the discovered host
* @param tenantId
* the tenant ID.
* @param hostType
* the host type.
* @param clusterIpAddresses
* the IP addresses in the cluster.
* @return the ID of the cluster.
*/
protected URI findClusterByAddresses(Host discoveredHost, URI tenantId, Host.HostType hostType, List<String> clusterIpAddresses) {
for (Host host : getModelClient().hosts().findAll(tenantId.toString(), true)) {
// Skip unclustered hosts
if (NullColumnValueGetter.isNullURI(host.getCluster())) {
continue;
}
// Skip hosts of other types
if (!StringUtils.equals(host.getType(), hostType.toString())) {
continue;
}
// Skip the current discovered host because the cluster may have changed
if (host.getId().toString().equals(discoveredHost.getId().toString())) {
continue;
}
try {
InetAddress hostAddress = Inet4Address.getByName(host.getHostName());
if (clusterIpAddresses.contains(hostAddress.getHostAddress())) {
return host.getCluster();
}
} catch (UnknownHostException ignore) {
getLog().error(ignore.getMessage(), ignore);
}
// Check the ip interfaces for the host
for (IpInterface ipInterface : getModelClient().ipInterfaces().findByHost(host.getId(), true)) {
if (clusterIpAddresses.contains(ipInterface.getIpAddress())) {
return host.getCluster();
}
}
}
return null;
}
/**
* Finds a cluster by name in the given tenant.
*
* @param tenantId
* the tenant ID.
* @param clusterName
* the name of the cluster.
* @return the ID of the cluster.
*/
protected URI findClusterByName(URI tenantId, String clusterName) {
for (Cluster cluster : getModelClient().clusters().findByLabel(tenantId.toString(), clusterName, true)) {
if (cluster.getLabel().equals(clusterName)) {
return cluster.getId();
}
}
return null;
}
protected URI createNewCluster(URI tenant, String clusterName) {
Cluster cluster = new Cluster();
cluster.setLabel(clusterName);
cluster.setTenant(tenant);
getModelClient().save(cluster);
return cluster.getId();
}
protected abstract void setNativeGuid(Host host);
protected void checkDuplicateHost(Host host, String nativeGuid) {
if (nativeGuid != null && !nativeGuid.isEmpty()) {
for (Host existingHost : modelClient.hosts().findByNativeGuid(nativeGuid, true)) {
if (!existingHost.getId().equals(host.getId())) {
ComputeSystemControllerException ex =
ComputeSystemControllerException.exceptions.duplicateSystem("Host", existingHost.getLabel());
DiscoveryStatusUtils.markAsFailed(modelClient, host, ex.getMessage(), ex);
throw ex;
}
}
}
}
/**
* This method clears/removes ScaleIO initiators
*
* @param initiators
* list of initiators
*/
protected void clearScaleIOInitiators(List<Initiator> initiators) {
Iterator<Initiator> iterator = initiators.iterator();
while (iterator.hasNext()) {
Initiator initiator = iterator.next();
if (StringUtils.equalsIgnoreCase(initiator.getProtocol(),
Protocol.ScaleIO.name())) {
iterator.remove();
}
}
}
}