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.
- data/.travis.yml +0 -1
- data/README.md +49 -6
- data/init.d/debian-6-openvpn-status-web +40 -0
- data/lib/openvpn-status-web.rb +66 -35
- data/lib/openvpn-status-web/main.html.erb +58 -55
- data/lib/openvpn-status-web/parser/modern_stateless.rb +41 -0
- data/lib/openvpn-status-web/parser/v1.rb +54 -0
- data/lib/openvpn-status-web/parser/v2.rb +12 -0
- data/lib/openvpn-status-web/parser/v3.rb +12 -0
- data/lib/openvpn-status-web/status.rb +8 -0
- data/lib/openvpn-status-web/version.rb +1 -1
- data/openvpn-status-web.gemspec +3 -2
- data/spec/parser/modern_stateless_spec.rb +55 -0
- data/spec/parser/v1_spec.rb +50 -0
- data/spec/spec_helper.rb +21 -0
- metadata +49 -27
- data/init.d/openvpn-status-web +0 -48
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,17 +1,60 @@
|
|
1
1
|
# openvpn-status-web
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
*
|
11
|
-
*
|
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
|
data/lib/openvpn-status-web.rb
CHANGED
@@ -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(
|
30
|
-
@
|
31
|
-
|
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
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
51
|
-
text = File.open(
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
(
|
60
|
-
|
61
|
-
|
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 !=
|
76
|
-
puts "Usage: openvpn-status-web
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
<% client_list.each do |client| %>
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
68
|
-
</table>
|
69
|
-
</div>
|
69
|
+
</tbody>
|
70
|
+
</table>
|
71
|
+
</div>
|
70
72
|
|
71
|
-
<h2>Routing Table</h2>
|
72
|
-
<div>
|
73
|
-
<table>
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
<% routing_table.each do |
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
90
|
-
</table>
|
91
|
-
</div>
|
91
|
+
</tbody>
|
92
|
+
</table>
|
93
|
+
</div>
|
92
94
|
|
93
|
-
<h2>Global Stats</h2>
|
94
|
-
<div>
|
95
|
-
<table>
|
96
|
-
|
97
|
-
<% global_stats.each do |
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
data/openvpn-status-web.gemspec
CHANGED
@@ -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
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
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: &
|
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: *
|
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: &
|
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: *
|
35
|
+
version_requirements: *70153409956540
|
47
36
|
- !ruby/object:Gem::Dependency
|
48
37
|
name: bundler
|
49
|
-
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: *
|
46
|
+
version_requirements: *70153409956040
|
58
47
|
- !ruby/object:Gem::Dependency
|
59
48
|
name: rake
|
60
|
-
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: *
|
57
|
+
version_requirements: *70153409955620
|
69
58
|
- !ruby/object:Gem::Dependency
|
70
59
|
name: rspec
|
71
|
-
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: *
|
68
|
+
version_requirements: *70153409955160
|
80
69
|
- !ruby/object:Gem::Dependency
|
81
70
|
name: rack-test
|
82
|
-
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: *
|
91
|
-
description: Small Rack application
|
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
|
data/init.d/openvpn-status-web
DELETED
@@ -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
|