openvpn-status-web 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,6 @@ language: ruby
3
3
  rvm:
4
4
  - 2.0.0
5
5
  - 1.9.3
6
- - 1.8.7
7
6
 
8
7
  gemfile:
9
8
  - Gemfile
data/README.md CHANGED
@@ -1,17 +1,60 @@
1
1
  # openvpn-status-web
2
2
 
3
- Small (another word for naive in this case, it's simple and serves my needs) [rack](http://rack.github.com/) app
4
- providing the information the [OpenVPN](http://openvpn.net/index.php/open-source.html) server collects in it's status file
5
- especially including a list of currently connected clients (common name, remote address, traffic, ...).
6
- It comes with a Debian 6 compatible init.d file.
3
+ [![Build Status](https://travis-ci.org/cmur2/openvpn-status-web.png)](https://travis-ci.org/cmur2/openvpn-status-web)
4
+
5
+ ## Description
6
+
7
+ Small (another word for naive in this case, it's simple and serves my needs) [Rack](http://rack.github.com/) application providing the information the [OpenVPN](http://openvpn.net/index.php/open-source.html) server collects in it's status file especially including a list of currently connected clients (common name, remote address, traffic, ...).
7
8
 
8
9
  It lacks:
9
10
 
10
- * authentication
11
- * caching (parses file on each request, page does auto-refresh every minute as OpenVPN updates the status file these often)
11
+ * tracking multiple status at the same time
12
+ * newer status file versions than v1
13
+ * caching (parses file on each request, page does auto-refresh every minute as OpenVPN updates the status file these often by default)
12
14
  * management interface support
13
15
  * *possibly more...*
14
16
 
17
+ ## Usage
18
+
19
+ Install the gem:
20
+
21
+ gem install openvpn-status-web
22
+
23
+ Create a configuration file in YAML format somewhere:
24
+
25
+ ```yaml
26
+ # listen address and port
27
+ host: "0.0.0.0"
28
+ port: "8080"
29
+ # optional: drop priviliges in case you want to but you should give this user at least read access on the log files
30
+ user: "nobody"
31
+ group: "nogroup"
32
+ # logfile is optional, logs to STDOUT else
33
+ logfile: "openvpn-status-web.log"
34
+ # display name for humans and the status file path
35
+ name: "My Small VPN"
36
+ status_file: "/var/log/openvpn-status.log"
37
+ ```
38
+
39
+ Your OpenVPN configuration should contain something like this:
40
+
41
+ ```
42
+ # ...snip...
43
+ status /var/log/openvpn-status.log
44
+ status-version 1
45
+ # ...snip...
46
+ ```
47
+
48
+ ## Advanced topics
49
+
50
+ ### Authentication
51
+
52
+ If the information exposed is important to you serve it via the VPN or use a webserver as a proxy to handle SSL and/or HTTP authentication.
53
+
54
+ ### Init scripts
55
+
56
+ The [Debian 6 init.d script](init.d/debian-6-openvpn-status-web) assumes that openvpn-status-web is installed into the system ruby (no RVM support) and the config.yaml is at /opt/openvpn-status-web/config.yaml. Modify to your needs.
57
+
15
58
  ## License
16
59
 
17
60
  openvpn-statsu-web is licensed under the Apache License, Version 2.0. See LICENSE for more information.
@@ -0,0 +1,40 @@
1
+ #! /bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: openvpn-status-web
4
+ # Required-Start: $remote_fs $syslog
5
+ # Required-Stop: $remote_fs $syslog
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: 0 1 6
8
+ # Short-Description: Handle openvpn-status-web gem
9
+ ### END INIT INFO
10
+
11
+ # using the system ruby's gem binaries directory
12
+ DAEMON="/var/lib/gems/1.8/bin/openvpn-status-web"
13
+
14
+ CONFIG_FILE="/opt/openvpn-status-web/config.yaml"
15
+
16
+ DAEMON_OPTS="$CONFIG_FILE"
17
+
18
+ test -x $DAEMON || exit 0
19
+
20
+ . /lib/lsb/init-functions
21
+
22
+ case "$1" in
23
+ start)
24
+ log_daemon_msg "Starting openvpn-web-status" "openvpn-web-status"
25
+ start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/openvpn-web-status.pid" --background --exec $DAEMON -- $DAEMON_OPTS
26
+ ;;
27
+ stop)
28
+ log_daemon_msg "Stopping openvpn-web-status" "openvpn-web-status"
29
+ start-stop-daemon --stop --quiet --oknodo --pidfile "/var/run/openvpn-web-status.pid"
30
+ ;;
31
+ restart|force-reload)
32
+ log_daemon_msg "Restarting openvpn-web-status" "openvpn-web-status"
33
+ start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile "/var/run/openvpn-web-status.pid"
34
+ start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/openvpn-web-status.pid" --background --exec $DAEMON -- $DAEMON_OPTS
35
+ ;;
36
+ *)
37
+ echo "Usage: $0 {start|stop|restart|force-reload}" >&2
38
+ exit 1
39
+ ;;
40
+ esac
@@ -1,12 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'date'
3
4
  require 'etc'
4
5
  require 'logger'
5
6
  require 'ipaddr'
6
7
  require 'yaml'
7
8
  require 'rack'
9
+ require 'erb'
8
10
  require 'metriks'
11
+ require 'better_errors'
9
12
 
13
+ require 'openvpn-status-web/status'
14
+ require 'openvpn-status-web/parser/v1'
15
+ require 'openvpn-status-web/parser/v2'
16
+ require 'openvpn-status-web/parser/v3'
10
17
  require 'openvpn-status-web/int_patch'
11
18
  require 'openvpn-status-web/version'
12
19
 
@@ -26,18 +33,25 @@ module OpenVPNStatusWeb
26
33
  end
27
34
 
28
35
  class Daemon
29
- def initialize(name, file)
30
- @name = name
31
- @file = file
36
+ def initialize(vpns)
37
+ @vpns = vpns
38
+
39
+ @main_tmpl = read_template(File.join(File.dirname(__FILE__), 'openvpn-status-web/main.html.erb'))
32
40
  end
33
41
 
34
42
  def call(env)
35
- main_tmpl = read_template(File.join(File.dirname(__FILE__), 'openvpn-status-web/main.html.erb'))
43
+ return [405, {"Content-Type" => "text/plain"}, ["Method Not Allowed"]] if env["REQUEST_METHOD"] != "GET"
44
+ return [404, {"Content-Type" => "text/plain"}, ["Not Found"]] if env["PATH_INFO"] != "/"
45
+
36
46
  # variables for template
37
- name = @name
38
- client_list, routing_table, global_stats = read_status_log(@file)
39
-
40
- html = main_tmpl.result(binding)
47
+ vpns = @vpns
48
+ stati = {}
49
+ @vpns.each do |name,config|
50
+ stati[name] = parse_status_log(config)
51
+ end
52
+ # eval
53
+ html = @main_tmpl.result(binding)
54
+
41
55
  [200, {"Content-Type" => "text/html"}, [html]]
42
56
  end
43
57
 
@@ -47,50 +61,67 @@ module OpenVPNStatusWeb
47
61
  ERB.new(text)
48
62
  end
49
63
 
50
- def read_status_log(file)
51
- text = File.open(file, 'rb') do |f| f.read end
52
-
53
- current_section = :none
54
- client_list = []
55
- routing_table = []
56
- global_stats = []
57
-
58
- text.lines.each do |line|
59
- (current_section = :cl; next) if line == "OpenVPN CLIENT LIST\n"
60
- (current_section = :rt; next) if line == "ROUTING TABLE\n"
61
- (current_section = :gs; next) if line == "GLOBAL STATS\n"
62
- (current_section = :end; next) if line == "END\n"
63
-
64
- case current_section
65
- when :cl then client_list << line.strip.split(',')
66
- when :rt then routing_table << line.strip.split(',')
67
- when :gs then global_stats << line.strip.split(',')
68
- end
64
+ def parse_status_log(vpn)
65
+ text = File.open(vpn['status_file'], 'rb') do |f| f.read end
66
+
67
+ case vpn['version']
68
+ when 1
69
+ OpenVPNStatusWeb::Parser::V1.new.parse_status_log(text)
70
+ when 2
71
+ OpenVPNStatusWeb::Parser::V2.new.parse_status_log(text)
72
+ when 3
73
+ OpenVPNStatusWeb::Parser::V3.new.parse_status_log(text)
74
+ else
75
+ raise "No suitable parser for status-version #{vpn['version']}"
69
76
  end
70
-
71
- [client_list[2..-1], routing_table[1..-1], global_stats]
72
77
  end
73
78
 
74
79
  def self.run!
75
- if ARGV.length != 4
76
- puts "Usage: openvpn-status-web vpn-name status-log listen-host listen-port"
80
+ if ARGV.length != 1
81
+ puts "Usage: openvpn-status-web config_file"
82
+ exit 1
83
+ end
84
+
85
+ config_file = ARGV[0]
86
+
87
+ if not File.file?(config_file)
88
+ puts "Config file not found!"
77
89
  exit 1
78
90
  end
91
+
92
+ puts "openvpn-status-web version #{OpenVPNStatusWeb::VERSION}"
93
+ puts "Using config file #{config_file}"
94
+
95
+ config = YAML::load(File.open(config_file, 'r') { |f| f.read })
79
96
 
80
- OpenVPNStatusWeb.logger = Logger.new(STDOUT)
97
+ if config['logfile']
98
+ OpenVPNStatusWeb.logger = Logger.new(config['logfile'])
99
+ else
100
+ OpenVPNStatusWeb.logger = Logger.new(STDOUT)
101
+ end
81
102
 
82
103
  OpenVPNStatusWeb.logger.progname = "openvpn-status-web"
83
104
  OpenVPNStatusWeb.logger.formatter = LogFormatter.new
84
105
 
85
106
  OpenVPNStatusWeb.logger.info "Starting..."
86
107
 
108
+ # drop privs (first change group than user)
109
+ Process::Sys.setgid(Etc.getgrnam(config['group']).gid) if config['group']
110
+ Process::Sys.setuid(Etc.getpwnam(config['user']).uid) if config['user']
111
+
112
+ # configure rack
113
+ app = Daemon.new(config['vpns'])
114
+ if ENV['RACK_ENV'] == "development"
115
+ app = BetterErrors::Middleware.new(app)
116
+ BetterErrors.application_root = File.expand_path("..", __FILE__)
117
+ end
118
+
87
119
  Signal.trap('INT') do
88
120
  OpenVPNStatusWeb.logger.info "Quitting..."
89
121
  Rack::Handler::WEBrick.shutdown
90
122
  end
91
-
92
- app = Daemon.new(ARGV[0], ARGV[1])
93
- Rack::Handler::WEBrick.run app, :Host => ARGV[2], :Port => ARGV[3]
123
+
124
+ Rack::Handler::WEBrick.run app, :Host => config['host'], :Port => config['port']
94
125
  end
95
126
  end
96
127
  end
@@ -42,67 +42,70 @@ thead {
42
42
  </head>
43
43
  <body>
44
44
 
45
- <h1>OpenVPN Status for <%= name %></h1>
45
+ <% vpns.each do |name,config| %>
46
+ <% status = stati[name] %>
47
+ <h1>OpenVPN Status for <%= name %></h1>
46
48
 
47
- <h2>Client List</h2>
48
- <div>
49
- <table>
50
- <thead>
51
- <td class="first">Common Name</td>
52
- <td class="middle">Real Address</td>
53
- <td class="middle">Data Received</td>
54
- <td class="middle">Data Sent</td>
55
- <td class="last">Connected Since</td>
56
- </thead>
57
- <tbody>
58
- <% client_list.each do |client| %>
59
- <tr>
60
- <td class="first"><%= client[0] %></td>
61
- <td class="middle"><%= client[1] %></td>
62
- <td class="middle"><%= client[2].to_i.as_bytes %></td>
63
- <td class="middle"><%= client[3].to_i.as_bytes %></td>
64
- <td class="last"><%= client[4] %></td>
65
- </tr>
49
+ <h2>Client List</h2>
50
+ <div>
51
+ <table>
52
+ <thead>
53
+ <td class="first">Common Name</td>
54
+ <td class="middle">Real Address</td>
55
+ <td class="middle">Data Received</td>
56
+ <td class="middle">Data Sent</td>
57
+ <td class="last">Connected Since</td>
58
+ </thead>
59
+ <tbody>
60
+ <% status.client_list.each do |client| %>
61
+ <tr>
62
+ <td class="first"><%= client[0] %></td>
63
+ <td class="middle"><%= client[1] %></td>
64
+ <td class="middle"><%= client[2].to_i.as_bytes %></td>
65
+ <td class="middle"><%= client[3].to_i.as_bytes %></td>
66
+ <td class="last"><%= client[4].strftime('%-d.%-m.%Y %H:%M:%S') %></td>
67
+ </tr>
66
68
  <% end %>
67
- </tbody>
68
- </table>
69
- </div>
69
+ </tbody>
70
+ </table>
71
+ </div>
70
72
 
71
- <h2>Routing Table</h2>
72
- <div>
73
- <table>
74
- <thead>
75
- <td class="first">Virtual Address</td>
76
- <td class="middle">Common Name</td>
77
- <td class="middle">Real Address</td>
78
- <td class="last">Last Ref</td>
79
- </thead>
80
- <tbody>
81
- <% routing_table.each do |e| %>
82
- <tr>
83
- <td class="first"><%= e[0] %></td>
84
- <td class="middle"><%= e[1] %></td>
85
- <td class="middle"><%= e[2] %></td>
86
- <td class="last"><%= e[3] %></td>
87
- </tr>
73
+ <h2>Routing Table</h2>
74
+ <div>
75
+ <table>
76
+ <thead>
77
+ <td class="first">Virtual Address</td>
78
+ <td class="middle">Common Name</td>
79
+ <td class="middle">Real Address</td>
80
+ <td class="last">Last Ref</td>
81
+ </thead>
82
+ <tbody>
83
+ <% status.routing_table.each do |route| %>
84
+ <tr>
85
+ <td class="first"><%= route[0] %></td>
86
+ <td class="middle"><%= route[1] %></td>
87
+ <td class="middle"><%= route[2] %></td>
88
+ <td class="last"><%= route[3].strftime('%-d.%-m.%Y %H:%M:%S') %></td>
89
+ </tr>
88
90
  <% end %>
89
- </tbody>
90
- </table>
91
- </div>
91
+ </tbody>
92
+ </table>
93
+ </div>
92
94
 
93
- <h2>Global Stats</h2>
94
- <div>
95
- <table>
96
- <tbody>
97
- <% global_stats.each do |e| %>
98
- <tr>
99
- <td><%= e[0] %>:</td>
100
- <td><%= e[1] %></td>
101
- </tr>
95
+ <h2>Global Stats</h2>
96
+ <div>
97
+ <table>
98
+ <tbody>
99
+ <% status.global_stats.each do |global| %>
100
+ <tr>
101
+ <td><%= global[0] %>:</td>
102
+ <td><%= global[1] %></td>
103
+ </tr>
104
+ <% end %>
105
+ </tbody>
106
+ </table>
107
+ </div>
102
108
  <% end %>
103
- </tbody>
104
- </table>
105
- </div>
106
109
 
107
110
  </body>
108
111
  </html>
@@ -0,0 +1,41 @@
1
+
2
+ module OpenVPNStatusWeb
3
+ module Parser
4
+ class ModernStateless
5
+ def self.parse_status_log(text, sep)
6
+ status = Status.new
7
+ status.client_list = []
8
+ status.routing_table = []
9
+ status.global_stats = []
10
+
11
+ text.lines.each do |line|
12
+ parts = line.strip.split(sep)
13
+ status.client_list << parse_client(parts[1..5]) if parts[0] == "CLIENT_LIST"
14
+ status.routing_table << parse_route(parts[1..4]) if parts[0] == "ROUTING_TABLE"
15
+ status.global_stats << parse_global(parts[1..2]) if parts[0] == "GLOBAL_STATS"
16
+ end
17
+
18
+ status
19
+ end
20
+
21
+ private
22
+
23
+ def self.parse_client(client)
24
+ client[2] = client[2].to_i
25
+ client[3] = client[3].to_i
26
+ client[4] = DateTime.strptime(client[4], '%a %b %d %k:%M:%S %Y')
27
+ client
28
+ end
29
+
30
+ def self.parse_route(route)
31
+ route[3] = DateTime.strptime(route[3], '%a %b %d %k:%M:%S %Y')
32
+ route
33
+ end
34
+
35
+ def self.parse_global(global)
36
+ global[1] = global[1].to_i
37
+ global
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,54 @@
1
+
2
+ module OpenVPNStatusWeb
3
+ module Parser
4
+ class V1
5
+ def parse_status_log(text)
6
+ current_section = :none
7
+ client_list = []
8
+ routing_table = []
9
+ global_stats = []
10
+
11
+ text.lines.each do |line|
12
+ (current_section = :cl; next) if line == "OpenVPN CLIENT LIST\n"
13
+ (current_section = :rt; next) if line == "ROUTING TABLE\n"
14
+ (current_section = :gs; next) if line == "GLOBAL STATS\n"
15
+ (current_section = :end; next) if line == "END\n"
16
+
17
+ case current_section
18
+ when :cl
19
+ client_list << line.strip.split(',')
20
+ when :rt
21
+ routing_table << line.strip.split(',')
22
+ when :gs
23
+ global_stats << line.strip.split(',')
24
+ end
25
+ end
26
+
27
+ status = Status.new
28
+ status.client_list = client_list[2..-1].map { |client| parse_client(client) }
29
+ status.routing_table = routing_table[1..-1].map { |route| parse_route(route) }
30
+ status.global_stats = global_stats.map { |global| parse_global(global) }
31
+ status
32
+ end
33
+
34
+ private
35
+
36
+ def parse_client(client)
37
+ client[2] = client[2].to_i
38
+ client[3] = client[3].to_i
39
+ client[4] = DateTime.strptime(client[4], '%a %b %d %k:%M:%S %Y')
40
+ client
41
+ end
42
+
43
+ def parse_route(route)
44
+ route[3] = DateTime.strptime(route[3], '%a %b %d %k:%M:%S %Y')
45
+ route
46
+ end
47
+
48
+ def parse_global(global)
49
+ global[1] = global[1].to_i
50
+ global
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,12 @@
1
+
2
+ require 'openvpn-status-web/parser/modern_stateless'
3
+
4
+ module OpenVPNStatusWeb
5
+ module Parser
6
+ class V2
7
+ def parse_status_log(text)
8
+ OpenVPNStatusWeb::Parser::ModernStateless.parse_status_log(text, ',')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+
2
+ require 'openvpn-status-web/parser/modern_stateless'
3
+
4
+ module OpenVPNStatusWeb
5
+ module Parser
6
+ class V3
7
+ def parse_status_log(text)
8
+ OpenVPNStatusWeb::Parser::ModernStateless.parse_status_log(text, "\t")
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module OpenVPNStatusWeb
3
+ class Status
4
+ attr_accessor :client_list
5
+ attr_accessor :routing_table
6
+ attr_accessor :global_stats
7
+ end
8
+ end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module OpenVPNStatusWeb
3
- VERSION = "0.0.1"
3
+ VERSION = "1.0.0"
4
4
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.name = 'openvpn-status-web'
8
8
  s.version = OpenVPNStatusWeb::VERSION
9
9
  s.summary = 'openvpn-status-web'
10
- s.description = 'Small Rack application that parses and serves the OpenVPN status file.'
10
+ s.description = 'Small Rack (Ruby) application serving OpenVPN status file.'
11
11
  s.author = 'Christian Nicolai'
12
12
  s.email = 'chrnicolai@gmail.com'
13
13
  s.license = 'Apache License Version 2.0'
@@ -21,11 +21,12 @@ Gem::Specification.new do |s|
21
21
  s.executables = ['openvpn-status-web']
22
22
 
23
23
  s.add_runtime_dependency 'rack'
24
- s.add_runtime_dependency 'json'
25
24
  s.add_runtime_dependency 'metriks'
26
25
 
27
26
  s.add_development_dependency 'bundler', '~> 1.3'
28
27
  s.add_development_dependency 'rake'
29
28
  s.add_development_dependency 'rspec'
30
29
  s.add_development_dependency 'rack-test'
30
+ s.add_development_dependency 'better_errors'
31
+ s.add_development_dependency 'binding_of_caller'
31
32
  end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenVPNStatusWeb::Parser::ModernStateless do
4
+ {
5
+ 2 => status_v2,
6
+ 3 => status_v3
7
+ }.each do |version, status|
8
+ context "for status-version #{version}" do
9
+ context 'for client list' do
10
+ it 'parses common names' do
11
+ status.client_list.map { |client| client[0] }.should be == ["foo", "bar"]
12
+ end
13
+
14
+ it 'parses real addresses' do
15
+ status.client_list.map { |client| client[1] }.should be == ["1.2.3.4:1234", "1.2.3.5:1235"]
16
+ end
17
+
18
+ it 'parses received bytes' do
19
+ status.client_list.map { |client| client[2] }.should be == [11811160064, 512]
20
+ end
21
+
22
+ it 'parses sent bytes' do
23
+ status.client_list.map { |client| client[3] }.should be == [4194304, 2048]
24
+ end
25
+
26
+ it 'parses connected since date' do
27
+ status.client_list.map { |client| client[4] }.should be == [DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0)]
28
+ end
29
+ end
30
+
31
+ context 'for routing table' do
32
+ it 'parses virtual addresses' do
33
+ status.routing_table.map { |route| route[0] }.should be == ["192.168.0.0/24", "192.168.66.2", "192.168.66.3", "2001:db8:0:0::1000"]
34
+ end
35
+
36
+ it 'parses common names' do
37
+ status.routing_table.map { |route| route[1] }.should be == ["foo", "bar", "foo", "bar"]
38
+ end
39
+
40
+ it 'parses real addresses' do
41
+ status.routing_table.map { |route| route[2] }.should be == ["1.2.3.4:1234", "1.2.3.5:1235", "1.2.3.4:1234", "1.2.3.5:1235"]
42
+ end
43
+
44
+ it 'parses last ref date' do
45
+ status.routing_table.map { |route| route[3] }.should be == [DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0)]
46
+ end
47
+ end
48
+
49
+ it 'parses global stats' do
50
+ status.global_stats.size.should be == 1
51
+ status.global_stats.first.should be == ["Max bcast/mcast queue length", 42]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenVPNStatusWeb::Parser::V1 do
4
+ def status; status_v1; end
5
+
6
+ context 'for client list' do
7
+ it 'parses common names' do
8
+ status.client_list.map { |client| client[0] }.should be == ["foo", "bar"]
9
+ end
10
+
11
+ it 'parses real addresses' do
12
+ status.client_list.map { |client| client[1] }.should be == ["1.2.3.4:1234", "1.2.3.5:1235"]
13
+ end
14
+
15
+ it 'parses received bytes' do
16
+ status.client_list.map { |client| client[2] }.should be == [11811160064, 512]
17
+ end
18
+
19
+ it 'parses sent bytes' do
20
+ status.client_list.map { |client| client[3] }.should be == [4194304, 2048]
21
+ end
22
+
23
+ it 'parses connected since date' do
24
+ status.client_list.map { |client| client[4] }.should be == [DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0)]
25
+ end
26
+ end
27
+
28
+ context 'for routing table' do
29
+ it 'parses virtual addresses' do
30
+ status.routing_table.map { |route| route[0] }.should be == ["192.168.0.0/24", "192.168.66.2", "192.168.66.3", "2001:db8:0:0::1000"]
31
+ end
32
+
33
+ it 'parses common names' do
34
+ status.routing_table.map { |route| route[1] }.should be == ["foo", "bar", "foo", "bar"]
35
+ end
36
+
37
+ it 'parses real addresses' do
38
+ status.routing_table.map { |route| route[2] }.should be == ["1.2.3.4:1234", "1.2.3.5:1235", "1.2.3.4:1234", "1.2.3.5:1235"]
39
+ end
40
+
41
+ it 'parses last ref date' do
42
+ status.routing_table.map { |route| route[3] }.should be == [DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0), DateTime.new(2012,1,1,23,42,0)]
43
+ end
44
+ end
45
+
46
+ it 'parses global stats' do
47
+ status.global_stats.size.should be == 1
48
+ status.global_stats.first.should be == ["Max bcast/mcast queue length", 42]
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'rack/test'
5
+
6
+ require 'openvpn-status-web'
7
+
8
+ def status_v1
9
+ text = File.open('examples/status.v1', 'rb') do |f| f.read end
10
+ OpenVPNStatusWeb::Parser::V1.new.parse_status_log text
11
+ end
12
+
13
+ def status_v2
14
+ text = File.open('examples/status.v2', 'rb') do |f| f.read end
15
+ OpenVPNStatusWeb::Parser::V2.new.parse_status_log text
16
+ end
17
+
18
+ def status_v3
19
+ text = File.open('examples/status.v3', 'rb') do |f| f.read end
20
+ OpenVPNStatusWeb::Parser::V3.new.parse_status_log text
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openvpn-status-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2013-05-03 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
16
- requirement: &70153082865000 !ruby/object:Gem::Requirement
16
+ requirement: &70153409956960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,21 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70153082865000
25
- - !ruby/object:Gem::Dependency
26
- name: json
27
- requirement: &70153082864580 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: *70153082864580
24
+ version_requirements: *70153409956960
36
25
  - !ruby/object:Gem::Dependency
