Add command line parameters
This commit is contained in:
parent
f715389aa6
commit
ab88c610e1
5 changed files with 100 additions and 81 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,4 +1,3 @@
|
||||||
/sandbox
|
*.gv
|
||||||
/*.gv
|
*.pdf
|
||||||
/*.pdf
|
|
||||||
.idea
|
.idea
|
||||||
|
|
2
Pipfile
2
Pipfile
|
@ -10,4 +10,4 @@ graphviz = "*"
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.7"
|
python_version = "3"
|
||||||
|
|
4
Pipfile.lock
generated
4
Pipfile.lock
generated
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "dd7e5eeba8f90bf7a95640eb165b90b96af947ddf7220542916f95709f01f14d"
|
"sha256": "b980cd984b5794dc93711b11dcaadee33a6ee25a811c7aa8c8a6c7db32a78204"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
"python_version": "3.7"
|
"python_version": "3"
|
||||||
},
|
},
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
|
|
26
README.md
26
README.md
|
@ -1,13 +1,23 @@
|
||||||
Docker Network Graph
|
# Docker Network Graph
|
||||||
--------------------
|
|
||||||
|
|
||||||
Sample python script to draw graph of Docker networks and containers
|
Sample python script to draw graph of Docker networks and containers
|
||||||
|
|
||||||
|
|
||||||
Install/run
|
## Usage
|
||||||
===========
|
usage: docker-net-graph.py [-h] [-v] [-o OUT]
|
||||||
|
|
||||||
#> git clone https://github.com/LeoVerto/docker-network-graph-poc.git
|
Generate docker network graph.
|
||||||
#> cd docker-network-graph-poc
|
|
||||||
#> pipenv install
|
optional arguments:
|
||||||
#> pipenv run python docker-net-graph.py
|
-h, --help show this help message and exit
|
||||||
|
-v, --verbose Verbose output
|
||||||
|
-o OUT, --out OUT Write output to file
|
||||||
|
|
||||||
|
In most cases what you want to run are the following couple commands:
|
||||||
|
|
||||||
|
git clone https://github.com/LeoVerto/docker-network-graph-poc.git
|
||||||
|
cd docker-network-graph-poc
|
||||||
|
pipenv install
|
||||||
|
pipenv run python docker-net-graph.py -o output.gv
|
||||||
|
|
||||||
|
This will end up generating a .pdf file containing the graph.
|
144
docker-net-graph.py
Normal file → Executable file
144
docker-net-graph.py
Normal file → Executable file
|
@ -1,80 +1,90 @@
|
||||||
from docker import Client
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pprint
|
|
||||||
import json
|
import json
|
||||||
|
import argparse
|
||||||
|
from docker import Client
|
||||||
from graphviz import Graph
|
from graphviz import Graph
|
||||||
|
|
||||||
dot = Graph(comment='Docker Network Graph',
|
|
||||||
graph_attr=dict( rankdir="TB", packmode='graph', pack='true')
|
|
||||||
)
|
|
||||||
|
|
||||||
docker_client = Client(os.environ.get("DOCKER_HOST", "unix:///var/run/docker.sock"))
|
def generate_graph(verbose: bool, file: str):
|
||||||
|
dot = Graph(comment='Docker Network Graph',
|
||||||
|
graph_attr=dict(rankdir="TB", packmode='graph', pack='true')
|
||||||
|
)
|
||||||
|
|
||||||
def dump_json(obj):
|
docker_client = Client(os.environ.get("DOCKER_HOST", "unix:///var/run/docker.sock"))
|
||||||
print(json.dumps(obj, indent=4))
|
|
||||||
|
|
||||||
for c in sorted(docker_client.containers()):
|
def dump_json(obj):
|
||||||
name = c['Names'][0]
|
print(json.dumps(obj, indent=4))
|
||||||
container_id = c['Id']
|
|
||||||
|
|
||||||
node_id = 'container_%s' % container_id
|
|
||||||
|
|
||||||
iface_labels = []
|
|
||||||
|
|
||||||
for net_name, net_info in c['NetworkSettings']['Networks'].items():
|
|
||||||
label_iface = "<%s> %s" % (net_info['EndpointID'], net_info['IPAddress'])
|
|
||||||
|
|
||||||
iface_labels.append(label_iface)
|
|
||||||
|
|
||||||
print('|'.join(iface_labels))
|
|
||||||
|
|
||||||
|
|
||||||
dot.node(node_id,
|
|
||||||
shape='record',
|
|
||||||
label="{ %s | { %s } }" % (name, '|'.join(iface_labels)),
|
|
||||||
fillcolor='#ff9999',
|
|
||||||
style='filled')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for net in docker_client.networks():
|
for c in docker_client.containers():
|
||||||
net_name = net['Name']
|
name = c['Names'][0]
|
||||||
|
container_id = c['Id']
|
||||||
try:
|
|
||||||
gateway = net['IPAM']['Config'][0]['Gateway']
|
|
||||||
except IndexError:
|
|
||||||
gateway = None
|
|
||||||
|
|
||||||
try:
|
node_id = 'container_%s' % container_id
|
||||||
subnet = net['IPAM']['Config'][0]['Subnet']
|
|
||||||
except IndexError:
|
|
||||||
subnet = None
|
|
||||||
|
|
||||||
print("Network: %s %s gw:%s" % ( net_name, subnet,gateway))
|
iface_labels = []
|
||||||
|
|
||||||
net_node_id = "net_%s" % (net_name,)
|
for net_name, net_info in c['NetworkSettings']['Networks'].items():
|
||||||
|
label_iface = "<%s> %s" % (net_info['EndpointID'], net_info['IPAddress'])
|
||||||
net_label_html = '<br/>'.join([s for s in ['<font color="#777777"><i>network</i></font>', net_name, subnet, gateway] if s != None])
|
|
||||||
|
iface_labels.append(label_iface)
|
||||||
dot.node(net_node_id,
|
|
||||||
shape='record',
|
if verbose:
|
||||||
label="{<gw_iface> %s| %s }" % (gateway, net_name),
|
print('|'.join(iface_labels))
|
||||||
fillcolor='#99ff99',
|
|
||||||
style='filled')
|
dot.node(node_id,
|
||||||
|
shape='record',
|
||||||
|
label="{ %s | { %s } }" % (name, '|'.join(iface_labels)),
|
||||||
|
fillcolor='#ff9999',
|
||||||
|
style='filled')
|
||||||
|
|
||||||
|
for net in docker_client.networks():
|
||||||
|
net_name = net['Name']
|
||||||
|
|
||||||
|
try:
|
||||||
|
gateway = net['IPAM']['Config'][0]['Gateway']
|
||||||
|
except IndexError:
|
||||||
|
gateway = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
subnet = net['IPAM']['Config'][0]['Subnet']
|
||||||
|
except IndexError:
|
||||||
|
subnet = None
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print("Network: %s %s gw:%s" % (net_name, subnet, gateway))
|
||||||
|
|
||||||
|
net_node_id = "net_%s" % (net_name,)
|
||||||
|
|
||||||
|
net_label_html = '<br/>'.join([s for s in ['<font color="#777777"><i>network</i></font>', net_name, subnet, gateway] if s is not None])
|
||||||
|
|
||||||
|
dot.node(net_node_id,
|
||||||
|
shape='record',
|
||||||
|
label="{<gw_iface> %s| %s }" % (gateway, net_name),
|
||||||
|
fillcolor='#99ff99',
|
||||||
|
style='filled')
|
||||||
|
|
||||||
|
for container_id, container in sorted(net['Containers'].items()):
|
||||||
|
if verbose:
|
||||||
|
dump_json(container)
|
||||||
|
if verbose:
|
||||||
|
print(" * ", container['Name'], container['IPv4Address'], container['IPv6Address'])
|
||||||
|
|
||||||
|
container_node_id = 'container_%s' % container_id
|
||||||
|
|
||||||
|
container_iface_ref = "%s:%s" % (container_node_id, container['EndpointID'])
|
||||||
|
|
||||||
|
dot.edge(container_iface_ref, net_node_id+":gw_iface")
|
||||||
|
|
||||||
|
print(dot.source)
|
||||||
|
if file:
|
||||||
|
dot.render(file)
|
||||||
|
|
||||||
|
|
||||||
for container_id, container in sorted(net['Containers'].items()):
|
if __name__ == "__main__":
|
||||||
dump_json(container)
|
parser = argparse.ArgumentParser(description="Generate docker network graph.")
|
||||||
print(" * ", container['Name'], container['IPv4Address'], container['IPv6Address'])
|
parser.add_argument("-v", "--verbose", help="Verbose output", action="store_true")
|
||||||
|
parser.add_argument("-o", "--out", help="Write output to file", type=str)
|
||||||
container_node_id = 'container_%s' % container_id
|
args = parser.parse_args()
|
||||||
|
|
||||||
container_iface_ref = "%s:%s" % (container_node_id, container['EndpointID'])
|
generate_graph(args.verbose, args.out)
|
||||||
|
|
||||||
dot.edge(container_iface_ref, net_node_id+":gw_iface")
|
|
||||||
|
|
||||||
print(dot.source)
|
|
||||||
dot.render('dng.gv')
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue