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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 234eece9031326d4bbc2ca1a61ea72e8252337a2
4
- data.tar.gz: 0afac00d5009ca9ed6d064ba04f2837da9fcfff4
3
+ metadata.gz: c95b1e3a5c4aa98a5447d7e5b3a96a244537b9c1
4
+ data.tar.gz: a7c0ea9f9ca44e1b3dbc266a09b86840a428c8c2
5
5
  SHA512:
6
- metadata.gz: 287343119b27f3522b1fa6834261aa11b9d0d95489ae81d100c4e8074fc673d010062068f3cd80ccc2b56f15bccec5eae98fff2fddc8c23504f10ec5ca22a821
7
- data.tar.gz: 842bb4fd1a09597b33a02898daeb4b021ea6ff617fbe7c777c665801ca69ecf5ff4e57673d0e25d2188de7fc105a53bc2ea98f882dffe5fdbfec4b5527a82f43
6
+ metadata.gz: d5cc6089f1c1012e478da97d78ec10fc6942a27f0c1eadf32cdcf67f7cbb07a54133e167ed2d4f0ca341a2ed384e471b62c126174ddd2b94bb0ba3f0b9618cb5
7
+ data.tar.gz: 32cb75f7b6e716dd598bffc27cf51ffee2512a703529137d8ad254f60467dfc16c8022ea50f98447fffca43fe5de6696b352e5cc4c15b54fbfc11abfa6a7d3c0
@@ -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
- system("dot -Tpng \"#{@fname}\" -o \"#{pngname}\"")
17
+
18
+ unless system("dot -Tpng \"#{@fname}\" -o \"#{pngname}\"")
19
+ puts "Failed to execute dot command: #$?"
20
+ end
26
21
  end
27
22
 
28
- # For OVSVS & LinuxBridge
29
- def br_begin(name, br_type)
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
- def br_iface(name, mark, dump, inet, tag, peer, remote=nil)
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('>','&gt;')}"
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]} &lt;-&gt; #{dump[1]}]"
44
- else
45
- label += "<BR/>[#{dump[0]} --&gt; #{dump[1]}]" if dump[0]
46
- label += "<BR/>[#{dump[3]} &lt;-- #{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
- # For IPNetNS
61
- def ns_begin(name)
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
- @nsname = name
41
+
42
+ yield NSWriter.new(@dot, @dot_peers, name)
43
+
44
+ @dot.puts ' }'
69
45
  end
70
46
 
71
- def ns_br_iface(name)
72
- @dot_peers << " #{escape(name)} -- ns__#{escape(@nsname)} " +
73
- "[style=dashed,lhead=cluster_ns__#{escape(@nsname)}]"
47
+ private
48
+ def escape(name)
49
+ Utils.escape_nodename(name)
74
50
  end
75
51
 
76
- def ns_iface(name, mark, dump, inet, tag, peer)
77
- br_iface(name, mark, dump, inet, tag, peer)
78
- # @dot.puts " #{escape(name)} -- #{escape(@last)} [style=invis]" if @last
79
- # @last = name
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('>','&gt;')}"
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]} &lt;-&gt; #{dump[1]}]"
68
+ else
69
+ label += "<BR/>[#{dump[0]} --&gt; #{dump[1]}]" if dump[0]
70
+ label += "<BR/>[#{dump[3]} &lt;-- #{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]}&lt;=&gt;#{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
- def ns_end
83
- @dot.puts ' }'
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
@@ -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+?)(?:@\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
- Utils.execute('brctl show')
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
@@ -7,16 +7,17 @@ require_relative 'dotwriter'
7
7
 
8
8
  module OVSImager
9
9
  class OVSImager
10
- DOT_FILENAME = 'interfaces.dot'
11
- PNG_FILENAME = 'interfaces.png'
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
- @dotwriter = DotWriter.new(DOT_FILENAME)
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(@ping_from && @ping_to, @ping_from, @ping_to)
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(PNG_FILENAME) if @dotwriter
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.br_begin(br[:name], 'OVS ')
87
-
88
- br[:ports].each do |port|
89
- name = port[:name]
90
- iface = @ifaces[name] || {}
91
- inet = iface[:inet] || []
92
- tag = port[:tag] ? ' (tag=' + port[:tag] + ')' : ''
93
- peer = port[:peer] || iface[:peer]
94
- remote = port[:remote_ip] ?
95
- " #{port[:local_ip] || ''} => #{port[:remote_ip]}" : ''
96
- patch = peer ? ' <-> ' + peer : remote
97
- ns = iface[:ns] == :root ? '' : iface[:ns]
98
-
99
- show_iface_common(name, inet, patch, tag, ns)
100
- @dotwriter.br_iface(name, @mark[name], @dump_result[name],
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
- @done[name] = true
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.br_begin(name, '')
112
- br[:interfaces].each do |ifname|
113
- iface = @ifaces[ifname]
114
- next unless iface
115
- @dotwriter.br_iface(ifname, @mark[ifname], @dump_result[ifname],
116
- iface[:inet], iface[:tag], iface[:peer])
117
- show_iface iface
118
- @done[ifname] = true
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.ns_begin(name)
128
- ifaces.each do |iface|
129
- ifname = iface[:name]
130
- if ifname != 'lo' and !iface[:inet].empty?
131
- if @done[ifname]
132
- @dotwriter.ns_br_iface(ifname)
133
- else
134
- @dotwriter.ns_iface(ifname, @mark[ifname], @dump_result[ifname],
135
- iface[:inet], iface[:tag], iface[:peer])
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
 
@@ -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 -I #{@from} #{@to} >/dev/null", "r")
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(/length #{SIZE+8}/) &&
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)}
@@ -15,5 +15,9 @@ module OVSImager
15
15
  end
16
16
  return out
17
17
  end
18
+
19
+ def self.escape_nodename(name)
20
+ name.to_s.gsub('-', '_')
21
+ end
18
22
  end
19
23
  end
@@ -1,3 +1,3 @@
1
1
  module OVSImager
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
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.3
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-10 00:00:00.000000000 Z
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.2.3
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.