37
26
  name: metriks
38
- requirement: &70153082864160 !ruby/object:Gem::Requirement
27
+ requirement: &70153409956540 !ruby/object:Gem::Requirement
39
28
  none: false
40
29
  requirements:
41
30
  - - ! '>='
@@ -43,10 +32,10 @@ dependencies:
43
32
  version: '0'
44
33
  type: :runtime
45
34
  prerelease: false
46
- version_requirements: *70153082864160
35
+ version_requirements: *70153409956540
47
36
  - !ruby/object:Gem::Dependency
48
37
  name: bundler
49
- requirement: &70153082863660 !ruby/object:Gem::Requirement
38
+ requirement: &70153409956040 !ruby/object:Gem::Requirement
50
39
  none: false
51
40
  requirements:
52
41
  - - ~>
@@ -54,10 +43,10 @@ dependencies:
54
43
  version: '1.3'
55
44
  type: :development
56
45
  prerelease: false
57
- version_requirements: *70153082863660
46
+ version_requirements: *70153409956040
58
47
  - !ruby/object:Gem::Dependency
59
48
  name: rake
60
- requirement: &70153082863240 !ruby/object:Gem::Requirement
49
+ requirement: &70153409955620 !ruby/object:Gem::Requirement
61
50
  none: false
62
51
  requirements:
