ovsimager 0.0.3 → 0.0.4
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.
- checksums.yaml +4 -4
- data/lib/ovsimager/dotwriter.rb +75 -47
- data/lib/ovsimager/ipnetns.rb +6 -1
- data/lib/ovsimager/linuxbridge.rb +10 -4
- data/lib/ovsimager/ovsimager.rb +67 -45
- data/lib/ovsimager/tcpdump.rb +24 -5
- data/lib/ovsimager/utils.rb +4 -0
- data/lib/ovsimager/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c95b1e3a5c4aa98a5447d7e5b3a96a244537b9c1
|
4
|
+
data.tar.gz: a7c0ea9f9ca44e1b3dbc266a09b86840a428c8c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5cc6089f1c1012e478da97d78ec10fc6942a27f0c1eadf32cdcf67f7cbb07a54133e167ed2d4f0ca341a2ed384e471b62c126174ddd2b94bb0ba3f0b9618cb5
|
7
|
+
data.tar.gz: 32cb75f7b6e716dd598bffc27cf51ffee2512a703529137d8ad254f60467dfc16c8022ea50f98447fffca43fe5de6696b352e5cc4c15b54fbfc11abfa6a7d3c0
|
data/lib/ovsimager/dotwriter.rb
CHANGED
@@ -5,82 +5,110 @@ module OVSImager
|
|
5
5
|
@dot = File.open(fname, 'w')
|
6
6
|
@dot.puts 'graph interfaces {'
|
7
7
|
@dot.puts ' compound=true'
|
8
|
-
@dot.puts ' node [shape=rect]'
|
8
|
+
@dot.puts ' node [shape=rect,margin=0.1]'
|
9
9
|
@dot_peers = []
|
10
10
|
end
|
11
11
|
|
12
|
-
def escape(name)
|
13
|
-
name.to_s.gsub('-', '_')
|
14
|
-
end
|
15
|
-
|
16
|
-
def mark2color(mark)
|
17
|
-
{'<' => 'red', '>' => 'pink', '*' => 'yellow'}[mark]
|
18
|
-
end
|
19
|
-
|
20
12
|
def finish(pngname)
|
21
13
|
@dot.puts @dot_peers.join "\n"
|
22
14
|
@dot.puts '}'
|
23
15
|
@dot.close
|
24
16
|
@dot = nil
|
25
|
-
|
17
|
+
|
18
|
+
unless system("dot -Tpng \"#{@fname}\" -o \"#{pngname}\"")
|
19
|
+
puts "Failed to execute dot command: #$?"
|
20
|
+
end
|
26
21
|
end
|
27
22
|
|
28
|
-
#
|
29
|
-
def
|
23
|
+
# Draw OVSVS & LinuxBridge
|
24
|
+
def bridge(name, br_type)
|
30
25
|
@dot.puts " subgraph cluster_br__#{escape(name)} {"
|
31
26
|
@dot.puts " label = \"#{br_type}Bridge #{name}\""
|
32
|
-
end
|
33
27
|
|
34
|
-
|
35
|
-
fill = mark ? "fillcolor=#{mark2color(mark)},style=filled," : ''
|
36
|
-
label = "#{name}<BR/><FONT POINT-SIZE=\"10\">#{inet.join(',')}"
|
37
|
-
if tag or remote
|
38
|
-
label += "<BR/>#{tag}#{remote && remote.gsub('>','>')}"
|
39
|
-
end
|
40
|
-
if dump
|
41
|
-
label += " </FONT><FONT COLOR=\"blue\">"
|
42
|
-
if dump[0] && dump[2] && dump[0] == dump[3] && dump[1] == dump[2]
|
43
|
-
label += "<BR/>[#{dump[0]} <-> #{dump[1]}]"
|
44
|
-
else
|
45
|
-
label += "<BR/>[#{dump[0]} --> #{dump[1]}]" if dump[0]
|
46
|
-
label += "<BR/>[#{dump[3]} <-- #{dump[2]}]" if dump[2]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
label += " </FONT>"
|
50
|
-
@dot.puts " #{escape(name)} [#{fill}label=<#{label}>]"
|
51
|
-
if peer && name <= peer
|
52
|
-
@dot_peers << " #{escape(name)} -- #{escape(peer)}"
|
53
|
-
end
|
54
|
-
end
|
28
|
+
yield BridgeWriter.new(@dot, @dot_peers)
|
55
29
|
|
56
|
-
def br_end
|
57
30
|
@dot.puts " }"
|
58
31
|
end
|
59
32
|
|
60
|
-
#
|
61
|
-
def
|
33
|
+
# Draw IPNetNS
|
34
|
+
def namespace(name)
|
62
35
|
@dot.puts " subgraph cluster_ns__#{escape(name)} {"
|
63
36
|
@dot.puts " label = \"Namespace\\n#{name}\""
|
64
37
|
@dot.puts " style = \"filled\""
|
65
38
|
@dot.puts " fillcolor = \"#eeeeee\""
|
66
39
|
@dot.puts " ns__#{escape(name)} " +
|
67
40
|
"[label=\"\",style=invis,width=0,height=0,margin=0]"
|
68
|
-
|
41
|
+
|
42
|
+
yield NSWriter.new(@dot, @dot_peers, name)
|
43
|
+
|
44
|
+
@dot.puts ' }'
|
69
45
|
end
|
70
46
|
|
71
|
-
|
72
|
-
|
73
|
-
|
47
|
+
private
|
48
|
+
def escape(name)
|
49
|
+
Utils.escape_nodename(name)
|
74
50
|
end
|
75
51
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
52
|
+
class BridgeWriter
|
53
|
+
def initialize(dot, dot_peers)
|
54
|
+
@dot = dot
|
55
|
+
@dot_peers = dot_peers
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_iface(name, mark, dump, inet, tag, peer, remote=nil)
|
59
|
+
fill = mark ? "fillcolor=#{mark2color(mark)},style=filled," : ''
|
60
|
+
label = "#{name}<BR/><FONT POINT-SIZE=\"10\">#{inet.join('<BR/>')}"
|
61
|
+
if tag or remote
|
62
|
+
label += "<BR/>#{tag}#{remote && remote.gsub('>','>')}"
|
63
|
+
end
|
64
|
+
if dump
|
65
|
+
label += " </FONT><FONT COLOR=\"blue\">"
|
66
|
+
if dump[0] && dump[2] && dump[0] == dump[3] && dump[1] == dump[2]
|
67
|
+
label += "<BR/>[#{dump[0]} <-> #{dump[1]}]"
|
68
|
+
else
|
69
|
+
label += "<BR/>[#{dump[0]} --> #{dump[1]}]" if dump[0]
|
70
|
+
label += "<BR/>[#{dump[3]} <-- #{dump[2]}]" if dump[2]
|
71
|
+
end
|
72
|
+
if dump[4] && dump[4][:cap]
|
73
|
+
cap = dump[4][:cap]
|
74
|
+
label += "<BR/><FONT POINT-SIZE=\"10\">(#{cap[2]} #{cap[3]} " +
|
75
|
+
"#{cap[0]}<=>#{cap[1]})</FONT>"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
label += " </FONT>"
|
79
|
+
@dot.puts " #{escape(name)} [#{fill}label=<#{label}>]"
|
80
|
+
if peer
|
81
|
+
if peer[0] == ' '
|
82
|
+
@dot_peers << " #{escape(peer.strip)} -- #{escape(name)}"
|
83
|
+
elsif name <= peer
|
84
|
+
@dot_peers << " #{escape(name)} -- #{escape(peer)}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def mark2color(mark)
|
91
|
+
{'<' => 'red', '>' => 'pink', '*' => 'yellow'}[mark]
|
92
|
+
end
|
93
|
+
|
94
|
+
def escape(name)
|
95
|
+
Utils.escape_nodename(name)
|
96
|
+
end
|
80
97
|
end
|
81
98
|
|
82
|
-
|
83
|
-
|
99
|
+
# Draw namespace
|
100
|
+
class NSWriter < BridgeWriter
|
101
|
+
def initialize(dot, dot_peers, nsname)
|
102
|
+
@dot = dot
|
103
|
+
@dot_peers = dot_peers
|
104
|
+
@nsname = nsname
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_br_iface(name)
|
108
|
+
@dot_peers << " #{escape(name)} -- ns__#{escape(@nsname)} " +
|
109
|
+
"[style=dashed,lhead=cluster_ns__#{escape(@nsname)}]"
|
110
|
+
end
|
84
111
|
end
|
112
|
+
|
85
113
|
end
|
86
114
|
end
|
data/lib/ovsimager/ipnetns.rb
CHANGED
@@ -17,6 +17,10 @@ module OVSImager
|
|
17
17
|
find_veth_pair
|
18
18
|
end
|
19
19
|
|
20
|
+
def ns()
|
21
|
+
@ns
|
22
|
+
end
|
23
|
+
|
20
24
|
def to_hash()
|
21
25
|
@ifaces
|
22
26
|
end
|
@@ -36,8 +40,9 @@ module OVSImager
|
|
36
40
|
private
|
37
41
|
def parse(out, args)
|
38
42
|
out.split(/\n(?=[^ \s])/).map do |iface|
|
39
|
-
if iface.match(/^(\d+):\s+(\S+?)(
|
43
|
+
if iface.match(/^(\d+):\s+(\S+?)(?:@(\S+))?:+/)
|
40
44
|
params = {:id => $1, :name => $2}
|
45
|
+
params[:peer] = $3 if $3 && $3 != 'NONE' && $3[0,2] != 'if'
|
41
46
|
yield params, iface, args
|
42
47
|
else
|
43
48
|
STDERR.puts "IPNetNS: parse error: #{iface}"
|
@@ -2,21 +2,26 @@ require_relative 'utils'
|
|
2
2
|
|
3
3
|
module OVSImager
|
4
4
|
class LinuxBridge
|
5
|
-
def initialize()
|
5
|
+
def initialize(ns=[])
|
6
6
|
brctl_out = exec_brtcl
|
7
7
|
@br = parse brctl_out
|
8
|
+
ns.each do |n|
|
9
|
+
brctl_out = exec_brtcl(n)
|
10
|
+
@br = @br.merge parse(brctl_out, ns)
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
14
|
def to_hash()
|
11
15
|
return @br
|
12
16
|
end
|
13
17
|
|
14
|
-
def exec_brtcl()
|
15
|
-
|
18
|
+
def exec_brtcl(ns=nil)
|
19
|
+
ns_prefix = ns ? "ip netns exec #{ns} " : ''
|
20
|
+
Utils.execute(ns_prefix + 'brctl show', !!ns)
|
16
21
|
end
|
17
22
|
|
18
23
|
private
|
19
|
-
def parse(str)
|
24
|
+
def parse(str, ns=:root)
|
20
25
|
params = {}
|
21
26
|
str.split(/\n(?=\S)/)[1..-1].map do |br|
|
22
27
|
data = br.split
|
@@ -25,6 +30,7 @@ module OVSImager
|
|
25
30
|
:id => data[1],
|
26
31
|
:stp => data[2],
|
27
32
|
:interfaces => [data[0]] + data[3..-1],
|
33
|
+
:ns => ns,
|
28
34
|
}
|
29
35
|
end
|
30
36
|
params
|
data/lib/ovsimager/ovsimager.rb
CHANGED
@@ -7,16 +7,17 @@ require_relative 'dotwriter'
|
|
7
7
|
|
8
8
|
module OVSImager
|
9
9
|
class OVSImager
|
10
|
-
|
11
|
-
|
10
|
+
DEFAULT_DOT_FILENAME = 'interfaces.dot'
|
11
|
+
DEFAULT_PNG_FILENAME = 'interfaces.png'
|
12
12
|
|
13
13
|
def initialize(dump_mode=false, ping_from=nil, ping_to=nil)
|
14
14
|
@netns = IPNetNS.new
|
15
15
|
@ifaces = @netns.ifaces_hash
|
16
|
-
@linbr = LinuxBridge.new
|
16
|
+
@linbr = LinuxBridge.new(@netns.ns)
|
17
17
|
@ovsvs = OVSVS.new
|
18
18
|
|
19
|
-
@
|
19
|
+
@dot_filename = DEFAULT_DOT_FILENAME
|
20
|
+
@png_filename = DEFAULT_PNG_FILENAME
|
20
21
|
|
21
22
|
@dump_mode = dump_mode
|
22
23
|
@mark = {}
|
@@ -41,12 +42,17 @@ module OVSImager
|
|
41
42
|
"send ping to specified address") do |v|
|
42
43
|
@ping_to = v
|
43
44
|
end
|
45
|
+
opts.on("-o FILENAME.png", "--out FILENAME.png",
|
46
|
+
"output PNG filename (default: interfaces.png)") do |v|
|
47
|
+
@png_filename = v
|
48
|
+
@dot_filename = v.sub(/\.png$/i, '') + '.dot'
|
49
|
+
end
|
44
50
|
end.parse!
|
45
51
|
end
|
46
52
|
|
47
53
|
def execute_dump
|
48
54
|
return unless @dump_mode
|
49
|
-
tcpdump = TcpDump.new(
|
55
|
+
tcpdump = TcpDump.new(!!@ping_to, @ping_from, @ping_to)
|
50
56
|
@dump_result = tcpdump.test(@ifaces)
|
51
57
|
@dump_result.each do |(iface, result)|
|
52
58
|
if result[0] && result[2]
|
@@ -60,17 +66,18 @@ module OVSImager
|
|
60
66
|
end
|
61
67
|
|
62
68
|
def show_all
|
69
|
+
@dotwriter = DotWriter.new(@dot_filename)
|
63
70
|
show_ovsvs
|
64
71
|
puts '-' * 80
|
65
72
|
show_linbr
|
66
73
|
puts '-' * 80
|
67
74
|
show_netns
|
68
|
-
@dotwriter.finish(
|
75
|
+
@dotwriter.finish(@png_filename)
|
69
76
|
@dotwriter = nil
|
70
77
|
end
|
71
78
|
|
72
79
|
private
|
73
|
-
def show_iface_common(name, inet, patch, tag='', ns='')
|
80
|
+
def show_iface_common(name, inet, patch='', tag='', ns='')
|
74
81
|
puts " [#{@mark[name]||' '}] #{name}#{tag}#{patch}\t" +
|
75
82
|
"#{inet.join(',')}\t#{ns}"
|
76
83
|
end
|
@@ -83,62 +90,77 @@ module OVSImager
|
|
83
90
|
def show_ovsvs
|
84
91
|
@ovsvs.to_hash[:bridges].each do |br|
|
85
92
|
puts "OVS Bridge #{br[:name]}:"
|
86
|
-
@dotwriter.
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
93
|
+
@dotwriter.bridge(br[:name], 'OVS ') do |dot_br|
|
94
|
+
|
95
|
+
br[:ports].each do |port|
|
96
|
+
name = port[:name]
|
97
|
+
iface = @ifaces[name] || {}
|
98
|
+
inet = iface[:inet] || []
|
99
|
+
tag = port[:tag] ? ' (tag=' + port[:tag] + ')' : ''
|
100
|
+
peer = port[:peer] || iface[:peer]
|
101
|
+
remote = port[:remote_ip] ?
|
102
|
+
" #{port[:local_ip] || ''} => #{port[:remote_ip]}" : ''
|
103
|
+
patch = peer ? ' <-> ' + peer : remote
|
104
|
+
ns = iface[:ns] == :root ? '' : iface[:ns]
|
105
|
+
|
106
|
+
show_iface_common(name, inet, patch, tag, ns)
|
107
|
+
dot_br.add_iface(name, @mark[name], @dump_result[name],
|
101
108
|
inet, tag, peer, remote)
|
102
|
-
|
109
|
+
@done[name] = true
|
110
|
+
|
111
|
+
port[:interfaces].each do |port_if|
|
112
|
+
port_name = port_if[:name]
|
113
|
+
if port_name != name
|
114
|
+
print " "
|
115
|
+
port_inet = @ifaces[port_name] && @ifaces[port_name][:inet]
|
116
|
+
show_iface_common(port_name, inet)
|
117
|
+
dot_br.add_iface(port_name, @mark[port_name],
|
118
|
+
@dump_result[port_name], port_inet,
|
119
|
+
'', ' '+name)
|
120
|
+
###
|
121
|
+
@done[port_name] = true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
103
126
|
end
|
104
|
-
@dotwriter.br_end
|
105
127
|
end
|
106
128
|
end
|
107
129
|
|
108
130
|
def show_linbr
|
109
131
|
@linbr.to_hash.each do |name, br|
|
110
132
|
puts "Bridge #{name}"
|
111
|
-
@dotwriter.
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
133
|
+
@dotwriter.bridge(name, '') do |dot_br|
|
134
|
+
br[:interfaces].each do |ifname|
|
135
|
+
iface = @ifaces[ifname]
|
136
|
+
next unless iface
|
137
|
+
dot_br.add_iface(ifname, @mark[ifname], @dump_result[ifname],
|
138
|
+
iface[:inet], iface[:tag], iface[:peer])
|
139
|
+
show_iface iface
|
140
|
+
@done[ifname] = true
|
141
|
+
end
|
119
142
|
end
|
120
|
-
@dotwriter.br_end
|
121
143
|
end
|
122
144
|
end
|
123
145
|
|
124
146
|
def show_netns
|
125
147
|
@netns.to_hash.each do |name, ifaces|
|
126
148
|
puts "Namespace #{name}"
|
127
|
-
@dotwriter.
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
149
|
+
@dotwriter.namespace(name) do |dot_ns|
|
150
|
+
ifaces.each do |iface|
|
151
|
+
ifname = iface[:name]
|
152
|
+
if ifname != 'lo' and !iface[:inet].empty?
|
153
|
+
if @done[ifname]
|
154
|
+
dot_ns.add_br_iface(ifname)
|
155
|
+
else
|
156
|
+
dot_ns.add_iface(ifname, @mark[ifname], @dump_result[ifname],
|
157
|
+
iface[:inet], iface[:tag], iface[:peer])
|
158
|
+
end
|
136
159
|
end
|
160
|
+
show_iface iface unless @done[iface[:name]]
|
161
|
+
@done[iface[:name]] = true
|
137
162
|
end
|
138
|
-
show_iface iface unless @done[iface[:name]]
|
139
|
-
@done[iface[:name]] = true
|
140
163
|
end
|
141
|
-
@dotwriter.ns_end
|
142
164
|
end
|
143
165
|
end
|
144
166
|
|
data/lib/ovsimager/tcpdump.rb
CHANGED
@@ -14,8 +14,8 @@ module OVSImager
|
|
14
14
|
result = {}
|
15
15
|
ping = nil
|
16
16
|
if @ping
|
17
|
-
puts "Sending ping from #{@from} to #{@to} ..."
|
18
|
-
ping = IO.popen("ping -s #{SIZE} -c 15
|
17
|
+
puts "Sending ping from #{@from||'default'} to #{@to} ..."
|
18
|
+
ping = IO.popen("ping -s #{SIZE} -c 15 #{@from?'-I '+@from:''} #{@to} >/dev/null", "r")
|
19
19
|
end
|
20
20
|
|
21
21
|
threads = ifaces.map do |(iface, iref)|
|
@@ -23,10 +23,13 @@ module OVSImager
|
|
23
23
|
Thread.current[:iface] = iface
|
24
24
|
ns = iref[:ns]
|
25
25
|
nscmd = ns == :root ? '' : "ip netns exec #{ns} "
|
26
|
-
dump = IO.popen("exec #{nscmd}tcpdump -v -l -n -i #{iface} \\( icmp or udp port 4789 \\) and greater #{SIZE} 2>&1", "r")
|
26
|
+
dump = IO.popen("exec #{nscmd}tcpdump -v -l -n -nn -i #{iface} \\( icmp or udp port 4789 or proto gre \\) and greater #{SIZE} 2>&1", "r")
|
27
27
|
puts dump.gets
|
28
28
|
time_end = Time.now + 5
|
29
29
|
req_from = req_to = rep_from = rep_to = nil
|
30
|
+
cap_from = cap_to = cap_type = cap_id = nil
|
31
|
+
cap = false
|
32
|
+
cap_line = 0
|
30
33
|
while (waitmax = time_end - Time.now) > 0 do
|
31
34
|
rs, ws, = IO.select([dump], [], [], waitmax)
|
32
35
|
break unless rs
|
@@ -34,7 +37,14 @@ module OVSImager
|
|
34
37
|
msg = r.gets
|
35
38
|
break unless msg
|
36
39
|
# puts msg
|
37
|
-
if msg.match(/
|
40
|
+
if msg.match(/([\da-f\.:]+)\.\d+ > ([\da-f\.:]+)\.\d+: (VXLAN).*vni (\d+)/) ||
|
41
|
+
msg.match(/([\da-f\.:]+) > ([\da-f\.:]+): (GRE).*key=([\da-f]+)/)
|
42
|
+
cap_from = $1
|
43
|
+
cap_to = $2
|
44
|
+
cap_type = $3
|
45
|
+
cap_id = $4
|
46
|
+
cap_line = 3
|
47
|
+
elsif msg.match(/length #{SIZE+8}/) &&
|
38
48
|
msg.match(/([\da-f\.:]+) > ([\da-f\.:]+): ICMP echo (request|reply)/)
|
39
49
|
if $3 == 'request'
|
40
50
|
req_from = $1
|
@@ -43,14 +53,23 @@ module OVSImager
|
|
43
53
|
rep_from = $1
|
44
54
|
rep_to = $2
|
45
55
|
end
|
56
|
+
if cap_line > 0
|
57
|
+
if $3 == 'request'
|
58
|
+
cap = [cap_from, cap_to, cap_type, cap_id]
|
59
|
+
else
|
60
|
+
cap = [cap_to, cap_from, cap_type, cap_id]
|
61
|
+
end
|
62
|
+
end
|
46
63
|
break if req_from && req_to && rep_from && rep_to
|
47
64
|
end
|
65
|
+
cap_line -= 1 if cap_line > 0
|
48
66
|
end
|
49
67
|
end
|
50
68
|
puts "Killing tcpdump(#{dump.pid}) on interface #{iface}."
|
51
69
|
Process.kill('TERM', dump.pid)
|
52
70
|
dump.close
|
53
|
-
result[iface] = [req_from, req_to, rep_from, rep_to]
|
71
|
+
result[iface] = [req_from, req_to, rep_from, rep_to, {}]
|
72
|
+
result[iface][4][:cap] = cap if cap
|
54
73
|
end
|
55
74
|
end
|
56
75
|
threads.each {|th| th.join(10)}
|
data/lib/ovsimager/utils.rb
CHANGED
data/lib/ovsimager/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ovsimager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- NeoCat
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
105
|
version: '0'
|
106
106
|
requirements: []
|
107
107
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.
|
108
|
+
rubygems_version: 2.4.5
|
109
109
|
signing_key:
|
110
110
|
specification_version: 4
|
111
111
|
summary: Draw graph of Open vSwitch virtual bridges.
|