qnotifier 0.6.5 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest CHANGED
@@ -2,10 +2,12 @@ Manifest
2
2
  Rakefile
3
3
  bin/qnotifier
4
4
  config/qnotifier_config.yml
5
+ config/README
5
6
  gem-public_cert.pem
6
7
  lib/command.rb
7
8
  lib/plugin.rb
8
9
  lib/qnotifier.rb
10
+ lib/qnotifierd.rb
9
11
  lib/storage.rb
10
12
  lib/web_service.rb
11
13
  plugins/apache.rb
@@ -19,3 +21,5 @@ plugins/rails.rb
19
21
  plugins/ruby.rb
20
22
  plugins/system.rb
21
23
  plugins/urls.rb
24
+ init.d/qnotifier
25
+
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ Echoe.new('qnotifier', '0.6.0') do |p|
7
7
  p.url = "http://qnotifier.com/qnotifier_gem"
8
8
  p.author = "Gersham Meharg"
9
9
  p.email = "gersham@qnotifier.com"
10
- p.ignore_pattern = ["var/*"]
10
+ p.ignore_pattern = ["var/*", "tmp/*"]
11
11
  p.runtime_dependencies = ["rest-client >=1.4.2", "json >=1.2.3", "elif >=0.1.0"]
12
12
  p.development_dependencies = ["rest-client >=1.4.2", "json >=1.2.3", "elif >=0.1.0"]
13
13
  p.executable_pattern = ["bin/*"]
data/bin/qnotifier CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
  $LOAD_PATH.unshift( File.join( File.dirname(__FILE__), '../lib' ) )
3
3
  require "command"
4
-
5
4
  Qnotifier::Command.run(ARGV)
6
5
 
data/config/README ADDED
@@ -0,0 +1 @@
1
+ Plugin Development
@@ -14,7 +14,7 @@
14
14
  # Whether to resend alerts
15
15
  resend_alerts: true
16
16
  # resend_alert_time: Resend alerts every X seconds
17
- resend_alert_time: 1 #14400 # Four hours
17
+ resend_alert_time: 14400 # Four hours
18
18
 
19
19
  # alert_email_address: if this is defined any alerts will be copied to this email address
20
20
  # Note that is in addition to the Qnotifier notifications
@@ -24,11 +24,6 @@ resend_alert_time: 1 #14400 # Four hours
24
24
  # PLUGINS
25
25
  #
26
26
 
27
- # Where to look for user generated plugins
28
- # To add your own plugin subclass the Qnotifier::Plugin class and put this in to this directory
29
- # You may add configuration parameters here based on the name of your plugin class
30
- plugin_directory: "/var/lib/qnotifier/plugins"
31
-
32
27
  # The System Plugin must be enabled, this gives basic stats like load average, disk use, memory, etc
33
28
  System:
34
29
  # use_load: either one_minute, five_minute or fifteen_minutes
@@ -64,8 +59,16 @@ Iostat:
64
59
  Mysql:
65
60
  # Enabled, set to either true or false
66
61
  enabled: false
67
- username: "username"
68
- password: "password"
62
+ username:
63
+ password:
64
+ # The name of the mysqladmin binary
65
+ mysqladmin: "mysqladmin"
66
+ # Uncomment and edit to set a remote host
67
+ #hostname: "hostname"
68
+ # MySQL Port if not localhost, must be used with hostname above
69
+ #port: 3306
70
+ # Queries per second over which to generate an alert
71
+ #alert_qps_threshold: 100
69
72
 
70
73
  # The Network Interface built-in plugin
71
74
  Network:
@@ -100,7 +103,7 @@ Rails:
100
103
  # The Ruby built-in plugin
101
104
  Ruby:
102
105
  enabled: true
103
- list_gems: true
106
+ list_gems: false
104
107
 
105
108
  #
106
109
  # Other settings that you generally will not need to modify
data/init.d/qnotifier ADDED
File without changes
data/lib/command.rb CHANGED
@@ -1,65 +1,179 @@
1
1
  #!/usr/bin/env ruby
2
- require "optparse"
3
2
  require "qnotifier"
3
+ require "robustthread"
4
4
 
5
5
  module Qnotifier
6
6
  class Command
7
-
8
- def self.parse_options(argv)
9
- options = {}
10
-
11
- op = OptionParser.new do |opts|
12
-
13
- opts.banner = "\nQnotifier is an agent for the Qnotifer server reporting and alerting system"
14
- opts.separator "See http://qnotifier.com for details."
15
- opts.separator "\nUsage:"
16
7
 
17
- opts.on("-r", "--register CODE", String, "Register agent with code.") do |register_code|
18
- qnotifier = Qnotifier::Process.new
19
- qnotifier.debug = true if @debug
20
- qnotifier.register(register_code)
21
- exit
22
- end
23
- opts.on("-w", "--wipe", "Wipes the saved variables, including registration. You must recreate the server and reregister after this.") do
24
- qnotifier = Qnotifier::Process.new
25
- qnotifier.debug = true if @debug
8
+ def self.run(argv)
9
+ unless Process.euid == 0
10
+ puts "QNotifier can only be started by root"
11
+ exit
12
+ end
13
+
14
+ if has_api_key?
15
+ if argv.first == 'stop'
16
+ stop
17
+
18
+ elsif argv.first == 'start'
19
+ start
20
+
21
+ elsif argv.first == 'restart'
22
+ stop
23
+ start
24
+
25
+ elsif argv.first == 'zap'
26
+ zap
27
+
28
+ elsif argv.first == 'reset'
29
+ print "\nWould you like to reset the state of the saved variables? (y/n):"
30
+ response = STDIN.gets.chomp.downcase!
31
+ if response == "y" || response == "yes"
32
+ `rm /var/lib/qnotifier/saved_variables.yml`
33
+ end
34
+
35
+ elsif argv.first == 'register'
36
+ qnotifier = Qnotifier::MainProcess.new
26
37
  qnotifier.wipe
27
- exit
38
+ setup_registration
39
+
40
+ elsif argv.first == 'debug'
41
+ puts "Running once in debug mode (use this mode to debug your plugins)\n\n"
42
+ qnotifier = Qnotifier::MainProcess.new
43
+ $qnotifier_debug = true
44
+ qnotifier.run
45
+
46
+ else
47
+ puts "usage: qnotifier start | stop | restart | zap | reset | register | debug"
48
+ puts "see http://qnotifier.com for more details"
28
49
  end