63
52
  - - ! '>='
@@ -65,10 +54,10 @@ dependencies:
65
54
  version: '0'
66
55
  type: :development
67
56
  prerelease: false
68
- version_requirements: *70153082863240
57
+ version_requirements: *70153409955620
69
58
  - !ruby/object:Gem::Dependency
70
59
  name: rspec
71
- requirement: &70153082862780 !ruby/object:Gem::Requirement
60
+ requirement: &70153409955160 !ruby/object:Gem::Requirement
72
61
  none: false
73
62
  requirements:
74
63
  - - ! '>='
@@ -76,10 +65,32 @@ dependencies:
76
65
  version: '0'
77
66
  type: :development
78
67
  prerelease: false
79
- version_requirements: *70153082862780
68
+ version_requirements: *70153409955160
80
69
  - !ruby/object:Gem::Dependency
81
70
  name: rack-test
82
- requirement: &70153082862360 !ruby/object:Gem::Requirement
71
+ requirement: &70153409954740 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70153409954740
80
+ - !ruby/object:Gem::Dependency
81
+ name: better_errors
82
+ requirement: &70153409954320 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70153409954320
91
+ - !ruby/object:Gem::Dependency
92
+ name: binding_of_caller
93
+ requirement: &70153409953900 !ruby/object:Gem::Requirement
83
94
  none: false
