quark 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- command, arguments = data.chomp.split(' ',2)
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 => true
52
+ :success => true,
53
+ :command => command,
54
+ :arguments => arguments,
55
+ :userdata => userdata
45
56
  }
46
57
  else
47
58
  return {
48
- :success => true,
49
- :command => data.chomp,
50
- :results => rv
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 => data.chomp,
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 => data.chomp,
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
@@ -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.0
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: &20685860 !ruby/object:Gem::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: *20685860
24
+ version_requirements: *16802640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: eventmachine
27
- requirement: &20685400 !ruby/object:Gem::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: *20685400
35
+ version_requirements: *16802180
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: redis
38
- requirement: &20701280 !ruby/object:Gem::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: *20701280
46
+ version_requirements: *16801740
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: hiredis
49
- requirement: &20700820 !ruby/object:Gem::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: *20700820
57
+ version_requirements: *16801300
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: em-synchrony
60
- requirement: &20700220 !ruby/object:Gem::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: *20700220
68
+ version_requirements: *16800800
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: em-websocket
71
- requirement: &20699700 !ruby/object:Gem::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: *20699700
79
+ version_requirements: *16800320
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: hashlib
82
- requirement: &20699020 !ruby/object:Gem::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: *20699020
90
+ version_requirements: *16799780
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: sinatra
93
- requirement: &20698360 !ruby/object:Gem::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: *20698360
101
+ version_requirements: *16799240
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: sinatra-cross_origin
104
- requirement: &20697860 !ruby/object:Gem::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: *20697860
112
+ version_requirements: *16797920
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: thin
115
- requirement: &20697320 !ruby/object:Gem::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: *20697320
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: