2
0
mirror of https://github.com/munin-monitoring/contrib.git synced 2018-11-08 00:59:34 +01:00

Bitcoind Multi-Plugin

This commit is contained in:
Mike Koss 2012-02-14 20:21:10 -08:00
parent b70ddc793b
commit 6cd082a78c

212
plugins/other/bitcoind_ Executable file
View File

@ -0,0 +1,212 @@
#!/usr/bin/env python
# bitcoind_ Munin plugin for Bitcoin Server Variables
#
# by Mike Koss
# Feb 14, 2012, MIT License
#
# You need to be able to authenticate to the bitcoind server to issue rpc's.
# This plugin supporst 2 ways to do that:
#
# 1) In /etc/munin/plugin-conf.d/bitcoin.conf place:
#
# [bitcoind_*]
# user your-username
#
# Then be sure your $HOME/.bitcoin/bitcoin.conf has the correct authentication info:
# rpcconnect, rpcport, rpcuser, rpcpassword
#
# 2) Place your bitcoind authentication directly in /etc/munin/plugin-conf.d/bitcoin.conf
#
# [bitcoind_*]
# env.rpcport 8332
# env.rpcconnect 127.0.0.1
# env.rpcuser your-username-here
# env.rpcpassword your-password-here
#
# To install all available graphs:
#
# sudo munin-node-configure --libdir=. --suggest --shell | sudo bash
#
# Leave out the "| bash" to get a list of commands you can select from to install
# individual graphs.
#
# Munin plugin tags:
#
#%# family=auto
#%# capabilities=autoconf suggest
import os
import sys
import re
import urllib2
import json
DEBUG = False
def main():
# getinfo variable is read from command name - probably the sym-link name.
request_var = sys.argv[0].split('_')[1] or 'balance'
request_labels = {'balance': ('Wallet Balance', 'BTC'),
'blocks': ('Block Number', 'Blocks', 160000),
'connections': ('Peer Connections', 'Connections'),
'difficulty': ('Current Difficulty', 'Difficulty', 1000000),
'errors': ("Errors", 'Errors',)
}
labels = request_labels[request_var]
if len(sys.argv) > 1 and sys.argv[1] == 'suggest':
for var_name in request_labels.keys():
print var_name
return
if len(sys.argv) > 1 and sys.argv[1] == 'config':
print 'graph_category bitcoin'
print 'graph_title Bitcoin %s' % labels[0]
print 'graph_vlabel %s' % labels[1]
print '%s.label %s' % (request_var, request_var)
if len(labels) >= 3:
print "graph_args --lower-limit %d" % labels[2]
return
# Munin should send connection options via environment vars
bitcoin_options = get_env_options('rpcconnect', 'rpcport', 'rpcuser', 'rpcpassword')
bitcoin_options.rpcconnect = bitcoin_options.get('rpcconnect', '127.0.0.1')
bitcoin_options.rpcport = bitcoin_options.get('rpcport', '8332')
if bitcoin_options.get('rpcuser') is None:
conf_file = os.path.join(os.path.expanduser('~/.bitcoin'), 'bitcoin.conf')
bitcoin_options = parse_conf(conf_file)
bitcoin_options.require('rpcuser', 'rpcpassword')
bitcoin = ServiceProxy('http://%s:%s' % (bitcoin_options.rpcconnect,
bitcoin_options.rpcport),
username=bitcoin_options.rpcuser,
password=bitcoin_options.rpcpassword)
(info, error) = bitcoin.getinfo()
if error:
if len(sys.argv) > 1 and sys.argv[1] == 'autoconf':
print 'no'
return
else:
raise ValueError("Could not connect to Bitcoin server.")
if len(sys.argv) > 1 and sys.argv[1] == 'autoconf':
print 'yes'
return
print "%s.value %s" % (request_var, info[request_var])
def parse_conf(filename):
""" Bitcoin config file parser. """
options = Options()
re_line = re.compile(r'^\s*([^#]*)\s*(#.*)?$')
re_setting = re.compile(r'^(.*)\s*=\s*(.*)$')
try:
with open(filename) as file:
for line in file.readlines():
line = re_line.match(line).group(1).strip()
m = re_setting.match(line)
if m is None:
continue
(var, value) = (m.group(1), m.group(2).strip())
options[var] = value
except:
pass
return options
def get_env_options(*vars):
options = Options()
for var in vars:
options[var] = os.getenv(var)
return options
class Options(dict):
"""A dict that allows for object-like property access syntax."""
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
def require(self, *names):
missing = []
for name in names:
if self.get(name) is None:
missing.append(name)
if len(missing) > 0:
raise ValueError("Missing required setting%s: %s." %
('s' if len(missing) > 1 else '',
', '.join(missing)))
class ServiceProxy(object):
"""
Proxy for a JSON-RPC web service. Calls to a function attribute
generates a JSON-RPC call to the host service. If a callback
keyword arg is included, the call is processed as an asynchronous
request.
Each call returns (result, error) tuple.
"""
def __init__(self, url, username=None, password=None):
self.url = url
self.id = 0
self.username = username
self.password = password
def __getattr__(self, method):
self.id += 1
return Proxy(self, method, id=self.id)
class Proxy(object):
def __init__(self, service, method, id=None):
self.service = service
self.method = method
self.id = id
def __call__(self, *args):
if DEBUG:
arg_strings = [json.dumps(arg) for arg in args]
print "Calling %s(%s) @ %s" % (self.method,
', '.join(arg_strings),
self.service.url)
data = {
'method': self.method,
'params': args,
'id': self.id,
}
request = urllib2.Request(self.service.url, json.dumps(data))
if self.service.username:
# Strip the newline from the b64 encoding!
b64 = ('%s:%s' % (self.service.username, self.service.password)).encode('base64')[:-1]
request.add_header('Authorization', 'Basic %s' % b64)
try:
body = urllib2.urlopen(request).read()
except Exception, e:
return (None, e)
if DEBUG:
print 'RPC Response (%s): %s' % (self.method, json.dumps(body, indent=4))
try:
data = json.loads(body)
except ValueError, e:
return (None, e.message)
# TODO: Check that id matches?
return (data['result'], data['error'])
if __name__ == "__main__":
main()