network_drawer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 62b2a058777853b011aa999b5bbfaff1f5aea5e9
4
+ data.tar.gz: ca9f5bdc9aaa639146504074fa6b2d769ecf9656
5
+ SHA512:
6
+ metadata.gz: e6368873678680cf9cb5c72f6c666e7f453fe992ce3c8659b9b7fab18eedff3157f7e01ac41c8cbe5d41dca0338e241f101217efd42e2a7258c503013a6eb600
7
+ data.tar.gz: 8a6ede793ae512557266d3e48591e77fe55609962c686dd71bff6c07e2cee18eff3a664491225ca47ab952d6bb4bbfe9d4b3cd1246dc598c28b02eecf2a4036d
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /vendor/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in network_drawer.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Hiroshi Ota
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,130 @@
1
+ # NetworkDrawer
2
+
3
+ A network diagram drawer with json.
4
+
5
+ ## Installation
6
+
7
+ Ensure you can use [Graphviz](http://www.graphviz.org/), before installing network_drawer,
8
+
9
+ You can install Graphviz as follow
10
+ ```
11
+ # Mac OS X with brew
12
+ brew install graphviz
13
+
14
+ # CentOS
15
+ yum -y install graphviz
16
+
17
+ # Ubuntu
18
+ apt-get install graphviz
19
+ ```
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'network_drawer'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install network_drawer
34
+
35
+ ## Usage
36
+
37
+ You can draw a network diagram:
38
+
39
+ $ bundle exec network_drawer draw examples/simple.json
40
+
41
+ You can draw a network diagram with specified style and png format:
42
+
43
+ $ bundle exec network_drawer draw examples/simple.json -s examples/simple_style.json
44
+
45
+ You can draw a network diagram with specified style and png format:
46
+
47
+ $ bundle exec network_drawer draw examples/simple.json -f png
48
+
49
+ Then You will get a network diagram.
50
+
51
+ [sample diagram(SVG)](examples/simple.svg)
52
+
53
+ ![sample diagram(PNG)](examples/simple.png)
54
+
55
+ ## How to describe
56
+
57
+ You can describe diagram with JSON.
58
+
59
+ ### Network diagram
60
+
61
+ You can describe diagram as follows:
62
+
63
+
64
+ ```json
65
+ {
66
+ "layers": {
67
+ "Web": {
68
+ "nodes": [
69
+ {
70
+ "WebLB": {
71
+ "ports": ["80/tcp", "443/tcp"],
72
+ "type": "LB",
73
+ "url": "https://github.com/otahi/network_drawer/"
74
+ }
75
+ },
76
+ { "Web001": { "ports": ["80/tcp"] } },
77
+ { "Web002": { "ports": ["80/tcp"] } }
78
+ ]
79
+ },
80
+ "App": {
81
+ "nodes": [
82
+ { "AppLB": { "ports": ["80/tcp", "25/tcp"], "type": "LB" } },
83
+ { "App001": { "ports": ["80/tcp", "25/tcp"] } },
84
+ { "App002": { "ports": ["80/tcp", "25/tcp"] } }
85
+ ]
86
+ }
87
+ },
88
+ "nodes": [
89
+ { "Browser": {}, "type": "Client" },
90
+ { "Mail Server": {}, "type": "Client" }
91
+ ],
92
+ "connections": [
93
+ { "from": "Browser", "to": "WebLB:80/tcp", "type": "HTTP" },
94
+ { "from": "Browser", "to": "WebLB:443/tcp" },
95
+ { "from": "Mail Server", "to": "AppLB:25/tcp" , "type": "SMTP" },
96
+ { "from": "WebLB", "to": "Web001:80/tcp", "type": "HTTP" },
97
+ { "from": "WebLB", "to": "Web002:80/tcp", "type": "HTTP" },
98
+ { "from": "Web001", "to": "AppLB:80/tcp", "type": "HTTP" },
99
+ { "from": "Web002", "to": "AppLB:80/tcp", "type": "HTTP" },
100
+ { "from": "AppLB", "to": "App001:25/tcp", "type": "SMTP" },
101
+ { "from": "AppLB", "to": "App001:80/tcp", "type": "HTTP" },
102
+ { "from": "AppLB", "to": "App002:25/tcp", "type": "SMTP" },
103
+ { "from": "AppLB", "to": "App002:80/tcp", "type": "HTTP" }
104
+ ]
105
+ }
106
+
107
+ ```
108
+
109
+ ### Style
110
+
111
+ You can specify your style for nodes or edges with [Graphviz attributes](http://www.graphviz.org/content/attrs).
112
+
113
+ ```json
114
+ {
115
+ "types": {
116
+ "LB": { "style": "rounded,filled,dotted", "fillcolor": "azure" },
117
+ "HTTP": { "color": "blue" },
118
+ "SMTP": { "color": "green" }
119
+ }
120
+ }
121
+ ```
122
+
123
+
124
+ ## Contributing
125
+
126
+ 1. Fork it ( https://github.com/otahi/network_drawer/fork )
127
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
128
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
129
+ 4. Push to the branch (`git push origin my-new-feature`)
130
+ 5. Create a new Pull Request
@@ -0,0 +1,18 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task default: :update
4
+
5
+ task :update do
6
+ env = {
7
+ 'BUNDLE_GEMFILE' => nil,
8
+ 'GEM_HOME' => nil
9
+ }
10
+
11
+ %w(png svg).each do |f|
12
+ command = 'bundle exec bin/network_drawer '
13
+ command << 'draw examples/simple.json '
14
+ command << "-s examples/simple_style.json -f #{f}"
15
+
16
+ system(env, command)
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'network_drawer'
4
+
5
+ NetworkDrawer::Cli.start(ARGV)
@@ -0,0 +1,53 @@
1
+ digraph simple {
2
+ subgraph cluster1 {
3
+ label="Web";
4
+ fontname="Helvetica";
5
+ 3[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td border='1' port="p80tcp">80/tcp</td><td border='1' port="p443tcp">443/tcp</td></tr><tr border='1'><td border='1' colspan="2">WebLB</td></tr></table>>,tooltip="WebLB",URL="https://github.com/otahi/network_drawer/",style="rounded,filled,dotted",fillcolor="azure"];
6
+ }
7
+ subgraph cluster1 {
8
+ label="Web";
9
+ fontname="Helvetica";
10
+ 4[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td border='1' port="p80tcp">80/tcp</td></tr><tr border='1'><td border='1' colspan="1">Web001</td></tr></table>>,tooltip="Web001",URL=""];
11
+ }
12
+ subgraph cluster1 {
13
+ label="Web";
14
+ fontname="Helvetica";
15
+ 5[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td border='1' port="p80tcp">80/tcp</td></tr><tr border='1'><td border='1' colspan="1">Web002</td></tr></table>>,tooltip="Web002",URL=""];
16
+ }
17
+ subgraph cluster3 {
18
+ label="App";
19
+ fontname="Helvetica";
20
+ 6[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td border='1' port="p80tcp">80/tcp</td><td border='1' port="p25tcp">25/tcp</td></tr><tr border='1'><td border='1' colspan="2">AppLB</td></tr></table>>,tooltip="AppLB",URL="",style="rounded,filled,dotted",fillcolor="azure"];
21
+ }
22
+ subgraph cluster3 {
23
+ label="App";
24
+ fontname="Helvetica";
25
+ 7[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td border='1' port="p80tcp">80/tcp</td><td border='1' port="p25tcp">25/tcp</td></tr><tr border='1'><td border='1' colspan="2">App001</td></tr></table>>,tooltip="App001",URL=""];
26
+ }
27
+ subgraph cluster3 {
28
+ label="App";
29
+ fontname="Helvetica";
30
+ 8[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td border='1' port="p80tcp">80/tcp</td><td border='1' port="p25tcp">25/tcp</td></tr><tr border='1'><td border='1' colspan="2">App002</td></tr></table>>,tooltip="App002",URL=""];
31
+ }
32
+ rankdir="TB";
33
+ fontname="Helvetica";
34
+ 1[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td>Browser</td></tr></table>>,tooltip="Browser",URL=""];
35
+ 2[fontname="Helvetica",shape="box",label=<<table border='0'><tr border='1'><td>Mail Server</td></tr></table>>,tooltip="Mail Server",URL=""];
36
+ 3;
37
+ 6;
38
+ 4;
39
+ 5;
40
+ 7;
41
+ 8;
42
+ 1 -> 3:p80tcp[color="blue"];
43
+ 1 -> 3:p443tcp;
44
+ 2 -> 6:p25tcp[color="green"];
45
+ 3 -> 4:p80tcp[color="blue"];
46
+ 3 -> 5:p80tcp[color="blue"];
47
+ 4 -> 6:p80tcp[color="blue"];
48
+ 5 -> 6:p80tcp[color="blue"];
49
+ 6 -> 7:p25tcp[color="green"];
50
+ 6 -> 7:p80tcp[color="blue"];
51
+ 6 -> 8:p25tcp[color="green"];
52
+ 6 -> 8:p80tcp[color="blue"];
53
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "layers": {
3
+ "Web": {
4
+ "nodes": [
5
+ {
6
+ "WebLB": {
7
+ "ports": ["80/tcp", "443/tcp"],
8
+ "type": "LB",
9
+ "url": "https://github.com/otahi/network_drawer/"
10
+ }
11
+ },
12
+ { "Web001": { "ports": ["80/tcp"] } },
13
+ { "Web002": { "ports": ["80/tcp"] } }
14
+ ]
15
+ },
16
+ "App": {
17
+ "nodes": [
18
+ { "AppLB": { "ports": ["80/tcp", "25/tcp"], "type": "LB" } },
19
+ { "App001": { "ports": ["80/tcp", "25/tcp"] } },
20
+ { "App002": { "ports": ["80/tcp", "25/tcp"] } }
21
+ ]
22
+ }
23
+ },
24
+ "nodes": [
25
+ { "Browser": {}, "type": "Client" },
26
+ { "Mail Server": {}, "type": "Client" }
27
+ ],
28
+ "connections": [
29
+ { "from": "Browser", "to": "WebLB:80/tcp", "type": "HTTP" },
30
+ { "from": "Browser", "to": "WebLB:443/tcp" },
31
+ { "from": "Mail Server", "to": "AppLB:25/tcp" , "type": "SMTP" },
32
+ { "from": "WebLB", "to": "Web001:80/tcp", "type": "HTTP" },
33
+ { "from": "WebLB", "to": "Web002:80/tcp", "type": "HTTP" },
34
+ { "from": "Web001", "to": "AppLB:80/tcp", "type": "HTTP" },
35
+ { "from": "Web002", "to": "AppLB:80/tcp", "type": "HTTP" },
36
+ { "from": "AppLB", "to": "App001:25/tcp", "type": "SMTP" },
37
+ { "from": "AppLB", "to": "App001:80/tcp", "type": "HTTP" },
38
+ { "from": "AppLB", "to": "App002:25/tcp", "type": "SMTP" },
39
+ { "from": "AppLB", "to": "App002:80/tcp", "type": "HTTP" }
40
+ ]
41
+ }
Binary file
@@ -0,0 +1,166 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <!-- Generated by graphviz version 2.34.0 (20140129.0153)
5
+ -->
6
+ <!-- Title: simple Pages: 1 -->
7
+ <svg width="336pt" height="460pt"
8
+ viewBox="0.00 0.00 336.00 460.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9
+ <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 456)">
10
+ <title>simple</title>
11
+ <polygon fill="white" stroke="white" points="-4,4 -4,-456 332,-456 332,4 -4,4"/>
12
+ <g id="clust1" class="cluster"><title>cluster1</title>
13
+ <polygon fill="none" stroke="black" points="105,-212 105,-408 295,-408 295,-212 105,-212"/>
14
+ <text text-anchor="middle" x="200" y="-390" font-family="Helvetica,sans-Serif" font-size="14.00">Web</text>
15
+ </g>
16
+ <g id="clust2" class="cluster"><title>cluster3</title>
17
+ <polygon fill="none" stroke="black" points="63,-8 63,-204 320,-204 320,-8 63,-8"/>
18
+ <text text-anchor="middle" x="191.5" y="-186" font-family="Helvetica,sans-Serif" font-size="14.00">App</text>
19
+ </g>
20
+ <!-- 3 -->
21
+ <g id="node1" class="node"><title>3</title>
22
+ <g id="a_node1"><a xlink:href="https://github.com/otahi/network_drawer/" xlink:title="WebLB">
23
+ <path fill="azure" stroke="black" stroke-dasharray="1,5" d="M247,-375.5C247,-375.5 153,-375.5 153,-375.5 147,-375.5 141,-369.5 141,-363.5 141,-363.5 141,-328.5 141,-328.5 141,-322.5 147,-316.5 153,-316.5 153,-316.5 247,-316.5 247,-316.5 253,-316.5 259,-322.5 259,-328.5 259,-328.5 259,-363.5 259,-363.5 259,-369.5 253,-375.5 247,-375.5"/>
24
+ <polygon fill="none" stroke="black" points="151,-346 151,-369 196,-369 196,-346 151,-346"/>
25
+ <text text-anchor="start" x="154.431" y="-351.9" font-family="Helvetica,sans-Serif" font-size="14.00">80/tcp</text>
26
+ <polygon fill="none" stroke="black" points="198,-346 198,-369 249,-369 249,-346 198,-346"/>
27
+ <text text-anchor="start" x="200.538" y="-351.9" font-family="Helvetica,sans-Serif" font-size="14.00">443/tcp</text>
28
+ <polygon fill="none" stroke="black" points="151,-322 151,-344 249,-344 249,-322 151,-322"/>
29
+ <text text-anchor="start" x="177.168" y="-327" font-family="Helvetica,sans-Serif" font-size="14.00">WebLB</text>
30
+ </a>
31
+ </g>
32
+ </g>
33
+ <!-- 4 -->
34
+ <g id="node2" class="node"><title>4</title>
35
+ <g id="a_node2"><a xlink:title="Web001">
36
+ <polygon fill="none" stroke="black" points="191,-279.5 113,-279.5 113,-220.5 191,-220.5 191,-279.5"/>
37
+ <polygon fill="none" stroke="black" points="123,-250 123,-273 181,-273 181,-250 123,-250"/>
38
+ <text text-anchor="start" x="132.931" y="-255.9" font-family="Helvetica,sans-Serif" font-size="14.00">80/tcp</text>
39
+ <polygon fill="none" stroke="black" points="123,-226 123,-248 181,-248 181,-226 123,-226"/>
40
+ <text text-anchor="start" x="126.051" y="-231" font-family="Helvetica,sans-Serif" font-size="14.00">Web001</text>
41
+ </a>
42
+ </g>
43
+ </g>
44
+ <!-- 3&#45;&gt;4 -->
45
+ <g id="edge4" class="edge"><title>3&#45;&gt;4:p80tcp</title>
46
+ <path fill="none" stroke="blue" d="M199.141,-316.436C198.045,-299.18 195.572,-278.608 190.026,-268.401"/>
47
+ <polygon fill="blue" stroke="blue" points="192,-265.499 182,-262 187.636,-270.972 192,-265.499"/>
48
+ </g>
49
+ <!-- 5 -->
50
+ <g id="node3" class="node"><title>5</title>
51
+ <g id="a_node3"><a xlink:title="Web002">
52
+ <polygon fill="none" stroke="black" points="287,-279.5 209,-279.5 209,-220.5 287,-220.5 287,-279.5"/>
53
+ <polygon fill="none" stroke="black" points="219,-250 219,-273 277,-273 277,-250 219,-250"/>
54
+ <text text-anchor="start" x="228.931" y="-255.9" font-family="Helvetica,sans-Serif" font-size="14.00">80/tcp</text>
55
+ <polygon fill="none" stroke="black" points="219,-226 219,-248 277,-248 277,-226 219,-226"/>
56
+ <text text-anchor="start" x="222.051" y="-231" font-family="Helvetica,sans-Serif" font-size="14.00">Web002</text>
57
+ </a>
58
+ </g>
59
+ </g>
60
+ <!-- 3&#45;&gt;5 -->
61
+ <g id="edge5" class="edge"><title>3&#45;&gt;5:p80tcp</title>
62
+ <path fill="none" stroke="blue" d="M200.859,-316.436C201.955,-299.18 204.428,-278.608 209.974,-268.401"/>
63
+ <polygon fill="blue" stroke="blue" points="212.364,-270.972 218,-262 208,-265.499 212.364,-270.972"/>
64
+ </g>
65
+ <!-- 6 -->
66
+ <g id="node4" class="node"><title>6</title>
67
+ <g id="a_node4"><a xlink:title="AppLB">
68
+ <path fill="azure" stroke="black" stroke-dasharray="1,5" d="M218.25,-171.5C218.25,-171.5 131.75,-171.5 131.75,-171.5 125.75,-171.5 119.75,-165.5 119.75,-159.5 119.75,-159.5 119.75,-124.5 119.75,-124.5 119.75,-118.5 125.75,-112.5 131.75,-112.5 131.75,-112.5 218.25,-112.5 218.25,-112.5 224.25,-112.5 230.25,-118.5 230.25,-124.5 230.25,-124.5 230.25,-159.5 230.25,-159.5 230.25,-165.5 224.25,-171.5 218.25,-171.5"/>
69
+ <polygon fill="none" stroke="black" points="130,-142 130,-165 175,-165 175,-142 130,-142"/>
70
+ <text text-anchor="start" x="133.431" y="-147.9" font-family="Helvetica,sans-Serif" font-size="14.00">80/tcp</text>
71
+ <polygon fill="none" stroke="black" points="177,-142 177,-165 221,-165 221,-142 177,-142"/>
72
+ <text text-anchor="start" x="179.931" y="-147.9" font-family="Helvetica,sans-Serif" font-size="14.00">25/tcp</text>
73
+ <polygon fill="none" stroke="black" points="130,-118 130,-140 221,-140 221,-118 130,-118"/>
74
+ <text text-anchor="start" x="154.483" y="-123" font-family="Helvetica,sans-Serif" font-size="14.00">AppLB</text>
75
+ </a>
76
+ </g>
77
+ </g>
78
+ <!-- 4&#45;&gt;6 -->
79
+ <g id="edge6" class="edge"><title>4&#45;&gt;6:p80tcp</title>
80
+ <path fill="none" stroke="blue" d="M152,-220.191C152,-207.217 152,-191.442 152,-176.321"/>
81
+ <polygon fill="blue" stroke="blue" points="155.5,-176 152,-166 148.5,-176 155.5,-176"/>
82
+ </g>
83
+ <!-- 5&#45;&gt;6 -->
84
+ <g id="edge7" class="edge"><title>5&#45;&gt;6:p80tcp</title>
85
+ <path fill="none" stroke="blue" d="M212.526,-220.441C192.443,-204.036 169.215,-184.601 158.292,-173.792"/>
86
+ <polygon fill="blue" stroke="blue" points="161.006,-171.581 152,-166 155.559,-175.979 161.006,-171.581"/>
87
+ </g>
88
+ <!-- 7 -->
89
+ <g id="node5" class="node"><title>7</title>
90
+ <g id="a_node5"><a xlink:title="App001">
91
+ <polygon fill="none" stroke="black" points="182.25,-75.5 71.75,-75.5 71.75,-16.5 182.25,-16.5 182.25,-75.5"/>
92
+ <polygon fill="none" stroke="black" points="82,-46 82,-69 127,-69 127,-46 82,-46"/>
93
+ <text text-anchor="start" x="85.4312" y="-51.9" font-family="Helvetica,sans-Serif" font-size="14.00">80/tcp</text>
94
+ <polygon fill="none" stroke="black" points="129,-46 129,-69 173,-69 173,-46 129,-46"/>
95
+ <text text-anchor="start" x="131.931" y="-51.9" font-family="Helvetica,sans-Serif" font-size="14.00">25/tcp</text>
96
+ <polygon fill="none" stroke="black" points="82,-22 82,-44 173,-44 173,-22 82,-22"/>
97
+ <text text-anchor="start" x="103.366" y="-27" font-family="Helvetica,sans-Serif" font-size="14.00">App001</text>
98
+ </a>
99
+ </g>
100
+ </g>
101
+ <!-- 6&#45;&gt;7 -->
102
+ <g id="edge8" class="edge"><title>6&#45;&gt;7:p25tcp</title>
103
+ <path fill="none" stroke="green" d="M160.623,-112.37C156.71,-102.677 153.148,-91.4558 151.693,-80.2467"/>
104
+ <polygon fill="green" stroke="green" points="155.167,-79.7409 151,-70 148.183,-80.2135 155.167,-79.7409"/>
105
+ </g>
106
+ <!-- 6&#45;&gt;7 -->
107
+ <g id="edge9" class="edge"><title>6&#45;&gt;7:p80tcp</title>
108
+ <path fill="none" stroke="blue" d="M126.856,-112.424C116.765,-103.478 108.243,-92.5876 105.197,-79.976"/>
109
+ <polygon fill="blue" stroke="blue" points="108.667,-79.5118 104,-70 101.716,-80.3458 108.667,-79.5118"/>
110
+ </g>
111
+ <!-- 8 -->
112
+ <g id="node6" class="node"><title>8</title>
113
+ <g id="a_node6"><a xlink:title="App002">
114
+ <polygon fill="none" stroke="black" points="311.25,-75.5 200.75,-75.5 200.75,-16.5 311.25,-16.5 311.25,-75.5"/>
115
+ <polygon fill="none" stroke="black" points="211,-46 211,-69 256,-69 256,-46 211,-46"/>
116
+ <text text-anchor="start" x="214.431" y="-51.9" font-family="Helvetica,sans-Serif" font-size="14.00">80/tcp</text>
117
+ <polygon fill="none" stroke="black" points="258,-46 258,-69 302,-69 302,-46 258,-46"/>
118
+ <text text-anchor="start" x="260.931" y="-51.9" font-family="Helvetica,sans-Serif" font-size="14.00">25/tcp</text>
119
+ <polygon fill="none" stroke="black" points="211,-22 211,-44 302,-44 302,-22 211,-22"/>
120
+ <text text-anchor="start" x="232.366" y="-27" font-family="Helvetica,sans-Serif" font-size="14.00">App002</text>
121
+ </a>
122
+ </g>
123
+ </g>
124
+ <!-- 6&#45;&gt;8 -->
125
+ <g id="edge10" class="edge"><title>6&#45;&gt;8:p25tcp</title>
126
+ <path fill="none" stroke="green" d="M230.267,-125.47C252.302,-116.05 273.686,-101.572 278.832,-80.0328"/>
127
+ <polygon fill="green" stroke="green" points="282.32,-80.3376 280,-70 275.367,-79.5283 282.32,-80.3376"/>
128
+ </g>
129
+ <!-- 6&#45;&gt;8 -->
130
+ <g id="edge11" class="edge"><title>6&#45;&gt;8:p80tcp</title>
131
+ <path fill="none" stroke="blue" d="M181.385,-112.436C186.051,-94.4294 192.996,-72.8124 201.291,-63.1294"/>
132
+ <polygon fill="blue" stroke="blue" points="203.16,-66.0907 210,-58 199.607,-60.059 203.16,-66.0907"/>
133
+ </g>
134
+ <!-- 1 -->
135
+ <g id="node7" class="node"><title>1</title>
136
+ <g id="a_node7"><a xlink:title="Browser">
137
+ <polygon fill="none" stroke="black" points="237,-452 161,-452 161,-416 237,-416 237,-452"/>
138
+ <text text-anchor="start" x="173.328" y="-427.9" font-family="Helvetica,sans-Serif" font-size="14.00">Browser</text>
139
+ </a>
140
+ </g>
141
+ </g>
142
+ <!-- 1&#45;&gt;3 -->
143
+ <g id="edge1" class="edge"><title>1&#45;&gt;3:p80tcp</title>
144
+ <path fill="none" stroke="blue" d="M187.672,-415.857C182.088,-406.095 176.087,-393.216 173.879,-380.063"/>
145
+ <polygon fill="blue" stroke="blue" points="177.357,-379.657 173,-370 170.384,-380.267 177.357,-379.657"/>
146
+ </g>
147
+ <!-- 1&#45;&gt;3 -->
148
+ <g id="edge2" class="edge"><title>1&#45;&gt;3:p443tcp</title>
149
+ <path fill="none" stroke="black" d="M209.893,-415.8C215.261,-406.018 221.032,-393.129 223.155,-380.02"/>
150
+ <polygon fill="black" stroke="black" points="226.647,-380.259 224,-370 219.672,-379.67 226.647,-380.259"/>
151
+ </g>
152
+ <!-- 2 -->
153
+ <g id="node8" class="node"><title>2</title>
154
+ <g id="a_node8"><a xlink:title="Mail Server">
155
+ <polygon fill="none" stroke="black" points="94.25,-268 -0.25,-268 -0.25,-232 94.25,-232 94.25,-268"/>
156
+ <text text-anchor="start" x="12.1035" y="-243.9" font-family="Helvetica,sans-Serif" font-size="14.00">Mail Server</text>
157
+ </a>
158
+ </g>
159
+ </g>
160
+ <!-- 2&#45;&gt;6 -->
161
+ <g id="edge3" class="edge"><title>2&#45;&gt;6:p25tcp</title>
162
+ <path fill="none" stroke="green" d="M70.1237,-231.94C79.4894,-225.355 90.5446,-217.983 101,-212 139.498,-189.969 190.301,-210.355 198.004,-175.979"/>
163
+ <polygon fill="green" stroke="green" points="201.489,-176.298 199,-166 194.524,-175.603 201.489,-176.298"/>
164
+ </g>
165
+ </g>
166
+ </svg>
@@ -0,0 +1,7 @@
1
+ {
2
+ "types": {
3
+ "LB": { "style": "rounded,filled,dotted", "fillcolor": "azure" },
4
+ "HTTP": { "color": "blue" },
5
+ "SMTP": { "color": "green" }
6
+ }
7
+ }
@@ -0,0 +1,5 @@
1
+ require 'network_drawer/version'
2
+ require 'network_drawer/cli'
3
+ require 'network_drawer/diagram'
4
+ require 'network_drawer/source'
5
+ require 'network_drawer/style'
@@ -0,0 +1,21 @@
1
+ require 'network_drawer'
2
+ require 'thor'
3
+
4
+ module NetworkDrawer
5
+ # Cli for NetworkDrawer
6
+ class Cli < Thor
7
+ desc 'draw SOURCE', 'draw network diagram with SOURCE file'
8
+ option(:style, aliases: :s,
9
+ banner: 'STYLE_FILE_IN_JSON')
10
+ option(:format, aliases: :f,
11
+ banner: 'OUTPUT_FILE_FORMAT such as svg, png',
12
+ default: :svg)
13
+ def draw(source_file)
14
+ src = Source.read(source_file)
15
+ op = { style: Style.read(options[:style]) }
16
+ op.merge!(format: options[:format])
17
+ dest_file = source_file.gsub(File.extname(source_file), '')
18
+ Diagram.draw(src, dest_file, op)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,144 @@
1
+ require 'gviz'
2
+
3
+ module NetworkDrawer
4
+ # Replesent of source file
5
+ class Diagram
6
+ TOP_LAYER = :networkdrawertop
7
+ DEFAULT_OPTIONS = {}
8
+ DEFAULT_STYLE = { fontname: 'Helvetica' }
9
+ DEFAULT_NODE_STYLE = { fontname: 'Helvetica', shape: 'box' }
10
+ DEFAULT_LINE_STYLE = {}
11
+
12
+ def self.draw(source, dest_file, options = {})
13
+ dia = new(source, dest_file, options)
14
+ dia.draw
15
+ end
16
+
17
+ def initialize(source, dest_file, options = {})
18
+ @source = source
19
+ @dest_file = dest_file
20
+ @options = DEFAULT_OPTIONS.merge(options)
21
+ @title = @options[:title] ? @options[:title] :
22
+ File.basename(@dest_file, '.*')
23
+ @nodes = {}
24
+ @layers = {}
25
+ @style = options[:style]
26
+ @gv = Gviz.new(@title)
27
+ end
28
+
29
+ def draw
30
+ @gv.global(rankdir: 'TB')
31
+ @gv.global(DEFAULT_STYLE)
32
+ create_nodes
33
+ create_connections
34
+
35
+ @gv.save @dest_file, @options[:format]
36
+ end
37
+
38
+ private
39
+
40
+ def create_nodes
41
+ built_nodes = build_nodes(TOP_LAYER => @source)
42
+ built_nodes[TOP_LAYER].each_value do |t|
43
+ @gv.global DEFAULT_STYLE
44
+ node_style = { label: t[:label], tooltip: t[:name], URL: t[:url] }
45
+ node_style =
46
+ override_style(:node, node_style, t[:type])
47
+ @gv.node(t[:id], node_style)
48
+ end
49
+
50
+ built_nodes[:layers].each_pair do |n, l|
51
+ layer_name = n
52
+ id = "#{@layers.size + 1}".to_sym
53
+ @layers.merge!(layer_name => id)
54
+ l.each_value do |v|
55
+ node_style = { label: v[:label], tooltip: v[:name], URL: v[:url] }
56
+ node_style =
57
+ override_style(:node, node_style, v[:type])
58
+ @gv.subgraph "cluster#{id}" do
59
+ global label: layer_name
60
+ global DEFAULT_STYLE
61
+ node(v[:id], node_style)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def build_nodes(layer)
68
+ layer_name = layer.keys.first
69
+ nodes = layer[layer_name][:nodes]
70
+ built_nodes = {}
71
+ nodes.each_with_index do |s, i|
72
+ id = "#{@nodes.size + 1}".to_sym
73
+ name = s.keys.first
74
+ ports = s[name][:ports] ? s[name][:ports] : []
75
+ url = s[name][:url] ? s[name][:url] : nil
76
+ label = build_node_label(name: name, ports: ports)
77
+ type = s[name][:type] ? s[name][:type].to_sym : nil
78
+ node = {
79
+ id: id, name: name, label: label,
80
+ ports: ports, type: type, url: url
81
+ }
82
+ built_nodes.merge!(name => node)
83
+ @nodes.merge!(name => node)
84
+ end if nodes
85
+
86
+ layers = layer[layer_name][:layers]
87
+ built_layers = {}
88
+ layers.each_pair do |k, v|
89
+ built_layers.merge!(build_nodes(k => v))
90
+ end if layers
91
+ built_nodes = { layer_name => built_nodes, layers: built_layers }
92
+ end
93
+
94
+ def build_node_label(opt = {})
95
+ if opt[:ports].empty?
96
+ label = "<tr border='1'><td>#{opt[:name]}</td></tr>"
97
+ else
98
+ label = "<tr border='1'>"
99
+ opt[:ports].each_with_index do |p, j|
100
+ label << "<td border='1' port=\"p#{p.gsub('/', '')}\">#{p}</td>"
101
+ end
102
+ label << '</tr>'
103
+ label << "<tr border='1'><td border='1' colspan=\"#{opt[:ports].size}\">#{opt[:name]}</td></tr>"
104
+ end
105
+ "<table border='0'>#{label}</table>"
106
+ end
107
+
108
+ def create_connections
109
+ return if @source[:connections].nil?
110
+ seq = 0
111
+ @source[:connections].each do |c|
112
+ from_name, from_port = c[:from].to_s.split(':')
113
+ to_name, to_port = c[:to].to_s.split(':')
114
+ from_id = @nodes[from_name.to_sym][:id]
115
+ to_id = @nodes[to_name.to_sym][:id]
116
+
117
+ from = from_port ? "#{from_id}:p#{from_port}" : from_id
118
+ to = to_port ? "#{to_id}:p#{to_port}" : to_id
119
+ line_style = override_style(:line, {}, :"#{c[:type]}")
120
+
121
+ @gv.edge "#{from}_#{to}_#{seq}".gsub('/', '').to_sym, line_style
122
+ seq += 1
123
+ end
124
+ end
125
+
126
+ def override_style(type, origin, style_type)
127
+
128
+ default =
129
+ case type
130
+ when :line
131
+ DEFAULT_LINE_STYLE
132
+ when :node
133
+ DEFAULT_NODE_STYLE
134
+ else
135
+ DEFAULT_STYLE
136
+ end
137
+ origin = {} unless origin
138
+ return default.merge(origin) unless style_type
139
+ style = @style[:types][style_type] if @style[:types]
140
+ style ||= {}
141
+ default.merge(origin).merge(style)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,10 @@
1
+ require 'json'
2
+
3
+ module NetworkDrawer
4
+ # Replesent of source file
5
+ class Source
6
+ def self.read(file_name, type = :json)
7
+ JSON.parse(File.read(file_name), symbolize_names: true)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ require 'json'
2
+
3
+ module NetworkDrawer
4
+ # Replesent of style file
5
+ class Style
6
+ def self.read(file_name, type = :json)
7
+ if file_name
8
+ JSON.parse(File.read(file_name), symbolize_names: true)
9
+ else
10
+ {}
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module NetworkDrawer
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'network_drawer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'network_drawer'
8
+ spec.version = NetworkDrawer::VERSION
9
+ spec.authors = ['Hiroshi Ota']
10
+ spec.email = ['otahi.pub@gmail.com']
11
+ spec.summary = 'Network diagram drawer with json'
12
+ spec.description = 'Network diagram drawer with json'
13
+ spec.homepage = 'https://github.com/otahi/network_drawer'
14
+ spec.license = 'MIT'
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'gviz', '~> 0.3.4'
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.7'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rubocop', '0.24.1'
25
+ spec.add_development_dependency 'coveralls', '~> 0.7'
26
+ spec.add_development_dependency 'byebug', '~> 3.4.0'
27
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: network_drawer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hiroshi Ota
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gviz
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.24.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.24.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 3.4.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 3.4.0
97
+ description: Network diagram drawer with json
98
+ email:
99
+ - otahi.pub@gmail.com
100
+ executables:
101
+ - network_drawer
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/network_drawer
111
+ - examples/simple.dot
112
+ - examples/simple.json
113
+ - examples/simple.png
114
+ - examples/simple.svg
115
+ - examples/simple_style.json
116
+ - lib/network_drawer.rb
117
+ - lib/network_drawer/cli.rb
118
+ - lib/network_drawer/diagram.rb
119
+ - lib/network_drawer/source.rb
120
+ - lib/network_drawer/style.rb
121
+ - lib/network_drawer/version.rb
122
+ - network_drawer.gemspec
123
+ homepage: https://github.com/otahi/network_drawer
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.2.2
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Network diagram drawer with json
147
+ test_files: []