Package ZenUtils :: Module CmdBase
[hide private]
[frames] | no frames]

Source Code for Module ZenUtils.CmdBase

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  __doc__="""CmdBase 
 15   
 16  Provide utility functions for logging and config file parsing 
 17  to command line programs 
 18   
 19   
 20  $Id: CmdBase.py,v 1.10 2004/04/04 02:22:21 edahl Exp $""" 
 21   
 22  __version__ = "$Revision: 1.10 $"[11:-2] 
 23   
 24  import os 
 25  import sys 
 26  import logging 
 27  import logging.config 
 28  from optparse import OptionParser, SUPPRESS_HELP, NO_DEFAULT 
 29   
 30   
31 -def parseconfig(options):
32 """parse a config file which has key value pairs delimited by white space""" 33 if not os.path.exists(options.configfile): 34 print >>sys.stderr, "WARN: config file %s not found skipping" % ( 35 options.configfile) 36 return 37 lines = open(options.configfile).readlines() 38 for line in lines: 39 if line.lstrip().startswith('#'): continue 40 if line.strip() == '': continue 41 key, value = line.split(None, 1) 42 value = value.rstrip('\r\n') 43 key = key.lower() 44 defval = getattr(options, key, None) 45 if defval: value = type(defval)(value) 46 setattr(options, key, value)
47 48
49 -class DMDError: pass
50
51 -class CmdBase:
52 53 doesLogging = True 54
55 - def __init__(self, noopts=0):
56 self.usage = "%prog [options]" 57 self.noopts = noopts 58 self.args = [] 59 self.parser = None 60 self.buildParser() 61 self.buildOptions() 62 self.parseOptions() 63 if self.options.configfile: 64 parseconfig(self.options) 65 if self.doesLogging: 66 self.setupLogging()
67 68
69 - def setupLogging(self):
70 rlog = logging.getLogger() 71 rlog.setLevel(logging.WARN) 72 mname = self.__class__.__name__ 73 self.log = logging.getLogger("zen."+ mname) 74 zlog = logging.getLogger("zen") 75 zlog.setLevel(self.options.logseverity) 76 if self.options.logpath: 77 logdir = self.options.logpath 78 if not os.path.isdir(os.path.dirname(logdir)): 79 raise SystemExit("logpath:%s doesn't exist" % logdir) 80 logfile = os.path.join(logdir, mname.lower()+".log") 81 h = logging.FileHandler(logfile) 82 h.setFormatter(logging.Formatter( 83 "%(asctime)s %(levelname)s %(name)s: %(message)s", 84 "%Y-%m-%d %H:%M:%S")) 85 rlog.addHandler(h) 86 else: 87 logging.basicConfig()
88 89
90 - def buildParser(self):
91 if not self.parser: 92 self.parser = OptionParser(usage=self.usage, 93 version="%prog " + __version__)
94
95 - def buildOptions(self):
96 """basic options setup sub classes can add more options here""" 97 self.buildParser() 98 if self.doesLogging: 99 self.parser.add_option('-v', '--logseverity', 100 dest='logseverity', 101 default=20, 102 type='int', 103 help='Logging severity threshold') 104 self.parser.add_option('--logpath',dest='logpath', 105 help='override default logging path') 106 self.parser.add_option("-C", "--configfile", 107 dest="configfile", 108 help="config file must define all params (see man)")
109 110 111
112 - def pretty_print_config_comment( self, comment ):
113 """Quick and dirty pretty printer for comments that happen to be longer than can comfortably 114 be seen on the display.""" 115 116 max_size= 40 117 # 118 # As a heuristic we'll accept strings that are +- text_window 119 # size in length. 120 # 121 text_window= 5 122 123 if len( comment ) <= max_size + text_window: 124 return comment 125 126 # 127 # First, take care of embedded newlines and expand them out to array entries 128 # 129 new_comment= [] 130 all_lines= comment.split( '\n' ) 131 for line in all_lines: 132 if len(line) <= max_size + text_window: 133 new_comment.append( line ) 134 continue 135 136 start_position= max_size - text_window 137 while len(line) > max_size + text_window: 138 index= line.find( ' ', start_position ) 139 if index != -1: 140 new_comment.append( line[ 0:index ] ) 141 line= line[ index: ] 142 143 else: 144 if start_position == 0: 145 # 146 # If we get here it means that the line is just one big string with no spaces 147 # in it. There's nothing that we can do except print it out. Doh! 148 # 149 new_comment.append( line ) 150 break 151 152 # 153 # Okay, haven't found anything to split on -- go back and try again 154 # 155 start_position= start_position - text_window 156 if start_position < 0: 157 start_position= 0 158 159 else: 160 new_comment.append( line ) 161 162 return "\n# ".join( new_comment )
163 164 165
166 - def generate_configs( self, parser, options ):
167 """Create a configuration file based on the long-form of the option names""" 168 169 # 170 # Header for the configuration file 171 # 172 daemon_name= os.path.basename( sys.argv[0] ) 173 daemon_name= daemon_name.replace( '.py', '' ) 174 175 print """# 176 # Configuration file for %s 177 # 178 # To enable a particular option, uncomment the desired entry. 179 # 180 # Parameter Setting 181 # --------- -------""" % ( daemon_name ) 182 183 184 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' ) 185 186 # 187 # Create an entry for each of the command line flags 188 # 189 # NB: Ideally, this should print out only the option parser dest 190 # entries, rather than the command line options. 191 # 192 import re 193 for opt in parser.option_list: 194 if opt.help is SUPPRESS_HELP: 195 continue 196 197 # 198 # Get rid of the short version of the command 199 # 200 option_name= re.sub( r'.*/--', '', "%s" % opt ) 201 202 # 203 # And what if there's no short version? 204 # 205 option_name= re.sub( r'^--', '', "%s" % option_name ) 206 207 # 208 # Don't display anything we shouldn't be displaying 209 # 210 if option_name in options_to_ignore: 211 continue 212 213 # 214 # Find the actual value specified on the command line, if any, 215 # and display it 216 # 217 value= getattr( parser.values, opt.dest ) 218 219 default_value= parser.defaults.get( opt.dest ) 220 if default_value is NO_DEFAULT or default_value is None: 221 default_value= "" 222 default_string= "" 223 if default_value != "": 224 default_string= ", default: " + str( default_value ) 225 226 comment= self.pretty_print_config_comment( opt.help + default_string ) 227 228 # 229 # NB: I would prefer to use tabs to separate the parameter name 230 # and value, but I don't know that this would work. 231 # 232 print """# 233 # %s 234 #%s %s""" % ( comment, option_name, value ) 235 236 # 237 # Pretty print and exit 238 # 239 print "#" 240 sys.exit( 0 )
241 242 243
244 - def generate_xml_table( self, parser, options ):
245 """Create a Docbook table based on the long-form of the option names""" 246 247 # 248 # Header for the configuration file 249 # 250 daemon_name= os.path.basename( sys.argv[0] ) 251 daemon_name= daemon_name.replace( '.py', '' ) 252 253 print """<?xml version="1.0" encoding="UTF-8"?> 254 255 <section version="4.0" xmlns="http://docbook.org/ns/docbook" 256 xmlns:xlink="http://www.w3.org/1999/xlink" 257 xmlns:xi="http://www.w3.org/2001/XInclude" 258 xmlns:svg="http://www.w3.org/2000/svg" 259 xmlns:mml="http://www.w3.org/1998/Math/MathML" 260 xmlns:html="http://www.w3.org/1999/xhtml" 261 xmlns:db="http://docbook.org/ns/docbook" 262 263 xml:id="%s.options" 264 > 265 266 <title>%s Options</title> 267 <para /> 268 <table frame="all"> 269 <caption>%s <indexterm><primary>Daemons</primary><secondary>%s</secondary></indexterm> options</caption> 270 <tgroup cols="2"> 271 <colspec colname="option" colwidth="1*" /> 272 <colspec colname="description" colwidth="2*" /> 273 <thead> 274 <row> 275 <entry> <para>Option</para> </entry> 276 <entry> <para>Description</para> </entry> 277 </row> 278 </thead> 279 <tbody> 280 """ % ( daemon_name, daemon_name, daemon_name, daemon_name ) 281 282 283 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' ) 284 285 # 286 # Create an entry for each of the command line flags 287 # 288 # NB: Ideally, this should print out only the option parser dest 289 # entries, rather than the command line options. 290 # 291 import re 292 for opt in parser.option_list: 293 if opt.help is SUPPRESS_HELP: 294 continue 295 296 # 297 # Create a Docbook-happy version of the option strings 298 # Yes, <arg></arg> would be better semantically, but the output 299 # just looks goofy in a table. Use literal instead. 300 # 301 all_options= '<literal>' + re.sub( r'/', '</literal>,</para> <para><literal>', "%s" % opt ) + '</literal>' 302 303 # 304 # Don't display anything we shouldn't be displaying 305 # 306 option_name= re.sub( r'.*/--', '', "%s" % opt ) 307 option_name= re.sub( r'^--', '', "%s" % option_name ) 308 if option_name in options_to_ignore: 309 continue 310 311 default_value= parser.defaults.get( opt.dest ) 312 if default_value is NO_DEFAULT or default_value is None: 313 default_value= "" 314 default_string= "" 315 if default_value != "": 316 default_string= "<para> Default: <literal>" + str( default_value ) + "</literal></para>\n" 317 318 comment= self.pretty_print_config_comment( opt.help ) 319 320 # 321 # TODO: Determine the variable name used and display the --option_name=variable_name 322 # 323 if opt.action in [ 'store_true', 'store_false' ]: 324 print """<row> 325 <entry> <para>%s</para> </entry> 326 <entry> 327 <para>%s</para> 328 %s</entry> 329 </row> 330 """ % ( all_options, comment, default_string ) 331 332 else: 333 target= '=<replaceable>' + opt.dest.lower() + '</replaceable>' 334 all_options= all_options + target 335 all_options= re.sub( r',', target + ',', all_options ) 336 print """<row> 337 <entry> <para>%s</para> </entry> 338 <entry> 339 <para>%s</para> 340 %s</entry> 341 </row> 342 """ % ( all_options, comment, default_string ) 343 344 345 346 # 347 # Close the table elements 348 # 349 print """</tbody></tgroup> 350 </table> 351 <para /> 352 </section> 353 """ 354 sys.exit( 0 )
355 356 357
358 - def parseOptions(self):
359 360 self.parser.add_option("--genconf", 361 action="store_true", 362 default=False, 363 help="Generate a template configuration file" ) 364 365 self.parser.add_option("--genxmltable", 366 action="store_true", 367 default=False, 368 help="Generate a Docbook table showing command-line switches." ) 369 370 if self.noopts: 371 args = [] 372 else: 373 import sys 374 args = sys.argv[1:] 375 (self.options, self.args) = self.parser.parse_args(args=args) 376 377 if self.options.genconf: 378 self.generate_configs( self.parser, self.options ) 379 380 if self.options.genxmltable: 381 self.generate_xml_table( self.parser, self.options )
382