29
- opts.on("-v", "--version", "Displays the agent version") do |version|
30
- puts "Qnotifier Agent Version: #{Qnotifier::VERSION}"
31
- exit
32
- end
33
- opts.on("-d", "--debug", "Run in debug mode") do |debug|
34
- @debug = true
50
+
51
+ else
52
+ setup_directories
53
+ setup_registration
54
+ end
55
+ end
56
+
57
+ def self.has_api_key?
58
+ api_key = nil
59
+ begin
60
+ api_key_file = File.open("/var/lib/qnotifier/api_key")
61
+ while (line = api_key_file.readline)
62
+ api_key = line
35
63
  end
36
- opts.on("-h", "--help", "Shows this help") do
37
- puts opts
64
+ rescue EOFError
65
+ rescue Exception => e
66
+ end
67
+ if api_key
68
+ return true
69
+ else
70
+ return false
71
+ end
72
+ end
73
+
74
+ def self.setup_directories
75
+ var_dir = "/var/lib/qnotifier"
76
+ unless File.exists?(var_dir)
77
+ begin
78
+ puts "Creating #{var_dir}"
79
+ FileUtils.mkdir_p var_dir
80
+ FileUtils.mkdir_p "#{var_dir}/plugins"
81
+ default_config_file = File.dirname(__FILE__) + "/../config/qnotifier_config.yml"
82
+ `cp #{default_config_file} #{var_dir}/qnotifier_config.yml`
83
+ readme_file = File.dirname(__FILE__) + "/../config/README"
84
+ `cp #{readme_file} #{var_dir}/plugins/README`
85
+ rescue Exception => e
86
+ puts "Can't setup the var directory: #{e}"
87
+ puts "Exiting"
38
88
  exit
39
89
  end
40
- opts.separator "\n"
41
90
  end
91
+ end
92
+
93
+ def self.setup_registration
94
+ puts "Starting QNotifier Agent Setup\n\n"
95
+ puts "In order to use this agent you must enter the six number Registration Code that was presented to you when the server was created in the QNotifier system (in the iPhone/iPad/Website).\n\n"
96
+ print "Registration Code: "
97
+ register_code = STDIN.gets.chomp
42
98
 
43
- begin
44
- op.parse!(argv)
45
- @usage = op.to_s
46
- rescue OptionParser::InvalidOption
47
- puts op
48
- exit
49
- rescue OptionParser::MissingArgument
50
- puts op
99
+ if register_code and register_code.size == 6
100
+ qnotifier = Qnotifier::MainProcess.new
101
+ if qnotifier.register(register_code)
102
+ puts "\nOK, the server has been successfully registered."
103
+ puts "You now can control the agent as a daemon using 'qnotifier start|stop|restart'"
104
+ copy_init
105
+ puts "Doing initial update..."
106
+ qnotifier.run
107
+ puts "\nPlease start the qnotifier daemon now."
108
+ else
109
+ puts "Exiting."
110
+ end
111
+ else
112
+ puts "\nInvalid code, please enter a six number Registration Code to use this agent.\n"
51
113
  exit
52
114
  end
53
- options
54
115
  end
55
116
 
56
- def self.run(argv)
57
- argv.push("--help") if argv.first == 'help'
58
- options = parse_options(argv)
59
- qnotifier = Qnotifier::Process.new
60
- qnotifier.debug = true if @debug
61
- qnotifier.run
117
+ def self.copy_init
118
+ print "\nWould you like to install the qnotifier init script in /etc/init.d? (y/n):"
119
+ response = STDIN.gets.chomp.downcase!
120
+ if response == "y" || response == "yes"
121
+ init_file = File.dirname(__FILE__) + "/../init/qnotifier"
122
+ `cp #{init_file} /etc/init.d/`
123
+ end
124
+ end
125
+
126
+ def self.running
127
+ pid_file = "/var/lib/qnotifier/qnotifer.pid"
128
+ if File.exists?(pid_file)
129
+ begin
130
+ pid = `cat #{pid_file}`.to_i
131
+ Process.getpgid(pid)
132
+ return true
133
+ rescue
134
+ `rm #{pid_file}`
135
+ return false
136
+ end
137
+ else
138
+ return false
139
+ end
140
+ end
141
+
142
+ def self.zap
143
+ pid_file = "/var/lib/qnotifier/qnotifer.pid"
144
+ if File.exists?(pid_file)
145
+ `rm #{pid_file}`
146
+ end
147
+ end
148
+
149
+ def self.start
150
+ puts "Starting QNotifier..."
151
+ pid_file = "/var/lib/qnotifier/qnotifer.pid"
152
+ if running
153
+ puts "Daemon is already running, check pid #{pid_file}"
154
+ exit
155
+ end
156
+
157
+ pid = fork do
158
+ qnotifier = Qnotifier::MainProcess.new
159
+ RobustThread.loop(:seconds => 30) do
160
+ qnotifier.run
161
+ end
162
+ sleep
163
+ end
164
+ Process.detach pid
165
+ `echo #{pid} > /var/lib/qnotifier/qnotifer.pid`
166
+ puts "Running (#{pid})"
167
+ end
168
+
169
+ def self.stop
170
+ puts "Stopping QNotifier..."
171
+ pid_file = "/var/lib/qnotifier/qnotifer.pid"
172
+ if running
173
+ pid = `cat #{pid_file}`.to_i
174
+ Process.kill "TERM", pid
175
+ `rm /var/lib/qnotifier/qnotifer.pid`
176
+ end
62
177
  end
63
178
  end
64
- end
65
-
179
+ end
data/lib/plugin.rb CHANGED
@@ -27,7 +27,7 @@ module Qnotifier
27
27
  # Only run this plugin if it's enabled
28
28
  return unless @options["enabled"]
29
29
 
30
- Qnotifier.log.debug("Plugin #{@class_name} running")
30
+ #Qnotifier.log.info("Plugin #{@class_name} running")
31
31
  main
32
32
  end
33
33
 
@@ -45,15 +45,13 @@ module Qnotifier
45
45
  # Alert is an alert to the adminstrators that something requires looking at
46
46
  # Severity should usually be 3
47
47
  def alert(name, message, severity = 3)
48
- @alerts = {@class_name => []} unless @alerts
49
- @alert_count += 1
50
48
  # Only send the alert if we haven't already
