/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.routing.internal;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.rcenvironment.core.communication.common.NetworkGraph;
import de.rcenvironment.core.communication.common.NetworkGraphLink;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.model.NetworkResponse;
import de.rcenvironment.core.communication.model.NetworkRoutingInformation;
import de.rcenvironment.core.communication.protocol.MessageMetaData;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.incubator.GraphvizUtils;
import de.rcenvironment.core.utils.incubator.GraphvizUtils.DotFileBuilder;
/**
* Provides tools to generate human readable string representations of routing components (such as {@link TopologyMap}) that are useful for
* debugging and monitoring.
*
* @author Phillip Kroll
* @author Robert Mischke
*/
public final class NetworkFormatter {
private static final String GRAPHVIZ_STYLE_KEY = "style";
private static final String GRAPHVIZ_STYLE_VALUE_DASHED = "dashed";
private static final String GRAPHVIZ_STYLE_VALUE_BOLD = "bold";
private NetworkFormatter() {
// no instance
}
/**
* TODO krol_ph: Enter comment!
*
* @param networkGraph The network Graph
* @return A string representation for debugging/displaying.
*/
public static String linkList(TopologyMap networkGraph) {
return linkList(networkGraph.getAllLinks());
}
/**
* Formats a collection of links to a string.
*
* @param linkCollection The collection of links.
* @return A string representation for debugging/displaying.
*/
public static String linkList(Collection<TopologyLink> linkCollection) {
String result = "";
List<TopologyLink> linkList = new ArrayList<TopologyLink>(linkCollection);
Collections.sort(linkList);
for (TopologyLink link : linkList) {
result += StringUtils.format(" %s --[%s]--> %s (Hash=%s)\n",
link.getSource().getInstanceNodeSessionIdString(),
link.getConnectionId(),
link.getDestination().getInstanceNodeSessionIdString(),
link.hashCode());
}
return result;
}
/**
* Formats a list of nodes to a string.
*
* @param networkGraph The network graph.
* @return A string representation for debugging/displaying.
*/
public static String nodeList(TopologyMap networkGraph) {
String result = "";
List<TopologyNode> networkNodes = new ArrayList<TopologyNode>(networkGraph.getNodes());
Collections.sort(networkNodes);
SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSS");
for (TopologyNode networkNode : networkNodes) {
String nodeIdInfo = networkNode.getNodeIdentifier().getInstanceNodeSessionIdString();
// mark local node
if (nodeIdInfo.equals(networkGraph.getLocalNodeId().getInstanceNodeSessionIdString())) {
nodeIdInfo += "*";
}
result +=
StringUtils.format(
" [%9$s] %1$s, --%4$s-> * --%5$s->, '%8$s', Seq=%2$s, Created=%3$tk:%3$tM:%3$tS, Conv=%6$s, Hash=%7$s\n", nodeIdInfo,
// TODO quick & dirty; improve
networkNode.getSequenceNumber() + " (" + timestampFormat.format(new Date(networkNode.getSequenceNumber()))
+ ")",
networkNode.getCreatedTime(),
networkGraph.getPredecessors(networkNode).size(),
networkGraph.getSuccessors(networkNode).size(),
(networkNode.getLastGraphHashCode() == networkGraph.hashCode()),
networkNode.hashCode(),
networkNode.getDisplayName(),
networkNode.getIsWorkflowHost());
}
return result;
}
/**
* TODO krol_ph: Enter comment!
*
* @param networkGraph The network graph.
* @return A string representation for debugging/displaying.
*/
public static String graphMetaData(TopologyMap networkGraph) {
return StringUtils.format(" Local Node Id: %s, Nodes: %s, Links: %s, Fully conv.: %s, Hash=%s\n",
networkGraph.getLocalNodeId().getInstanceNodeSessionIdString(),
networkGraph.getNodeCount(),
networkGraph.getLinkCount(),
networkGraph.hasSameTopologyHashesForAllNodes(),
networkGraph.hashCode());
}
/**
* TODO krol_ph: Enter comment!
*
* @param networkGraph The network graph.
* @return A string representation for debugging/displaying.
*/
public static String summary(TopologyMap networkGraph) {
return StringUtils.format("Topology Metadata:\n%sKnown Nodes:\n%sLinks:\n%s",
NetworkFormatter.graphMetaData(networkGraph),
NetworkFormatter.nodeList(networkGraph),
NetworkFormatter.linkList(networkGraph));
}
/**
* Renders the current network topology to a Graphviz "dot" file.
*
* @param topologyMap the {@link TopologyMap} to render
* @return the "dot" file script content
*/
public static String topologyToGraphviz(TopologyMap topologyMap) {
DotFileBuilder builder = GraphvizUtils.createDotFileBuilder("rce_network");
List<TopologyNode> networkNodes = new ArrayList<TopologyNode>(topologyMap.getNodes());
Map<String, String> backwardEdgeProperties = new HashMap<String, String>();
backwardEdgeProperties.put(GRAPHVIZ_STYLE_KEY, GRAPHVIZ_STYLE_VALUE_DASHED);
for (TopologyNode networkNode : networkNodes) {
// TODO filter to only include reachable nodes
String vertexId = networkNode.getNodeIdentifier().getInstanceNodeIdString(); // only use persistent part to avoid clutter
String label = networkNode.getDisplayName();
builder.addVertex(vertexId, label);
}
List<TopologyLink> linkList = new ArrayList<TopologyLink>(topologyMap.getAllLinks());
for (TopologyLink link : linkList) {
String edgeId = link.getConnectionId();
String label = edgeId.substring(0, edgeId.indexOf('-'));
if (label.endsWith("s")) {
builder.addEdge(link.getSource().getInstanceNodeIdString(), link.getDestination().getInstanceNodeIdString(), label);
} else {
builder.addEdge(link.getSource().getInstanceNodeIdString(), link.getDestination().getInstanceNodeIdString(), label,
backwardEdgeProperties);
}
}
// mark local node
String localNodeId = topologyMap.getLocalNodeId().getInstanceNodeIdString();
builder.addVertexProperty(localNodeId, GRAPHVIZ_STYLE_KEY, GRAPHVIZ_STYLE_VALUE_BOLD);
// generate script
return builder.getScriptContent();
}
/**
* Formats message to string.
*
* @param messageContent The message content.
* @param metaData The meta data of the message.
* @return A string representation for debugging/displaying.
*/
public static String message(Serializable messageContent, Map<String, String> metaData) {
MessageMetaData handler = MessageMetaData.wrap(metaData);
return StringUtils.format("Src='%s', Dst='%s', Body='%s', HopC=%d, MsgId='%s', Trace='%s'",
handler.getSender(),
handler.getFinalRecipient(),
messageContent.toString(),
handler.getHopCount(),
handler.getMessageId(),
handler.getTrace());
}
/**
* Formats LSA to string.
*
* @param lsa The links state advertisement.
* @return A string representation for debugging/displaying.
*/
public static String lsa(LinkStateAdvertisement lsa) {
return StringUtils.format("owner=%s, links=%s, seq=%s, type=%s, hash=%s\n%s",
lsa.getOwner(),
lsa.getLinks().size(),
lsa.getSequenceNumber(),
lsa.getReason(),
lsa.getGraphHashCode(),
linkList(lsa.getLinks()));
}
/**
* Formats LSA cache to string.
*
* @param lsaCache The LSA cache.
* @return A string representation for debugging/displaying.
*/
public static String lsaCache(LinkStateAdvertisementBatch lsaCache) {
String result = StringUtils.format("size=%s\n", lsaCache.size());
for (LinkStateAdvertisement lsa : lsaCache.values()) {
result += lsa(lsa) + "\n";
}
return result;
}
/**
* Formats a network rout to string.
*
* @param networkRoute The network route.
* @return A string representation for debugging/displaying.
*/
public static String networkRoute(NetworkRoute networkRoute) {
String result =
StringUtils.format("length: %s, %s, time: %s ms", networkRoute.getPath().size(),
networkRoute.getSource().getInstanceNodeSessionIdString(),
networkRoute.getComputationalEffort());
for (TopologyLink link : networkRoute.getPath()) {
result += StringUtils.format(" --> %s", link.getDestination().getInstanceNodeSessionIdString());
}
return result;
}
/**
* Formats network statistics to string.
*
* @param networkStats The network statistics.
* @return A string representation for debugging/displaying.
*/
public static String networkStats(NetworkStats networkStats) {
return StringUtils.format(
"\nSuccessful communications: %s\n"
+ "Failed communications: %s\n\n"
+ "LSAs send: %s\n"
+ "LSAs received: %s\n"
+ "LSAs rejected: %s\n\n"
+ "Max received hop count: %s\n"
+ "Max time to live: %s\n"
+ "Number of computed routes: %s\n\n"
+ "Average hop count of send LSAs: %s\n"
+ "Average hop count of received LSAs: %s\n"
+ "Average hop count of rejected LSAs: %s\n",
networkStats.getSuccessfulCommunications(),
networkStats.getFailedCommunications(),
networkStats.getSentLSAs(),
networkStats.getReceivedLSAs(),
networkStats.getRejectedLSAs(),
networkStats.getMaxReceivedHopCount(),
networkStats.getMaxTimeToLive(),
networkStats.getShortestPathComputations(),
networkStats.averageHopCountOfSentLSAs(),
networkStats.averageHopCountOfReceivedLSAs(),
networkStats.averageHopCountOfRejectedLSAs());
}
/**
* Formats network response to string.
*
* @param networkResponse The network response.
* @return A string representation for debugging/displaying.
*/
public static String networkResponseToString(NetworkResponse networkResponse) {
return StringUtils.format("id=%s, succ=%s, code=%s, header=%s",
networkResponse.getRequestId(),
networkResponse.isSuccess(),
networkResponse.getResultCode(),
networkResponse.accessRawMetaData().toString());
}
/**
* Generates a human-readable representation of a {@link TopologyMap}'s contents.
*
* @param topologyMap the {@link TopologyMap} to render
* @param extendedInfo whether extended (ie, developer) information should be added
* @return the formatted, multi-line string
*/
public static String formatTopologyMap(TopologyMap topologyMap, boolean extendedInfo) {
return StringUtils.format("Nodes:\n%sConnections:\n%s",
NetworkFormatter.formatTopologyNodes(topologyMap, extendedInfo),
NetworkFormatter.formatTopologyLinks(topologyMap, extendedInfo));
}
/**
* Renders the current network topology to a simple console-ready "network information" output.
*
* @param networkGraph the {@link TopologyMap} to render
* @return the information text.
*/
public static String networkGraphToConsoleInfo(NetworkGraph networkGraph) {
StringBuilder buffer = new StringBuilder();
List<InstanceNodeSessionId> networkNodes = new ArrayList<InstanceNodeSessionId>(networkGraph.getNodeIds());
buffer.append("Reachable network nodes (" + networkNodes.size() + " total):\n");
for (InstanceNodeSessionId nodeId : networkNodes) {
buffer.append(StringUtils.format(" %s [%s]\n", nodeId.getAssociatedDisplayName(), nodeId.getInstanceNodeSessionIdString()));
}
buffer.append("Message channels (" + networkGraph.getLinkCount() + " total):\n");
for (NetworkGraphLink link : networkGraph.getLinks()) {
buffer.append(StringUtils.format(" %s--[%s]->[%s]\n", link.getSourceNodeId(), link.getLinkId(), link.getTargetNodeId()));
}
return buffer.toString();
}
/**
* Renders the current network topology to a Graphviz "dot" file.
*
* @param networkGraph the {@link TopologyMap} to render
* @param markSpanningTree TODO
* @return the "dot" file script content
*/
public static String networkGraphToGraphviz(NetworkGraph networkGraph, boolean markSpanningTree) {
DotFileBuilder builder = GraphvizUtils.createDotFileBuilder("rce_network");
List<InstanceNodeSessionId> networkNodes = new ArrayList<InstanceNodeSessionId>(networkGraph.getNodeIds());
// prepare spanning tree data if requested, or set an empty placeholder
Set<NetworkGraphLink> spanningTreeLinks;
if (markSpanningTree) {
NetworkRoutingInformation routingInformation = networkGraph.getRoutingInformation();
if (routingInformation == null) {
throw new IllegalArgumentException("Spanning tree requested for a graph without routing information");
}
spanningTreeLinks = routingInformation.getSpanningTreeLinks();
} else {
// placeholder
spanningTreeLinks = new HashSet<NetworkGraphLink>();
}
// add vertices
for (InstanceNodeSessionId nodeId : networkNodes) {
String vertexId = nodeId.getInstanceNodeIdString(); // only use the persistent part to avoid clutter
String label = nodeId.getAssociatedDisplayName(); // TODO improve
builder.addVertex(vertexId, label);
}
// add edges
List<NetworkGraphLink> linkList = new ArrayList<NetworkGraphLink>(networkGraph.getLinks());
// TODO move style handling into GraphViz builder? (requires adding edge ids) - misc_ro
StringBuilder styleBuilder = new StringBuilder();
for (NetworkGraphLink link : linkList) {
String edgeId = link.getLinkId();
String label = edgeId.substring(0, edgeId.indexOf('-'));
Map<String, String> edgeProperties = new HashMap<String, String>();
if (label.endsWith("r")) {
styleBuilder.append(GRAPHVIZ_STYLE_VALUE_DASHED);
}
if (spanningTreeLinks.contains(link)) {
if (styleBuilder.length() != 0) {
styleBuilder.append(",");
}
styleBuilder.append(GRAPHVIZ_STYLE_VALUE_BOLD);
}
edgeProperties.put(GRAPHVIZ_STYLE_KEY, styleBuilder.toString());
styleBuilder.setLength(0);
builder.addEdge(link.getSourceNodeId().getInstanceNodeIdString(), link.getTargetNodeId().getInstanceNodeIdString(), label,
edgeProperties);
}
// mark local node
String localNodeId = networkGraph.getLocalNodeId().getInstanceNodeIdString();
builder.addVertexProperty(localNodeId, GRAPHVIZ_STYLE_KEY, GRAPHVIZ_STYLE_VALUE_BOLD);
// generate script
return builder.getScriptContent();
}
private static String formatTopologyNodes(TopologyMap topologyMap, boolean extendedInfo) {
InstanceNodeSessionId localNodeId = topologyMap.getLocalNodeId();
Set<InstanceNodeSessionId> reachableNodes = topologyMap.getIdsOfReachableNodes(false);
List<TopologyNode> topologyNodes = new ArrayList<TopologyNode>(topologyMap.getNodes());
StringBuilder result = new StringBuilder();
Collections.sort(topologyNodes);
for (TopologyNode topologyNode : topologyNodes) {
// do not show unreachable nodes (at least, not by default)
InstanceNodeSessionId nodeId = topologyNode.getNodeIdentifier();
if (!reachableNodes.contains(nodeId)) {
continue;
}
// mark local node
String markers = "";
if (nodeId.equals(localNodeId)) {
markers += " <self>";
}
result.append(
StringUtils.format(" %s%s\n",
nodeId,
markers));
}
return result.toString();
}
private static String formatTopologyLinks(TopologyMap topologyMap, boolean extendedInfo) {
StringBuilder result = new StringBuilder();
List<TopologyLink> linkList = new ArrayList<TopologyLink>(topologyMap.getAllLinks());
Collections.sort(linkList);
for (TopologyLink link : linkList) {
result.append(StringUtils.format(" %s --[%s]--> %s\n",
link.getSource(),
link.getConnectionId(),
link.getDestination()));
}
return result.toString();
}
}