systemd_mon_mod 0.1.1
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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/Rakefile +2 -0
- data/bin/systemd_mon +4 -0
- data/lib/systemd_mon/callback_manager.rb +39 -0
- data/lib/systemd_mon/cli.rb +86 -0
- data/lib/systemd_mon/dbus_manager.rb +35 -0
- data/lib/systemd_mon/dbus_unit.rb +70 -0
- data/lib/systemd_mon/error.rb +19 -0
- data/lib/systemd_mon/formatters/base.rb +18 -0
- data/lib/systemd_mon/formatters/state_table_formatter.rb +32 -0
- data/lib/systemd_mon/logger.rb +33 -0
- data/lib/systemd_mon/monitor.rb +98 -0
- data/lib/systemd_mon/notification.rb +34 -0
- data/lib/systemd_mon/notification_centre.rb +77 -0
- data/lib/systemd_mon/notifier_loader.rb +21 -0
- data/lib/systemd_mon/notifiers/base.rb +39 -0
- data/lib/systemd_mon/notifiers/email.rb +65 -0
- data/lib/systemd_mon/notifiers/hipchat.rb +62 -0
- data/lib/systemd_mon/notifiers/slack.rb +99 -0
- data/lib/systemd_mon/state.rb +59 -0
- data/lib/systemd_mon/state_change.rb +119 -0
- data/lib/systemd_mon/state_value.rb +48 -0
- data/lib/systemd_mon/unit_with_state.rb +33 -0
- data/lib/systemd_mon/version.rb +3 -0
- data/lib/systemd_mon.rb +5 -0
- data/systemd_mon.gemspec +24 -0
- metadata +120 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'systemd_mon/error'
|
2
|
+
require 'systemd_mon/logger'
|
3
|
+
|
4
|
+
module SystemdMon
|
5
|
+
class NotificationCentre
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
self.notifiers = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def classes
|
13
|
+
notifiers.map(&:class)
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
notifiers.each do |notifier|
|
18
|
+
yield notifier
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_notifier(notifier)
|
23
|
+
unless notifier.respond_to?(:notify!)
|
24
|
+
raise NotifierError, "Notifier #{notifier.class} must respond to 'notify!'"
|
25
|
+
end
|
26
|
+
self.notifiers << notifier
|
27
|
+
end
|
28
|
+
|
29
|
+
def notify_start!(hostname)
|
30
|
+
each_notifier do |notifier|
|
31
|
+
if notifier.respond_to?(:notify_start!)
|
32
|
+
Logger.puts "Notifying SystemdMon start via #{notifier.class}"
|
33
|
+
notifier.notify_start! hostname
|
34
|
+
else
|
35
|
+
Logger.debug { "#{notifier.class} doesn't respond to 'notify_start!', not sending notification" }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def notify_stop!(hostname)
|
41
|
+
each_notifier do |notifier|
|
42
|
+
if notifier.respond_to?(:notify_stop!)
|
43
|
+
Logger.puts "Notifying SystemdMon stop via #{notifier.class}"
|
44
|
+
notifier.notify_stop! hostname
|
45
|
+
else
|
46
|
+
Logger.debug { "#{notifier.class} doesn't respond to 'notify_start!', not sending notification" }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def notify!(notification)
|
52
|
+
each_notifier do |notifier|
|
53
|
+
Logger.puts "Notifying state change of #{notification.unit.name} via #{notifier.class}"
|
54
|
+
notifier.notify! notification
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
alias :<< :add_notifier
|
59
|
+
|
60
|
+
protected
|
61
|
+
attr_accessor :notifiers
|
62
|
+
|
63
|
+
def each_notifier
|
64
|
+
notifiers.map { |notifier|
|
65
|
+
Thread.new do
|
66
|
+
begin
|
67
|
+
yield notifier
|
68
|
+
rescue => e
|
69
|
+
Logger.error "Failed to send notification via #{notifier.class}:\n"
|
70
|
+
Logger.error " #{e.class}: #{e.message}\n"
|
71
|
+
Logger.debug_error { "\n\t#{e.backtrace.join('\n\t')}\n" }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
}.each(&:join)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SystemdMon
|
2
|
+
class NotifierLoader
|
3
|
+
def get_class(name)
|
4
|
+
class_name = camel_case(name)
|
5
|
+
get_class_const(class_name)
|
6
|
+
rescue NameError
|
7
|
+
require "systemd_mon/notifiers/#{name}"
|
8
|
+
get_class_const(class_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def camel_case(name)
|
13
|
+
return name if name !~ /_/ && name =~ /[A-Z]+.*/
|
14
|
+
name.split('_').map { |e| e.capitalize }.join
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_class_const(name)
|
18
|
+
Notifiers.const_get(name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'systemd_mon/logger'
|
2
|
+
|
3
|
+
module SystemdMon::Notifiers
|
4
|
+
class Base
|
5
|
+
def initialize(options)
|
6
|
+
self.options = options
|
7
|
+
self.me = self.class.name
|
8
|
+
end
|
9
|
+
|
10
|
+
# Subclasses must respond to a unit change
|
11
|
+
def notify!(notification)
|
12
|
+
raise "Notifier #{self.class} does not respond to notify!"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Subclasses can choose to do something when SystemdMon starts
|
16
|
+
# E.g. with
|
17
|
+
#
|
18
|
+
# def notify_start!(hostname)
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Subclasses can choose to do something when SystemdMon stops
|
22
|
+
# E.g. with
|
23
|
+
#
|
24
|
+
# def notify_stop!(hostname)
|
25
|
+
# end
|
26
|
+
|
27
|
+
def log(message)
|
28
|
+
SystemdMon::Logger.puts "#{me}: #{message}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def debug(message = nil, &blk)
|
32
|
+
message = "#{me}: #{message}" if message
|
33
|
+
SystemdMon::Logger.debug message, &blk
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
attr_accessor :options, :me
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'systemd_mon/error'
|
2
|
+
require 'systemd_mon/notifiers/base'
|
3
|
+
require 'systemd_mon/formatters/state_table_formatter'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'mail'
|
7
|
+
rescue LoadError
|
8
|
+
raise SystemdMon::NotifierDependencyError, "The 'mail' gem is required by the email notifier"
|
9
|
+
end
|
10
|
+
|
11
|
+
module SystemdMon::Notifiers
|
12
|
+
class Email < Base
|
13
|
+
def initialize(*)
|
14
|
+
super
|
15
|
+
if options['smtp']
|
16
|
+
opts = options
|
17
|
+
Mail.defaults do
|
18
|
+
delivery_method :smtp, Hash[opts['smtp'].map { |h, k| [h.to_sym, k] }]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
validate_options!
|
23
|
+
end
|
24
|
+
|
25
|
+
def notify!(notification)
|
26
|
+
unit = notification.unit
|
27
|
+
subject = "#{notification.type_text}: #{unit.name} on #{notification.hostname}: #{unit.state_change.status_text}"
|
28
|
+
message = "Systemd unit #{unit.name} on #{notification.hostname} #{unit.state_change.status_text}: #{unit.state.active} (#{unit.state.sub})\n\n"
|
29
|
+
if unit.state_change.length > 1
|
30
|
+
message << SystemdMon::Formatters::StateTableFormatter.new(unit).as_text
|
31
|
+
end
|
32
|
+
message << "\nRegards, SystemdMon"
|
33
|
+
|
34
|
+
send_mail subject, message
|
35
|
+
|
36
|
+
log "sent email notification"
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
attr_accessor :options
|
41
|
+
|
42
|
+
def validate_options!
|
43
|
+
unless options.has_key?("to")
|
44
|
+
raise NotifierError, "The 'to' address must be set to use the email notifier"
|
45
|
+
end
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_mail(subject, message)
|
50
|
+
debug("Sending email to #{options['to']}:")
|
51
|
+
debug(%Q{ -> Subject: "#{subject}"})
|
52
|
+
debug(%Q{ -> Message: "#{message}"})
|
53
|
+
|
54
|
+
mail = Mail.new do
|
55
|
+
subject subject
|
56
|
+
body message
|
57
|
+
end
|
58
|
+
mail.to = options['to']
|
59
|
+
if options['from']
|
60
|
+
mail.from options['from']
|
61
|
+
end
|
62
|
+
mail.deliver!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'systemd_mon/error'
|
2
|
+
require 'systemd_mon/notifiers/base'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'hipchat'
|
6
|
+
rescue LoadError
|
7
|
+
raise SystemdMon::NotifierDependencyError, "The 'hipchat' gem is required by the hipchat notifier"
|
8
|
+
end
|
9
|
+
|
10
|
+
module SystemdMon::Notifiers
|
11
|
+
class Hipchat < Base
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
self.client = ::HipChat::Client.new(
|
15
|
+
options['token'],
|
16
|
+
:api_version => 'v2')
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify_start!(hostname)
|
20
|
+
chat "SystemdMon is starting on #{hostname}",
|
21
|
+
'green'
|
22
|
+
end
|
23
|
+
|
24
|
+
def notify_stop!(hostname)
|
25
|
+
chat "SystemdMon is stopping on #{hostname}",
|
26
|
+
'yellow'
|
27
|
+
end
|
28
|
+
|
29
|
+
def notify!(notification)
|
30
|
+
unit = notification.unit
|
31
|
+
message = "#{notification.type_text}: systemd unit #{unit.name} on #{notification.hostname} #{unit.state_change.status_text}: #{unit.state.active} (#{unit.state.sub})"
|
32
|
+
|
33
|
+
chat message,
|
34
|
+
color(notification.type)
|
35
|
+
|
36
|
+
log "sent hipchat notification"
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
attr_accessor :client, :options
|
41
|
+
|
42
|
+
def chat(message, shade)
|
43
|
+
client[options['room']].send(
|
44
|
+
options['username'],
|
45
|
+
message,
|
46
|
+
:color => shade)
|
47
|
+
end
|
48
|
+
|
49
|
+
def color(type)
|
50
|
+
case type
|
51
|
+
when :alert
|
52
|
+
'red'
|
53
|
+
when :warning
|
54
|
+
'yellow'
|
55
|
+
when :info
|
56
|
+
'purple'
|
57
|
+
else
|
58
|
+
'green'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'systemd_mon/error'
|
2
|
+
require 'systemd_mon/notifiers/base'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'slack-notifier'
|
6
|
+
rescue LoadError
|
7
|
+
raise SystemdMon::NotifierDependencyError, "The 'slack-notifier' gem (> 1.0) is required by the slack notifier"
|
8
|
+
end
|
9
|
+
|
10
|
+
module SystemdMon::Notifiers
|
11
|
+
class Slack < Base
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
self.notifier = ::Slack::Notifier.new(
|
15
|
+
options.fetch('webhook_url'),
|
16
|
+
channel: options['channel'],
|
17
|
+
username: options['username'],
|
18
|
+
icon_emoji: options['icon_emoji'],
|
19
|
+
icon_url: options['icon_url'])
|
20
|
+
end
|
21
|
+
|
22
|
+
def notify_start!(hostname)
|
23
|
+
message = "@channel SystemdMon is starting on #{hostname}"
|
24
|
+
|
25
|
+
attach = {
|
26
|
+
fallback: message,
|
27
|
+
text: message,
|
28
|
+
color: "good"
|
29
|
+
}
|
30
|
+
|
31
|
+
notifier.ping "", attachments: [attach]
|
32
|
+
end
|
33
|
+
|
34
|
+
def notify_stop!(hostname)
|
35
|
+
message = "@channel SystemdMon is stopping on #{hostname}"
|
36
|
+
|
37
|
+
attach = {
|
38
|
+
fallback: message,
|
39
|
+
text: message,
|
40
|
+
color: "danger"
|
41
|
+
}
|
42
|
+
|
43
|
+
notifier.ping "", attachments: [attach]
|
44
|
+
end
|
45
|
+
|
46
|
+
def notify!(notification)
|
47
|
+
unit = notification.unit
|
48
|
+
message = "@channel #{notification.type_text}: systemd unit #{unit.name} on #{notification.hostname} #{unit.state_change.status_text}"
|
49
|
+
|
50
|
+
attach = {
|
51
|
+
fallback: "@channel #{message}: #{unit.state.active} (#{unit.state.sub})",
|
52
|
+
color: color(notification.type),
|
53
|
+
fields: fields(notification)
|
54
|
+
}
|
55
|
+
|
56
|
+
debug("sending slack message with attachment: ")
|
57
|
+
debug(attach.inspect)
|
58
|
+
|
59
|
+
notifier.ping message, attachments: [attach]
|
60
|
+
log "sent slack notification"
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
attr_accessor :notifier
|
65
|
+
|
66
|
+
def fields(notification)
|
67
|
+
f = [
|
68
|
+
{
|
69
|
+
title: "Hostname",
|
70
|
+
value: notification.hostname,
|
71
|
+
short: true
|
72
|
+
},
|
73
|
+
{
|
74
|
+
title: "Unit",
|
75
|
+
value: notification.unit.name,
|
76
|
+
short: true
|
77
|
+
}
|
78
|
+
]
|
79
|
+
|
80
|
+
changes = notification.unit.state_change.diff.map(&:last)
|
81
|
+
f.concat(changes.map { |v|
|
82
|
+
{ title: v.display_name, value: v.value, short: true }
|
83
|
+
})
|
84
|
+
end
|
85
|
+
|
86
|
+
def color(type)
|
87
|
+
case type
|
88
|
+
when :alert
|
89
|
+
'danger'
|
90
|
+
when :warning
|
91
|
+
'#FF9900'
|
92
|
+
when :info
|
93
|
+
'#0099CC'
|
94
|
+
else
|
95
|
+
'good'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'systemd_mon/state_value'
|
2
|
+
|
3
|
+
module SystemdMon
|
4
|
+
class State
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :active, :sub, :loaded, :unit_file, :all_states
|
8
|
+
|
9
|
+
def initialize(active, sub, loaded, unit_file, type=nil)
|
10
|
+
timestamp = Time.now
|
11
|
+
@active = StateValue.new("active", active, timestamp, *active_states(type))
|
12
|
+
@sub = StateValue.new("status", sub, timestamp)
|
13
|
+
@loaded = StateValue.new("loaded", loaded, timestamp, %w(loaded))
|
14
|
+
@unit_file = StateValue.new("file", unit_file, timestamp, *file_states(type))
|
15
|
+
@all_states = [@active, @sub, @loaded, @unit_file]
|
16
|
+
end
|
17
|
+
|
18
|
+
def active_states(type)
|
19
|
+
case type
|
20
|
+
when 'oneshot'
|
21
|
+
[%w(inactive), %w(failed)]
|
22
|
+
else
|
23
|
+
[%w(active), %w(inactive failed)]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def file_states(type)
|
28
|
+
case type
|
29
|
+
when 'oneshot'
|
30
|
+
[[], []]
|
31
|
+
else
|
32
|
+
[%w(enabled linked-runtime static), %w(disabled)]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def each
|
37
|
+
@all_states.each do |state|
|
38
|
+
yield state
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def ok?
|
43
|
+
all?(&:ok?)
|
44
|
+
end
|
45
|
+
|
46
|
+
def fail?
|
47
|
+
any?(&:fail?)
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
@all_states.join(', ')
|
52
|
+
end
|
53
|
+
|
54
|
+
def ==(other)
|
55
|
+
@all_states == other.all_states
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module SystemdMon
|
2
|
+
class StateChange
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :states
|
6
|
+
|
7
|
+
def initialize(original_state = nil)
|
8
|
+
self.states = []
|
9
|
+
states << original_state if original_state
|
10
|
+
end
|
11
|
+
|
12
|
+
def last
|
13
|
+
states.last
|
14
|
+
end
|
15
|
+
|
16
|
+
def length
|
17
|
+
states.length
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(state)
|
21
|
+
self.states << state
|
22
|
+
@diff = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def each
|
26
|
+
states.each do |state|
|
27
|
+
yield state
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def changes
|
32
|
+
states[1..-1]
|
33
|
+
end
|
34
|
+
|
35
|
+
def recovery?
|
36
|
+
first.fail? && last.ok?
|
37
|
+
end
|
38
|
+
|
39
|
+
def ok?
|
40
|
+
last.ok?
|
41
|
+
end
|
42
|
+
|
43
|
+
def fail?
|
44
|
+
last.fail?
|
45
|
+
end
|
46
|
+
|
47
|
+
def restart?
|
48
|
+
first.ok? && last.ok? && changes.any? { |s| s.active == "deactivating" }
|
49
|
+
end
|
50
|
+
|
51
|
+
def auto_restart?
|
52
|
+
first.ok? && last.ok? && changes.any? { |s| s.sub == "auto-restart" }
|
53
|
+
end
|
54
|
+
|
55
|
+
def reload?
|
56
|
+
first.ok? && last.ok? && changes.any? { |s| s.active == "reloading" }
|
57
|
+
end
|
58
|
+
|
59
|
+
def still_fail?
|
60
|
+
length > 1 && first.fail? && last.fail?
|
61
|
+
end
|
62
|
+
|
63
|
+
def status_text
|
64
|
+
if recovery?
|
65
|
+
"recovered"
|
66
|
+
elsif auto_restart?
|
67
|
+
"automatically restarted"
|
68
|
+
elsif restart?
|
69
|
+
"restarted"
|
70
|
+
elsif reload?
|
71
|
+
"reloaded"
|
72
|
+
elsif still_fail?
|
73
|
+
"still failed"
|
74
|
+
elsif fail?
|
75
|
+
"failed"
|
76
|
+
else
|
77
|
+
"started"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def important?
|
82
|
+
if length == 1
|
83
|
+
first.fail?
|
84
|
+
else
|
85
|
+
diff.map(&:last).any?(&:important?)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def diff
|
90
|
+
@diff ||= zipped.reject { |states|
|
91
|
+
match = states.first.value
|
92
|
+
states.all? { |s| s.value == match }
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def zipped
|
97
|
+
if length == 1
|
98
|
+
first.all_states
|
99
|
+
else
|
100
|
+
first.all_states.zip(*changes.map(&:all_states))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
diff.inject("") { |s, (*states)|
|
106
|
+
first = states.shift
|
107
|
+
s << "#{first.name} state changed from #{first.value} to "
|
108
|
+
s << states.map(&:value).join(" then ")
|
109
|
+
s << "\n"
|
110
|
+
s
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
protected
|
115
|
+
attr_accessor :original, :changed
|
116
|
+
attr_writer :states
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SystemdMon
|
2
|
+
class StateValue
|
3
|
+
attr_reader :name, :value, :ok_states, :failure_states, :timestamp
|
4
|
+
|
5
|
+
def initialize(name, value, timestamp, ok_states = [], failure_states = [])
|
6
|
+
self.name = name
|
7
|
+
self.value = value
|
8
|
+
self.ok_states = ok_states
|
9
|
+
self.failure_states = failure_states
|
10
|
+
self.timestamp = timestamp
|
11
|
+
end
|
12
|
+
|
13
|
+
def display_name
|
14
|
+
name.capitalize
|
15
|
+
end
|
16
|
+
|
17
|
+
def important?
|
18
|
+
ok_states.include?(value) || failure_states.include?(value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ok?
|
22
|
+
if ok_states.any?
|
23
|
+
ok_states.include?(value)
|
24
|
+
else
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def fail?
|
30
|
+
if failure_states.any?
|
31
|
+
failure_states.include?(value)
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
value
|
39
|
+
end
|
40
|
+
|
41
|
+
def ==(other)
|
42
|
+
other.is_a?(SystemdMon::StateValue) && value == other.value
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
attr_writer :name, :value, :ok_states, :failure_states, :timestamp
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'systemd_mon/state_change'
|
2
|
+
|
3
|
+
module SystemdMon
|
4
|
+
class UnitWithState
|
5
|
+
attr_reader :unit, :state_change
|
6
|
+
|
7
|
+
def initialize(unit)
|
8
|
+
self.unit = unit
|
9
|
+
self.state_change = StateChange.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
unit.name
|
14
|
+
end
|
15
|
+
|
16
|
+
def <<(state)
|
17
|
+
self.state_change << state
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_state
|
21
|
+
state_change.last
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset!
|
25
|
+
self.state_change = StateChange.new(current_state)
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :state :current_state
|
29
|
+
|
30
|
+
protected
|
31
|
+
attr_writer :state_change, :unit
|
32
|
+
end
|
33
|
+
end
|
data/lib/systemd_mon.rb
ADDED
data/systemd_mon.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'systemd_mon/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "systemd_mon_mod"
|
8
|
+
spec.version = SystemdMon::VERSION
|
9
|
+
spec.authors = ["Jon Cairns", "Dmitriy Maksakov"]
|
10
|
+
spec.email = ["jon@joncairns.com", "mksvdmtr@yandex.ru"]
|
11
|
+
spec.summary = %q{Monitor systemd units and trigger alerts for failed states (Mod for mattermost @channel mentioning)}
|
12
|
+
spec.description = %q{Monitor systemd units and trigger alerts for failed states (Mod for mattermost @channel mentioning)}
|
13
|
+
spec.homepage = "https://github.com/mksvdmtr/systemd_mon_mod"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "ruby-dbus", "~> 0.11.0"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
end
|