51
49
  if Qnotifier::Storage.get("AlertTime_#{@class_name}-#{name}")
52
50
  # Check to see if we need to resend the alert
53
51
  if @config["resend_alerts"] and Qnotifier::Storage.get("AlertTime_#{@class_name}-#{name}") < Time.now - @config["resend_alert_time"]
54
52
  add_alert(name, message, severity)
55
53
  else
56
- Qnotifier.log.debug("Already sent alert: #{message}")
54
+ Qnotifier.log.debug("Already sent alert: #{message} (resends every #{@config["resend_alert_time"]}s)")
57
55
  end
58
56
  else
59
57
  add_alert(name, message, severity)
@@ -62,7 +60,8 @@ module Qnotifier
62
60
 
63
61
  # Add the alert to the outbound queue
64
62
  def add_alert(name, message, severity)
65
-
63
+ @alerts = {@class_name => []} unless @alerts
64
+ @alert_count += 1
66
65
  if severity == 1 # emergency
67
66
  log.fatal message
68
67
  elsif severity == 2 # critical
data/lib/qnotifier.rb CHANGED
@@ -12,7 +12,11 @@ module Qnotifier
12
12
  def self.log
13
13
  unless @logger
14
14
  begin
15
- @logger = Logger.new("/var/log/qnotifier.log","monthly")
15
+ if $qnotifier_debug
16
+ @logger = Logger.new($stdout)
17
+ else
18
+ @logger = Logger.new("/var/lib/qnotifier/qnotifier.log", "monthly")
19
+ end
16
20
  rescue Exception => e
17
21
  puts "Error starting logging - using stdout (#{e.message})"
18
22
  @logger = Logger.new($stdout)
@@ -21,10 +25,8 @@ module Qnotifier
21
25
  return @logger
22
26
  end
23
27
 
24
- class Process
25
-
26
- attr_accessor :debug
27
-
28
+ class MainProcess
29
+
28
30
  def initialize
29
31
  Qnotifier::Storage.restore
30
32
 
@@ -36,8 +38,7 @@ module Qnotifier
36
38
 
37
39
  def start_logging
38
40
  # Logging
39
- if @debug
40
- puts "Starting in debug mode"
41
+ if $qnotifier_debug
41
42
  Qnotifier.log.level = Logger::DEBUG
42
43
  else
43
44
  Qnotifier.log.level = Logger::INFO
@@ -47,25 +48,11 @@ module Qnotifier
47
48
  def run
48
49
  start_logging
49
50
  load_config
50
-
51
- last_run = Qnotifier::Storage.get("last_run")
52
-
51
+
53
52
  if Qnotifier::Storage.get("server_disabled_pings")
54
53
  Qnotifier.log.fatal "Disabled by the Qnotifer API, please re-register (qnotifier --register key) this agent to enable. Exiting."
55
54
  exit
56
55
  end
57
-
58
- # Start up
59
- Qnotifier.log.info "Running#{", last run: #{last_run}" if last_run}"
60
- Qnotifier::Storage.put("last_run", Time.now) unless @debug
61
- unless @debug
62
- if last_run and (Time.now - last_run) < 120
63
- message = "Starting too frequently, please run every 5 minutes (use debug mode to override)\nLast start up: #{last_run}"
64
- puts message
65
- Qnotifier.log.fatal message
66
- exit
67
- end
68
- end
69
56
 
70
57
  unless @config["api_key"]
71
58
  error_message = "Missing API Key, please register this agent first."
@@ -80,7 +67,7 @@ module Qnotifier
80
67
  end
81
68
 
82
69
  # Load the user plugins
83
- Dir[@config["plugin_directory"] + '/*.rb'].each do |file|
70
+ Dir['/var/lib/qnotifier/plugins/*.rb'].each do |file|
84
71
  process_plugin_file(file)
85
72
  end
86
73
 
@@ -120,6 +107,7 @@ module Qnotifier
120
107
  rescue Errno::ENOENT => e
121
108
  Qnotifier.log.error "#{class_name} Plugin Error: #{e.message}"
122
109
  end
110
+
123
111
  @alerts.merge!(plugin.alerts) if plugin.alerts
124
112
  @reports.merge!(plugin.reports) if plugin.reports
125
113
  @stats.merge!(plugin.stats) if plugin.stats
@@ -132,7 +120,7 @@ module Qnotifier
132
120
  load_config
133
121
 
134
122
  if @config["api_key"]
135
- Qnotifier.log.error "Server already registered. You should not keep registering the server, you only need to do this once."
123
+ Qnotifier.log.error "Server was already registered. Re-registering."
136
124
  end
137
125
 
138
126
  Qnotifier::Storage.wipe
@@ -141,8 +129,12 @@ module Qnotifier
141
129
  webservice.config = @config
142
130
  api_key = webservice.register_server(code)
143
131
 
144
- save_api_key(api_key)
145
-
132
+ if api_key
133
+ save_api_key(api_key)
134
+ return api_key
135
+ else
136
+ return nil
137
+ end
146
138
  end
147
139
 
148
140
  def wipe
@@ -156,38 +148,24 @@ module Qnotifier
156
148
  return unless api_key
157
149
  @config["api_key"] = api_key
158
150
  begin
159
- var_dir = "/var/lib/qnotifier"
160
- FileUtils.mkdir_p var_dir unless File.exists?(var_dir)
161
151
  File.open("/var/lib/qnotifier/api_key", 'w') {|f| f.write(api_key) }
162
152
  File.chmod(0600, "/var/lib/qnotifier/api_key")
163
153
  rescue Exception => e
164
154
  Qnotifier.log.fatal "Can't save API Key #{e.message}"
165
155
  end
166
156
  end
167
-
157
+
168
158
  def load_config
169
-
170
- # Load from /etc/qnotifier_config.yml if the file is there
159
+ # Load from /var/lib/qnotifier/qnotifier_config.yml if the file is there
171
160
  begin
172
- @config = YAML::load(File.open('/etc/qnotifier_config.yml'))
161
+ @config = YAML::load(File.open('/var/lib/qnotifier/qnotifier_config.yml'))
173
162
  rescue Exception => e
