Permalink
Browse files

use 3.1.x format for generating packets

  • Loading branch information...
1 parent df51ce1 commit 413004cfe04a80a88f4f1af8d770d9cbbf778c34 @igrigorik committed Jan 28, 2010
Showing with 116 additions and 30 deletions.
  1. +64 −22 lib/gmetric.rb
  2. +52 −8 spec/gmetric_spec.rb
View
@@ -1,6 +1,9 @@
+require "stringio"
+require "socket"
+
module Ganglia
class GMetric
-
+
SLOPE = {
'zero' => 0,
'positive' => 1,
@@ -9,41 +12,80 @@ class GMetric
'unspecified' => 4
}
- def self.pack(data)
- xdr = XDRPacket.new
+ def self.send(host, port, metric)
+ conn = UDPSocket.new
+ conn.connect(host, port)
+ gmetric = self.pack(metric)
+
+ conn.send gmetric[0], 0
+ conn.send gmetric[1], 0
+ end
- xdr.pack_int(0) # type gmetric
- xdr.pack_string(data[:type]) # one of: string, int8, uint8, int16, uint16, int32, uint32, float, double
- xdr.pack_string(data[:name]) # name of the metric
- xdr.pack_string(data[:val].to_s) # value of the metric
- xdr.pack_string(data[:units]) # units for the value, e.g. 'kb/sec'
- xdr.pack_int(SLOPE[data[:slope]]) # sign of the derivative of the value over time, one of zero, positive, negative, both, default both
- xdr.pack_uint(data[:tmax].to_i) # maximum time in seconds between gmetric calls, default 60
- xdr.pack_uint(data[:dmax].to_i) # lifetime in seconds of this metric, default=0, meaning unlimited
+ def self.pack(metric)
+ metric = {
+ :hostname => '',
+ :spoof => 0,
+ :units => '',
+ :slope => 'both',
+ :tmax => 60,
+ :dmax => 0
+ }.merge(metric)
+
+ raise "Missing key, value, type" if not metric.key? :name or not metric.key? :value or not metric.key? :type
+ raise "Invalid metric type" if not %w(string int8 uint8 int16 uint16 int32 uint32 float double).include? metric[:type]
+
+ meta = XDRPacket.new
+ data = XDRPacket.new
+
+ # METADATA payload
+ meta.pack_int(128) # gmetadata_full
+ meta.pack_string(metric[:hostname]) # hostname
+ meta.pack_string(metric[:name].to_s) # name of the metric
+ meta.pack_int(metric[:spoof].to_i) # spoof hostname flag
+
+ meta.pack_string(metric[:type].to_s) # one of: string, int8, uint8, int16, uint16, int32, uint32, float, double
+ meta.pack_string(metric[:name].to_s) # name of the metric
+ meta.pack_string(metric[:units].to_s) # units for the value, e.g. 'kb/sec'
+ meta.pack_int(SLOPE[metric[:slope]]) # sign of the derivative of the value over time, one of zero, positive, negative, both, default both
+ meta.pack_uint(metric[:tmax].to_i) # maximum time in seconds between gmetric calls, default 60
+ meta.pack_uint(metric[:dmax].to_i) # lifetime in seconds of this metric, default=0, meaning unlimited
+ meta.pack_int(0)
+
+ # DATA payload
+ data.pack_int(128+5) # string message
+ data.pack_string(metric[:hostname].to_s) # hostname
+ data.pack_string(metric[:name].to_s) # name of the metric
+ data.pack_int(metric[:spoof].to_i) # spoof hostname flag
+ data.pack_string("%s") #
+ data.pack_string(metric[:value].to_s) # value of the metric
- xdr.get_buffer
+ [meta.data, data.data]
end
end
class XDRPacket
def initialize
- @data = []
+ @data = StringIO.new
end
- def pack_int(data)
-
+ def pack_uint(data)
+ # big endian unsigned long
+ @data << [data].pack("N")
end
+ alias :pack_int :pack_uint
def pack_string(data)
-
- end
+ len = data.size
+ pack_uint(len)
- def pack_uint(data)
-
+ # pad the string
+ len = ((len+3) / 4) * 4
+ data = data + ("\0" * (len - data.size))
+ @data << data
end
- def get_buffer
- @data.join
+ def data
+ @data.string
end
end
-end
+end
View
@@ -1,23 +1,67 @@
require "helper"
describe Ganglia::GMetric do
-
+
+ describe Ganglia::XDRPacket do
+ def hex(data)
+ [data].pack("H")
+ end
+
+ it "should pack an int & uint into XDR format" do
+ xdr = Ganglia::XDRPacket.new
+ xdr.pack_int(1)
+ xdr.data.should == "\000\000\000\001"
+
+ xdr = Ganglia::XDRPacket.new
+ xdr.pack_uint(8)
+ xdr.data.should == "\000\000\000\b"
+ end
+
+ it "should pack string" do
+ xdr = Ganglia::XDRPacket.new
+ xdr.pack_string("test")
+ xdr.data.should == "\000\000\000\004test"
+ end
+ end
+
it "should pack GMetric into XDR format from Ruby hash" do
- result = "0000000000000006737472696e67000000000003666f6f00000000036261720000000000000000030000003c00000000"
data = {
:slope => 'both',
:name => 'foo',
- :val => 'bar',
+ :value => 'bar',
:tmax => 60,
:units => '',
:dmax => 0,
:type => 'string'
}
-
+
g = Ganglia::GMetric.pack(data)
- g.size.should == 48
- # g.hex.should == result
-
+ g.size.should == 2
+ g[0].should == "\000\000\000\200\000\000\000\000\000\000\000\003foo\000\000\000\000\000\000\000\000\006string\000\000\000\000\000\003foo\000\000\000\000\000\000\000\000\003\000\000\000<\000\000\000\000\000\000\000\000"
+ g[1].should == "\000\000\000\205\000\000\000\000\000\000\000\003foo\000\000\000\000\000\000\000\000\002%s\000\000\000\000\000\003bar\000"
+ end
+
+ it "should raise an error on missing name, value, type" do
+ %w(name value type).each do |key|
+ lambda {
+ data = {:name => 'a', :type => 'b', :value => 'c'}
+ data.delete key.to_sym
+ Ganglia::GMetric.pack(data)
+ }.should raise_error
+ end
end
-end
+ it "should verify type and raise error on invalid type" do
+ %w(string int8 uint8 int16 uint16 int32 uint32 float double).each do |type|
+ lambda {
+ data = {:name => 'a', :type => type, :value => 'c'}
+ Ganglia::GMetric.pack(data)
+ }.should_not raise_error
+ end
+
+ lambda {
+ data = {:name => 'a', :type => 'int', :value => 'c'}
+ Ganglia::GMetric.pack(data)
+ }.should raise_error
+ end
+end

0 comments on commit 413004c

Please sign in to comment.