systemd_mon_mod 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|