174
- Qnotifier.log.error "Can't open /etc/qnotifier_config.yml (loading default config and copying it to /etc/qnotifier_config.yml)"
175
- begin
176
- default_config_file = File.dirname(__FILE__) + "/../config/qnotifier_config.yml"
177
- @config = YAML::load(File.open(default_config_file))
178
- `cp #{default_config_file} /etc/qnotifier_config.yml`
179
- rescue Exception => e
180
- Qnotifier.log.fatal "Can't open default config #{e.message}"
181
- exit
182
- end
163
+ Qnotifier.log.error "Can't open /var/lib/qnotifier/qnotifier_config.yml"
164
+ exit
183
165
  end
184
166
 
185
167
  unless @config["api_key"]
186
168
  begin
187
- # Make the var dir if it doesn't exist
188
- var_dir = "/var/lib/qnotifier"
189
- FileUtils.mkdir_p var_dir unless File.exists?(var_dir)
190
-
191
169
  api_key_file = File.open("/var/lib/qnotifier/api_key")
192
170
  while (line = api_key_file.readline)
193
171
  @config["api_key"] = line
data/lib/storage.rb CHANGED
@@ -14,7 +14,7 @@ module Qnotifier
14
14
  FileUtils.mkdir_p var_dir unless File.exists?(var_dir)
15
15
  @@stored_variables = YAML::load(File.open(saved_variables))
16
16
  rescue Exception => e
17
- Qnotifier.log.debug "Error loading stored variables file. #{e.message}"
17
+ # Qnotifier.log.debug "Error loading stored variables file. #{e.message}"
18
18
  end
19
19
  @@restored = true
20
20
  end
data/lib/web_service.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'rest_client'
3
3
  require 'json'
4
- require "storage"
4
+ require 'storage'
5
+ require 'pp'
5
6
 
6
7
  module Qnotifier
7
8
  class WebService
@@ -14,10 +15,17 @@ module Qnotifier
14
15
  end
15
16
 
16
17
  def send_alerts(alerts)
17
-
18
+
18
19
  return if alerts.empty?
19
- Qnotifier.log.debug "Sending #{alerts.count} alerts"
20
20
 
21
+ if $qnotifier_debug
22
+ puts "\nAlerts:"
23
+ pp alerts
24
+ puts "\nNot sending alerts to server due to debug mode."
25
+ end
26
+
27
+ Qnotifier.log.info "Sending #{alerts.count} alerts #{alerts}"
28
+
21
29
  # Hostname
22
30
  unless @config["hostname"]
23
31
  @hostname = `hostname`.strip
@@ -38,7 +46,11 @@ module Qnotifier
38
46
  response = resource["/send_alert"].post(message, :content_type => 'application/json')
39
47
 
40
48
  if response.code == 200
41
- Qnotifier.log.debug "Server Response: HTTP #{response.code} #{response.body}"
49
+ body = JSON.parse(response.body)
50
+ if body[:poll_interval]
51
+ @@poll_interval = body[:poll_interval].to_i
52
+ end
53
+ Qnotifier.log.debug "Server Response: HTTP #{response.code} #{body[:message]}"
42
54
  else
43
55
  Qnotifier.log.warn "Server Response: HTTP #{response.code} #{response.body}"
44
56
  end
@@ -55,8 +67,21 @@ module Qnotifier
55
67
 
56
68
  def send_reports_stats(reports, stats, alert_count)
57
69
 
70
+ # Only send reports every 300s
71
+ last_run = Qnotifier::Storage.get("last_run")
72
+ if $qnotifier_debug
73
+ puts "\nStats:"
74
+ pp stats
75
+ puts "\nReports:"
76
+ pp reports
77
+ puts "\nNot sending data to server due to debug mode."
78
+ return
79
+ end
80
+ return if last_run and (Time.now - last_run) <= 300
81
+ Qnotifier::Storage.put("last_run", Time.now)
82
+
58
83
  return if reports.empty? and stats.empty?
59
- Qnotifier.log.debug "Sending Reports and Stats"
84
+ Qnotifier.log.info "Sending Reports and Stats"
60
85
 
61
86
  # Hostname
62
87
  unless @config["hostname"]
@@ -80,7 +105,11 @@ module Qnotifier
80
105
  response = resource["/update_server"].post(message, :content_type => 'application/json')
81
106
 
82
107
  if response.code == 200
83
- Qnotifier.log.debug "Server Response: HTTP #{response.code} #{response.body}"
108
+ body = JSON.parse(response.body)
109
+ if body[:poll_interval]
110
+ @@poll_interval = body[:poll_interval].to_i
111
+ end
112
+ Qnotifier.log.debug "Server Response: HTTP #{response.code} #{body[:message]}"
84
113
  else
85
114
  Qnotifier.log.warn "Server Response: HTTP #{response.code} #{response.body}"
86
115
  end
@@ -102,6 +131,7 @@ module Qnotifier
102
131
  message = {"temp_key" => temp_key, "hostname" => `hostname`.strip}.to_json
103
132
 
104
133
  begin
134
+ puts "Connecting to the QNotifier web service..."
105
135
  resource = RestClient::Resource.new(@config["api_endpoint"],
106
136
  :timeout => 30,
107
137
  :headers => { :user_agent => "Qnotifier-#{Qnotifier::VERSION}" })
@@ -109,9 +139,6 @@ module Qnotifier
109
139
 
110
140
  if response.code == 200 and response.body
111
141
  Qnotifier.log.debug "Registration Request Response: HTTP #{response.code} #{response.body}"
112
- puts "OK, the server has been successfully registered."
113
- puts "You now need to call this script from cron every 5 minutes, add to root's crontab (sudo crontab -e):"
114
- puts "*/5 * * * * qnotifier"
115
142
  return response.body
116
143
  else
117
144
  Qnotifier.log.warn "Registration Request Response: HTTP #{response.code} #{response.body}"
@@ -119,14 +146,14 @@ module Qnotifier
119
146
 
120
147
  rescue Exception => e
121
148
  if e.message == "Bad Request"
122
- puts "Can\'t find that key to register the server, please check it and try again. The key may have expired."
123
- Qnotifier.log.error "Can\'t find that key to register the server, please check it and try again. The key may have expired."
149
+ puts "QNotifer web service: Can\'t find that code, please check it and try again.\n"
150
+ Qnotifier.log.error "QNotifer web service: Can\'t find that code, please check it and try again."
124
151
  else
125
152
  Qnotifier.log.error "Registering Server: Can't connect to #{@config["api_endpoint"]} - #{e.message}"
