ovsimager 0.0.2

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: 5a4813849f38c8999ec90b9f68e1607e681681e9
4
+ data.tar.gz: c08b1ec71b6b0a1b5e04c459bdc4f7238823edab
5
+ SHA512:
6
+ metadata.gz: 9a270e213b0422701c25006ee62397d623ecfd5c19a483459936c2da2f38e7c62abce02afc83634c25f618fbb76e196c3474dd3b83e24868452381e54aa0de42
7
+ data.tar.gz: adcdff1ffb9073c446e171c64592e41247eb51e21978bd7a4591bb38b3ac77fe78092891f6fbbb09ce44461a6e55f7579a486f8ea996f0d0509686555bd062a4
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ *.dot
15
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ovsimager.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Tomoki Sekiyama
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,48 @@
1
+ # OVSImager
2
+
3
+ OVSImager draws a graph that describes relationship among Open vSwitch
4
+ bridges, Linux bridges, and namespaces for routing. It can also mark
5
+ the ports where ping packets went through using tcpdump, which is a
6
+ useful feature for trouble-shooting in SDN environments.
7
+
8
+ ## Installation
9
+
10
+ $ gem install ovsimager
11
+ # sudo yum install graphviz
12
+
13
+ ## Usage
14
+
15
+ ### Draw a graph of Open vSwitch'es
16
+
17
+ $ ovsimager
18
+ ( => interfaces.png will be generated. )
19
+
20
+ ![sample](sample-interfaces.png)
21
+
22
+ ### Trace ping packets
23
+
24
+ Execute ping packet with size = 400 byte, and trace them:
25
+
26
+ $ ping -s 400 192.0.2.1
27
+ $ sudo ovsimager -d
28
+ ( => interfaces.png will be generated. )
29
+
30
+ ![sample](sample-ping-trace.png)
31
+
32
+ Or, ovsimager also can send ping:
33
+
34
+ $ sudo ovsimager -d -f 10.0.0.1 -t 192.0.2.1
35
+
36
+ Colors:
37
+
38
+ - Yellow => both 'ping' and 'pong' are went through
39
+ - Pink => only 'ping' is went through
40
+ - Red => only 'pong' is went through
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it ( https://github.com/NeoCat/ovsimager/fork )
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create a new Pull Request
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_dir = File.join(File.dirname(__FILE__), '..', 'lib')
4
+ $LOAD_PATH.unshift lib_dir if File.directory?(lib_dir)
5
+
6
+ require 'ovsimager'
7
+
8
+ ovsimager = OVSImager::OVSImager.new
9
+ ovsimager.parse_options
10
+ ovsimager.execute_dump
11
+ ovsimager.show_all
@@ -0,0 +1,2 @@
1
+ require "ovsimager/version"
2
+ require "ovsimager/ovsimager"
@@ -0,0 +1,86 @@
1
+ module OVSImager
2
+ class DotWriter
3
+ def initialize(fname)
4
+ @fname = fname
5
+ @dot = File.open(fname, 'w')
6
+ @dot.puts 'graph interfaces {'
7
+ @dot.puts ' compound=true'
8
+ @dot.puts ' node [shape=rect]'
9
+ @dot_peers = []
10
+ end
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
+ def finish(pngname)
21
+ @dot.puts @dot_peers.join "\n"
22
+ @dot.puts '}'
23
+ @dot.close
24
+ @dot = nil
25
+ system("dot -Tpng \"#{@fname}\" -o \"#{pngname}\"")
26
+ end
27
+
28
+ # For OVSVS & LinuxBridge
29
+ def br_begin(name, br_type)
30
+ @dot.puts " subgraph cluster_br__#{escape(name)} {"
31
+ @dot.puts " label = \"#{br_type}Bridge #{name}\""
32
+ end
33
+
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
55
+
56
+ def br_end
57
+ @dot.puts " }"
58
+ end
59
+
60
+ # For IPNetNS
61
+ def ns_begin(name)
62
+ @dot.puts " subgraph cluster_ns__#{escape(name)} {"
63
+ @dot.puts " label = \"Namespace\\n#{name}\""
64
+ @dot.puts " style = \"filled\""
65
+ @dot.puts " fillcolor = \"#eeeeee\""
66
+ @dot.puts " ns__#{escape(name)} " +
67
+ "[label=\"\",style=invis,width=0,height=0,margin=0]"
68
+ @nsname = name
69
+ end
70
+
71
+ def ns_br_iface(name)
72
+ @dot_peers << " #{escape(name)} -- ns__#{escape(@nsname)} " +
73
+ "[style=dashed,lhead=cluster_ns__#{escape(@nsname)}]"
74
+ end
75
+
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
80
+ end
81
+
82
+ def ns_end
83
+ @dot.puts ' }'
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,97 @@
1
+ require_relative 'utils'
2
+
3
+ module OVSImager
4
+ class IPNetNS
5
+ def initialize()
6
+ @ns = exec_ip('netns').split(/\n/)
7
+ @ifaces = {:root => parse_address(exec_ip('a'), :root)}
8
+ links = parse_link(exec_ip('-d link'), :root)
9
+ merge_link_type(@ifaces[:root], links)
10
+ @ns.each {|ns|
11
+ out = exec_ip("netns exec #{ns} ip a", true)
12
+ @ifaces[ns] = parse_address(out, ns)
13
+ out = exec_ip("netns exec #{ns} ip -d link", true)
14
+ links = parse_link(out, ns)
15
+ merge_link_type(@ifaces[ns], links)
16
+ }
17
+ find_veth_pair
18
+ end
19
+
20
+ def to_hash()
21
+ @ifaces
22
+ end
23
+
24
+ def ifaces_hash()
25
+ @ifaces.inject({}) {|h, (ns, v)| v.each {|i| h[i[:name]] = i}; h}
26
+ end
27
+
28
+ def ifaces_ary()
29
+ @ifaces.inject([]) {|a, (ns, v)| v.each {|i| a[i[:id].to_i] = i}; a}
30
+ end
31
+
32
+ def exec_ip(args, root=false)
33
+ Utils.execute("ip #{args}", root)
34
+ end
35
+
36
+ private
37
+ def parse(out, args)
38
+ out.split(/\n(?=[^ \s])/).map do |iface|
39
+ if iface.match(/^(\d+):\s+(\S+?):+/)
40
+ params = {:id => $1, :name => $2}
41
+ yield params, iface, args
42
+ else
43
+ STDERR.puts "IPNetNS: parse error: #{iface}"
44
+ {}
45
+ end
46
+ end
47
+ end
48
+
49
+ def parse_address(out, ns)
50
+ parse(out, ns) do |params, iface, ns|
51
+ params[:ns] = ns
52
+ params[:mac] = $1 if iface.match(/link\/\w+ (\S+)/)
53
+ [:inet, :inet6].each do |key|
54
+ params[key] = iface.scan(/#{key.to_s} (\S+)/)
55
+ end
56
+ [:mtu, :state].each do |key|
57
+ params[key] = $1 if iface.match(/#{key.to_s} (\S+)/)
58
+ end
59
+ params
60
+ end
61
+ end
62
+
63
+ def parse_link(out, ns)
64
+ parse(out, ns) do |params, iface, ns|
65
+ params[:ns] = ns
66
+ params[:mac] = $1 if iface.match(/link\/\w+ (\S+)/)
67
+ params[:type] = (iface.split(/\n/)[2] || '').strip
68
+ params
69
+ end
70
+ end
71
+
72
+ def merge_link_type(ifaces, links)
73
+ link_types = links.inject({}) {|h, link| h[link[:id]] = link[:type]; h}
74
+ ifaces.each {|iface| iface[:type] = link_types[iface[:id]]}
75
+ end
76
+
77
+ def find_veth_pair()
78
+ ifaces = ifaces_ary
79
+ ifaces.each do |iface|
80
+ next unless iface
81
+ if iface[:type] == 'veth' && !iface[:peer]
82
+ if iface[:ns] == :root
83
+ out = Utils::execute("ethtool -S #{iface[:name]}")
84
+ else
85
+ out = exec_ip("netns exec #{iface[:ns]} ethtool -S #{iface[:name]}", root=true)
86
+ end
87
+ if out.match /peer_ifindex: (\d+)/
88
+ iface[:peer] = ifaces[$1.to_i][:name]
89
+ ifaces[$1.to_i][:peer] = iface[:name]
90
+ else
91
+ STDERR.puts("Failed to lookup veth peer of '#{iface[:name]}'")
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'utils'
2
+
3
+ module OVSImager
4
+ class LinuxBridge
5
+ def initialize()
6
+ brctl_out = exec_brtcl
7
+ @br = parse brctl_out
8
+ end
9
+
10
+ def to_hash()
11
+ return @br
12
+ end
13
+
14
+ def exec_brtcl()
15
+ Utils.execute('brctl show')
16
+ end
17
+
18
+ private
19
+ def parse(str)
20
+ params = {}
21
+ str.split(/\n(?=\S)/)[1..-1].map do |br|
22
+ data = br.split
23
+ params[data[0]] = {
24
+ :name => data[0],
25
+ :id => data[1],
26
+ :stp => data[2],
27
+ :interfaces => [data[0]] + data[3..-1],
28
+ }
29
+ end
30
+ params
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,145 @@
1
+ require 'optparse'
2
+ require_relative 'ovsvs'
3
+ require_relative 'linuxbridge'
4
+ require_relative 'ipnetns'
5
+ require_relative 'tcpdump'
6
+ require_relative 'dotwriter'
7
+
8
+ module OVSImager
9
+ class OVSImager
10
+ DOT_FILENAME = 'interfaces.dot'
11
+ PNG_FILENAME = 'interfaces.png'
12
+
13
+ def initialize(dump_mode=false, ping_from=nil, ping_to=nil)
14
+ @netns = IPNetNS.new
15
+ @ifaces = @netns.ifaces_hash
16
+ @linbr = LinuxBridge.new
17
+ @ovsvs = OVSVS.new
18
+
19
+ @dotwriter = DotWriter.new(DOT_FILENAME)
20
+
21
+ @dump_mode = dump_mode
22
+ @mark = {}
23
+ @dump_result = {}
24
+ @ping_from = ping_from
25
+ @ping_to = ping_to
26
+ @done = {'lo' => true}
27
+ end
28
+
29
+ def parse_options
30
+ OptionParser.new do |opts|
31
+ opts.banner = "Usage: #$0 [options]"
32
+ opts.on("-d", "--dump",
33
+ "enable dump mode (trace ping -s 400 packets)") do
34
+ @dump_mode = true
35
+ end
36
+ opts.on("-f ADDRESS", "--from ADDRESS",
37
+ "send ping from specified address") do |v|
38
+ @ping_from = v
39
+ end
40
+ opts.on("-t ADDRESS", "--to ADDRESS",
41
+ "send ping to specified address") do |v|
42
+ @ping_to = v
43
+ end
44
+ end.parse!
45
+ end
46
+
47
+ def execute_dump
48
+ return unless @dump_mode
49
+ tcpdump = TcpDump.new(@ping_from && @ping_to, @ping_from, @ping_to)
50
+ @dump_result = tcpdump.test(@ifaces)
51
+ @dump_result.each do |(iface, result)|
52
+ if result[0] && result[2]
53
+ @mark[iface] = '*'
54
+ elsif result[0]
55
+ @mark[iface] = '>'
56
+ elsif result[2]
57
+ @mark[iface] = '<'
58
+ end
59
+ end
60
+ end
61
+
62
+ def show_all
63
+ show_ovsvs
64
+ puts '-' * 80
65
+ show_linbr
66
+ puts '-' * 80
67
+ show_netns
68
+ @dotwriter.finish(PNG_FILENAME) if @dotwriter
69
+ @dotwriter = nil
70
+ end
71
+
72
+ private
73
+ def show_iface_common(name, inet, patch, tag='', ns='')
74
+ puts " [#{@mark[name]||' '}] #{name}#{tag}#{patch}\t" +
75
+ "#{inet.join(',')}\t#{ns}"
76
+ end
77
+
78
+ def show_iface(iface)
79
+ patch = iface[:peer] ? " <-> #{iface[:peer]}" : ''
80
+ show_iface_common(iface[:name], iface[:inet], patch)
81
+ end
82
+
83
+ def show_ovsvs
84
+ @ovsvs.to_hash[:bridges].each do |br|
85
+ 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],
101
+ inet, tag, peer, remote)
102
+ @done[name] = true
103
+ end
104
+ @dotwriter.br_end
105
+ end
106
+ end
107
+
108
+ def show_linbr
109
+ @linbr.to_hash.each do |name, br|
110
+ puts "Bridge #{name}"
111
+ @dotwriter.br_begin(name, '')
112
+ br[:interfaces].each do |ifname|
113
+ iface = @ifaces[ifname]
114
+ @dotwriter.br_iface(ifname, @mark[ifname], @dump_result[ifname],
115
+ iface[:inet], iface[:tag], iface[:peer])
116
+ show_iface iface
117
+ @done[ifname] = true
118
+ end
119
+ @dotwriter.br_end
120
+ end
121
+ end
122
+
123
+ def show_netns
124
+ @netns.to_hash.each do |name, ifaces|
125
+ puts "Namespace #{name}"
126
+ @dotwriter.ns_begin(name)
127
+ ifaces.each do |iface|
128
+ ifname = iface[:name]
129
+ if ifname != 'lo' and !iface[:inet].empty?
130
+ if @done[ifname]
131
+ @dotwriter.ns_br_iface(ifname)
132
+ else
133
+ @dotwriter.ns_iface(ifname, @mark[ifname], @dump_result[ifname],
134
+ iface[:inet], iface[:tag], iface[:peer])
135
+ end
136
+ end
137
+ show_iface iface unless @done[iface[:name]]
138
+ @done[iface[:name]] = true
139
+ end
140
+ @dotwriter.ns_end
141
+ end
142
+ end
143
+
144
+ end
145
+ end
@@ -0,0 +1,72 @@
1
+ require_relative 'utils'
2
+
3
+ module OVSImager
4
+ class OVSVS
5
+ def initialize()
6
+ vsctl_out = exec_vstcl
7
+ @vs = parse(vsctl_out, ['bridge', 'port', 'interface'])
8
+ @vs[:bridges] ||= []
9
+
10
+ # Mark the peer port.
11
+ @vs[:bridges].each do |br|
12
+ br[:ports].each do |port|
13
+ iface = port[:interfaces][0]
14
+ if iface[:type] == 'patch' && iface[:options].match(/peer="?(\S+?)"?[,\}]/)
15
+ port[:peer] = $1
16
+ end
17
+ if iface[:type] == 'gre' || iface[:type] == 'vxlan'
18
+ if iface[:options].match(/remote_ip="?(\S+?)"?[,\}]/)
19
+ port[:remote_ip] = $1
20
+ end
21
+ if iface[:options].match(/local_ip="?(\S+?)"?[,\}]/)
22
+ port[:local_ip] = $1
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # Move the port that has the same name with the interface to first.
29
+ @vs[:bridges].each do |vs|
30
+ vs[:ports].sort!{|a, b|
31
+ vs[:name] == a[:name] ? -1 : vs[:name] == b[:name] ? 1 :
32
+ a[:ns] == b[:ns] ? a[:name] <=> b[:name] : a[:ns] <=> b[:ns]
33
+ }
34
+ end
35
+ end
36
+
37
+ def to_hash()
38
+ return @vs
39
+ end
40
+
41
+ def exec_vstcl()
42
+ begin
43
+ Utils.execute('ovs-vsctl show', root=true)
44
+ rescue
45
+ ''
46
+ end
47
+ end
48
+
49
+ private
50
+ def parse(str, types)
51
+ name, attrs = str.split(/\n/, 2)
52
+ return {} unless name
53
+ name.gsub!(/^\"|\"$/, '')
54
+ params = {:name => name}
55
+ return params unless attrs
56
+
57
+ indent = attrs.match(/^(\s*)/)[1]
58
+ attrs.gsub!(/^#{indent}([^ ]*):\s+"?(.*?)"?\s*(?:$|\n)/) do |m|
59
+ params[$1.to_sym] = $2
60
+ ''
61
+ end
62
+ return params if types.empty?
63
+
64
+ params[(types[0]+'s').downcase.to_sym] =
65
+ attrs.split(/\s+#{types[0]} /i)[1..-1].map do |cstr|
66
+ parse(cstr, types[1..-1])
67
+ end
68
+
69
+ return params
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,64 @@
1
+ require 'time'
2
+
3
+ module OVSImager
4
+ class TcpDump
5
+ SIZE = 400
6
+ def initialize(ping=false, from=nil, to=nil)
7
+ throw 'must be root.' if Process::UID.eid != 0
8
+ @ping = ping
9
+ @from = from
10
+ @to = to
11
+ end
12
+
13
+ def test(ifaces)
14
+ result = {}
15
+ ping = nil
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")
19
+ end
20
+
21
+ threads = ifaces.map do |(iface, iref)|
22
+ Thread.new do
23
+ Thread.current[:iface] = iface
24
+ ns = iref[:ns]
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")
27
+ puts dump.gets
28
+ time_end = Time.now + 5
29
+ req_from = req_to = rep_from = rep_to = nil
30
+ while (waitmax = time_end - Time.now) > 0 do
31
+ rs, ws, = IO.select([dump], [], [], waitmax)
32
+ break unless rs
33
+ if r = rs[0]
34
+ msg = r.gets
35
+ break unless msg
36
+ # puts msg
37
+ if msg.match(/length #{SIZE+8}/) &&
38
+ msg.match(/([\da-f\.:]+) > ([\da-f\.:]+): ICMP echo (request|reply)/)
39
+ if $3 == 'request'
40
+ req_from = $1
41
+ req_to = $2
42
+ else
43
+ rep_from = $1
44
+ rep_to = $2
45
+ end
46
+ break if req_from && req_to && rep_from && rep_to
47
+ end
48
+ end
49
+ end
50
+ puts "Killing tcpdump(#{dump.pid}) on interface #{iface}."
51
+ Process.kill('TERM', dump.pid)
52
+ dump.close
53
+ result[iface] = [req_from, req_to, rep_from, rep_to]
54
+ end
55
+ end
56
+ threads.each {|th| th.join(10)}
57
+ if @ping
58
+ Process.kill('TERM', ping.pid)
59
+ ping.close
60
+ end
61
+ return result
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,19 @@
1
+ require 'open3'
2
+
3
+ module OVSImager
4
+ class Utils
5
+ def self.get_root_helper(root=true)
6
+ return '' if not root or Process::UID.eid == 0
7
+ root ? 'sudo ' : ''
8
+ end
9
+
10
+ def self.execute(cmd, root=false)
11
+ root_helper = self.get_root_helper(root)
12
+ out = `#{root_helper}#{cmd}`
13
+ if $? != 0
14
+ raise "command execution failure: #{$?}"
15
+ end
16
+ return out
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module OVSImager
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ovsimager/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ovsimager"
8
+ spec.version = OVSImager::VERSION
9
+ spec.authors = ["NeoCat"]
10
+ spec.email = ["neocat@neocat.jp"]
11
+ spec.summary = %q{Draw graph of Open vSwitch virtual bridges.}
12
+ spec.description = %q{OVSImager draws a graph that describes relationship among Open vSwitch bridges, Linux bridges, and namespaces for routing. It can also mark the ports where ping packets went through using tcpdump, which is a useful feature for trouble-shooting in SDN environments. }
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ end
Binary file
Binary file
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ovsimager do
4
+ it 'has a version number' do
5
+ expect(Ovsimager::VERSION).not_to be nil
6
+ end
7
+
8
+ it 'does something useful' do
9
+ expect(false).to eq(true)
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'ovsimager'
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ovsimager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - NeoCat
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: 'OVSImager draws a graph that describes relationship among Open vSwitch
56
+ bridges, Linux bridges, and namespaces for routing. It can also mark the ports where
57
+ ping packets went through using tcpdump, which is a useful feature for trouble-shooting
58
+ in SDN environments. '
59
+ email:
60
+ - neocat@neocat.jp
61
+ executables:
62
+ - ovsimager
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - ".rspec"
68
+ - ".travis.yml"
69
+ - Gemfile
70
+ - LICENSE.txt
71
+ - README.md
72
+ - Rakefile
73
+ - bin/ovsimager
74
+ - lib/ovsimager.rb
75
+ - lib/ovsimager/dotwriter.rb
76
+ - lib/ovsimager/ipnetns.rb
77
+ - lib/ovsimager/linuxbridge.rb
78
+ - lib/ovsimager/ovsimager.rb
79
+ - lib/ovsimager/ovsvs.rb
80
+ - lib/ovsimager/tcpdump.rb
81
+ - lib/ovsimager/utils.rb
82
+ - lib/ovsimager/version.rb
83
+ - ovsimager.gemspec
84
+ - sample-interfaces.png
85
+ - sample-ping-trace.png
86
+ - spec/ovsimager_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: ''
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.2.3
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Draw graph of Open vSwitch virtual bridges.
112
+ test_files:
113
+ - spec/ovsimager_spec.rb
114
+ - spec/spec_helper.rb