/*
* Copyright (c) 2008-2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.services.util;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
import com.emc.storageos.model.property.PropertyConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Utility to multicast and list node configuration(s)
* 1. Use publish to multicast the local node's configuration
* 2. Use list to get the nodes which are publishing there configurations
*/
public class MulticastUtil {
private static final Logger _log = LoggerFactory.getLogger(MulticastUtil.class);
private JmDNS jmdns;
private static Charset UTF_8 = Charset.forName("UTF-8");
private static void sleep(long ms) {
final long expirationTime = System.currentTimeMillis() + ms;
while (true) {
final long sleepDelay = expirationTime - System.currentTimeMillis();
if (sleepDelay <= 0) {
return;
}
try {
Thread.sleep(sleepDelay);
} catch (Exception e) {
;
}
}
}
private static InetAddress getLinkLocalAddress(String networkInterfaceName) throws SocketException {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
for (NetworkInterface networkInterface : Collections.list(networkInterfaces)) {
if (networkInterfaceName == null || networkInterfaceName.equals(networkInterface.getName())) {
Enumeration<InetAddress> ips = networkInterface.getInetAddresses();
for (InetAddress ip : Collections.list(ips)) {
if (ip.isLinkLocalAddress()) {
return ip;
}
}
}
}
return null;
}
private MulticastUtil(JmDNS jmdns) {
this.jmdns = jmdns;
}
public static MulticastUtil create(String networkInterfaceName) throws IOException {
JmDNS jmdns = JmDNS.create(MulticastUtil.getLinkLocalAddress(networkInterfaceName));
return new MulticastUtil(jmdns);
}
public static MulticastUtil create() throws IOException {
return MulticastUtil.create(null);
}
public void close() throws IOException {
if (this.jmdns != null) {
this.jmdns.unregisterAllServices();
this.jmdns.close();
this.jmdns = null;
}
}
/**
* Publish local node configuration via multicast
*
* @param serviceName name of service to be published. (e.g. release version for installation)
* instanceName name of instance which is publishing. (e.g. the local node id etc.)
* nodeConfig info to be published
* publishTime how long the info would be published
*/
public void publish(String serviceName, String instanceName, Map<String, String> values, long publishTime) throws IOException {
ServiceInfo info = ServiceInfo.create("_" + serviceName + "._tcp.local.", instanceName, 9999, 0, 0, values);
this.jmdns.registerService(info);
if (publishTime > 0) {
sleep(publishTime);
this.close();
}
}
/**
* List published node(s) configuration in the network via multicast
*
* @param serviceName name of service published. (e.g. release version for installation)
* @return node(s) configuration list
*/
public Map<String, Map<String, String>> list(String serviceName) {
Map<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();
ServiceInfo[] infos = jmdns.list("_" + serviceName + "._tcp.local.");
for (ServiceInfo info : infos) {
_log.info("ServiceInfo:{}", info);
// Construct the key
final String[] hostAddrs = info.getHostAddresses();
final StringBuffer buf = new StringBuffer();
for (String hostAddr : hostAddrs) {
buf.append(hostAddr);
buf.append(';');
}
final String key = buf.toString();
_log.info("\tkey:{}", key);
// Construct the values
final Map<String, String> values = new HashMap<String, String>();
for (Enumeration<String> e = info.getPropertyNames(); e.hasMoreElements();) {
final String prop = e.nextElement();
final String value = new String(info.getPropertyBytes(prop));
_log.info("\tprop:{}, value:{}", prop, value);
values.put(prop, value);
}
// Put <key,values> into the results
if (values.isEmpty()) {
_log.warn("values are empty for key: {}", key);
}
results.put(key, values.isEmpty() ? null : values);
}
return results;
}
/*
* Broadcast local configuration to others over the network.
*
* @return true if broadcast successful, false if failed.
*/
public static boolean doBroadcast(String releaseVersion, Configuration config, long publishTime) {
boolean taskSuccess = true;
_log.debug("{} - broadcasting cluster configuration {}", config.getNodeId(), config.toString());
try {
MulticastUtil.create(config.getHwConfig().get(PropertyConstants.PROPERTY_KEY_NETIF))
.publish(releaseVersion, config.getNodeId(), config.getConfigMap(), publishTime);
} catch (IOException e) {
taskSuccess = false;
_log.error("broadcast configuration caught exception with: " + e.getMessage());
} finally {
_log.info("broadcast configuration via {} for {} is done",
config.getHwConfig().get(PropertyConstants.PROPERTY_KEY_NETIF),
config.getScenario());
}
return taskSuccess;
}
// to be removed
private static void server(String nodeId, Map<String, String> clusterConfig) {
final String serviceName = "vipr-2.2.0.0.1";
try {
MulticastUtil.create().publish(serviceName, nodeId, clusterConfig, 10000);
} catch (Exception e) {
_log.error(e.getMessage(), e);
System.exit(1);
}
}
// to be removed
private static void client() {
final String serviceName = "vipr-2.2.0.0.1";
try {
MulticastUtil.create().list(serviceName);
} catch (Exception e) {
_log.error(e.getMessage(), e);
System.exit(1);
}
}
}