Permalink
Browse files

Major restructuring

  • Loading branch information...
1 parent d4f0174 commit 3a677a1002d22bdd1d6b92f2c4b002bc0608f9d4 @sosedoff committed Nov 3, 2011
View
@@ -1,3 +1,5 @@
require 'munin-ruby/version'
-require 'munin-ruby/stat'
+require 'munin-ruby/errors'
+require 'munin-ruby/parser'
+require 'munin-ruby/connection'
require 'munin-ruby/node'
@@ -0,0 +1,72 @@
+require 'socket'
+
+module Munin
+ class Connection
+ include Munin::Parser
+
+ attr_reader :host, :port
+
+ # Initialize a new connection to munin-node server
+ #
+ # host - Server host (default: 127.0.0.1)
+ # port - Server port (default: 4949)
+ #
+ def initialize(host='127.0.0.1', port=4949)
+ @host = host
+ @port = port
+ @socket = nil
+ @connected = false
+ end
+
+ # Returns true if socket is connected
+ #
+ def connected?
+ @connected == true
+ end
+
+ # Establish connection to the server
+ #
+ def connect
+ begin
+ @socket = TCPSocket.new(@host, @port)
+ @socket.sync = true
+ welcome = @socket.gets
+ rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::ECONNRESET => ex
+ raise Munin::ConnectionError, ex.message
+ rescue EOFError
+ raise Munin::AccessDenied
+ end
+ end
+
+ # Close connection
+ #
+ def close
+ @socket.close unless @socket.nil?
+ end
+
+ # Send a string of data followed by a newline symbol
+ #
+ def send_data(str)
+ connect unless connected?
+ @socket.puts("#{str.strip}\n")
+ end
+
+ # Reads a single line from socket
+ #
+ def read_line
+ @socket.gets.strip
+ end
+
+ # Reads a packet of data until '.' reached
+ #
+ def read_packet
+ lines = []
+ while(str = @socket.readline) do
+ break if str.strip == '.'
+ lines << str.strip
+ end
+ parse_error(lines)
+ lines
+ end
+ end
+end
View
@@ -0,0 +1,8 @@
+module Munin
+ class MuninError < StandardError ; end
+ class ConnectionError < MuninError ; end
+ class AccessDenied < MuninError ; end
+ class InvalidResponse < MuninError ; end
+ class UnknownService < MuninError ; end
+ class BadExit < MuninError ; end
+end
View
@@ -1,112 +1,55 @@
-require 'socket'
-
module Munin
- class SessionError < StandardError ; end
- class NoSuchService < StandardError ; end
- class AccessDenied < StandardError ; end
-
class Node
- attr_reader :host, :port
- attr_reader :version, :services
- attr_reader :timestamp
+ include Munin::Parser
- # Initialize a new Munin::Node object
+ attr_reader :connection
+
+ # Initialize a new Node instance
#
- # host - Server hostname or IP address
- # opts - Additional options.
- # :port - Node server port (default to 4949)
- # :fetch - String or Array of service names ONLY to fetch
+ # host - Server host
+ # port - Server port
#
- def initialize(host, opts={})
- @host = host
- @port = opts[:port] || 4949
- @stats = {}
- @services = []
- @version = ''
- @only_services = opts[:fetch] || []
-
- if @only_services.kind_of?(Array)
- @only_services.uniq!
- elsif @only_services.kind_of?(String)
- @only_services = @only_services.scan(/[a-z\d\-\_]{1,}/i).uniq
- else
- @only_services = []
- end
-
- run
+ def initialize(host='127.0.0.1', port=4949)
+ @connection = Munin::Connection.new(host, port)
end
- # Returns a set of parameters for the service
- #
- # name - Service name
- #
- # @return [Munin::Stat]
+ # Get a node version
#
- def service(name)
- if has_service?(name)
- @stats[name]
+ def version
+ connection.send_data("version")
+ str = connection.read_line
+ if str =~ /^munins node on/
+ str.split.last
else
- raise Munin::NoSuchService, "Service with name #{name} does not exist."
+ raise InvalidResponse
end
end
- # Returns true if node has the service
+ # Get a list of all available metrics
#
- # name - Service name
- #
- def has_service?(name)
- @stats.key?(name)
+ def list
+ connection.send_data("list")
+ connection.read_line.split
end
- # Fetch multiple services at once
+ # Get a configuration information for service
#
- # service_names - Array of service names to fetch
+ # service - Name of the service
#
- def snapshot(service_names=[])
- service_names.uniq.map { |n| service(n) }
+ def config(service)
+ connection.send_data("config #{service}")
+ lines = connection.read_packet
+ parse_config(lines)
end
- private
-
- # Fetch node information and stats
- def run
- begin
- @timestamp = Time.now
- @socket = TCPSocket.new(@host, @port)
- @socket.sync = true ; @socket.gets
- fetch_version
- fetch_services
- @socket.close
- rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::ECONNRESET => ex
- raise Munin::SessionError, ex.message
- rescue EOFError
- raise Munin::AccessDenied
- end
- end
-
- # Fetch node server version
- def fetch_version
- @socket.puts("version")
- @version = @socket.readline.strip.split(' ').last
- end
-
- # Fetch list of services and its stats
- def fetch_services
- @socket.puts("list")
- services = @socket.readline.split(' ').map { |s| s.strip }.sort
- services = services.select { |s| @only_services.include?(s) } unless @only_services.empty?
- services.each { |s| @services << s ; @stats[s] = fetch(s) }
- end
-
- # Fetch service information
+ # Get all service metrics values
+ #
+ # service - Name of the service
+ #
def fetch(service)
- @socket.puts("fetch #{service}")
- content = []
- while(str = @socket.readline) do
- break if str.strip == '.'
- content << str.strip.split(' ')
- end
- Stat.new(service, content)
+ connection.send_data("fetch #{service}")
+ lines = connection.read_packet
+ parse_fetch(lines)
end
end
-end
+end
View
@@ -0,0 +1,57 @@
+module Munin
+ module Parser
+ private
+
+ # Parse 'config' request
+ #
+ def parse_config(data)
+ config = {:graph => {}, :metrics => {}}
+ data.each do |l|
+ if l =~ /^graph_/
+ key_name, value = l.scan(/^graph_([\w]+)\s(.*)/).flatten
+ config[:graph][key_name] = value
+ elsif l =~ /^[a-z]+\./
+ matches = l.scan(/^([a-z\d\-\_]+)\.([a-z\d\-\_]+)\s(.*)/).flatten
+ config[:metrics][matches[0]] ||= {}
+ config[:metrics][matches[0]][matches[1]] = matches[2]
+ end
+ end
+
+ # Now, lets process the args hash
+ if config[:graph].key?('args')
+ args = {}
+ config[:graph]['args'].scan(/--([a-z\-\_]+)\s([\d]+)\s?/).each do |arg|
+ args[arg.first] = arg.last
+ end
+ config[:graph]['args'] = args
+ end
+
+ config
+ end
+
+ # Parse 'fetch' request
+ #
+ def parse_fetch(data)
+ process_data(data)
+ end
+
+ def process_data(lines)
+ data = {}
+ lines.each do |line|
+ line = line.split
+ key = line.first.split('.value').first
+ data[key] = line.last
+ end
+ data
+ end
+
+ def parse_error(lines)
+ if lines.size == 1
+ case lines.first
+ when '# Unknown service' then raise UnknownService
+ when '# Bad exit' then raise BadExit
+ end
+ end
+ end
+ end
+end
View
@@ -1,30 +0,0 @@
-module Munin
- class Stat
- attr_reader :name, :params
-
- # Initialize a new Munin::Stat object
- #
- # name - Attribute name
- # rows - Array of parameters
- #
- def initialize(name, rows=[])
- @name = name
- @params = {}
- unless rows.empty?
- rows.each do |r|
- begin
- key = r.first.split('.value').first
- @params[key] = r.last
- rescue
- end
- end
- end
- end
-
- # Hash representation of the mertic
- #
- def to_hash
- {:name => self.name, :params => self.params}
- end
- end
-end
@@ -1,5 +1,5 @@
module Munin
unless defined?(::Munin::VERSION)
- VERSION = '0.1.0'
+ VERSION = '0.2.0'
end
end

0 comments on commit 3a677a1

Please sign in to comment.