84
95
  requirements:
85
96
  - - ! '>='
@@ -87,8 +98,8 @@ dependencies:
87
98
  version: '0'
88
99
  type: :development
89
100
  prerelease: false
90
- version_requirements: *70153082862360
91
- description: Small Rack application that parses and serves the OpenVPN status file.
101
+ version_requirements: *70153409953900
102
+ description: Small Rack (Ruby) application serving OpenVPN status file.
92
103
  email: chrnicolai@gmail.com
93
104
  executables:
94
105
  - openvpn-status-web
@@ -105,12 +116,20 @@ files:
105
116
  - examples/status.v1
106
117
  - examples/status.v2
107
118
  - examples/status.v3
108
- - init.d/openvpn-status-web
119
+ - init.d/debian-6-openvpn-status-web
109
120
  - lib/openvpn-status-web.rb
110
121
  - lib/openvpn-status-web/int_patch.rb
111
122
  - lib/openvpn-status-web/main.html.erb
123
+ - lib/openvpn-status-web/parser/modern_stateless.rb
124
+ - lib/openvpn-status-web/parser/v1.rb
125
+ - lib/openvpn-status-web/parser/v2.rb
126
+ - lib/openvpn-status-web/parser/v3.rb
127
+ - lib/openvpn-status-web/status.rb
112
128
  - lib/openvpn-status-web/version.rb