126
153
  end
127
154
  end
128
155
 
129
156
  return nil
130
- end
157
+ end
131
158
  end
132
159
  end
data/plugins/apache.rb CHANGED
@@ -8,6 +8,34 @@ module Qnotifier
8
8
  end
9
9
 
10
10
  def main
11
+
12
+ # require "net/http", "uri"
13
+ #
14
+ # url = URI.parse(option("server_url"))
15
+ # req = Net::HTTP::Get.new(url.path + "?" + url.query.to_s)
16
+ # res = Net::HTTP.start(url.host, url.port) {|http|
17
+ # http.request(req)
18
+ # }
19
+ # values = {}
20
+ # res.body.split("\n").each do |item|
21
+ # k, v = item.split(": ")
22
+ # values[k] = v
23
+ # end
24
+ # total_accesses = values['Total Accesses'].to_i
25
+ # if (memory(:last_run_total_accesses) && memory(:last_run_time))
26
+ # accesses_since_last_run = total_accesses - memory(:last_run_total_accesses)
27
+ # seconds_since_last_run = Time.now - memory(:last_run_time)
28
+ # else
29
+ # accesses_since_last_run = total_accesses
30
+ # seconds_since_last_run = values['Uptime'].to_i
31
+ # end
32
+ #
33
+ # current_accesses_per_second = accesses_since_last_run / seconds_since_last_run
34
+ #
35
+ # report(:current_load => current_accesses_per_second)
36
+ # remember(:last_run_time => Time.now)
37
+ # remember(:last_run_total_accesses => total_accesses)
38
+ #
11
39
  end
12
40
  end
13
41
  end
data/plugins/mysql.rb CHANGED
@@ -4,10 +4,47 @@ module Qnotifier
4
4
 
5
5
  def initialize
6
6
  @defaults = {
7
+ "port" => 3306,
8
+ "mysqladmin" => "mysqladmin"
7
9
  }
8
10
  end
9
11
 
10
12
  def main
13
+ begin
14
+ require 'mysql'
15
+ rescue LoadError => e
16
+ raise "Can't load the MySQL gem, please make sure it's installed"
17
+ end
18
+
19
+ unless @options["username"] and @options["password"]
20
+ raise "Username and/or password missing for MySQL"
21
+ end
22
+
23
+ if @options["hostname"]
24
+ mysql_status = `#{@options["mysqladmin"]} status -u #{@options["username"]} -p#{@options["password"]} -h #{@options["hostname"]} -P #{@options["port"]}`
25
+ else
26
+ mysql_status = `#{@options["mysqladmin"]} status -u #{@options["username"]} -p#{@options["password"]}`
27
+ end
28
+
29
+ if $?.exitstatus == 1
30
+ alert("connection", "Can't connect to MySQL")
31
+ raise "Can't connect to mysql"
32
+ else
33
+ reset_alert("connection", "Connection to MySQL now OK")
34
+ end
35
+
36
+ if mysql_status =~ /Queries per second avg: ([\d.]+)(,*)\Z/
37
+ qps = $1
38
+ report("Queries/s", qps)
39
+ if @options["alert_qps_threshold"] and qps.to_f > @options["alert_qps_threshold"]
40
+ alert("qps", "MySQL is at #{qps} queries/s")
41
+ elsif @options["alert_qps_threshold"]
42
+ reset_alert("qps", "MySQL is now #{qps} queries/s")
43
+ end
44
+ stat("status", mysql_status)
45
+ else
46
+ Qnotifier.log.error "Can't get mysqladmin status, check auth info and database"
47
+ end
11
48
  end
12
49
  end
13
50
  end
data/plugins/nginx.rb CHANGED
@@ -4,6 +4,7 @@ module Qnotifier
4
4
 
5
5
  def initialize
6
6
  @defaults = {
7
+ "status_url" => "http://127.0.0.1/nginx_status"
7
8
  }
8
9
  end
9
10
 
data/plugins/ruby.rb CHANGED
@@ -11,16 +11,18 @@ module Qnotifier
11
11
  # Ruby Info
12
12
  ruby_info = [RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_PATCHLEVEL, RUBY_PLATFORM]
13
13
  stat("Ruby", "ruby %s (%s patchlevel %s) [%s]" % ruby_info)
14
-
14
+
15
15
  # Gems
16
- gem_list = `gem list`.split("\n")
17
- gems = []
18
- if gem_list
19
- for line in gem_list
20
- next if line.match(/^[\*\n]/)
21
- gems << line.strip
22
- end
23
- stat("Gems", gems.join(", "))
16
+ if @config["list_gems"]
17
+ gem_list = `gem list`.split("\n")
18
+ gems = []
19
+ if gem_list
20
+ for line in gem_list
21
+ next if line.match(/^[\*\n]/)
22
+ gems << line.strip
23
+ end
24
+ stat("Gems", gems.join(", "))
25
+ end
24
26
  end
25
27
  end
26
28
  end
data/plugins/system.rb CHANGED
@@ -19,7 +19,7 @@ module Qnotifier
19
19
  def main
20
20
 
21
21
  # Uptime, Users and Load Average
22
- uptime = `uptime`.strip
22
+ uptime = `uptime 2> /dev/null`.strip
23
23
  if uptime
24
24
  if uptime =~ /load average(s*): ([\d.]+)(,*) ([\d.]+)(,*) ([\d.]+)\Z/
25
25
  load_one_minute = $2.to_f
@@ -42,7 +42,7 @@ module Qnotifier
42
42
  end
43
43
 
44
44
  report("Load", load_average)
45
- stat("Load", load_average)
45
+ stat("Load", load_average)
46
46
 
47
47
  if @options["alert_load_threshold"] != 0 and load_average >= @options["alert_load_threshold"]
48
48
  alert("load", "Load is at #{load_average} #{"(normalized)" if @options[:normalize_load]}")
@@ -57,31 +57,6 @@ module Qnotifier
57
57
  stat("Uptime", uptime)
58
58
  end
59
59
 
