docker-network-graph/docker-net-graph.py

123 lines
3.5 KiB
Python
Raw Normal View History

2018-08-31 04:50:58 +02:00
#!/usr/bin/python3
2016-09-29 23:48:22 +02:00
import os
import json
2018-08-31 04:50:58 +02:00
import argparse
import random
2018-08-31 04:50:58 +02:00
from docker import Client
2016-09-29 23:48:22 +02:00
from graphviz import Graph
# colorlover.scales["12"]["qual"]["Paired"] converted to hex strings
2018-11-27 03:22:10 +01:00
COLORS = ["#1f78b4", "#33a02c", "#e31a1c", "#ff7f00", "#6a3d9a", "#b15928", "#a6cee3", "#b2df8a", "#fdbf6f",
"#cab2d6", "#ffff99"]
i = 0
def get_unique_color():
global i
if i < len(COLORS):
c = COLORS[i]
i += 1
else:
2018-11-27 03:22:10 +01:00
# Generate random color if we've already used the 12 preset ones
c = "#%06x".format(random.randint(0, 0xFFFFFF))
2018-11-27 03:22:10 +01:00
return c
2018-08-31 04:50:58 +02:00
def generate_graph(verbose: bool, file: str):
2018-11-27 03:22:10 +01:00
g = Graph(comment="Docker Network Graph", engine="sfdp", format="png",
graph_attr=dict(splines="true"))
2018-08-31 04:50:58 +02:00
docker_client = Client(os.environ.get("DOCKER_HOST", "unix:///var/run/docker.sock"))
def dump_json(obj):
print(json.dumps(obj, indent=4))
for c in docker_client.containers():
2018-11-27 03:22:10 +01:00
name = c["Names"][0][1:]
container_id = c["Id"]
2018-08-31 04:50:58 +02:00
2018-11-27 03:22:10 +01:00
node_id = f"container_{container_id}"
2018-08-31 04:50:58 +02:00
iface_labels = []
2018-11-27 03:22:10 +01:00
for net_name, net_info in c["NetworkSettings"]["Networks"].items():
label_iface = f"<{net_info['EndpointID']}> {net_info['IPAddress']}"
2018-08-31 04:50:58 +02:00
iface_labels.append(label_iface)
2018-11-27 03:22:10 +01:00
labels = "|".join(iface_labels)
2018-08-31 04:50:58 +02:00
if verbose:
2018-11-27 03:22:10 +01:00
print(labels)
2018-08-31 04:50:58 +02:00
g.node(node_id,
2018-11-27 03:22:10 +01:00
shape="record",
label=f"{{ {name} | { {labels} } }}",
fillcolor="#ff9999",
style="filled"
)
2018-08-31 04:50:58 +02:00
for net in sorted(docker_client.networks(), key=lambda k: k["Name"]):
2018-11-27 03:22:10 +01:00
net_name = net["Name"]
color = get_unique_color()
2018-08-31 04:50:58 +02:00
try:
2018-11-27 03:22:10 +01:00
gateway = net["IPAM"]["Config"][0]["Gateway"]
except (KeyError, IndexError):
2018-09-10 18:45:36 +02:00
# This network doesn't seem to be used, skip it
continue
2018-08-31 04:50:58 +02:00
2018-08-31 21:34:24 +02:00
internal = ""
2018-08-31 04:50:58 +02:00
try:
2018-11-27 03:22:10 +01:00
if net["Internal"]:
internal = "| Internal"
2018-09-10 18:45:36 +02:00
except KeyError:
2018-08-31 21:34:24 +02:00
pass
isolated = ""
try:
if net["Options"]["com.docker.network.bridge.enable_icc"] == "false":
isolated = "| Containers isolated"
except KeyError:
pass
2018-08-31 04:50:58 +02:00
if verbose:
2018-11-27 03:22:10 +01:00
print(f"Network: {net_name} {internal} gw:{gateway}")
2018-08-31 04:50:58 +02:00
2018-11-27 03:22:10 +01:00
net_node_id = f"net_{net_name}"
2018-08-31 04:50:58 +02:00
2018-11-27 03:22:10 +01:00
label = f"{{<gw_iface> {gateway} | {net_name} {internal} {isolated}}}"
2018-08-31 04:50:58 +02:00
g.node(net_node_id,
2018-11-27 03:22:10 +01:00
shape="record",
2018-08-31 15:31:36 +02:00
label=label,
fillcolor=color,
2018-11-27 03:22:10 +01:00
style="filled"
2018-08-31 15:31:36 +02:00
)
2018-08-31 04:50:58 +02:00
2018-11-27 03:22:10 +01:00
if net["Containers"]:
for container_id, container in sorted(net["Containers"].items()):
2018-11-08 14:59:01 +01:00
if verbose:
dump_json(container)
2018-11-27 03:22:10 +01:00
print(" * ", container["Name"], container["IPv4Address"], container["IPv6Address"])
2018-08-31 04:50:58 +02:00
2018-11-27 03:22:10 +01:00
container_node_id = f"container_{container_id}"
2018-08-31 04:50:58 +02:00
2018-11-27 03:22:10 +01:00
container_iface_ref = f"{container_node_id}:{container['EndpointID']}"
2018-08-31 04:50:58 +02:00
2018-11-27 03:22:10 +01:00
g.edge(container_iface_ref, f"{net_node_id}:gw_iface", color=color)
2018-08-31 04:50:58 +02:00
print(g.source)
2018-08-31 04:50:58 +02:00
if file:
g.render(file)
2018-08-31 04:50:58 +02:00
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate docker network graph.")
parser.add_argument("-v", "--verbose", help="Verbose output", action="store_true")
parser.add_argument("-o", "--out", help="Write output to file", type=str)
args = parser.parse_args()
generate_graph(args.verbose, args.out)