/*
* Copyright (c) 2014-2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.server.geo;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Collections;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.cassandra.auth.IInternodeAuthenticator;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.common.DbConfigConstants;
import com.emc.storageos.db.server.impl.DbServiceImpl;
import com.emc.storageos.db.server.impl.DrInternodeAuthenticator;
/**
* InternnodeAuthenticator for geodb. It maintains a blacklist to refuse gossip connection
* from nodes in remote vdc. The use case if for vdc disconnect/reconnect, we need block
* geodb connection from disconnected vdc.
*
* The blacklist is stored in ZK under /config/geodbconfig. The blacklist is reloaded
* each time during dbservice startup.
*/
public class GeoInternodeAuthenticator extends DrInternodeAuthenticator implements GeoInternodeAuthenticatorMBean {
private static final Logger log = LoggerFactory.getLogger(GeoInternodeAuthenticator.class);
private static final String SEPARATOR = ",";
private Set<InetAddress> blacklist;
/**
* Initial Blacklist
*/
public GeoInternodeAuthenticator() {
blacklist = new HashSet<InetAddress>();
}
@Override
public boolean authenticate(InetAddress remoteAddress, int remotePort)
{
if (!super.authenticate(remoteAddress, remotePort)) {
return false;
}
if (blacklist.contains(remoteAddress)) {
log.debug("Refuse internode communication for {}", remoteAddress);
return false;
}
log.info("Allow internode communication for {}", remoteAddress);
return true;
}
/**
* Called by Cassandra startup routine to initialize this instance
*/
@Override
public void validateConfiguration() throws ConfigurationException
{
super.validateConfiguration();
log.info("Initialize GeoInternodeAuthenticator");
reloadBlacklist();
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName(MBEAN_NAME);
mbs.registerMBean(this, name);
} catch (Exception ex) {
log.error("Register MBean error ", ex);
throw new ConfigurationException("Initialize GeoInternodeAuthenticator error", ex);
}
}
@Override
public void addToBlacklist(List<String> nodeList) {
log.info("Add nodes {} to blacklist", nodeList);
for (String nodeIp : nodeList) {
try {
InetAddress addr = InetAddress.getByName(nodeIp);
blacklist.add(addr);
} catch (UnknownHostException ex) {
log.error("Unrecognized ip in blacklist", ex);
}
}
saveBlacklist();
}
@Override
public void removeFromBlacklist(List<String> nodeList) {
log.info("Remove nodes {} from blacklist", nodeList);
for (String nodeIp : nodeList) {
try {
InetAddress addr = InetAddress.getByName(nodeIp);
blacklist.remove(addr);
} catch (UnknownHostException ex) {
log.warn("Unrecognized ip in blacklist", ex);
}
}
saveBlacklist();
}
@Override
public List<String> getBlacklist() {
List<String> result = new ArrayList<>();
for (InetAddress addr : blacklist) {
result.add(addr.getHostAddress());
}
return Collections.unmodifiableList(result);
}
private void reloadBlacklist() {
blacklist.clear();
String peerIPs = DbServiceImpl.instance.getConfigValue(DbConfigConstants.NODE_BLACKLIST);
if (peerIPs != null) {
for (String nodeIp : StringUtils.split(peerIPs, SEPARATOR)) {
try {
InetAddress addr = InetAddress.getByName(nodeIp);
blacklist.add(addr);
} catch (UnknownHostException ex) {
log.error("Unrecognized ip in blacklist", ex);
}
}
}
log.info("Reload blacklist from ZK {}", blacklist);
}
private void saveBlacklist() {
List<String> ipList = new ArrayList<String>();
for (InetAddress addr : blacklist) {
ipList.add(addr.getHostAddress());
}
String value = StringUtils.join(ipList, SEPARATOR);
DbServiceImpl.instance.setConfigValue(DbConfigConstants.NODE_BLACKLIST, value);
}
}