source: trunk/daemon/togad.py @ 56

Last change on this file since 56 was 56, checked in by bastiaans, 18 years ago

daemon/togad.py:

  • Bugfix
File size: 19.8 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, 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, metricname, metriclist ):
634
635                if not self.lastStored.has_key( host ):
636                        self.lastStored[ host ] = { }
637
638                last_update_time = 0
639
640                for metric in metriclist:
641
642                        if metric['name'] == metricname:
643
644                                if metric['time'] > last_update_time:
645
646                                        last_update_time = metric['time']
647
648                if self.lastStored[ host ].has_key( metricname ):
649                       
650                        if last_update_time <= self.lastStored[ host ][ metricname ]:
651                                return 1
652
653                self.lastStored[ host ][ metricname ] = last_update_time
654
655        def storeMetrics( self ):
656
657                for hostname, mymetrics in self.myMetrics.items():     
658
659                        for metricname, mymetric in mymetrics.items():
660
661                                metrics_to_store = [ ]
662
663                                # <ATOMIC>
664                                #
665                                self.slot.acquire() 
666
667                                while len( self.myMetrics[ hostname ][ metricname ] ) > 0:
668
669                                        if len( self.myMetrics[ hostname ][ metricname ] ) > 0:
670                                                metrics_to_store.append( self.myMetrics[ hostname ][ metricname ].pop( 0 ) )
671
672                                self.slot.release()
673                                #
674                                # </ATOMIC>
675
676                                # Create a mapping table, each metric to the period where it should be stored
677                                #
678                                metric_serial_table = self.determineSerials( hostname, metricname, metrics_to_store )
679
680                                update_rets = [ ]
681
682                                for period, pmetric in metric_serial_table.items():
683
684                                        self.createCheck( hostname, metricname, period )       
685
686                                        update_ret = self.update( hostname, metricname, period, pmetric )
687
688                                        if update_ret == 0:
689
690                                                debug_msg( 9, 'stored metric %s for %s' %( hostname, metricname ) )
691                                        else:
692                                                debug_msg( 9, 'metric update failed' )
693
694                                        update_rets.append( update_ret )
695
696                                if not (1) in update_rets:
697
698                                        self.memLastUpdate( hostname, metricname, metrics_to_store )
699
700        def makeTimeSerial( self ):
701                "Generate a time serial. Seconds since epoch"
702
703                # Seconds since epoch
704                mytime = int( time.time() )
705
706                return mytime
707
708        def makeRrdPath( self, host, metricname, timeserial ):
709                """
710                Make a RRD location/path and filename
711                If a metric or timeserial are supplied the complete locations
712                will be made, else just the host directory
713                """
714
715                rrd_dir = '%s/%s/%s/%s' %( check_dir(ARCHIVE_PATH), self.cluster, host, timeserial )
716                rrd_file = '%s/%s.rrd' %( rrd_dir, metricname )
717
718                return rrd_dir, rrd_file
719
720        def getLastRrdTimeSerial( self, host ):
721                """
722                Find the last timeserial (directory) for this host
723                This is determined once every host
724                """
725
726                newest_timeserial = 0
727
728                for dir in self.timeserials[ host ]:
729
730                        valid_dir = 1
731
732                        for letter in dir:
733                                if letter not in string.digits:
734                                        valid_dir = 0
735
736                        if valid_dir:
737                                timeserial = dir
738                                if timeserial > newest_timeserial:
739                                        newest_timeserial = timeserial
740
741                if newest_timeserial:
742                        return newest_timeserial
743                else:
744                        return 0
745
746        def determinePeriod( self, host, check_serial ):
747
748                period_serial = 0
749
750                if self.timeserials.has_key( host ):
751
752                        for serial in self.timeserials[ host ]:
753
754                                if check_serial >= serial and period_serial < serial:
755
756                                        period_serial = serial
757
758                return period_serial
759
760        def determineSerials( self, host, metricname, metriclist ):
761                """
762                Determine the correct serial and corresponding rrd to store
763                for a list of metrics
764                """
765
766                metric_serial_table = { }
767
768                for metric in metriclist:
769
770                        if metric['name'] == metricname:
771
772                                period = self.determinePeriod( host, metric['time'] )   
773
774                                archive_secs = ARCHIVE_HOURS_PER_RRD * (60 * 60)
775
776                                if (int( metric['time'] ) - int( period ) ) > archive_secs:
777
778                                        # This one should get it's own new period
779                                        period = metric['time']
780                                        self.timeserials[ host ].append( period )
781
782                                if not metric_serial_table.has_key( period ):
783
784                                        metric_serial_table[ period ] = [ ]
785
786                                metric_serial_table[ period ].append( metric )
787
788                return metric_serial_table
789
790        def createCheck( self, host, metricname, timeserial ):
791                "Check if an .rrd allready exists for this metric, create if not"
792
793                debug_msg( 9, 'rrdcreate: using timeserial %s for %s/%s' %( timeserial, host, metricname ) )
794               
795                rrd_dir, rrd_file = self.makeRrdPath( host, metricname, timeserial )
796
797                if not os.path.exists( rrd_dir ):
798                        os.makedirs( rrd_dir )
799                        debug_msg( 9, 'created dir %s' %( str(rrd_dir) ) )
800
801                if not os.path.exists( rrd_file ):
802
803                        interval = self.config.getInterval( self.cluster )
804                        heartbeat = 8 * int( interval )
805
806                        params = [ ]
807
808                        params.append( '--step' )
809                        params.append( str( interval ) )
810
811                        params.append( '--start' )
812                        params.append( str( int( timeserial ) - 1 ) )
813
814                        params.append( 'DS:sum:GAUGE:%d:U:U' %heartbeat )
815                        params.append( 'RRA:AVERAGE:0.5:1:%s' %(ARCHIVE_HOURS_PER_RRD * 240) )
816
817                        self.rrdm.create( str(rrd_file), params )
818
819                        debug_msg( 9, 'created rrd %s' %( str(rrd_file) ) )
820
821        def update( self, host, metricname, timeserial, metriclist ):
822
823                debug_msg( 9, 'rrdupdate: using timeserial %s for %s/%s' %( timeserial, host, metricname ) )
824
825                rrd_dir, rrd_file = self.makeRrdPath( host, metricname, timeserial )
826
827                update_list = self.makeUpdateList( host, metriclist )
828
829                if len( update_list ) > 0:
830                        ret = self.rrdm.update( str(rrd_file), update_list )
831
832                        if ret:
833                                return 1
834               
835                        debug_msg( 9, 'updated rrd %s with %s' %( str(rrd_file), string.join( update_list ) ) )
836
837                return 0
838
839def main():
840        "Program startup"
841
842        myProcessor = GangliaXMLProcessor()
843
844        if DAEMONIZE:
845                myProcessor.daemon()
846        else:
847                myProcessor.run()
848
849def check_dir( directory ):
850        "Check if directory is a proper directory. I.e.: Does _not_ end with a '/'"
851
852        if directory[-1] == '/':
853                directory = directory[:-1]
854
855        return directory
856
857def debug_msg( level, msg ):
858
859        if (DEBUG_LEVEL >= level):
860                sys.stderr.write( msg + '\n' )
861
862def printTime( ):
863        "Print current time in human readable format"
864
865        return time.strftime("%a %d %b %Y %H:%M:%S")
866
867# Let's go
868if __name__ == '__main__':
869        main()
Note: See TracBrowser for help on using the repository browser.