openvpn-status-web 0.0.1 → 1.0.0
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.
- 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
|
+
[](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
|