60
- # CPU
61
- vmstat = `vmstat | tail -1`
62
- if vmstat
63
- vmstat = vmstat.split
64
- if vmstat.size == 16
65
-
66
- cpu = "User: #{vmstat[12].to_i}%, System: #{vmstat[13].to_i}%, Idle: #{vmstat[14].to_i}%, Wait: #{vmstat[15].to_i}%"
67
- cpu_percent = (100 - vmstat[14].to_i)
68
-
69
- stat("CPU", cpu)
70
- report("CPU %", cpu_percent)
71
-
72
- if @options["alert_cpu_threshold"] != 0 and cpu_percent >= @options["alert_cpu_threshold"]
73
- alert("cpu", "CPU is at #{cpu_percent}%")
74
- else
75
- reset_alert("cpu", "CPU is now #{cpu_percent}%")
76
- end
77
-
78
- else
79
- raise "Can't parse vmstat"
80
- end
81
- else
82
- raise "Can't run vmstat, is it installed/supported?"
83
- end
84
-
85
60
  # Disk
86
61
  df = `df -Phl`
87
62
  if df
@@ -106,48 +81,109 @@ module Qnotifier
106
81
  else
107
82
  raise "Can't run df -Phl"
108
83
  end
109
-
84
+
85
+ # CPU
86
+ if RUBY_PLATFORM =~ /linux/
87
+ vmstat = `vmstat | tail -1`
88
+ if vmstat
89
+ vmstat = vmstat.split
90
+ if vmstat.size >= 16
91
+
92
+ cpu = "User: #{vmstat[12].to_i}%, System: #{vmstat[13].to_i}%, Idle: #{vmstat[14].to_i}%, Wait: #{vmstat[15].to_i}%"
93
+ cpu_percent = (100 - vmstat[14].to_i)
94
+
95
+ stat("CPU", cpu)
96
+ report("CPU %", cpu_percent)
97
+
98
+ if @options["alert_cpu_threshold"] != 0 and cpu_percent >= @options["alert_cpu_threshold"]
99
+ alert("cpu", "CPU is at #{cpu_percent}%")
100
+ else
101
+ reset_alert("cpu", "CPU is now #{cpu_percent}%")
102
+ end
103
+
104
+ else
105
+ Qnotifier.log.error "Can't parse vmstat, is it installed?"
106
+ end
107
+ else
108
+ Qnotifier.log.error "Can't parse vmstat, is it installed?"
109
+ end
110
+
111
+ elsif RUBY_PLATFORM =~ /darwin/
112
+ iostat = `iostat | tail -1`
113
+ if iostat
114
+ iostat = iostat.split
115
+ if iostat.size >= 9
116
+ cpu = "User: #{iostat[3].to_i}%, System: #{iostat[4].to_i}%, Idle: #{iostat[5].to_i}%"
117
+ cpu_percent = (100 - iostat[5].to_i)
118
+
119
+ stat("CPU", cpu)
120
+ report("CPU %", cpu_percent)
121
+
122
+ if @options["alert_cpu_threshold"] != 0 and cpu_percent >= @options["alert_cpu_threshold"]
123
+ alert("cpu", "CPU is at #{cpu_percent}%")
124
+ else
125
+ reset_alert("cpu", "CPU is now #{cpu_percent}%")
126
+ end
127
+
128
+ else
129
+ Qnotifier.log.error "Can't parse iostat"
130
+ end
131
+ else
132
+ Qnotifier.log.error "Can't parse iostat"
133
+ end
134
+ else
135
+ Qnotifier.log.error "Can't parse CPU for this platform - #{RUBY_PLATFORM}"
136
+ end
110
137
 
111
138
  # Uname
112
139
  stat("Uname", `uname -srm`.strip)
113
140
 
114
141
  # Memory
115
- meminfo = {}
116
- `cat /proc/meminfo`.each_line do |line|
117
- _, key, value = *line.match(/^(\w+):\s+(\d+)\s/)
118
- meminfo[key] = value.to_i
119
- end
142
+ if RUBY_PLATFORM =~ /linux/
143
+ meminfo = {}
144
+ `cat /proc/meminfo`.each_line do |line|
145
+ _, key, value = *line.match(/^(\w+):\s+(\d+)\s/)
146
+ meminfo[key] = value.to_i
147
+ end
120
148
 
121
- memory = {}
122
- unless meminfo.empty?
123
- memory["Free"] = meminfo['MemFree'] / 1024
124
- memory["Total"] = meminfo['MemTotal'] / 1024
125
- memory_percent = ((memory["Total"] - memory["Free"]) / memory["Total"] * 100).to_i
126
- memory["Percent"] = memory_percent
127
-
128
- stat("Memory", "Free: #{memory["Free"]}M, Total: #{memory["Total"]}M")
129
- report("Memory %", memory_percent)
149
+ memory = {}
150
+ unless meminfo.empty?
151
+ memory["Free"] = meminfo['MemFree'] / 1024
152
+ memory["Total"] = meminfo['MemTotal'] / 1024
153
+ memory_percent = ((memory["Total"] - memory["Free"]) / memory["Total"] * 100).to_i
154
+ memory["Percent"] = memory_percent
155
+
156
+ stat("Memory", "Free: #{memory["Free"]}M, Total: #{memory["Total"]}M")
157
+ report("Memory %", memory_percent)
130
158
 
131
- if @options["alert_memory_threshold"] != 0 and memory_percent >= @options["alert_memory_threshold"]
132
- alert("memory", "Memory is at #{memory_percent}%")
159
+ if @options["alert_memory_threshold"] != 0 and memory_percent >= @options["alert_memory_threshold"]
160
+ alert("memory", "Memory is at #{memory_percent}%")
161
+ else
162
+ reset_alert("memory", "Memory is now #{memory_percent}%")
163
+ end
133
164
  else
134
- reset_alert("memory", "Memory is now #{memory_percent}%")
165
+ Qnotifier.log.error "Can't get memory information from /proc/meminfo (is it supported by this OS?)"
135
166
  end
167
+
168
+ elsif RUBY_PLATFORM =~ /darwin/
169
+ # Not supported on OSX currently
136
170
  else
137
- raise "Can't get memory information from /proc/meminfo (is it supported by this OS?)"
171
+ Qnotifier.log.error "Can't parse Memory for this platform - #{RUBY_PLATFORM}"
138
172
  end
139
-
140
-
173
+
141
174
  end
142
-
143
175
 
144
176
  def count_cpus
145
177
  if get("cpu_count")
146
178
  @cpu_count = get("cpu_count")
147
179
  else
