sensu 0.27.1 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/lib/sensu/api/routes/clients.rb +1 -1
- data/lib/sensu/api/routes/info.rb +15 -10
- data/lib/sensu/api/routes/request.rb +7 -1
- data/lib/sensu/api/routes/silenced.rb +2 -1
- data/lib/sensu/api/utilities/servers_info.rb +36 -0
- data/lib/sensu/api/utilities/transport_info.rb +1 -0
- data/lib/sensu/client/process.rb +72 -55
- data/lib/sensu/client/socket.rb +1 -1
- data/lib/sensu/constants.rb +1 -1
- data/lib/sensu/daemon.rb +3 -2
- data/lib/sensu/sandbox.rb +19 -0
- data/lib/sensu/server/filter.rb +1 -95
- data/lib/sensu/server/handle.rb +14 -7
- data/lib/sensu/server/process.rb +179 -43
- data/lib/sensu/server/socket.rb +1 -1
- data/lib/sensu/timers.rb +26 -0
- data/lib/sensu/utilities.rb +168 -2
- data/sensu.gemspec +2 -1
- metadata +21 -5
- data/lib/sensu/server/sandbox.rb +0 -21
data/lib/sensu/server/handle.rb
CHANGED
@@ -33,12 +33,14 @@ module Sensu
|
|
33
33
|
# @param handler [Hash] definition.
|
34
34
|
# @param event_data [Object] provided to the spawned handler
|
35
35
|
# process via STDIN.
|
36
|
-
|
36
|
+
# @param event_id [String] event UUID
|
37
|
+
def pipe_handler(handler, event_data, event_id)
|
37
38
|
options = {:data => event_data, :timeout => handler[:timeout]}
|
38
39
|
Spawn.process(handler[:command], options) do |output, status|
|
39
40
|
log_level = status == 0 ? :info : :error
|
40
41
|
@logger.send(log_level, "handler output", {
|
41
42
|
:handler => handler,
|
43
|
+
:event => { :id => event_id },
|
42
44
|
:output => output.split("\n+")
|
43
45
|
})
|
44
46
|
@in_progress[:events] -= 1 if @in_progress
|
@@ -121,10 +123,12 @@ module Sensu
|
|
121
123
|
#
|
122
124
|
# @param handler [Hash] definition.
|
123
125
|
# @param event_data [Object] to pass to the handler extension.
|
124
|
-
|
126
|
+
# @param event_id [String] event UUID
|
127
|
+
def handler_extension(handler, event_data, event_id)
|
125
128
|
handler.safe_run(event_data) do |output, status|
|
126
129
|
@logger.info("handler extension output", {
|
127
130
|
:extension => handler.definition,
|
131
|
+
:event => { :id => event_id },
|
128
132
|
:output => output,
|
129
133
|
:status => status
|
130
134
|
})
|
@@ -137,10 +141,11 @@ module Sensu
|
|
137
141
|
#
|
138
142
|
# @param handler [Hash] definition.
|
139
143
|
# @param event_data [Object] to pass to the handler type method.
|
140
|
-
|
144
|
+
# @param event_id [String] event UUID
|
145
|
+
def handler_type_router(handler, event_data, event_id)
|
141
146
|
case handler[:type]
|
142
147
|
when "pipe"
|
143
|
-
pipe_handler(handler, event_data)
|
148
|
+
pipe_handler(handler, event_data, event_id)
|
144
149
|
when "tcp"
|
145
150
|
tcp_handler(handler, event_data)
|
146
151
|
when "udp"
|
@@ -148,7 +153,7 @@ module Sensu
|
|
148
153
|
when "transport"
|
149
154
|
transport_handler(handler, event_data)
|
150
155
|
when "extension"
|
151
|
-
handler_extension(handler, event_data)
|
156
|
+
handler_extension(handler, event_data, event_id)
|
152
157
|
end
|
153
158
|
end
|
154
159
|
|
@@ -159,13 +164,15 @@ module Sensu
|
|
159
164
|
#
|
160
165
|
# @param handler [Hash] definition.
|
161
166
|
# @param event_data [Object] to pass to an event handler.
|
162
|
-
|
167
|
+
# @param event_id [String] event UUID
|
168
|
+
def handle_event(handler, event_data, event_id)
|
163
169
|
definition = handler.is_a?(Hash) ? handler : handler.definition
|
164
170
|
@logger.debug("handling event", {
|
165
171
|
:event_data => event_data,
|
172
|
+
:event => { :id => event_id },
|
166
173
|
:handler => definition
|
167
174
|
})
|
168
|
-
handler_type_router(handler, event_data)
|
175
|
+
handler_type_router(handler, event_data, event_id)
|
169
176
|
end
|
170
177
|
end
|
171
178
|
end
|
data/lib/sensu/server/process.rb
CHANGED
@@ -258,7 +258,7 @@ module Sensu
|
|
258
258
|
@in_progress[:events] += 1
|
259
259
|
filter_event(handler, event) do |event|
|
260
260
|
mutate_event(handler, event) do |event_data|
|
261
|
-
handle_event(handler, event_data)
|
261
|
+
handle_event(handler, event_data, event[:id])
|
262
262
|
end
|
263
263
|
end
|
264
264
|
end
|
@@ -627,6 +627,7 @@ module Sensu
|
|
627
627
|
end
|
628
628
|
else
|
629
629
|
client = create_client(client_key)
|
630
|
+
client[:type] = "proxy" if result[:check][:source]
|
630
631
|
update_client_registry(client) do
|
631
632
|
yield(client)
|
632
633
|
end
|
@@ -766,57 +767,145 @@ module Sensu
|
|
766
767
|
end
|
767
768
|
end
|
768
769
|
|
769
|
-
#
|
770
|
-
#
|
770
|
+
# Create and publish one or more proxy check requests. This
|
771
|
+
# method iterates through the Sensu client registry for clients
|
772
|
+
# that matched provided proxy request client attributes. A proxy
|
773
|
+
# check request is created for each client in the registry that
|
774
|
+
# matches the proxy request client attributes. Proxy check
|
775
|
+
# requests have their client tokens subsituted by the associated
|
776
|
+
# client attributes values. The check requests are published to
|
777
|
+
# the Transport via `publish_check_request()`.
|
778
|
+
#
|
779
|
+
# @param check [Hash] definition.
|
780
|
+
def publish_proxy_check_requests(check)
|
781
|
+
client_attributes = check[:proxy_requests][:client_attributes]
|
782
|
+
unless client_attributes.empty?
|
783
|
+
@redis.smembers("clients") do |clients|
|
784
|
+
clients.each do |client_name|
|
785
|
+
@redis.get("client:#{client_name}") do |client_json|
|
786
|
+
unless client_json.nil?
|
787
|
+
client = Sensu::JSON.load(client_json)
|
788
|
+
if attributes_match?(client, client_attributes)
|
789
|
+
@logger.debug("creating a proxy check request", {
|
790
|
+
:client => client,
|
791
|
+
:check => check
|
792
|
+
})
|
793
|
+
proxy_check, unmatched_tokens = object_substitute_tokens(check.dup, client)
|
794
|
+
if unmatched_tokens.empty?
|
795
|
+
proxy_check[:source] ||= client[:name]
|
796
|
+
publish_check_request(proxy_check)
|
797
|
+
else
|
798
|
+
@logger.warn("failed to publish a proxy check request", {
|
799
|
+
:reason => "unmatched client tokens",
|
800
|
+
:unmatched_tokens => unmatched_tokens,
|
801
|
+
:client => client,
|
802
|
+
:check => check
|
803
|
+
})
|
804
|
+
end
|
805
|
+
end
|
806
|
+
end
|
807
|
+
end
|
808
|
+
end
|
809
|
+
end
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
# Create a check request proc, used to publish check requests to
|
814
|
+
# for a check to the Sensu transport. Check requests are not
|
815
|
+
# published if subdued. This method determines if a check uses
|
816
|
+
# proxy check requests and calls the appropriate check request
|
817
|
+
# publish method.
|
818
|
+
#
|
819
|
+
# @param check [Hash] definition.
|
820
|
+
def create_check_request_proc(check)
|
821
|
+
Proc.new do
|
822
|
+
unless check_subdued?(check)
|
823
|
+
if check[:proxy_requests]
|
824
|
+
publish_proxy_check_requests(check)
|
825
|
+
else
|
826
|
+
publish_check_request(check)
|
827
|
+
end
|
828
|
+
else
|
829
|
+
@logger.info("check request was subdued", :check => check)
|
830
|
+
end
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
# Schedule a check request, using the check cron. This method
|
835
|
+
# determines the time until the next cron time (in seconds) and
|
836
|
+
# creats an EventMachine timer for the request. This method will
|
837
|
+
# be called after every check cron request for subsequent
|
838
|
+
# requests. The timer is stored in the timer hash under
|
839
|
+
# `:leader`, as check request publishing is a task for only the
|
840
|
+
# Sensu server leader, so it can be cancelled etc. The check
|
841
|
+
# cron request timer object is removed from the timer hash after
|
842
|
+
# the request is published, to stop the timer hash from growing
|
843
|
+
# infinitely.
|
844
|
+
#
|
845
|
+
# @param check [Hash] definition.
|
846
|
+
def schedule_check_cron_request(check)
|
847
|
+
cron_time = determine_check_cron_time(check)
|
848
|
+
@timers[:leader] << Timer.new(cron_time) do |timer|
|
849
|
+
create_check_request_proc(check).call
|
850
|
+
@timers[:leader].delete(timer)
|
851
|
+
schedule_check_cron_request(check)
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
# Calculate a check request splay, taking into account the
|
856
|
+
# current time and the request interval to ensure it's
|
771
857
|
# consistent between process restarts.
|
772
858
|
#
|
773
859
|
# @param check [Hash] definition.
|
774
|
-
def
|
860
|
+
def calculate_check_request_splay(check)
|
775
861
|
splay_hash = Digest::MD5.digest(check[:name]).unpack('Q<').first
|
776
862
|
current_time = (Time.now.to_f * 1000).to_i
|
777
863
|
(splay_hash - current_time) % (check[:interval] * 1000) / 1000.0
|
778
864
|
end
|
779
865
|
|
780
|
-
# Schedule check
|
781
|
-
# using
|
782
|
-
#
|
783
|
-
#
|
784
|
-
#
|
866
|
+
# Schedule check requests, using the check interval. This method
|
867
|
+
# using an intial calculated request splay EventMachine timer
|
868
|
+
# and an EventMachine periodic timer for subsequent check
|
869
|
+
# requests. The timers are stored in the timers hash under
|
870
|
+
# `:leader`, as check request publishing is a task for only the
|
871
|
+
# Sensu server leader, so they can be cancelled etc.
|
872
|
+
#
|
873
|
+
# @param check [Hash] definition.
|
874
|
+
def schedule_check_interval_requests(check)
|
875
|
+
request_splay = testing? ? 0 : calculate_check_request_splay(check)
|
876
|
+
interval = testing? ? 0.5 : check[:interval]
|
877
|
+
@timers[:leader] << Timer.new(request_splay) do
|
878
|
+
create_check_request = create_check_request_proc(check)
|
879
|
+
create_check_request.call
|
880
|
+
@timers[:leader] << PeriodicTimer.new(interval, &create_check_request)
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
884
|
+
# Schedule check requests. This method iterates through defined
|
885
|
+
# checks and uses the appropriate method of check request
|
886
|
+
# scheduling, either with the cron syntax or a numeric interval.
|
785
887
|
#
|
786
888
|
# @param checks [Array] of definitions.
|
787
|
-
def
|
889
|
+
def schedule_checks(checks)
|
788
890
|
checks.each do |check|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
@logger.info("check request was subdued", :check => check)
|
794
|
-
end
|
795
|
-
end
|
796
|
-
execution_splay = testing? ? 0 : calculate_check_execution_splay(check)
|
797
|
-
interval = testing? ? 0.5 : check[:interval]
|
798
|
-
@timers[:leader] << EM::Timer.new(execution_splay) do
|
799
|
-
create_check_request.call
|
800
|
-
@timers[:leader] << EM::PeriodicTimer.new(interval, &create_check_request)
|
891
|
+
if check[:cron]
|
892
|
+
schedule_check_cron_request(check)
|
893
|
+
else
|
894
|
+
schedule_check_interval_requests(check)
|
801
895
|
end
|
802
896
|
end
|
803
897
|
end
|
804
898
|
|
805
899
|
# Set up the check request publisher. This method creates an
|
806
900
|
# array of check definitions, that are not standalone checks,
|
807
|
-
# and do not have `:publish` set to `false`. The array
|
808
|
-
#
|
809
|
-
# (with a defined execution `:interval`). The array is provided
|
810
|
-
# to the `schedule_check_executions()` method.
|
901
|
+
# and do not have `:publish` set to `false`. The array is
|
902
|
+
# provided to the `schedule_checks()` method.
|
811
903
|
def setup_check_request_publisher
|
812
904
|
@logger.debug("scheduling check requests")
|
813
|
-
|
905
|
+
checks = @settings.checks.reject do |check|
|
814
906
|
check[:standalone] || check[:publish] == false
|
815
907
|
end
|
816
|
-
|
817
|
-
check[:standalone] || check[:publish] == false || !check[:interval].is_a?(Integer)
|
818
|
-
end
|
819
|
-
schedule_check_executions(standard_checks + extension_checks)
|
908
|
+
schedule_checks(checks)
|
820
909
|
end
|
821
910
|
|
822
911
|
# Publish a check result to the Transport for processing. A
|
@@ -948,7 +1037,7 @@ module Sensu
|
|
948
1037
|
# stored in the timers hash under `:leader`.
|
949
1038
|
def setup_client_monitor
|
950
1039
|
@logger.debug("monitoring client keepalives")
|
951
|
-
@timers[:leader] <<
|
1040
|
+
@timers[:leader] << PeriodicTimer.new(30) do
|
952
1041
|
determine_stale_clients
|
953
1042
|
end
|
954
1043
|
end
|
@@ -994,7 +1083,7 @@ module Sensu
|
|
994
1083
|
# is stored in the timers hash under `:leader`.
|
995
1084
|
def setup_check_result_monitor(interval = 30)
|
996
1085
|
@logger.debug("monitoring check results")
|
997
|
-
@timers[:leader] <<
|
1086
|
+
@timers[:leader] << PeriodicTimer.new(interval) do
|
998
1087
|
determine_stale_check_results(interval)
|
999
1088
|
end
|
1000
1089
|
end
|
@@ -1018,24 +1107,24 @@ module Sensu
|
|
1018
1107
|
(Time.now.to_f * 1000).to_i
|
1019
1108
|
end
|
1020
1109
|
|
1021
|
-
# Create/return the unique Sensu server
|
1022
|
-
#
|
1110
|
+
# Create/return the unique Sensu server ID for the current
|
1111
|
+
# process.
|
1023
1112
|
#
|
1024
1113
|
# @return [String]
|
1025
|
-
def
|
1026
|
-
@
|
1114
|
+
def server_id
|
1115
|
+
@server_id ||= random_uuid
|
1027
1116
|
end
|
1028
1117
|
|
1029
1118
|
# Become the Sensu server leader, responsible for specific
|
1030
1119
|
# duties (`leader_duties()`). Unless the current process is
|
1031
1120
|
# already the leader, this method sets the leader ID stored in
|
1032
|
-
# Redis to the unique random
|
1121
|
+
# Redis to the unique random server ID for the process. If the
|
1033
1122
|
# leader ID in Redis is successfully updated, `@is_leader` is
|
1034
1123
|
# set to true and `leader_duties()` is called to begin the
|
1035
1124
|
# tasks/duties of the Sensu server leader.
|
1036
1125
|
def become_the_leader
|
1037
1126
|
unless @is_leader
|
1038
|
-
@redis.set("leader",
|
1127
|
+
@redis.set("leader", server_id) do
|
1039
1128
|
@logger.info("i am now the leader")
|
1040
1129
|
@is_leader = true
|
1041
1130
|
leader_duties
|
@@ -1066,7 +1155,7 @@ module Sensu
|
|
1066
1155
|
end
|
1067
1156
|
|
1068
1157
|
# Updates the Sensu server leader lock timestamp. The current
|
1069
|
-
# leader ID is retrieved from Redis and compared with the
|
1158
|
+
# leader ID is retrieved from Redis and compared with the server
|
1070
1159
|
# ID of the current process to determine if it is still the
|
1071
1160
|
# Sensu server leader. If the current process is still the
|
1072
1161
|
# leader, the leader lock timestamp is updated. If the current
|
@@ -1075,7 +1164,7 @@ module Sensu
|
|
1075
1164
|
# more than one leader.
|
1076
1165
|
def update_leader_lock
|
1077
1166
|
@redis.get("leader") do |current_leader_id|
|
1078
|
-
if current_leader_id ==
|
1167
|
+
if current_leader_id == server_id
|
1079
1168
|
@redis.set("lock:leader", create_lock_timestamp) do
|
1080
1169
|
@logger.debug("updated leader lock timestamp")
|
1081
1170
|
end
|
@@ -1125,10 +1214,10 @@ module Sensu
|
|
1125
1214
|
# every 10 seconds. The timers are stored in the timers hash
|
1126
1215
|
# under `:run`.
|
1127
1216
|
def setup_leader_monitor
|
1128
|
-
@timers[:run] <<
|
1217
|
+
@timers[:run] << Timer.new(2) do
|
1129
1218
|
request_leader_election
|
1130
1219
|
end
|
1131
|
-
@timers[:run] <<
|
1220
|
+
@timers[:run] << PeriodicTimer.new(10) do
|
1132
1221
|
if @is_leader
|
1133
1222
|
update_leader_lock
|
1134
1223
|
else
|
@@ -1137,6 +1226,52 @@ module Sensu
|
|
1137
1226
|
end
|
1138
1227
|
end
|
1139
1228
|
|
1229
|
+
# Update the Sensu server registry, stored in Redis. This method
|
1230
|
+
# adds the local/current Sensu server info to the registry,
|
1231
|
+
# including its id, hostname, address, if its the current
|
1232
|
+
# leader, and some metrics. Sensu server registry entries expire
|
1233
|
+
# in 30 seconds unless updated.
|
1234
|
+
#
|
1235
|
+
# @yield [success] passes success status to optional
|
1236
|
+
# callback/block.
|
1237
|
+
# @yieldparam success [TrueClass,FalseClass] indicating if the
|
1238
|
+
# server registry update was a success.
|
1239
|
+
def update_server_registry
|
1240
|
+
@logger.debug("updating the server registry")
|
1241
|
+
process_cpu_times do |cpu_user, cpu_system, _, _|
|
1242
|
+
info = {
|
1243
|
+
:id => server_id,
|
1244
|
+
:hostname => system_hostname,
|
1245
|
+
:address => system_address,
|
1246
|
+
:is_leader => @is_leader,
|
1247
|
+
:metrics => {
|
1248
|
+
:cpu => {
|
1249
|
+
:user => cpu_user,
|
1250
|
+
:system => cpu_system
|
1251
|
+
}
|
1252
|
+
},
|
1253
|
+
:timestamp => Time.now.to_i
|
1254
|
+
}
|
1255
|
+
@redis.sadd("servers", server_id)
|
1256
|
+
server_key = "server:#{server_id}"
|
1257
|
+
@redis.set(server_key, Sensu::JSON.dump(info)) do
|
1258
|
+
@redis.expire(server_key, 30)
|
1259
|
+
@logger.info("updated server registry", :server => info)
|
1260
|
+
yield(true) if block_given?
|
1261
|
+
end
|
1262
|
+
end
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
# Set up the server registry updater. A periodic timer is
|
1266
|
+
# used to update the Sensu server info stored in Redis. The
|
1267
|
+
# timer is stored in the timers hash under `:run`.
|
1268
|
+
def setup_server_registry_updater
|
1269
|
+
update_server_registry
|
1270
|
+
@timers[:run] << PeriodicTimer.new(10) do
|
1271
|
+
update_server_registry
|
1272
|
+
end
|
1273
|
+
end
|
1274
|
+
|
1140
1275
|
# Unsubscribe from transport subscriptions (all of them). This
|
1141
1276
|
# method is called when there are issues with connectivity, or
|
1142
1277
|
# the process is stopping.
|
@@ -1169,6 +1304,7 @@ module Sensu
|
|
1169
1304
|
setup_keepalives
|
1170
1305
|
setup_results
|
1171
1306
|
setup_leader_monitor
|
1307
|
+
setup_server_registry_updater
|
1172
1308
|
@state = :running
|
1173
1309
|
end
|
1174
1310
|
|
data/lib/sensu/server/socket.rb
CHANGED
data/lib/sensu/timers.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
|
3
|
+
module Sensu
|
4
|
+
class Timer < EventMachine::Timer; end
|
5
|
+
|
6
|
+
# This fix comes from http://soohwan.blogspot.ca/2011/02/fix-eventmachineperiodictimer.html
|
7
|
+
class PeriodicTimer < EventMachine::PeriodicTimer
|
8
|
+
alias :original_initialize :initialize
|
9
|
+
alias :original_schedule :schedule
|
10
|
+
|
11
|
+
# Record initial start time and the fixed interval, used for
|
12
|
+
# compensating for timer drift when scheduling the next call.
|
13
|
+
def initialize(interval, callback=nil, &block)
|
14
|
+
@start = Time.now
|
15
|
+
@fixed_interval = interval
|
16
|
+
original_initialize(interval, callback, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Calculate the timer drift and compensate for it.
|
20
|
+
def schedule
|
21
|
+
compensation = (Time.now - @start) % @fixed_interval
|
22
|
+
@interval = @fixed_interval - compensation
|
23
|
+
original_schedule
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/sensu/utilities.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
|
+
gem "parse-cron", "0.1.4"
|
2
|
+
|
1
3
|
require "securerandom"
|
4
|
+
require "sensu/sandbox"
|
5
|
+
require "parse-cron"
|
6
|
+
require "socket"
|
2
7
|
|
3
8
|
module Sensu
|
4
9
|
module Utilities
|
10
|
+
EVAL_PREFIX = "eval:".freeze
|
11
|
+
|
5
12
|
# Determine if Sensu is being tested, using the process name.
|
6
13
|
# Sensu is being test if the process name is "rspec",
|
7
14
|
#
|
@@ -16,7 +23,7 @@ module Sensu
|
|
16
23
|
# @param wait [Numeric] time to delay block calls.
|
17
24
|
# @param block [Proc] to call that needs to return true.
|
18
25
|
def retry_until_true(wait=0.5, &block)
|
19
|
-
|
26
|
+
Timer.new(wait) do
|
20
27
|
unless block.call
|
21
28
|
retry_until_true(wait, &block)
|
22
29
|
end
|
@@ -36,7 +43,7 @@ module Sensu
|
|
36
43
|
when hash_one[key].is_a?(Hash) && value.is_a?(Hash)
|
37
44
|
deep_merge(hash_one[key], value)
|
38
45
|
when hash_one[key].is_a?(Array) && value.is_a?(Array)
|
39
|
-
hash_one[key]
|
46
|
+
(hash_one[key] + value).uniq
|
40
47
|
else
|
41
48
|
value
|
42
49
|
end
|
@@ -44,6 +51,37 @@ module Sensu
|
|
44
51
|
merged
|
45
52
|
end
|
46
53
|
|
54
|
+
# Retrieve the system hostname. If the hostname cannot be
|
55
|
+
# determined and an error is thrown, `nil` will be returned.
|
56
|
+
#
|
57
|
+
# @return [String] system hostname.
|
58
|
+
def system_hostname
|
59
|
+
::Socket.gethostname rescue nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# Retrieve the system IP address. If a valid non-loopback
|
63
|
+
# IPv4 address cannot be found and an error is thrown,
|
64
|
+
# `nil` will be returned.
|
65
|
+
#
|
66
|
+
# @return [String] system ip address
|
67
|
+
def system_address
|
68
|
+
::Socket.ip_address_list.find { |address|
|
69
|
+
address.ipv4? && !address.ipv4_loopback?
|
70
|
+
}.ip_address rescue nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Retrieve the process CPU times. If the cpu times cannot be
|
74
|
+
# determined and an error is thrown, `[nil, nil, nil, nil]` will
|
75
|
+
# be returned.
|
76
|
+
#
|
77
|
+
# @return [Array] CPU times: utime, stime, cutime, cstime
|
78
|
+
def process_cpu_times(&callback)
|
79
|
+
determine_cpu_times = Proc.new do
|
80
|
+
::Process.times.to_a rescue [nil, nil, nil, nil]
|
81
|
+
end
|
82
|
+
EM::defer(determine_cpu_times, callback)
|
83
|
+
end
|
84
|
+
|
47
85
|
# Generate a random universally unique identifier.
|
48
86
|
#
|
49
87
|
# @return [String] random UUID.
|
@@ -124,6 +162,124 @@ module Sensu
|
|
124
162
|
[substituted, unmatched_tokens]
|
125
163
|
end
|
126
164
|
|
165
|
+
# Perform token substitution for an object. String values are
|
166
|
+
# passed to `substitute_tokens()`, arrays and sub-hashes are
|
167
|
+
# processed recursively. Numeric values are ignored.
|
168
|
+
#
|
169
|
+
# @param object [Object]
|
170
|
+
# @param attributes [Hash]
|
171
|
+
# @return [Array] containing the updated object with substituted
|
172
|
+
# values and an array of unmatched tokens.
|
173
|
+
def object_substitute_tokens(object, attributes)
|
174
|
+
unmatched_tokens = []
|
175
|
+
case object
|
176
|
+
when Hash
|
177
|
+
object.each do |key, value|
|
178
|
+
object[key], unmatched = object_substitute_tokens(value, attributes)
|
179
|
+
unmatched_tokens.push(*unmatched)
|
180
|
+
end
|
181
|
+
when Array
|
182
|
+
object.map! do |value|
|
183
|
+
value, unmatched = object_substitute_tokens(value, attributes)
|
184
|
+
unmatched_tokens.push(*unmatched)
|
185
|
+
value
|
186
|
+
end
|
187
|
+
when String
|
188
|
+
object, unmatched_tokens = substitute_tokens(object, attributes)
|
189
|
+
end
|
190
|
+
[object, unmatched_tokens.uniq]
|
191
|
+
end
|
192
|
+
|
193
|
+
# Process an eval attribute value, a Ruby `eval()` string
|
194
|
+
# containing an expression to be evaluated within the
|
195
|
+
# scope/context of a sandbox. This methods strips away the
|
196
|
+
# expression prefix, `eval:`, and substitues any dot notation
|
197
|
+
# tokens with the corresponding event data values. If there are
|
198
|
+
# unmatched tokens, this method will return `nil`.
|
199
|
+
#
|
200
|
+
# @object [Hash]
|
201
|
+
# @raw_eval_string [String]
|
202
|
+
# @return [String] processed eval string.
|
203
|
+
def process_eval_string(object, raw_eval_string)
|
204
|
+
eval_string = raw_eval_string.slice(5..-1)
|
205
|
+
eval_string, unmatched_tokens = substitute_tokens(eval_string, object)
|
206
|
+
if unmatched_tokens.empty?
|
207
|
+
eval_string
|
208
|
+
else
|
209
|
+
@logger.error("attribute value eval unmatched tokens", {
|
210
|
+
:object => object,
|
211
|
+
:raw_eval_string => raw_eval_string,
|
212
|
+
:unmatched_tokens => unmatched_tokens
|
213
|
+
})
|
214
|
+
nil
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Ruby `eval()` a string containing an expression, within the
|
219
|
+
# scope/context of a sandbox. This method is for attribute values
|
220
|
+
# starting with "eval:", with the Ruby expression following the
|
221
|
+
# colon. A single variable is provided to the expression, `value`,
|
222
|
+
# equal to the corresponding object attribute value. Dot notation
|
223
|
+
# tokens in the expression, e.g. `:::mysql.user:::`, are
|
224
|
+
# substituted with the corresponding object attribute values prior
|
225
|
+
# to evaluation. The expression is expected to return a boolean
|
226
|
+
# value.
|
227
|
+
#
|
228
|
+
# @param object [Hash]
|
229
|
+
# @param raw_eval_string [String] containing the Ruby
|
230
|
+
# expression to be evaluated.
|
231
|
+
# @param raw_value [Object] of the corresponding object
|
232
|
+
# attribute value.
|
233
|
+
# @return [TrueClass, FalseClass]
|
234
|
+
def eval_attribute_value(object, raw_eval_string, raw_value)
|
235
|
+
eval_string = process_eval_string(object, raw_eval_string)
|
236
|
+
unless eval_string.nil?
|
237
|
+
begin
|
238
|
+
value = Marshal.load(Marshal.dump(raw_value))
|
239
|
+
!!Sandbox.eval(eval_string, value)
|
240
|
+
rescue StandardError, SyntaxError => error
|
241
|
+
@logger.error("attribute value eval error", {
|
242
|
+
:object => object,
|
243
|
+
:raw_eval_string => raw_eval_string,
|
244
|
+
:raw_value => raw_value,
|
245
|
+
:error => error.to_s
|
246
|
+
})
|
247
|
+
false
|
248
|
+
end
|
249
|
+
else
|
250
|
+
false
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Determine if all attribute values match those of the
|
255
|
+
# corresponding object attributes. Attributes match if the value
|
256
|
+
# objects are equivalent, are both hashes with matching key/value
|
257
|
+
# pairs (recursive), have equal string values, or evaluate to true
|
258
|
+
# (Ruby eval).
|
259
|
+
#
|
260
|
+
# @param object [Hash]
|
261
|
+
# @param match_attributes [Object]
|
262
|
+
# @param object_attributes [Object]
|
263
|
+
# @return [TrueClass, FalseClass]
|
264
|
+
def attributes_match?(object, match_attributes, object_attributes=nil)
|
265
|
+
object_attributes ||= object
|
266
|
+
match_attributes.all? do |key, value_one|
|
267
|
+
value_two = object_attributes[key]
|
268
|
+
case
|
269
|
+
when value_one == value_two
|
270
|
+
true
|
271
|
+
when value_one.is_a?(Hash) && value_two.is_a?(Hash)
|
272
|
+
attributes_match?(object, value_one, value_two)
|
273
|
+
when value_one.to_s == value_two.to_s
|
274
|
+
true
|
275
|
+
when value_one.is_a?(String) && value_one.start_with?(EVAL_PREFIX)
|
276
|
+
eval_attribute_value(object, value_one, value_two)
|
277
|
+
else
|
278
|
+
false
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
127
283
|
# Determine if the current time falls within a time window. The
|
128
284
|
# provided condition must have a `:begin` and `:end` time, eg.
|
129
285
|
# "11:30:00 PM", or `false` will be returned.
|
@@ -189,5 +345,15 @@ module Sensu
|
|
189
345
|
false
|
190
346
|
end
|
191
347
|
end
|
348
|
+
|
349
|
+
# Determine the next check cron time.
|
350
|
+
#
|
351
|
+
# @param check [Hash] definition.
|
352
|
+
def determine_check_cron_time(check)
|
353
|
+
cron_parser = CronParser.new(check[:cron])
|
354
|
+
current_time = Time.now
|
355
|
+
next_cron_time = cron_parser.next(current_time)
|
356
|
+
next_cron_time - current_time
|
357
|
+
end
|
192
358
|
end
|
193
359
|
end
|