sensu 0.27.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|