source: trunk/daemon/togad.py @ 53

Last change on this file since 53 was 53, checked in by bastiaans, 19 years ago

daemon/togad.py:

  • Changed store functions, so that they will pop() data off list so experimenting with multiple storethreads can begin
File size: 19.6 KB
Line 
1#!/usr/bin/env python
2
3from xml.sax import make_parser
4from xml.sax.handler import ContentHandler
5import socket
6import sys
7import string
8import os
9import os.path
10import time
11import re
12import threading
13import mutex
14import random
15from types import *
16
17# Specify debugging level here;
18#
19# 11 = XML: metrics
20# 10 = XML: host, cluster, grid, ganglia
21# 9  = RRD activity, gmetad config parsing
22# 8  = RRD file activity
23# 7  = daemon threading
24#
25DEBUG_LEVEL = 7
26
27# Where is the gmetad.conf located
28#
29GMETAD_CONF = '/etc/gmetad.conf'
30
31# Where to store the archived rrd's
32#
33ARCHIVE_PATH = '/data/toga/rrds'
34
35# List of data_source names to archive for
36#
37ARCHIVE_SOURCES = [ "LISA Cluster" ]
38
39# Amount of hours to store in one single archived .rrd
40#
41ARCHIVE_HOURS_PER_RRD = 12
42
43# Wether or not to run as a daemon in background
44#
45DAEMONIZE = 0
46
47######################
48#                    #
49# Configuration ends #
50#                    #
51######################
52
53# What XML data types not to store
54#
55UNSUPPORTED_ARCHIVE_TYPES = [ 'string' ]
56
57# Maximum time (in seconds) a parsethread may run
58#
59PARSE_TIMEOUT = 60
60
61# Maximum time (in seconds) a storethread may run
62#
63STORE_TIMEOUT = 360
64
65"""
66This is TOrque-GAnglia's data Daemon
67"""
68
69class RRDMutator:
70        "A class for handling .rrd mutations"
71
72        binary = '/usr/bin/rrdtool'
73
74        def __init__( self, binary=None ):
75
76                if binary:
77                        self.binary = binary
78
79        def create( self, filename, args ):
80                return self.perform( 'create', '"' + filename + '"', args )
81
82        def update( self, filename, args ):
83                return self.perform( 'update', '"' + filename + '"', args )
84
85        def grabLastUpdate( self, filename ):
86
87                last_update = 0
88
89                debug_msg( 8, self.binary + ' info "' + filename + '"' )
90
91                for line in os.popen( self.binary + ' info "' + filename + '"' ).readlines():
92
93                        if line.find( 'last_update') != -1:
94
95                                last_update = line.split( ' = ' )[1]
96
97                if last_update:
98                        return last_update
99                else:
100                        return 0
101
102        def perform( self, action, filename, args ):
103
104                arg_string = None
105
106                if type( args ) is not ListType:
107                        debug_msg( 8, 'Arguments needs to be of type List' )
108                        return 1
109
110                for arg in args:
111
112                        if not arg_string:
113
114                                arg_string = arg
115                        else:
116                                arg_string = arg_string + ' ' + arg
117
118                debug_msg( 8, 'rrdm.perform(): ' + self.binary + ' ' + action + ' ' + filename + ' ' + arg_string  )
119
120                for line in os.popen( self.binary + ' ' + action + ' ' + filename + ' ' + arg_string ).readlines():
121
122                        if line.find( 'ERROR' ) != -1:
123
124                                error_msg = string.join( line.split( ' ' )[1:] )
125                                debug_msg( 8, error_msg )
126                                return 1
127
128                return 0
129
130class GangliaXMLHandler( ContentHandler ):
131        "Parse Ganglia's XML"
132
133        def __init__( self, config ):
134                self.config = config
135                self.clusters = { }
136                debug_msg( 0, printTime() + ' Checking existing toga rrd archive..' )
137                self.gatherClusters()
138                debug_msg( 0, printTime() + ' Check done.' )
139
140        def gatherClusters( self ):
141
142                archive_dir = check_dir(ARCHIVE_PATH)
143
144                hosts = [ ]
145
146                if os.path.exists( archive_dir ):
147
148                        dirlist = os.listdir( archive_dir )
149
150                        for item in dirlist:
151
152                                clustername = item
153
154                                if not self.clusters.has_key( clustername ) and clustername in ARCHIVE_SOURCES:
155
156                                        self.clusters[ clustername ] = RRDHandler( self.config, clustername )
157
158        def startElement( self, name, attrs ):
159                "Store appropriate data from xml start tags"
160
161                if name == 'GANGLIA_XML':
162
163                        self.XMLSource = attrs.get( 'SOURCE', "" )
164                        self.gangliaVersion = attrs.get( 'VERSION', "" )
165
166                        debug_msg( 10, 'Found XML data: source %s version %s' %( self.XMLSource, self.gangliaVersion ) )
167
168                elif name == 'GRID':
169
170                        self.gridName = attrs.get( 'NAME', "" )
171                        self.time = attrs.get( 'LOCALTIME', "" )
172
173                        debug_msg( 10, '`-Grid found: %s' %( self.gridName ) )
174
175                elif name == 'CLUSTER':
176
177                        self.clusterName = attrs.get( 'NAME', "" )
178                        self.time = attrs.get( 'LOCALTIME', "" )
179
180                        if not self.clusters.has_key( self.clusterName ) and self.clusterName in ARCHIVE_SOURCES:
181
182                                self.clusters[ self.clusterName ] = RRDHandler( self.config, self.clusterName )
183
184                                debug_msg( 10, ' |-Cluster found: %s' %( self.clusterName ) )
185
186                elif name == 'HOST' and self.clusterName in ARCHIVE_SOURCES:     
187
188                        self.hostName = attrs.get( 'NAME', "" )
189                        self.hostIp = attrs.get( 'IP', "" )
190                        self.hostReported = attrs.get( 'REPORTED', "" )
191
192                        debug_msg( 10, ' | |-Host found: %s - ip %s reported %s' %( self.hostName, self.hostIp, self.hostReported ) )
193
194                elif name == 'METRIC' and self.clusterName in ARCHIVE_SOURCES:
195
196                        type = attrs.get( 'TYPE', "" )
197
198                        if type not in UNSUPPORTED_ARCHIVE_TYPES:
199
200                                myMetric = { }
201                                myMetric['name'] = attrs.get( 'NAME', "" )
202                                myMetric['val'] = attrs.get( 'VAL', "" )
203                                myMetric['time'] = self.hostReported
204
205                                self.clusters[ self.clusterName ].memMetric( self.hostName, myMetric )
206
207                                debug_msg( 11, ' | | |-metric: %s:%s' %( myMetric['name'], myMetric['val'] ) )
208
209        def storeMetrics( self ):
210
211                for clustername, rrdh in self.clusters.items():
212
213                        ret = rrdh.storeMetrics()
214
215                        if ret:
216                                debug_msg( 9, 'An error occured while storing metrics for cluster %s' %clustername )
217                                return 1
218
219                return 0
220
221class GangliaXMLGatherer:
222        "Setup a connection and file object to Ganglia's XML"
223
224        s = None
225
226        def __init__( self, host, port ):
227                "Store host and port for connection"
228
229                self.host = host
230                self.port = port
231                self.connect()
232
233        def connect( self ):
234                "Setup connection to XML source"
235
236                for res in socket.getaddrinfo( self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM ):
237
238                        af, socktype, proto, canonname, sa = res
239
240                        try:
241
242                                self.s = socket.socket( af, socktype, proto )
243
244                        except socket.error, msg:
245
246                                self.s = None
247                                continue
248
249                        try:
250
251                                self.s.connect( sa )
252
253                        except socket.error, msg:
254
255                                self.s.close()
256                                self.s = None
257                                continue
258
259                        break
260
261                if self.s is None:
262
263                        debug_msg( 0, 'Could not open socket' )
264                        sys.exit( 1 )
265
266        def disconnect( self ):
267                "Close socket"
268
269                if self.s:
270                        self.s.close()
271                        self.s = None
272
273        def __del__( self ):
274                "Kill the socket before we leave"
275
276                self.disconnect()
277
278        def getFileObject( self ):
279                "Connect, and return a file object"
280
281                if self.s:
282                        # Apearantly, only data is received when a connection is made
283                        # therefor, disconnect and connect
284                        #
285                        self.disconnect()
286                        self.connect()
287
288                return self.s.makefile( 'r' )
289
290class GangliaXMLProcessor:
291
292        def __init__( self ):
293                "Setup initial XML connection and handlers"
294
295                self.config = GangliaConfigParser( GMETAD_CONF )
296
297                self.myXMLGatherer = GangliaXMLGatherer( 'localhost', 8651 ) 
298                self.myParser = make_parser()   
299                self.myHandler = GangliaXMLHandler( self.config )
300                self.myParser.setContentHandler( self.myHandler )
301
302        def daemon( self ):
303                "Run as daemon forever"
304
305                self.DAEMON = 1
306
307                # Fork the first child
308                #
309                pid = os.fork()
310
311                if pid > 0:
312
313                        sys.exit(0)  # end parent
314
315                # creates a session and sets the process group ID
316                #
317                os.setsid()
318
319                # Fork the second child
320                #
321                pid = os.fork()
322
323                if pid > 0:
324
325                        sys.exit(0)  # end parent
326
327                # Go to the root directory and set the umask
328                #
329                os.chdir('/')
330                os.umask(0)
331
332                sys.stdin.close()
333                sys.stdout.close()
334                #sys.stderr.close()
335
336                os.open('/dev/null', 0)
337                os.dup(0)
338                os.dup(0)
339
340                self.run()
341
342        def printTime( self ):
343                "Print current time in human readable format"
344
345                return time.strftime("%a %d %b %Y %H:%M:%S")
346
347        def run( self ):
348                "Main thread"
349
350                xmlthread = threading.Thread( None, self.processXML, 'xmlthread' )
351                storethread = threading.Thread( None, self.storeMetrics, 'storethread' )
352
353                while( 1 ):
354
355                        if not xmlthread.isAlive():
356                                # Gather XML at the same interval as gmetad
357
358                                # threaded call to: self.processXML()
359                                #
360                                xmlthread = threading.Thread( None, self.processXML, 'xmlthread' )
361                                xmlthread.start()
362
363                        if not storethread.isAlive():
364                                # Store metrics every .. sec
365
366                                # threaded call to: self.storeMetrics()
367                                #
368                                storethread = threading.Thread( None, self.storeMetrics, 'storethread' )
369                                storethread.start()
370               
371                        # Just sleep a sec here, to prevent daemon from going mad. We're all threads here anyway
372                        time.sleep( 1 ) 
373
374        def storeMetrics( self ):
375                "Store metrics retained in memory to disk"
376
377                debug_msg( 7, self.printTime() + ' - storethread(): started.' )
378
379                # Store metrics somewhere between every 60 and 180 seconds
380                #
381                STORE_INTERVAL = random.randint( 360, 640 )
382
383                storethread = threading.Thread( None, self.storeThread, 'storemetricthread' )
384                storethread.start()
385
386                debug_msg( 7, self.printTime() + ' - storethread(): Sleeping.. (%ss)' %STORE_INTERVAL )
387                time.sleep( STORE_INTERVAL )
388                debug_msg( 7, self.printTime() + ' - storethread(): Done sleeping.' )
389
390                if storethread.isAlive():
391
392                        debug_msg( 7, self.printTime() + ' - storethread(): storemetricthread() still running, waiting to finish..' )
393                        storethread.join( STORE_TIMEOUT ) # Maximum time is for storing thread to finish
394                        debug_msg( 7, self.printTime() + ' - storethread(): Done waiting.' )
395
396                debug_msg( 7, self.printTime() + ' - storethread(): finished.' )
397
398                return 0
399
400        def storeThread( self ):
401
402                debug_msg( 7, self.printTime() + ' - storemetricthread(): started.' )
403                debug_msg( 7, self.printTime() + ' - storemetricthread(): Storing data..' )
404                ret = self.myHandler.storeMetrics()
405                debug_msg( 7, self.printTime() + ' - storemetricthread(): Done storing.' )
406                debug_msg( 7, self.printTime() + ' - storemetricthread(): finished.' )
407               
408                return ret
409
410        def processXML( self ):
411                "Process XML"
412
413                debug_msg( 7, self.printTime() + ' - xmlthread(): started.' )
414
415                parsethread = threading.Thread( None, self.parseThread, 'parsethread' )
416                parsethread.start()
417
418                debug_msg( 7, self.printTime() + ' - xmlthread(): Sleeping.. (%ss)' %self.config.getLowestInterval() )
419                time.sleep( float( self.config.getLowestInterval() ) ) 
420                debug_msg( 7, self.printTime() + ' - xmlthread(): Done sleeping.' )
421
422                if parsethread.isAlive():
423
424                        debug_msg( 7, self.printTime() + ' - xmlthread(): parsethread() still running, waiting to finish..' )
425                        parsethread.join( PARSE_TIMEOUT ) # Maximum time for XML thread to finish
426                        debug_msg( 7, self.printTime() + ' - xmlthread(): Done waiting.' )
427
428                debug_msg( 7, self.printTime() + ' - xmlthread(): finished.' )
429
430                return 0
431
432        def parseThread( self ):
433
434                debug_msg( 7, self.printTime() + ' - parsethread(): started.' )
435                debug_msg( 7, self.printTime() + ' - parsethread(): Parsing XML..' )
436                ret = self.myParser.parse( self.myXMLGatherer.getFileObject() )
437                debug_msg( 7, self.printTime() + ' - parsethread(): Done parsing.' )
438                debug_msg( 7, self.printTime() + ' - parsethread(): finished.' )
439
440                return ret
441
442class GangliaConfigParser:
443
444        sources = [ ]
445
446        def __init__( self, config ):
447
448                self.config = config
449                self.parseValues()
450
451        def parseValues( self ):
452                "Parse certain values from gmetad.conf"
453
454                readcfg = open( self.config, 'r' )
455
456                for line in readcfg.readlines():
457
458                        if line.count( '"' ) > 1:
459
460                                if line.find( 'data_source' ) != -1 and line[0] != '#':
461
462                                        source = { }
463                                        source['name'] = line.split( '"' )[1]
464                                        source_words = line.split( '"' )[2].split( ' ' )
465
466                                        for word in source_words:
467
468                                                valid_interval = 1
469
470                                                for letter in word:
471
472                                                        if letter not in string.digits:
473
474                                                                valid_interval = 0
475
476                                                if valid_interval and len(word) > 0:
477
478                                                        source['interval'] = word
479                                                        debug_msg( 9, 'polling interval for %s = %s' %(source['name'], source['interval'] ) )
480       
481                                        # No interval found, use Ganglia's default     
482                                        if not source.has_key( 'interval' ):
483                                                source['interval'] = 15
484                                                debug_msg( 9, 'polling interval for %s defaulted to 15' %(source['name']) )
485
486                                        self.sources.append( source )
487
488        def getInterval( self, source_name ):
489
490                for source in self.sources:
491
492                        if source['name'] == source_name:
493
494                                return source['interval']
495
496                return None
497
498        def getLowestInterval( self ):
499
500                lowest_interval = 0
501
502                for source in self.sources:
503
504                        if not lowest_interval or source['interval'] <= lowest_interval:
505
506                                lowest_interval = source['interval']
507
508                # Return 15 when nothing is found, so that the daemon won't go insane with 0 sec delays
509                if lowest_interval:
510                        return lowest_interval
511                else:
512                        return 15
513
514class RRDHandler:
515
516        myMetrics = { }
517        lastStored = { }
518        timeserials = { }
519        slot = None
520
521        def __init__( self, config, cluster ):
522                self.block = 0
523                self.cluster = cluster
524                self.config = config
525                self.slot = threading.Lock()
526                self.rrdm = RRDMutator()
527                self.gatherLastUpdates()
528
529        def gatherLastUpdates( self ):
530                "Populate the lastStored list, containing timestamps of all last updates"
531
532                cluster_dir = '%s/%s' %( check_dir(ARCHIVE_PATH), self.cluster )
533
534                hosts = [ ]
535
536                if os.path.exists( cluster_dir ):
537
538                        dirlist = os.listdir( cluster_dir )
539
540                        for dir in dirlist:
541
542                                hosts.append( dir )
543
544                for host in hosts:
545
546                        host_dir = cluster_dir + '/' + host
547                        dirlist = os.listdir( host_dir )
548
549                        for dir in dirlist:
550
551                                if not self.timeserials.has_key( host ):
552
553                                        self.timeserials[ host ] = [ ]
554
555                                self.timeserials[ host ].append( dir )
556
557                        last_serial = self.getLastRrdTimeSerial( host )
558                        if last_serial:
559
560                                metric_dir = cluster_dir + '/' + host + '/' + last_serial
561                                if os.path.exists( metric_dir ):
562
563                                        dirlist = os.listdir( metric_dir )
564
565                                        for file in dirlist:
566
567                                                metricname = file.split( '.rrd' )[0]
568
569                                                if not self.lastStored.has_key( host ):
570
571                                                        self.lastStored[ host ] = { }
572
573                                                self.lastStored[ host ][ metricname ] = self.rrdm.grabLastUpdate( metric_dir + '/' + file )
574
575        def getClusterName( self ):
576                return self.cluster
577
578        def memMetric( self, host, metric ):
579
580                if self.myMetrics.has_key( host ):
581
582                        if self.myMetrics[ host ].has_key( metric['name'] ):
583
584                                for mymetric in self.myMetrics[ host ][ metric['name'] ]:
585
586                                        if mymetric['time'] == metric['time']:
587
588                                                # Allready have this metric, abort
589                                                return 1
590                        else:
591                                self.myMetrics[ host ][ metric['name'] ] = [ ]
592                else:
593                        self.myMetrics[ host ] = { }
594                        self.myMetrics[ host ][ metric['name'] ] = [ ]
595
596                # <ATOMIC>
597                #
598                self.slot.acquire()
599
600                self.myMetrics[ host ][ metric['name'] ].append( metric )
601
602                self.slot.release()
603                #
604                # </ATOMIC>
605
606        def makeUpdateList( self, host, metriclist ):
607
608                update_list = [ ]
609                metric = None
610
611                while len( metriclist ) > 0:
612
613                        metric = metriclist.pop( 0 )
614
615                        if self.checkStoreMetric( host, metric ):
616                                update_list.append( '%s:%s' %( metric['time'], metric['val'] ) )
617
618                return update_list
619
620        def checkStoreMetric( self, host, metric ):
621
622                if self.lastStored.has_key( host ):
623
624                        if self.lastStored[ host ].has_key( metric['name'] ):
625
626                                if metric['time'] <= self.lastStored[ host ][ metric['name'] ]:
627
628                                        # This is old
629                                        return 0
630
631                return 1
632
633        def memLastUpdate( self, host, metriclist ):
634
635                last_update_time = 0
636
637                for metric in metriclist:
638
639                        if metric['time'] > last_update_time:
640
641                                last_update_time = metric['time']
642
643                if not self.lastStored.has_key( host ):
644                        self.lastStored[ host ] = { }
645
646                if self.lastStored[ host ].has_key( metric['name'] ):
647                       
648                        if last_update_time <= self.lastStored[ host ][ metric['name'] ]:
649                                return 1
650
651                self.lastStored[ host ][ metric['name'] ] = last_update_time
652
653        def storeMetrics( self ):
654
655                for hostname, mymetrics in self.myMetrics.items():     
656
657                        for metricname, mymetric in mymetrics.items():
658
659                                metrics_to_store = [ ]
660
661                                # <ATOMIC>
662                                #
663                                self.slot.acquire() 
664
665                                while len( mymetric ) > 0:
666
667                                        metrics_to_store.append( mymetric.pop( 0 ) )
668
669                                self.slot.release()
670                                #
671                                # </ATOMIC>
672
673                                # Create a mapping table, each metric to the period where it should be stored
674                                #
675                                metric_serial_table = self.determineSerials( hostname, metricname, metrics_to_store )
676                                self.myMetrics[ hostname ][ metricname ] = [ ]
677
678                                update_rets = [ ]
679
680                                for period, pmetric in metric_serial_table.items():
681
682                                        self.createCheck( hostname, metricname, period )       
683
684                                        update_ret = self.update( hostname, metricname, period, pmetric )
685
686                                        if update_ret == 0:
687
688                                                debug_msg( 9, 'stored metric %s for %s' %( hostname, metricname ) )
689                                        else:
690                                                debug_msg( 9, 'metric update failed' )
691
692                                        update_rets.append( update_ret )
693
694                                if not (1) in update_rets:
695
696                                        self.memLastUpdate( hostname, mymetric )
697
698        def makeTimeSerial( self ):
699                "Generate a time serial. Seconds since epoch"
700
701                # Seconds since epoch
702                mytime = int( time.time() )
703
704                return mytime
705
706        def makeRrdPath( self, host, metricname, timeserial ):
707                """
708                Make a RRD location/path and filename
709                If a metric or timeserial are supplied the complete locations
710                will be made, else just the host directory
711                """
712
713                rrd_dir = '%s/%s/%s/%s' %( check_dir(ARCHIVE_PATH), self.cluster, host, timeserial )
714                rrd_file = '%s/%s.rrd' %( rrd_dir, metricname )
715
716                return rrd_dir, rrd_file
717
718        def getLastRrdTimeSerial( self, host ):
719                """
720                Find the last timeserial (directory) for this host
721                This is determined once every host
722                """
723
724                newest_timeserial = 0
725
726                for dir in self.timeserials[ host ]:
727
728                        valid_dir = 1
729
730                        for letter in dir:
731                                if letter not in string.digits:
732                                        valid_dir = 0
733
734                        if valid_dir:
735                                timeserial = dir
736                                if timeserial > newest_timeserial:
737                                        newest_timeserial = timeserial
738
739                if newest_timeserial:
740                        return newest_timeserial
741                else:
742                        return 0
743
744        def determinePeriod( self, host, check_serial ):
745
746                period_serial = 0
747
748                for serial in self.timeserials[ host ]:
749
750                        if check_serial >= serial and period_serial < serial:
751
752                                period_serial = serial
753
754                return period_serial
755
756        def determineSerials( self, host, metricname, metriclist ):
757                """
758                Determine the correct serial and corresponding rrd to store
759                for a list of metrics
760                """
761
762                metric_serial_table = { }
763
764                for metric in metriclist:
765
766                        if metric['name'] == metricname:
767
768                                period = self.determinePeriod( host, metric['time'] )   
769
770                                archive_secs = ARCHIVE_HOURS_PER_RRD * (60 * 60)
771
772                                if (int( metric['time'] ) - int( period ) ) > archive_secs:
773
774                                        # This one should get it's own new period
775                                        period = metric['time']
776                                        self.timeserials[ host ].append( period )
777
778                                if not metric_serial_table.has_key( period ):
779
780                                        metric_serial_table[ period ] = [ ]
781
782                                metric_serial_table[ period ].append( metric )
783
784                return metric_serial_table
785
786        def createCheck( self, host, metricname, timeserial ):
787                "Check if an .rrd allready exists for this metric, create if not"
788
789                debug_msg( 9, 'rrdcreate: using timeserial %s for %s/%s' %( timeserial, host, metricname ) )
790               
791                rrd_dir, rrd_file = self.makeRrdPath( host, metricname, timeserial )
792
793                if not os.path.exists( rrd_dir ):
794                        os.makedirs( rrd_dir )
795                        debug_msg( 9, 'created dir %s' %( str(rrd_dir) ) )
796
797                if not os.path.exists( rrd_file ):
798
799                        interval = self.config.getInterval( self.cluster )
800                        heartbeat = 8 * int( interval )
801
802                        params = [ ]
803
804                        params.append( '--step' )
805                        params.append( str( interval ) )
806
807                        params.append( '--start' )
808                        params.append( str( int( timeserial ) - 1 ) )
809
810                        params.append( 'DS:sum:GAUGE:%d:U:U' %heartbeat )
811                        params.append( 'RRA:AVERAGE:0.5:1:%s' %(ARCHIVE_HOURS_PER_RRD * 240) )
812
813                        self.rrdm.create( str(rrd_file), params )
814
815                        debug_msg( 9, 'created rrd %s' %( str(rrd_file) ) )
816
817        def update( self, host, metricname, timeserial, metriclist ):
818
819                debug_msg( 9, 'rrdupdate: using timeserial %s for %s/%s' %( timeserial, host, metricname ) )
820
821                rrd_dir, rrd_file = self.makeRrdPath( host, metricname, timeserial )
822
823                update_list = self.makeUpdateList( host, metriclist )
824
825                if len( update_list ) > 0:
826                        ret = self.rrdm.update( str(rrd_file), update_list )
827
828                        if ret:
829                                return 1
830               
831                        debug_msg( 9, 'updated rrd %s with %s' %( str(rrd_file), string.join( update_list ) ) )
832
833                return 0
834
835def main():
836        "Program startup"
837
838        myProcessor = GangliaXMLProcessor()
839
840        if DAEMONIZE:
841                myProcessor.daemon()
842        else:
843                myProcessor.run()
844
845def check_dir( directory ):
846        "Check if directory is a proper directory. I.e.: Does _not_ end with a '/'"
847
848        if directory[-1] == '/':
849                directory = directory[:-1]
850
851        return directory
852
853def debug_msg( level, msg ):
854
855        if (DEBUG_LEVEL >= level):
856                sys.stderr.write( msg + '\n' )
857
858def printTime( ):
859        "Print current time in human readable format"
860
861        return time.strftime("%a %d %b %Y %H:%M:%S")
862
863# Let's go
864if __name__ == '__main__':
865        main()
Note: See TracBrowser for help on using the repository browser.