113
129
  - openvpn-status-web.gemspec
130
+ - spec/parser/modern_stateless_spec.rb
131
+ - spec/parser/v1_spec.rb
132
+ - spec/spec_helper.rb
114
133
  homepage: https://github.com/cmur2/openvpn-status-web
115
134
  licenses:
116
135
  - Apache License Version 2.0
@@ -136,4 +155,7 @@ rubygems_version: 1.8.6
136
155
  signing_key:
137
156
  specification_version: 3
138
157
  summary: openvpn-status-web
139
- test_files: []
158
+ test_files:
159
+ - spec/parser/modern_stateless_spec.rb
160
+ - spec/parser/v1_spec.rb
161
+ - spec/spec_helper.rb
@@ -1,48 +0,0 @@
1
- #! /bin/sh
2
- ### BEGIN INIT INFO
3
- # Provides: openvpn-status-web
4
- # Required-Start: $remote_fs $syslog
5
- # Required-Stop: $remote_fs $syslog
6
- # Default-Start: 2 3 4 5
7
- # Default-Stop: 0 1 6
8
- # Short-Description: Handle openvpn-status-web
9
- ### END INIT INFO
10
-
11
- # your ruby interpreter
12
- DAEMON="/usr/bin/ruby"
13
-
14
- # some unique name identifying your VPN
15
- VPN_NAME="vpn.example.org"
16
-
17
- # path to the OpenVPN status log file
18
- STATUS_PATH="/var/log/openvpn-status.log"
19
-
20
- # host and port for this daemon to listen on
21
- HOST="127.0.0.1"
22
- PORT="3000"
23
-
24
- DAEMON_OPTS="/opt/openvpn-status-web/status.rb $VPN_NAME $STATUS_PATH $HOST $PORT"
25
-
26
- test -x $DAEMON || exit 0
27
-
28
- . /lib/lsb/init-functions
29
-
30
- case "$1" in
31
- start)
32
- log_daemon_msg "Starting openvpn-web-status for $VPN_NAME" "openvpn-web-status"
33
- start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/$VPN_NAME.pid" --background --exec $DAEMON -- $DAEMON_OPTS
34
- ;;
35
- stop)
36
- log_daemon_msg "Stopping openvpn-web-status for $VPN_NAME" "openvpn-web-status"
37
- start-stop-daemon --stop --quiet --oknodo --pidfile "/var/run/$VPN_NAME.pid"
38
- ;;
39
- restart|force-reload)
40
- log_daemon_msg "Restarting openvpn-web-status for $VPN_NAME" "openvpn-web-status"
41
- start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile "/var/run/$VPN_NAME.pid"
42
- start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/$VPN_NAME.pid" --background --exec $DAEMON -- $DAEMON_OPTS
43
- ;;
44
- *)
45
- echo "Usage: $0 {start|stop|restart|force-reload}" >&2
46
- exit 1
47
- ;;
48
- esac