Changeset 914

Show
Ignore:
Timestamp:
05/13/2008 03:13:27 AM (8 months ago)
Author:
chris
Message:

Improved packaging by including ez_setup.py, configured a console-script entry point, included config-sample and docs and added a README.txt.

version = '0.37.1svn'

Location:
eddie/trunk
Files:
3 added
3 modified
1 moved

Legend:

Unmodified
Added
Removed
  • eddie/trunk/bin/eddie-agent

    r903 r914  
    11#! /usr/bin/env python 
    22 
    3 ''' 
    4 File         : eddie-agent  
     3"""Command-line entry point for use during development. 
     4""" 
    55 
    6 Start Date   : 19971204  
    7  
    8 Description  : EDDIE Tool main startup module.  This script can be run 
    9   from the command-line, handles argument parsing, and initialises 
    10   all objects and threads required to startup an instance of EDDIE Tool. 
    11  
    12   All other modules will be imported from the eddietool package. 
    13  
    14 $Id$ 
    15 ''' 
    16  
    17  
    18 __copyright__ = 'Copyright (c) Chris Miles 2001-2007' 
    19  
    20 __author__ = 'Chris Miles; Rod Telford' 
    21  
    22 __url__ = 'http://eddie-tool.net/' 
    23  
    24 __license__ = ''' 
    25     This program is free software; you can redistribute it and/or modify 
    26     it under the terms of the GNU General Public License as published by 
    27     the Free Software Foundation; either version 2 of the License, or 
    28     (at your option) any later version. 
    29  
    30     This program is distributed in the hope that it will be useful, 
    31     but WITHOUT ANY WARRANTY; without even the implied warranty of 
    32     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    33     GNU General Public License for more details. 
    34  
    35     You should have received a copy of the GNU General Public License 
    36     along with this program; if not, write to the Free Software 
    37     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
    38 ''' 
    39  
    40 from eddietool.version import version as __version__ 
    41  
    42 # Python modules 
    43 import sys 
    44 import os 
    45 import time 
    46 import signal 
    47 import re 
    48 import threading 
    49 # optparse is only available in 2.3+, but optik provides the same  
    50 # functionality for python 2.2 
    51 try: 
    52     import optparse 
    53 except ImportError: 
    54     try: 
    55         import optik as optparse 
    56     except ImportError: 
    57         sys.stderr.write("Error: EDDIE requires Optik on Python 2.2.x (http://optik.sf.net)\n") 
    58         sys.exit(1) 
    59  
    60  
    61 # Work out the base EDDIE directory which should contain bin/, lib/, etc... 
    62 # cwd = os.getcwd() 
    63 # ewd = os.path.split(sys.argv[0])[0] 
    64 # fullp = os.path.join(cwd, ewd) 
    65 # basedir = os.path.join(fullp, '..') 
    66 # basedir = os.path.normpath(basedir) 
    67  
    68 # Determine system type 
    69 try: 
    70     import platform 
    71 except ImportError: 
    72     try: 
    73         uname = os.uname() 
    74     except AttributeError,err: 
    75         raise Exception( "Cannot determine platform: %s" %(err) ) 
    76     else: 
    77         osname = uname[0] 
    78         osver = uname[2] 
    79         osarch = uname[4] 
    80 else: 
    81     osname = platform.uname()[0] 
    82     osver = platform.uname()[2] 
    83     osarch = '' 
    84  
    85 systype = "%s/%s/%s" % (osname,osver,osarch) 
    86 #print "systype:", systype 
    87   
    88      
    89 # oslibdirs = [ os.path.join(basedir,'lib',osname,osver,osarch), 
    90 #               os.path.join(basedir,'lib',osname,osarch,osver), 
    91 #               os.path.join(basedir,'lib',osname,osver), 
    92 #               os.path.join(basedir,'lib',osname,osarch), 
    93 #               os.path.join(basedir,'lib',osname) ] 
    94  
    95 # commonlibdir = os.path.join(basedir, 'lib/common') 
    96 # chris 2004-09-02: lib/common/Extra/ holds 3rd party modules 
    97 # extralibdir = os.path.join(basedir, 'lib/common/Extra') 
    98 # sys.path = oslibdirs + [commonlibdir,extralibdir] + sys.path 
    99  
    100 # EDDIE common modules 
    101 from eddietool.common import parseConfig, directive, config, log, timeQueue, sockets, eddieElvin4, datacollect, utils, eddieSpread 
    102  
    103 # Main config file - this file INCLUDEs all other config files 
    104 # We set the default here, but this can be overridden on the command line 
    105 # configdir = os.path.join(basedir, 'config') 
    106 # default_config_file = os.path.join(configdir, 'eddie.cf') 
    107 # config_file = default_config_file 
    108  
    109 # Globals 
    110 global Config 
    111 global Options 
    112 global sthread 
    113 global cthread 
    114  
    115 # Read directive definitions from lib/common/Directives/ 
    116 import eddietool.common.Directives 
    117 config.loadExtraDirectives(eddietool.common.Directives.__path__[0]) 
    118  
    119 # Read system specific directives 
    120 # This is for directive modules in lib/<system>/Directives/ if it exists 
    121 #  for any <system>. 
    122 # for pth in oslibdirs: 
    123 #     subdir = os.path.join(pth, "Directives") 
    124 #     if os.path.isdir(subdir): 
    125 #         config.loadExtraDirectives(subdir) 
    126  
    127  
    128 def start_threads(sargs, cargs): 
    129     """Start any support threads that are required. 
    130     Currently these are: 
    131      - Scheduler thread: schedules directives to run [required] 
    132      - Console Server thread: handles connections to console port [optional] 
    133     """ 
    134  
    135     please_die.clear()                # reset thread signal 
    136  
    137     global sthread                # the Scheduler thread 
    138     sthread = threading.Thread(group=None, target=scheduler, name='Scheduler', args=sargs, kwargs={}) 
    139     sthread.start()                 # start the thread running 
    140  
    141     global cthread                # the Console Server thread 
    142     if config.consport > 0:        # don't start if CONSPORT=0 
    143         cthread = threading.Thread(group=None, target=sockets.console_server_thread, name='Console', args=cargs, kwargs={}) 
    144         cthread.setDaemon(1)        # mark thread as Daemon-thread so EDDIE will not block when trying to terminate 
    145         cthread.start() 
    146  
    147     return() 
    148  
    149  
    150 def stop_threads(): 
    151     """Stop any threads started by start_threads(). 
    152     """ 
    153  
    154     please_die.set()                # signal threads to die 
    155  
    156     sthread.join()                # wait for scheduler thread to die 
    157  
    158     if config.consport > 0:        # console thread not running if CONSPORT=0 
    159         cthread.join()                # wait for console thread to die 
    160  
    161     return() 
    162  
    163   
    164 def eddieexit(): 
    165     """Exit EDDIE cleanly. 
    166     """ 
    167  
    168     log.log( '<eddie>eddieexit(): EDDIE exiting cleanly.', 5 ) 
    169     # email admin any remaining messages 
    170     log.sendadminlog(1) 
    171     sys.exit(0) 
    172  
    173  
    174 def SigHandler( sig, frame ): 
    175     """Handle all the signals we are interested in. 
    176     """ 
    177  
    178     global Options 
    179  
    180     if 'SIGHUP' in dir(signal) and sig == signal.SIGHUP: 
    181         # SIGHUP (Hangup) - reload config 
    182         if not Options.daemon: 
    183             print "SIGHUP - reloading config" 
    184         log.log( '<eddie>SigHandler(): SIGHUP encountered - reloading config', 5 ) 
    185  
    186         stop_threads() 
    187  
    188         # reset config and read in config and rules 
    189         global Config 
    190         Config = config.Config( '__main__' ) 
    191  
    192         # read in config and rules 
    193         parseConfig.readConf(config_file, Config) 
    194  
    195         # Initialise check queue 
    196         q = timeQueue.timeQueue(0)                        # new timeQueue object, size is infinite 
    197         buildCheckQueue(q, Config) 
    198         Config.q = q 
    199  
    200         sargs = (q, Config, please_die) 
    201         cargs = (Config, please_die, config.consport) 
    202  
    203         start_threads(sargs, cargs) 
    204  
    205     elif 'SIGINT' in dir(signal) and sig == signal.SIGINT: 
    206         # SIGINT (CTRL-c) - quit now 
    207         log.log( '<eddie>SigHandler(): SIGINT (KeyboardInterrupt) encountered - quitting', 1 ) 
    208         log.log( '<eddie>SigHandler(): signalling scheduler thread to die', 6 ) 
    209         stop_threads() 
    210         eddieexit() 
    211  
    212     elif 'SIGTERM' in dir(signal) and sig == signal.SIGTERM: 
    213         # SIGTERM (Terminate) - quit now 
    214         log.log( '<eddie>SigHandler(): SIGTERM (Terminate) encountered - quitting', 1 ) 
    215         log.log( '<eddie>SigHandler(): signalling scheduler thread to die', 6 ) 
    216         stop_threads() 
    217         eddieexit() 
    218  
    219     elif 'SIGALRM' in dir(signal) and sig == signal.SIGALRM: 
    220         # SIGALRM (Alarm) - return to force a continue 
    221         return 
    222  
    223     else: 
    224         # un-handled signal - log & ignore it 
    225         log.log( '<eddie>SigHandler(): unknown signal received, %d - ignoring' % sig, 5 ) 
    226  
    227  
    228 def countFDs(): 
    229     """Count number of file descriptors in use. 
    230     """ 
    231  
    232     import errno 
    233     fdcnt = 0 
    234     for fd in range( 0, 1024 ): 
    235         try: 
    236             stat = os.fstat( fd ) 
    237             fdcnt = fdcnt + 1 
    238         except os.error, ( errnum, estr ): 
    239             if errnum != errno.EBADF: 
    240                 #raise os.error, ( errnum, estr ) 
    241                 log.log( '<eddie>countFDs(): exception os.error, %s, %s' %(errnum,estr), 5 ) 
    242  
    243     return fdcnt 
    244  
    245  
    246 def scheduler(q, Config, die_event): 
    247     """The EDDIE scheduler thread.  This thread tracks the queue of waiting 
    248     checks and executes them in their own thread as required.  It attempts 
    249     to limit the number of actual checking threads running to keep things 
    250     sane. 
    251     """ 
    252  
    253     while not die_event.isSet(): 
    254  
    255         loop_start = time.time()        # get time when loop started 
    256         while threading.activeCount() > config.num_threads: 
    257             # do nothing while we have no active threads to play with 
    258             # TODO: if we wait too long, something is probably wrong, so do something about it... 
    259             log.log( "<eddie>scheduler(): active thread count is %d - waiting till <= %d" % (threading.activeCount(),config.num_threads), 8 ) 
    260             if time.time() - loop_start > 30*60: 
    261                 # if this loop has been running for over 30 mins, then all 
    262                 # threads are locked badly and something is wrong.  Force an 
    263                 # exit... 
    264                 # (there is no ability to kill threads in current Python implementation) 
    265                 log.log( "<eddie>scheduler(): active thread count has been %d for over %d mins - forcing exit" % (threading.activeCount(), (time.time()-loop_start)/60), 1 ) 
    266                 eddieexit() 
    267  
    268             try: 
    269                 time.sleep(1) 
    270             except IOError: 
    271                 # Indicates a signal received under Linux, just continue 
    272                 # coz main thread should be setting up to exit. 
    273                 log.log( "<eddie>scheduler(): IOError received by sleep(1) #1 - assume exiting so ignoring", 8 ) 
    274                 pass 
    275  
    276         # we have spare threads so get next checking object 
    277         while not die_event.isSet(): 
    278             (c,t) = q.head(block=1)        # wait for next object from queue 
    279             log.log( "<eddie>scheduler(): waiting object is %s at %s" % (c,t), 9 ) 
    280             if t <= time.time(): 
    281                 log.log( "<eddie>scheduler(): object %s,%s is ready to run" % (c,t), 9 ) 
    282                 break 
    283             try: 
    284                 time.sleep(1) 
    285             except IOError: 
    286                 # Indicates a signal received under Linux, just continue 
    287                 # coz main thread should be setting up to exit. 
    288                 log.log( "<eddie>scheduler(): IOError received by sleep(1) #2 - assume exiting so ignoring", 8 ) 
    289                 pass 
    290  
    291         # break loop if we have been signalled to die 
    292         if die_event.isSet(): 
    293             break 
    294  
    295         (c,t) = q.get(block=1)        # retrieve next object from queue 
    296  
    297         if c.args.numchecks > 0: 
    298             # start check in a new thread 
    299             thr = threading.Thread(group=None, target=c.safeCheck, name="%s"%(c), args=(Config,), kwargs={}) 
    300             log.log( "<eddie>scheduler(): Starting new thread for %s, %s" % (c,thr), 8 ) 
    301             thr.setDaemon(1)        # mark thread as Daemon-thread so EDDIE will not block when trying to terminate 
    302                                     # with still-running threads. 
    303             thr.start()                # new thread starts running 
    304         else: 
    305             # when numchecks == 0 we don't do any checks at all... 
    306             log.log( "<eddie>scheduler(): Not scheduling checks for %s when numchecks=%d" % (c,c.args.numchecks), 7 ) 
    307  
    308     log.log( "<eddie>scheduler(): die_event received, scheduler exiting", 8 ) 
    309  
    310  
    311 def buildCheckQueue(q, Config): 
    312     """Build the queue of checks that the scheduler will start with. 
    313     """ 
    314  
    315     log.log( "<eddie>buildCheckQueue(): Adding directives to Queue for hostname '%s'" % (log.hostname), 8 ) 
    316  
    317     for i in Config.groupDirectives.keys(): 
    318         # if directive template is 'self', do not schedule it 
    319         d = Config.groupDirectives[i] 
    320         if d.args.template != 'self': 
    321             # chris 2002-12-24: skip directives specifying this hostname in excludehosts parameter 
    322             if log.hostname in d.excludehosts: 
    323                 log.log( "<eddie>buildCheckQueue(): skipped by excludehosts: %s" % (d), 8 ) 
    324             else: 
    325                 log.log( "<eddie>buildCheckQueue(): adding to Queue: %s" % (d), 8 ) 
    326                 q.put( (d,0) ) 
    327  
    328     # chris 2004-09-20: throw away any domain parts of hostname; group names can't contain dots 
    329     shorthostname = log.hostname.split('.')[0] 
    330  
    331     # chris 2004-12-30: replace '-' with '_' for now... 
    332     # TODO: this is a hack as group names in the config cannot contain '-'; this will 
    333     # be resolved in the future when proper matching options are implemented fully. 
    334     shorthostname = shorthostname.replace('-','_') 
    335  
    336     for c in Config.groups: 
    337         if c.name == shorthostname or (c.name in Config.classDict.keys() and shorthostname in Config.classDict[c.name]): 
    338             log.log( "<eddie>buildCheckQueue(): Adding checks from group %s to queue" % (c.name), 7 ) 
    339             buildCheckQueue(q, c) 
    340         else: 
    341             log.log( "<eddie>buildCheckQueue(): Not queueing group %s" % (c.name), 8 ) 
    342  
    343  
    344 def doArgs(): 
    345     """Parse command-line arguments. 
    346     """ 
    347     # define usage and version messages 
    348     usageMsg = "usage: %prog [options] eddie.cfg" 
    349     versionMsg = """EDDIE Tool %s""" % __version__ 
    350     try: 
    351         versionMsg += " (Build %s)" %'$Revision$'.split()[1] 
    352     except: 
    353         pass 
    354  
    355     # get a parser object and define our options 
    356     parser = optparse.OptionParser(usage=usageMsg, version=versionMsg) 
    357     # parser.add_option('-c', '--config', dest='config',                 \ 
    358     #                     metavar="FILE", help="Load config from FILE") 
    359     parser.add_option('--showconfig', action="store_true",         \ 
    360                         help="Dump config") 
    361     parser.add_option('-v', '--verbose', action="store_true",        \ 
    362                         help="Enable verbose output") 
    363     parser.add_option('-d', '--daemon', action="store_true",        \ 
    364                         help="Run as a daemon") 
    365     parser.set_defaults(showconfig=False, verbose=False,        \ 
    366                         daemon=False) 
    367  
    368     # Parse.  We dont accept arguments, so we complain if they're found. 
    369     (options, args) = parser.parse_args() 
    370     if len(args) != 1: 
    371         parser.error('Configuration file must be given as first argument.') 
    372  
    373     # All good - return the option dict 
    374     return (options, args[0]) 
    375  
    376  
    377 def main(): 
    378     """Startup routine - setup then start main loop. 
    379     """ 
    380      
    381     log.version = __version__        # Make version string available to other modules 
    382  
    383     # Parse command-line arguments 
    384     # instantiate global Options object 
    385     global Options 
    386     Options, config_file = doArgs() 
    387  
    388     # Catch most important signals 
    389     for sig in ('SIGALRM', 'SIGHUP', 'SIGINT', 'SIGTERM'): 
    390         if sig in dir(signal): 
    391             signal.signal( eval("signal.%s" %(sig)), SigHandler ) 
    392  
    393     # Get local hostname 
    394     try: 
    395         log.hostname = os.uname()[1] 
    396     except AttributeError: 
    397         try: 
    398             import platform 
    399             log.hostname = platform.node() 
    400         except ImportError,msg: 
    401             raise Exception( "Cannot determine hostname: %s" %(msg) ) 
    402  
    403     # instantiate global Config object 
    404     global Config 
    405     Config = config.Config( '__main__' ) 
    406  
    407     # data_modules handles access to all data collector modules 
    408     data_modules = datacollect.DataModules(osname, osver, osarch) 
    409     directive.data_modules = data_modules 
    410  
    411     # read in config and rules 
    412     parseConfig.readConf(config_file, Config) 
    413  
    414     try: 
    415         buildstr = " (Build %s)" %'$Revision$'.split()[1] 
    416     except: 
    417         buildstr = '' 
    418      
    419     # don't log till now because log file location is defined in configuration 
    420     log.log( "<eddie>main(): Configuration complete from '%s'" % (config_file), 6 ) 
    421     log.log( "<eddie>main(): EDDIE %s%s, systype: %s" % (__version__, buildstr, systype), 5 ) 
    422     log.log( "<eddie>main(): Python version: %s" % (sys.version), 5 ) 
    423     log.log( "<eddie>main(): oslibdirs: %s" % (data_modules.os_search_path), 8 ) 
    424  
    425     if Options.showconfig: 
    426         # Just display the configuration and exit 
    427         print "---Displaying EDDIE Configuration---" 
    428         print Config 
    429         eddieexit() 
    430  
    431     if Options.daemon: 
    432         # Create a child process, then have the parent exit 
    433         cpid = utils.create_child(True) 
    434         if cpid != 0: 
    435             log.log( "<eddie>main(): Created child process %d. Parent exiting..." % (cpid), 6 ) 
    436             print cpid 
    437             sys.exit(0)  # don't call eddieexit(), because its still running (as a daemon) 
    438  
    439     # Initialise Elvin connections and thread to handle Elvin messaging 
    440     try: 
    441         elvin = eddieElvin4.Elvin() 
    442     except eddieElvin4.ElvinInitError, details: 
    443         log.log( "<eddie>main(): Elvin init failed, %s, Elvin functionality will be disabled."%(details), 5 ) 
    444         elvin = None 
    445     else: 
    446         elvin.startup()                # Start up the Elvin management thread 
    447     Config.set_elvin(elvin) 
    448  
    449     # Initialise Spread connection and thread to handle Spread messaging 
    450     try: 
    451         spread = eddieSpread.Spread() 
    452     except eddieSpread.SpreadInitError, details: 
    453         log.log( "<eddie>main(): Spread init failed, %s, Spread functionality will be disabled."%(details), 5 ) 
    454         spread = None 
    455     else: 
    456         spread.startup()                # Start up the Spread management thread 
    457     Config.set_spread(spread) 
    458  
    459     # Main Loop 
    460     # Initialise check queue 
    461     q = timeQueue.timeQueue(0)                        # new timeQueue object, size is infinite 
    462     buildCheckQueue(q, Config) 
    463     Config.q = q 
    464  
    465     global please_die 
    466     please_die = threading.Event()                # Event object to notify the scheduler to die 
    467     global sthread 
    468  
    469     sargs = (q, Config, please_die) 
    470     cargs = (Config, please_die, config.consport) 
    471     start_threads(sargs, cargs) 
    472  
    473     while not please_die.isSet(): 
    474         try: 
    475             ### Perform housecleaning duties 
    476  
    477             # Count fds in use - for debugging 
    478             #numfds = countFDs() 
    479             #log.log( "<eddie>main(): FDs in use = %d." % (numfds), 8 ) 
    480  
    481             log.log( "<eddie>main(): Threads in use = %d." % (threading.activeCount()), 8 ) 
    482             log.log( "<eddie>main(): Threads: %s" % (threading.enumerate()), 8 ) 
    483  
    484             # check if any config/rules files have been modified 
    485             # if so, re-read config 
    486             if config.rescan_configs and Config.checkfiles(): 
    487                 log.log( '<eddie>main(): config files modified - signalling scheduler to die', 7 ) 
    488                 stop_threads() 
    489  
    490                 log.log( '<eddie>main(): config files modified - reloading config', 5 ) 
    491  
    492                 # reset config and read in config and rules 
    493                 Config = config.Config( '__main__' ) 
    494  
    495                 # read in config and rules 
    496                 parseConfig.readConf(config_file, Config) 
    497  
    498                 # Initialise check queue 
    499                 q = timeQueue.timeQueue(0)                        # new timeQueue object, size is infinite 
    500                 buildCheckQueue(q, Config) 
    501                 Config.q = q 
    502  
    503                 sargs = (q,Config,please_die) 
    504                 cargs = (Config, please_die, config.consport) 
    505                 start_threads(sargs,cargs) 
    506  
    507  
    508             # email admin the adminlog if required 
    509             log.sendadminlog() 
    510  
    511             #time.sleep(10*60)        # sleep for 10 minutes between housekeeping duties 
    512             please_die.wait(1*60)        # sleep for 1 minute between housekeeping duties 
    513                                         # or until all threads signalled to exit 
    514  
    515         except KeyboardInterrupt: 
    516             # CTRL-c hit - quit now 
    517             log.log( '<eddie>main(): KeyboardInterrupt encountered - quitting', 1 ) 
    518             eddieexit() 
    519  
    520  
    521     log.log( '<eddie>main(): main thread signalled to die - exiting', 1 ) 
    522     eddieexit() 
    523  
    524  
    525  
     6from eddietool.commands import agent 
    5267 
    5278## Start... 
    5289if __name__ == "__main__": 
    529     try: 
    530         main() 
    531      
    532     except parseConfig.ConfigError: 
    533         sys.exit(1) 
    534      
    535     except:                # catch any uncaught exceptions so we can log them 
    536         e = sys.exc_info() 
    537         import exceptions 
    538         if e[0] != exceptions.SystemExit: 
    539             import traceback 
    540             tb = traceback.format_list( traceback.extract_tb( e[2] ) ) 
    541             import string 
    542             tbstr = string.join(tb, '') 
    543             log.log( "<eddie.py>: EDDIE died with exception: %s, %s\n%s" % (e[0], e[1], tbstr), 1 ) 
    544             log.sendadminlog() 
    545             sys.stderr.write( "EDDIE died with exception:" ) 
    546             sys.stderr.write( tbstr + '\n' ) 
    547             sys.stderr.write( str(e[0]) + '\n' + str(e[1]) + '\n' ) 
    548             sys.exit(1) 
     10    agent() 
    54911 
    550  
    551     # email admin anything else... 
    552     log.sendadminlog() 
    553  
    554  
    555 ### 
    556 ### END eddie.py 
    557 ### 
  • eddie/trunk/eddietool/version.py

    r898 r914  
    3232''' 
    3333 
    34 version = '0.37svn' 
     34version = '0.37.1svn' 
  • eddie/trunk/setup.py

    r899 r914  
     1from ez_setup import use_setuptools 
     2use_setuptools() 
     3 
    14from setuptools import setup, find_packages 
    25import sys, os 
     
    2326        # -*- Extra requirements: -*- 
    2427    ], 
    25     entry_points=""" 
    26     # -*- Entry points: -*- 
    27     """, 
     28    entry_points={ 
     29        'console_scripts': [ 
     30            'eddie-agent = eddietool.commands:agent', 
     31        ], 
     32    }, 
     33    data_files = [ 
     34        ('config-sample', ['config-sample/eddie.cf']), 
     35        ('config-sample/rules', [ 
     36            'config-sample/rules/cache.rules', 
     37            'config-sample/rules/common.rules', 
     38            'config-sample/rules/dns.rules', 
     39            'config-sample/rules/host.rules', 
     40            'config-sample/rules/message.rules', 
     41            'config-sample/rules/news.rules', 
     42            'config-sample/rules/rrd.rules', 
     43            'config-sample/rules/sys_linux.rules', 
     44            'config-sample/rules/sys_solaris.rules', 
     45            'config-sample/rules/win32_sample.rules', 
     46        ]), 
     47        ('doc', ['doc/manual.html']), 
     48    ], 
    2849)