#!/usr/bin/python
# 
# Copyright (C) Rodolphe Franceschi
# 
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
# 
# 
# This plugin monitors a SOLR server configured for multicore by automaticaly
# getting core names from SOLR default page.
# 
# Tested on SOLR 1.4.0
# 
# Parameters:
# 	config   (required)
# 	autoconf (optional - used by munin-config)
#
# For the full list of options, refer to PLUGINOPTIONSLIST variable
# 
# Example of symlink creation on Debian Lenny
#   ln -s /usr/share/munin/plugins/solrmulticore_ /etc/munin/plugins/solrmulticore_avgRequestsPerSecond
#  
# Magic markers (Used by munin-config and some installation scripts.
# Optional):
#%# family=auto
#%# capabilities=autoconf

import sys, os 
import urllib2
import HTMLParser, urllib

try:
    from xml.etree import cElementTree as ET
except ImportError:
    try:
        import cElementTree as ET
    except ImportError:
        sys.exit(1)


## GLOBALS
SOLR_PORT = 8983
SOLR_HOST = "localhost"
PLUGINOPTIONSLIST = {
  "avgRequestsPerSecond" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Average requests per second' },
  "avgTimePerRequest" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Average time per request' },
  "errors" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Number of errors' },
  "timeouts" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Number of timeouts' },
  "requests" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Total number of requests' },
  "numDocs" : { 'xmlpath': '/solr-info/CORE/entry', 'xmlparententryname': 'searcher', 'label': 'Number of documents' },
#  "" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler' },
}

# Automatic extraction of core names from SOLR default page if empty array
SOLR_CORES = []
# If you do not want automatic gathering, feel free to put your array manually
#SOLR_CORES = [ "core0", "core1" ]


## FUNCTIONS
def getSolrCoreNameList():
    url = "http://%s:%s/solr/" % (SOLR_HOST, SOLR_PORT)
    class linkParser(HTMLParser.HTMLParser):
        def __init__(self):
            HTMLParser.HTMLParser.__init__(self)
            self.links = []
        def handle_starttag(self, tag, attrs):
            if tag=='a':
                self.links.append(dict(attrs)['href'])

    htmlSource = urllib.urlopen(url).read(200000)
    p = linkParser()
    p.feed(htmlSource)

    # Remove link to . (First one !)
    p.links.pop(0)

    dealcores = [ ]

    for link in p.links:
        dealcores.append ( link.split("/")[0] )

    return dealcores



def parseArgs():
    "Parses the name of the file "
    parts = sys.argv[0].split("_")

    params = { }
    params['valueName'] = parts[1]
    
    # Automatic / Manual Mode for core names
    if (len(SOLR_CORES) == 0):
        params['cores'] = getSolrCoreNameList()
    else:
        params['cores'] = SOLR_CORES

    params['cores'].sort()

    return params

# For multicore / Solr 1.3, URL is like that
def fetchUrl(core, xmlPath):
    URL="http://%s:%d/solr/%s/admin/stats.jsp"

    URLFULL = URL % (SOLR_HOST, SOLR_PORT, core)
    response = urllib2.urlopen(URLFULL)
    return parseXmlResponse(response, xmlPath)

def parseXmlResponse(response, xmlPath):
    root = ET.parse(response)
    queues = root.findall(xmlPath)
    return queues

#def fetchFile():
#    f = open("/tmp/stats.jsp.html")
#    return parseXmlResponse(f)

def getEntry(entries, entryName):
    for entry in entries:
        name = entry.findtext("name").strip()
        if (name != entryName):
            continue
        return entry.find("stats")

def getValue(entry, valueName):
    for stat in entry:
        if stat.get('name') == valueName:
            return stat.text
    #print "Could not find %s for entry" % valueName
    return 0




## MAIN
if len(sys.argv) > 1:
    if sys.argv[1]== "autoconf":
        # check connection
        sys.exit(0)
    elif sys.argv[1] == "config":
        params = parseArgs()

        # Extracting Generic graph datas
        print 'graph_title %s' % ( PLUGINOPTIONSLIST[params['valueName']]['label'] )
        print "graph_args --base 1000";
        print 'graph_vlabel Size %s' % params['valueName']
        print 'graph_category Solr'
        print 'graph_info Info for cores: %s' % ( ",".join(params['cores']) )

        # Iterations for core datas
        for core in params['cores']:
            #print core, params['valueName']
            print "%s.label %s" % (core, core)
            print "%s.type GAUGE" % (core)
            print "%s.min 0" % (core)
        sys.exit(0)


params = parseArgs()
for core in params['cores']:
    #print core, params['valueName']
    queues = fetchUrl(core, PLUGINOPTIONSLIST[params['valueName']]['xmlpath']) 
    searcher = getEntry(queues,  PLUGINOPTIONSLIST[params['valueName']]['xmlparententryname'])
    value = getValue(searcher, params['valueName']).strip()
    print "%s.value %s" % (core, value)

sys.exit(0)