148
- cpu_count = `cat /proc/cpuinfo | grep "model name" | wc -l`.to_i
149
- if cpu_count != 0
150
- @cpu_count = cpu_count
180
+ if RUBY_PLATFORM =~ /linux/
181
+ cpu_count = `cat /proc/cpuinfo | grep "model name" | wc -l`.to_i
182
+ if cpu_count != 0
183
+ @cpu_count = cpu_count
184
+ else
185
+ @cpu_count = 1
186
+ end
151
187
  else
152
188
  @cpu_count = 1
153
189
  end
data/qnotifier.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{qnotifier}
5
- s.version = "0.6.5"
5
+ s.version = "0.6.7"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Gersham Meharg"]
@@ -12,9 +12,9 @@ Gem::Specification.new do |s|
12
12
  s.email = %q{gersham@qnotifier.com}
13
13
  s.executables = ["qnotifier"]
14
14
  s.extra_rdoc_files = ["bin/qnotifier", "lib/command.rb", "lib/plugin.rb", "lib/qnotifier.rb", "lib/storage.rb", "lib/web_service.rb"]
15
- s.files = ["Manifest", "Rakefile", "bin/qnotifier", "config/qnotifier_config.yml", "gem-public_cert.pem", "lib/command.rb", "lib/plugin.rb", "lib/qnotifier.rb", "lib/storage.rb", "lib/web_service.rb", "plugins/apache.rb", "plugins/iostat.rb", "plugins/mysql.rb", "plugins/network.rb", "plugins/nginx.rb", "plugins/passenger.rb", "plugins/processes.rb", "plugins/rails.rb", "plugins/ruby.rb", "plugins/system.rb", "plugins/urls.rb", "qnotifier.gemspec"]
15
+ s.files = ["Manifest", "Rakefile", "bin/qnotifier", "config/README", "config/qnotifier_config.yml", "gem-public_cert.pem", "lib/command.rb", "lib/plugin.rb", "lib/qnotifier.rb", "lib/storage.rb", "lib/web_service.rb", "plugins/apache.rb", "plugins/iostat.rb", "plugins/mysql.rb", "plugins/network.rb", "plugins/nginx.rb", "plugins/passenger.rb", "plugins/processes.rb", "plugins/rails.rb", "plugins/ruby.rb", "plugins/system.rb", "plugins/urls.rb", "init.d/qnotifier", "qnotifier.gemspec"]
16
16
  s.homepage = %q{http://qnotifier.com/qnotifier_gem}
17
- s.post_install_message = %q{Qnotifier installed, please register by running 'qnotifier -r CODE' in order to register this server.}
17
+ s.post_install_message = %q{Qnotifier installed, please register by running 'qnotifier' in order to register this server.}
18
18
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Qnotifier"]
19
19
  s.require_paths = ["lib"]
20
20
  s.rubyforge_project = %q{qnotifier}
@@ -31,23 +31,21 @@ Gem::Specification.new do |s|
31
31
  s.add_runtime_dependency(%q<rest-client>, [">= 1.4.2"])
32
32
  s.add_runtime_dependency(%q<json>, [">= 1.2.3"])
33
33
  s.add_runtime_dependency(%q<elif>, [">= 0.1.0"])
34
+ s.add_runtime_dependency(%q<robustthread>, [">= 0.5.2"])
34
35
  s.add_development_dependency(%q<rest-client>, [">= 1.4.2"])
35
36
  s.add_development_dependency(%q<json>, [">= 1.2.3"])
36
37
  s.add_development_dependency(%q<elif>, [">= 0.1.0"])
38
+ s.add_development_dependency(%q<robustthread>, [">= 0.5.2"])
37
39
  else
38
40
  s.add_dependency(%q<rest-client>, [">= 1.4.2"])
39
41
  s.add_dependency(%q<json>, [">= 1.2.3"])
40
42
  s.add_dependency(%q<elif>, [">= 0.1.0"])
41
- s.add_dependency(%q<rest-client>, [">= 1.4.2"])
42
- s.add_dependency(%q<json>, [">= 1.2.3"])
43
- s.add_dependency(%q<elif>, [">= 0.1.0"])
43
+ s.add_dependency(%q<robustthread>, [">= 0.5.2"])
44
44
  end
45
45
  else
46
46
  s.add_dependency(%q<rest-client>, [">= 1.4.2"])
47
47
  s.add_dependency(%q<json>, [">= 1.2.3"])
48
48
  s.add_dependency(%q<elif>, [">= 0.1.0"])
49
- s.add_dependency(%q<rest-client>, [">= 1.4.2"])
50
- s.add_dependency(%q<json>, [">= 1.2.3"])
51
- s.add_dependency(%q<elif>, [">= 0.1.0"])
49
+ s.add_dependency(%q<robustthread>, [">= 0.5.2"])
52
50
  end
53
51
  end
data.tar.gz.sig CHANGED
@@ -1,3 +1,2 @@
1
- ��ʧ�N�}H5Ow�yw��7�����g�'I94��3�՟(�� ��x�~8���2XDw��?�e��]��%?_�~�`��F�����Q�����[�Om�G8<@0Q�YX��`�0*���:B�
2
- C�@�9t����%r
3
- ��@'�_����;2�3?��;�O+�v.�����pñk9��o��
1
+ zk?J���DB[)�<��^ax|��Xl�T��xFg��D� �AV �e�߅��Zq
2
+ ��r�У��ՋQN��ꣲ‚o%�����&��DH�IZww�rhu���ǃ毘
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qnotifier
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 9
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 6
8
- - 5
9
- version: 0.6.5
9
+ - 7
10
+ version: 0.6.7
10
11
  platform: ruby
11
12
  authors:
12
13
  - Gersham Meharg
@@ -42,9 +43,11 @@ dependencies:
42
43
  name: rest-client
43
44
  prerelease: false
44
45
  requirement: &id001 !ruby/object:Gem::Requirement
46
+ none: false
45
47
  requirements:
46
48
  - - ">="
47
49
  - !ruby/object:Gem::Version
50
+ hash: 3
48
51
  segments:
49
52
  - 1
50
53
  - 4
@@ -56,9 +59,11 @@ dependencies:
56
59
  name: json
57
60
  prerelease: false
58
61
  requirement: &id002 !ruby/object:Gem::Requirement
62
+ none: false
59
63
  requirements:
60
64
  - - ">="
61
65
  - !ruby/object:Gem::Version
66
+ hash: 25
62
67
  segments:
