quark 0.2.0 → 0.2.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.
- data/bin/collectd-quark +126 -0
- data/lib/quark/command_processor.rb +25 -8
- data/lib/quark/commands/subscribe.rb +15 -0
- data/lib/quark/servers/http.rb +1 -1
- data/lib/quark/subscription.rb +18 -0
- metadata +25 -21
data/bin/collectd-quark
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/bin/false
|
2
|
+
#===============================================================================
|
3
|
+
# collectd-quark - a write plugin to forward collectd metrics to a Quark server
|
4
|
+
#
|
5
|
+
# SOURCE
|
6
|
+
# https://github.com/ghetzel/quark
|
7
|
+
#
|
8
|
+
# AUTHOR
|
9
|
+
# Gary Hetzel <garyhetzel@gmail.com>
|
10
|
+
#===============================================================================
|
11
|
+
import collectd
|
12
|
+
import json
|
13
|
+
import sys
|
14
|
+
import os
|
15
|
+
import re
|
16
|
+
import subprocess
|
17
|
+
import socket
|
18
|
+
import time
|
19
|
+
import urllib2
|
20
|
+
|
21
|
+
ACTIONS = ['exec']
|
22
|
+
PLUGIN_NAME='quark'
|
23
|
+
|
24
|
+
quark_hostname = '127.0.0.1'
|
25
|
+
quark_port = 12161
|
26
|
+
quark_socket = None
|
27
|
+
quark_mode = 'tcp'
|
28
|
+
quark_prefix = ''
|
29
|
+
|
30
|
+
# -----------------------------------------------------------------------------
|
31
|
+
# CALLBACK: config()
|
32
|
+
# processes the collectd.conf configuration stanza for this plugin
|
33
|
+
#
|
34
|
+
def config(c):
|
35
|
+
global quark_hostname, quark_port, quark_socket, quark_mode, quark_prefix
|
36
|
+
|
37
|
+
for ci in c.children:
|
38
|
+
if ci.key == 'Hostname':
|
39
|
+
quark_hostname = ci.values[0]
|
40
|
+
elif ci.key == 'Port':
|
41
|
+
quark_port = int(ci.values[0])
|
42
|
+
elif ci.key == 'Socket':
|
43
|
+
quark_socket = ci.values[0]
|
44
|
+
elif ci.key == 'Mode':
|
45
|
+
quark_mode = ci.values[0]
|
46
|
+
elif ci.key == 'Prefix':
|
47
|
+
quark_prefix = ci.values[0]
|
48
|
+
|
49
|
+
if not quark_mode:
|
50
|
+
raise Exception('Must specify a Mode: socket,tcp,udp,http')
|
51
|
+
|
52
|
+
|
53
|
+
# -----------------------------------------------------------------------------
|
54
|
+
# CALLBACK: collectd write()
|
55
|
+
# this is what collectd calls when it receives a new metric observation
|
56
|
+
#
|
57
|
+
def write(vl, data=None):
|
58
|
+
global quark_prefix
|
59
|
+
|
60
|
+
plugin = vl.plugin + ('-'+vl.plugin_instance if len(vl.plugin_instance) > 0 else '')
|
61
|
+
type = vl.type + ('-'+vl.type_instance if len(vl.type_instance) > 0 else '')
|
62
|
+
metric = quark_prefix + vl.host + '.' + plugin + ('.'+type if len(type) > 0 else '')
|
63
|
+
|
64
|
+
for i in vl.values:
|
65
|
+
observe = push_metric(vl, metric, i)
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
# -----------------------------------------------------------------------------
|
70
|
+
# push_metric
|
71
|
+
# pushes a new metric observation onto a host/metric-keyed stack (checkstack)
|
72
|
+
# also performs the value check (calls check_value) and stores the status code
|
73
|
+
# finally, determines whether this observation is in violation based on the
|
74
|
+
# threshold configuration
|
75
|
+
#
|
76
|
+
# returns:
|
77
|
+
# the observation record as stored in checkstack (for convenience)
|
78
|
+
#
|
79
|
+
def push_metric(vlist, metric, value):
|
80
|
+
global quark_hostname, quark_port, quark_socket, quark_mode
|
81
|
+
rv = None
|
82
|
+
tm = vlist.time
|
83
|
+
if tm == 0:
|
84
|
+
tm = time.time()
|
85
|
+
|
86
|
+
tm = int(tm*1000)
|
87
|
+
|
88
|
+
payload = "OBSERVE %s %s %s" % (metric, value, tm)
|
89
|
+
|
90
|
+
if quark_mode == 'tcp':
|
91
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
92
|
+
s.connect((quark_hostname, quark_port))
|
93
|
+
s.send(payload)
|
94
|
+
rv = s.recv(1024)
|
95
|
+
s.close()
|
96
|
+
|
97
|
+
elif quark_mode == 'udp':
|
98
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
99
|
+
s.sendto(payload, (quark_hostname, quark_port))
|
100
|
+
|
101
|
+
elif quark_mode == 'socket':
|
102
|
+
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
103
|
+
s.connect(quark_socket)
|
104
|
+
s.send(payload)
|
105
|
+
rv = s.recv(1024)
|
106
|
+
s.close()
|
107
|
+
|
108
|
+
elif quark_mode == 'http':
|
109
|
+
rv = urllib2.urlopen("http://%s:%s/observe/%s/%s/%s" % (quark_hostname, quark_port, metric, value, tm)).read()
|
110
|
+
|
111
|
+
if not rv is None:
|
112
|
+
rv = json.loads(rv)
|
113
|
+
|
114
|
+
return rv
|
115
|
+
|
116
|
+
def shutdown():
|
117
|
+
print "Stopping collectd-quark"
|
118
|
+
|
119
|
+
|
120
|
+
# -----------------------------------------------------------------------------
|
121
|
+
# Register Callbacks
|
122
|
+
# -----------------------------------------------------------------------------
|
123
|
+
#collectd.register_init(init)
|
124
|
+
collectd.register_config(config)
|
125
|
+
collectd.register_write(write)
|
126
|
+
collectd.register_shutdown(shutdown)
|
@@ -26,14 +26,22 @@ module Quark
|
|
26
26
|
@_prefix = prefix
|
27
27
|
end
|
28
28
|
|
29
|
-
def self.process_command(data)
|
29
|
+
def self.process_command(data, userdata=nil)
|
30
30
|
# if the data perfectly resembles a graphite-formatted metric (without a command prefix)
|
31
31
|
# then assume the command is "observe"
|
32
32
|
if Quark::Config.get("quark.graphite_compat") and data =~ /^[\w\.\-]+ -?[\d\.]+ \d{10,13}$/
|
33
33
|
command = "OBSERVE"
|
34
34
|
arguments = data.chomp
|
35
35
|
else
|
36
|
-
|
36
|
+
if data =~ /\}$/
|
37
|
+
data, linedata = data.chomp.split('{',2)
|
38
|
+
|
39
|
+
userdata = (userdata || {}).merge(Hash[linedata.gsub(/(^\{|\}$)/,'').split(/,\s*/).collect{|i|
|
40
|
+
i.split('=',2)
|
41
|
+
}])
|
42
|
+
end
|
43
|
+
|
44
|
+
command, arguments = data.strip.chomp.split(' ',2)
|
37
45
|
end
|
38
46
|
|
39
47
|
begin
|
@@ -41,13 +49,18 @@ module Quark
|
|
41
49
|
rv = send(:"process_command_#{command.downcase}", arguments)
|
42
50
|
if rv.nil?
|
43
51
|
return {
|
44
|
-
:success
|
52
|
+
:success => true,
|
53
|
+
:command => command,
|
54
|
+
:arguments => arguments,
|
55
|
+
:userdata => userdata
|
45
56
|
}
|
46
57
|
else
|
47
58
|
return {
|
48
|
-
:success
|
49
|
-
:command
|
50
|
-
:
|
59
|
+
:success => true,
|
60
|
+
:command => command,
|
61
|
+
:arguments => arguments,
|
62
|
+
:results => rv,
|
63
|
+
:userdata => userdata
|
51
64
|
}
|
52
65
|
end
|
53
66
|
else
|
@@ -57,7 +70,9 @@ module Quark
|
|
57
70
|
rescue Quark::Error => e
|
58
71
|
return {
|
59
72
|
:success => false,
|
60
|
-
:command
|
73
|
+
:command => command,
|
74
|
+
:arguments => arguments,
|
75
|
+
:userdata => userdata,
|
61
76
|
:error => {
|
62
77
|
:class => e.class.name,
|
63
78
|
:message => e.message
|
@@ -67,7 +82,9 @@ module Quark
|
|
67
82
|
rescue Exception => e
|
68
83
|
return {
|
69
84
|
:success => false,
|
70
|
-
:command
|
85
|
+
:command => command,
|
86
|
+
:arguments => arguments,
|
87
|
+
:userdata => userdata,
|
71
88
|
:error => {
|
72
89
|
:class => e.class.name,
|
73
90
|
:message => e.message,
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Quark
|
2
|
+
module CommandProcessor
|
3
|
+
def self.process_command_subscribe(data)
|
4
|
+
channel, patterns = data.chomp.split(' ', 2)
|
5
|
+
channel = channel.to_sym
|
6
|
+
|
7
|
+
Quark::Server.register_subscription(channel, Quark::Subscription.new({
|
8
|
+
:channel => channel,
|
9
|
+
:patterns => patterns.split(/\s+/)
|
10
|
+
}))
|
11
|
+
|
12
|
+
return Quark::Server.subscriptions[channel]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/quark/servers/http.rb
CHANGED
@@ -35,7 +35,7 @@ module Quark
|
|
35
35
|
end
|
36
36
|
|
37
37
|
get '/call/:command/?*' do
|
38
|
-
MultiJson.dump(Quark::CommandProcessor.process_command("#{params[:command].upcase} #{params[:splat].first.gsub('/',' ')}".strip))
|
38
|
+
MultiJson.dump(Quark::CommandProcessor.process_command("#{params[:command].upcase} #{params[:splat].first.gsub('/',' ')}".strip, request.env['rack.request.query_hash']))
|
39
39
|
end
|
40
40
|
|
41
41
|
get '/observe/:metric/:value/?' do
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Quark
|
2
|
+
class Subscription
|
3
|
+
attr_reader :channel, :patterns
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@channel = (options[:channel] || :default).to_sym
|
7
|
+
@patterns = [*(options[:patterns] || [])].uniq
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_pattern(pattern)
|
11
|
+
@patterns << pattern unless @patterns.include?(pattern)
|
12
|
+
end
|
13
|
+
|
14
|
+
def <<(pattern)
|
15
|
+
add_pattern(pattern)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quark
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2014-05-06 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
16
|
-
requirement: &
|
16
|
+
requirement: &16802640 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - =
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.7.9
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *16802640
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: eventmachine
|
27
|
-
requirement: &
|
27
|
+
requirement: &16802180 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.0.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *16802180
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: redis
|
38
|
-
requirement: &
|
38
|
+
requirement: &16801740 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 3.0.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *16801740
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: hiredis
|
49
|
-
requirement: &
|
49
|
+
requirement: &16801300 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.5.2
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *16801300
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: em-synchrony
|
60
|
-
requirement: &
|
60
|
+
requirement: &16800800 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.0.3
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *16800800
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: em-websocket
|
71
|
-
requirement: &
|
71
|
+
requirement: &16800320 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 0.5.1
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *16800320
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: hashlib
|
82
|
-
requirement: &
|
82
|
+
requirement: &16799780 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: 0.0.35
|
88
88
|
type: :runtime
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *16799780
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: sinatra
|
93
|
-
requirement: &
|
93
|
+
requirement: &16799240 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :runtime
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *16799240
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: sinatra-cross_origin
|
104
|
-
requirement: &
|
104
|
+
requirement: &16797920 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *16797920
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: thin
|
115
|
-
requirement: &
|
115
|
+
requirement: &16797280 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,12 +120,13 @@ dependencies:
|
|
120
120
|
version: '0'
|
121
121
|
type: :runtime
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *16797280
|
124
124
|
description: A small service for logging and retrieving timeseries metrics into a
|
125
125
|
Redis server
|
126
126
|
email: garyhetzel@gmail.com
|
127
127
|
executables:
|
128
128
|
- quark
|
129
|
+
- collectd-quark
|
129
130
|
extensions: []
|
130
131
|
extra_rdoc_files: []
|
131
132
|
files:
|
@@ -137,14 +138,17 @@ files:
|
|
137
138
|
- lib/quark/servers/tcp.rb
|
138
139
|
- lib/quark/config.rb
|
139
140
|
- lib/quark/command_processor.rb
|
141
|
+
- lib/quark/commands/subscribe.rb
|
140
142
|
- lib/quark/commands/peek.rb
|
141
143
|
- lib/quark/commands/fetch.rb
|
142
144
|
- lib/quark/commands/observe.rb
|
143
145
|
- lib/quark/commands/ping.rb
|
144
146
|
- lib/quark/errors.rb
|
147
|
+
- lib/quark/subscription.rb
|
145
148
|
- lib/quark/server.rb
|
146
149
|
- lib/quark/base_server.rb
|
147
150
|
- bin/quark
|
151
|
+
- bin/collectd-quark
|
148
152
|
homepage: https://github.com/ghetzel/quark
|
149
153
|
licenses: []
|
150
154
|
post_install_message:
|