#!/usr/bin/python

# ganglia_hack.py (c) 12.apr.2006 Antti.Vanne@iki.fi
# extended by Nathan Bijnens <nathan@nathan.gs>
# Description:
# Quick hack to use ganglia in a cluster with a non-multicasting
# switch. Doesn't scale too well, usable on a 24 cluster though.
# Idea: listen to port _serverPort (8666 as default) for gmetad's
# request for XML data and when connected, gather XML from
# individual nodes from port _gmondport and serve it to gmetad.

# Originally a perl script now rewritten in Python

import sre
import SocketServer
import socket
import argparse

class queryGmond:
  """Query individual nodes for gmond data and optionally gmond header"""
  def __init__(self, nodeip, gmondport, queryHeader = False):
    self.nodeip = nodeip
    self.nodedata = ""
    self.gmondport = gmondport
    self.queryHeader = queryHeader
    self.header = ""
    self.datapat = sre.compile("<HOST NAME=\"[\w\.]+\" IP=\"%s\".*?</HOST>" % self.nodeip, sre.DOTALL)
    self.headerpat = sre.compile("<\?xml version.*?<CLUSTER NAME=.*?>", sre.DOTALL)

  def readData(self):
    bufsize = 100000
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((self.nodeip, self.gmondport))
    data = ""
    incoming = s.recv(bufsize)
    while (incoming != ""):
      data += incoming;
      incoming = s.recv(bufsize)
    s.close()
    return data


  def run(self):
    try:
      data = self.readData()
      # extract information concerning this node from the data
      self.nodedata = sre.findall(self.datapat, data)[0]
      if (self.queryHeader):
        # extract header
        self.header = sre.findall(self.headerpat, data)[0]

    except:
      print "Error fetching nodedata from node %s, port %d" % (self.nodeip, self.gmondport)

class reqHandler(SocketServer.StreamRequestHandler):
  """Handle serve requests: run all queryGmond objects"""
  
  def setup(self):
    self.qos = []
    with_header = True
    
    for host, port in self.nodes.iteritems():
      self.qos.append(queryGmond(host, gmondport = port, queryHeader = with_header))
      with_header = False
      
    self.wfile = self.request.makefile("wb", 0)
  def handle(self):
    self.qos[0].run()
    #self.qos[0].join()
    self.wfile.write(self.qos[0].header)
    self.wfile.write(self.qos[0].nodedata)

    for qo in self.qos[1:]: # run and wait queries this is faster
      qo.run()
      #qo.join()
      self.wfile.write(qo.nodedata)

    self.wfile.write("</CLUSTER>\n</GANGLIA_XML>\n")
  def finish(self):
    self.wfile.flush()
    self.wfile.close()

class CliConfiguration:
  def __init__(self):
    
    argParser = argparse.ArgumentParser(description="Ganglia Proxy")
    argParser.add_argument('nodes', nargs='+', metavar='nodes', help="Specify the nodes.")
    argParser.add_argument('--server-port', dest='server_port', default=8666, type=int )
    argParser.add_argument('--default-port', dest='default_port', default=8649, type=int )
    
    args = argParser.parse_args()
    
    self.nodes = self._parse_nodes(args.nodes, args.default_port)
    self.server_port = args.server_port
    
  def _parse_nodes(self, nodes, default_port):
    nodes_with_ports = {}
    
    for node in nodes:
      host, port = node.split(':')
      if(len(port) > 0):
        nodes_with_ports[host] = port
      else:
        nodes_with_ports[host] = default_port
    
    return nodes_with_ports

  
  
configuration = CliConfiguration()
reqHandler.nodes = configuration.nodes

s = SocketServer.TCPServer( ("", configuration.server_port), reqHandler)
s.serve_forever()


@@ -504,19 +504,8 @@
 on%0A%0A
-import sre%0A
 impo
@@ -549,16 +549,31 @@
 argparse
+%0Aimport libxml2
 %0A%0Aclass 
@@ -700,29 +700,8 @@
 port
-, queryHeader = False
 ):%0A 
@@ -728,31 +728,8 @@
 eip%0A
-    self.nodedata = %22%22%0A
     
@@ -758,251 +758,8 @@
 port
-%0A    self.queryHeader = queryHeader%0A    self.header = %22%22%0A    self.datapat = sre.compile(%22%3CHOST NAME=%5C%22%5B%5Cw%5C.%5D+%5C%22 IP=%5C%22%25s%5C%22.*?%3C/HOST%3E%22 %25 self.nodeip, sre.DOTALL)%0A    self.headerpat = sre.compile(%22%3C%5C?xml version.*?%3CCLUSTER NAME=.*?%3E%22, sre.DOTALL)
 %0A%0A  
@@ -1100,264 +1100,30 @@
     
-data = self.readData()%0A      # extract information concerning this node from the data%0A      self.nodedata = sre.findall(self.datapat, data)%5B0%5D%0A      if (self.queryHeader):%0A        # extract header%0A        self.header = sre.findall(self.headerpat, data)%5B0%5D%0A
+return self.readData()
 %0A   
@@ -1379,31 +1379,8 @@
  %5B%5D%0A
-    with_header = True%0A
     
@@ -1485,66 +1485,11 @@
 port
-, queryHeader = with_header))%0A      with_header = False%0A  
+))%0A
     
@@ -1537,16 +1537,21 @@
 wb%22, 0)%0A
+    %0A
   def ha
@@ -1570,134 +1570,39 @@
     
-self.qos%5B0%5D.run()%0A    #self.qos%5B0%5D.join()%0A    self.wfile.write(self.qos%5B0%5D.header)%0A    self.wfile.write(self.qos%5B0%5D.nodedata)%0A
+doc = None%0A    root = None%0A    
 %0A   
@@ -1624,12 +1624,8 @@
 .qos
-%5B1:%5D
 : # 
@@ -1670,16 +1670,42 @@
     
+newdoc = libxml2.parseDoc(
 qo.run()
 %0A   
@@ -1704,115 +1704,283 @@
 un()
+)
 %0A      
-#qo.join()%0A      self.wfile.write(qo.nodedata)%0A%0A    self.wfile.write(%22%3C/CLUSTER%3E%5Cn%3C/GANGLIA_XML%3E%5Cn%22)
+newroot = newdoc.getRootElement()%0A      %0A      if newroot:%0A        if not root:%0A          doc = newdoc%0A          root = newroot%0A        else:%0A          root.addChildList(newroot.children.copyNodeList())%0A          newdoc.freeDoc()%0A      %0A    self.wfile.write(doc)%0A    
 %0A  d
@@ -2783,20 +2783,25 @@
 host%5D = 
+int(
 port
+)
 %0A      e
@@ -2838,16 +2838,20 @@
 host%5D = 
+int(
 default_
@@ -2850,24 +2850,25 @@
 default_port
+)
 %0A    %0A    re

switch to libxml2 instead of regex