#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: set fileencoding=utf-8 """ =head1 NAME nanopool_ - Munin plugin to monitor nanopool ethereum user data. =head1 CONFIGURATION [nanopool_*] - Copy to /usr/share/munin/plugins - Create symlinks in /etc/munin/plugins to nanopool__, e.g. ln -s /usr/share/munin/plugins/nanopool_ /etc/munin/plugins/nanopool_hashrate_0x1234567890abcdef1234567890 Please use the account address in the format "0x
". - To enable graphs, link to one or more of the graph types available - Then restart munin-node Graph types and their links: balance: link to nanopool_balance Show current balance hashrate: link to nanopool_hashrate Show current calculated hashrate avghashrate: link to nanopool_avghashrate Show average hashrate of last hour worker: link to nanopool_worker Show worker data Thanks to the authors of other Python munin plugins. I've used some of them as inspiring example. =head1 VERSION 1.0.1 =head1 AUTHOR L =head1 LICENSE This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =head1 MAGIC MARKERS #%# family=contrib #%# capabilities=suggest =cut """ from __future__ import print_function import os import sys import json try: # Python 3 from urllib.request import Request from urllib.request import urlopen from urllib.request import URLError except: # Python 2 from urllib2 import Request from urllib2 import urlopen from urllib2 import URLError def define_graph_types(): graph_types = { "balance" : [ { "title" : "Balance of {0}".format(account_address), "type" : "GAUGE", "args" : "--base 1000 -l 0", "fields" : ["ETH"], "scale": "no", "info": "Balance in ETH" } ], "hashrate" : [ { "title" : "Hashrate of {0}".format(account_address), "type" : "GAUGE", "args" : "--base 1000 -l 0", "fields" : ["hashrate"], "info": "Current Calculated Hashrate in Mh/s" } ], "avghashrate" : [ { "title" : "Average Hashrate of {0}".format(account_address), "type" : "GAUGE", "args" : "--base 1000 -l 0", "fields" : ["avg_hashrate"], "info": "Average Hashrate of last hour in Mh/s" } ], "worker" : [ { "title" : "Worker" } ] } return graph_types def request_data(): url = "https://api.nanopool.org/v1/eth/user/{0}".format(account_address) req = Request(url) # this fake is necessary to get values, otherwise the request ends in a 403 error req.add_header('User-Agent', 'Nozilla/5.0') try: txt = urlopen(req).read() except URLError as err: print("API request error: {0}". format(err), file=sys.stderr) exit(1) except: print("Unhandled error:", sys.exc_info()[0]) exit(1) try: result = json.loads(txt.decode("utf-8")) except ValueError as err: print("Could not decode JSON error: {0}". format(err), file=sys.stderr) exit(1) return result def write_config_worker(): data = request_data() worker_data = sorted(data["data"]["workers"], key=lambda element: element["id"]) print("multigraph worker_hashrate_{0}".format(account_address)) print("graph_title Hashrate in Mh/s per worker ({0})".format(account_address)) print("graph_args --base 1000 -l 0") print("graph_vlabel Mh/s") print("graph_category other") print("graph_scale no") for val in worker_data: worker_name = "_".join(val["id"].split()) print("worker_{0}_hashrate.label {1}".format(worker_name, val["id"])) print("worker_{0}_hashrate.type GAUGE".format(worker_name)) print("worker_{0}_hashrate.info Hashrate of worker '{1}'".format(worker_name, val["id"])) print("worker_{0}_hashrate.min 0".format(worker_name)) print("worker_{0}_hashrate.draw LINE1".format(worker_name)) for val in worker_data: print("") worker_name = "_".join(val["id"].split()) print("multigraph worker_hashrate_{0}.worker_{1}".format(account_address, worker_name)) print("graph_title Hashrate in Mh/s of worker {0}".format(worker_name)) print("graph_args --base 1000 -l 0") print("graph_vlabel Mh/s") print("graph_category other") print("graph_scale no") print("whashrate.label hashrate") print("whashrate.type GAUGE") print("whashrate.info Hashrate of worker {0}".format(val["id"])) print("whashrate.min 0") print("whashrate.draw LINE1") print("") print("multigraph worker_shares_{0}".format(account_address)) print("graph_title Number of accepted shares ({0})".format(account_address)) print("graph_args --base 1000 -l 0") print("graph_vlabel Shares per ${graph_period}") print("graph_category other") print("graph_scale no") print("graph_period minute") for val in worker_data: worker_name = "_".join(val["id"].split()) print("worker_{0}_shares.label {1} ".format(worker_name, val["id"])) print("worker_{0}_shares.type COUNTER".format(worker_name)) print("worker_{0}_shares.info Accepted shares of worker '{1}'".format(worker_name, val["id"])) print("worker_{0}_shares.min 0".format(worker_name)) print("worker_{0}_shares.draw LINE1".format(worker_name)) for val in worker_data: worker_name = "_".join(val["id"].split()) print("") print("multigraph worker_shares_{0}.worker_{1}".format(account_address, worker_name)) print("graph_title Number of accepted shares {0}".format(worker_name)) print("graph_args --base 1000 -l 0") print("graph_vlabel Shares per ${graph_period}") print("graph_category other") print("graph_scale no") print("graph_period minute") print("wshares.label shares") print("wshares.type COUNTER") print("wshares.info Accepted shares of worker '{0}'".format(val["id"])) print("wshares.min 0") print("wshares.draw LINE1") def write_data_worker(data): worker_data = sorted(data["data"]["workers"], key=lambda element: element["id"]) print("multigraph worker_hashrate_{0}".format(account_address)) for val in worker_data: worker_name = "_".join(val["id"].split()) print("worker_{0}_hashrate.value {1}".format(worker_name, val["hashrate"])) for val in worker_data: print("") worker_name = "_".join(val["id"].split()) print("multigraph worker_hashrate_{0}.worker_{1}".format(account_address, worker_name)) print("whashrate.value {0}".format(val["hashrate"])) print("") print("multigraph worker_shares_{0}".format(account_address)) for val in worker_data: worker_name = "_".join(val["id"].split()) print("worker_{0}_shares.value {1}".format(worker_name, val["rating"])) for val in worker_data: worker_name = "_".join(val["id"].split()) print("") print("multigraph worker_shares_{0}.worker_{1}".format(account_address, worker_name)) print("wshares.value {0} ".format(val["rating"])) def write_config(): if graph_type not in GRAPH_TYPES.keys(): print("Unknown graph type '{0}'".format(graph_type), file=sys.stderr) exit(1) if graph_type == "worker": write_config_worker() return params = GRAPH_TYPES[graph_type] for item in params: print("graph_title {0}".format(item["title"])) print("graph_category other") if "info" in item: print("graph_info {0}".format(item["info"])) if "scale" in item: print("graph_scale {0}".format(item["scale"])) if "args" in item: print("graph_args {0}".format(item["args"])) for field in item["fields"]: print("{0}.label {1}".format(field, field)) print("{0}.type {1}".format(field, item["type"])) def write_suggest(): for item in GRAPH_TYPES.keys(): print(item) def write_data(): data = request_data() if graph_type == "balance": print("ETH.value {0}".format(data['data']['balance'])) elif graph_type == "hashrate": print("hashrate.value {0}".format(data["data"]["hashrate"])) elif graph_type == "avghashrate": print("avg_hashrate.value {0}".format(data["data"]["avgHashrate"]["h1"])) elif graph_type == "worker": write_data_worker(data) else: pass if __name__ == "__main__": program = sys.argv[0] try: graph_type, account_address = program.split("_")[1:] except ValueError: print("Please configure the plugin as described in the configuration section.") exit(1) GRAPH_TYPES = define_graph_types() if len(sys.argv) > 1 and sys.argv[1] == "config": write_config() elif len(sys.argv) > 1 and sys.argv[1] == "suggest": write_suggest() else: write_data()