63
68
  - 1
64
69
  - 2
@@ -70,9 +75,11 @@ dependencies:
70
75
  name: elif
71
76
  prerelease: false
72
77
  requirement: &id003 !ruby/object:Gem::Requirement
78
+ none: false
73
79
  requirements:
74
80
  - - ">="
75
81
  - !ruby/object:Gem::Version
82
+ hash: 27
76
83
  segments:
77
84
  - 0
78
85
  - 1
@@ -81,47 +88,85 @@ dependencies:
81
88
  type: :runtime
82
89
  version_requirements: *id003
83
90
  - !ruby/object:Gem::Dependency
84
- name: rest-client
91
+ name: robustthread
85
92
  prerelease: false
86
93
  requirement: &id004 !ruby/object:Gem::Requirement
94
+ none: false
87
95
  requirements:
88
96
  - - ">="
89
97
  - !ruby/object:Gem::Version
98
+ hash: 15
99
+ segments:
100
+ - 0
101
+ - 5
102
+ - 2
103
+ version: 0.5.2
104
+ type: :runtime
105
+ version_requirements: *id004
106
+ - !ruby/object:Gem::Dependency
107
+ name: rest-client
108
+ prerelease: false
109
+ requirement: &id005 !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ hash: 3
90
115
  segments:
91
116
  - 1
92
117
  - 4
93
118
  - 2
94
119
  version: 1.4.2
95
120
  type: :development
96
- version_requirements: *id004
121
+ version_requirements: *id005
97
122
  - !ruby/object:Gem::Dependency
98
123
  name: json
99
124
  prerelease: false
100
- requirement: &id005 !ruby/object:Gem::Requirement
125
+ requirement: &id006 !ruby/object:Gem::Requirement
126
+ none: false
101
127
  requirements:
102
128
  - - ">="
103
129
  - !ruby/object:Gem::Version
130
+ hash: 25
104
131
  segments:
105
132
  - 1
106
133
  - 2
107
134
  - 3
108
135
  version: 1.2.3
109
136
  type: :development
110
- version_requirements: *id005
137
+ version_requirements: *id006
111
138
  - !ruby/object:Gem::Dependency
112
139
  name: elif
113
140
  prerelease: false
114
- requirement: &id006 !ruby/object:Gem::Requirement
141
+ requirement: &id007 !ruby/object:Gem::Requirement
142
+ none: false
115
143
  requirements:
116
144
  - - ">="
117
145
  - !ruby/object:Gem::Version
146
+ hash: 27
118
147
  segments:
119
148
  - 0
120
149
  - 1
121
150
  - 0
122
151
  version: 0.1.0
123
152
  type: :development
124
- version_requirements: *id006
153
+ version_requirements: *id007
154
+ - !ruby/object:Gem::Dependency
155
+ name: robustthread
156
+ prerelease: false
157
+ requirement: &id008 !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ hash: 15
163
+ segments:
164
+ - 0
165
+ - 5
166
+ - 2
167
+ version: 0.5.2
168
+ type: :development
169
+ version_requirements: *id008
125
170
  description: The server side agent for the QNotifier monitoring system. This software will monitor local Linux system parameters and upload them to the QNotifier servers where users can use iPhone/iPad applications to view those stats and recieve alerts.
126
171
  email: gersham@qnotifier.com
127
172
  executables:
@@ -139,6 +184,7 @@ files:
139
184
  - Manifest
140
185
  - Rakefile
141
186
  - bin/qnotifier
187
+ - config/README
142
188
  - config/qnotifier_config.yml
143
189
  - gem-public_cert.pem
144
190
  - lib/command.rb
@@ -157,12 +203,13 @@ files:
157
203
  - plugins/ruby.rb
158
204
  - plugins/system.rb
159
205
  - plugins/urls.rb
206
+ - init.d/qnotifier
160
207
  - qnotifier.gemspec
161
208
  has_rdoc: true
162
209
  homepage: http://qnotifier.com/qnotifier_gem
163
210
  licenses: []
164
211
 
165
- post_install_message: Qnotifier installed, please register by running 'qnotifier -r CODE' in order to register this server.
212
+ post_install_message: Qnotifier installed, please register by running 'qnotifier' in order to register this server.
166
213
  rdoc_options:
167
214
  - --line-numbers
168
215
  - --inline-source
@@ -171,16 +218,20 @@ rdoc_options:
171
218
  require_paths:
172
219
  - lib
173
220
  required_ruby_version: !ruby/object:Gem::Requirement
221
+ none: false
174
222
  requirements:
175
223
  - - ">="
176
224
  - !ruby/object:Gem::Version
225
+ hash: 3
177
226
  segments:
178
227
  - 0
179
228
  version: "0"
180
229
  required_rubygems_version: !ruby/object:Gem::Requirement
230
+ none: false
181
231
  requirements:
182
232
  - - ">="
183
233
  - !ruby/object:Gem::Version
234
+ hash: 11
184
235
  segments:
185
236
  - 1
186
237
  - 2
@@ -188,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
239
  requirements: []
189
240
 
190
241
  rubyforge_project: qnotifier
191
- rubygems_version: 1.3.6
242
+ rubygems_version: 1.3.7
192
243
  signing_key:
193
244
  specification_version: 3
194
245
  summary: The server side agent for the QNotifier monitoring system
metadata.gz.sig CHANGED
@@ -1,4 +1 @@
1
- (���k�uOdˑ��E�dm ��
2
- �%[�2����P�ѕbs��b����[�4�b�)qz�@�4���z+� #i�fp�I���Djy�|Zu�N � �)<�67�kܗ.>�*��d옠�LտiF<��\u=
3
- �s��'���*X+�(��Ծ�s��
4
- ׾�w� 2�s�d���J~[ܛ�hWE�P�iz\(��He���+�� ._� �~?�����\J_�-�n줆���ݦg ���*0�
1
+ �d�q���`�#W� ھ ��Rl7y�k����U;�#���[��3^dx4�a��8xg.F)2U0�@E�T?Iw�K�qZ_6�D�xF�P+��`�> ��i����/�6JUG���I�^�@��B:�l2�X/'y״�f�{�ir�e-8���)�nJkTa hx6y?�O��L�!�̿CЄG��Y�� nq�HZ�mp��t�SRt9XuTڥv�����'��hG.�c!��_�%���DFm ��K�נx;