1
2
3
4
5
6
7
8
9
10
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 import os
21 import sys
22 import datetime
23 import logging
24
25 from logging import handlers
26 from optparse import OptionParser, SUPPRESS_HELP, NO_DEFAULT
27 from urllib import quote
28
29
30
31
32 from Products.ZenUtils.PkgResources import pkg_resources
33
34 from Products.ZenUtils.Utils import unused
35 unused(pkg_resources)
36
38
40 """
41 Class used for all Zenoss commands
42 """
43
44 doesLogging = True
45
70
71
73 """
74 Parse a config file which has key-value pairs delimited by white space,
75 and update the parser's option defaults with these values.
76
77 @parameter filename: name of configuration file
78 @type filename: string
79 """
80 outlines = []
81
82 try:
83 configFile = open(filename)
84 lines = configFile.readlines()
85 configFile.close()
86 except:
87 import traceback
88 print >>sys.stderr, "WARN: unable to read config file %s -- skipping" % \
89 filename
90 traceback.print_exc(0)
91 return
92
93 lineno = 0
94 modified = False
95 for line in lines:
96 outlines.append(line)
97 lineno += 1
98 if line.lstrip().startswith('#'): continue
99 if line.strip() == '': continue
100
101 try:
102 key, value = line.strip().split(None, 1)
103 except ValueError:
104 print >>sys.stderr, "WARN: missing value on line %d" % lineno
105 continue
106 flag= "--%s" % key
107 option= self.parser.get_option( flag )
108 if option is None:
109 print >>sys.stderr, "INFO: Commenting out unknown option '%s' found " \
110 "on line %d in config file" % (key, lineno)
111
112 outlines = outlines[:-1]
113 outlines.append('## %s' % line)
114 modified = True
115 continue
116
117
118
119 try:
120 if option.action in [ "store_true", "store_false" ]:
121 if value in ['True', 'true']:
122 value = True
123 else:
124 value = False
125 self.parser.set_default( option.dest, value )
126 else:
127 self.parser.set_default( option.dest, type(option.type)(value) )
128 except:
129 print >>sys.stderr, "Bad configuration value for" \
130 " %s at line %s, value = %s (type %s)" % (
131 option.dest, lineno, value, option.type )
132
133
134
135 if modified:
136 configFile = file(filename, 'w')
137 configFile.writelines(outlines)
138 configFile.close()
139
141 """
142 Set common logging options
143 """
144 rlog = logging.getLogger()
145 rlog.setLevel(logging.WARN)
146 mname = self.__class__.__name__
147 self.log = logging.getLogger("zen."+ mname)
148 zlog = logging.getLogger("zen")
149 zlog.setLevel(self.options.logseverity)
150 if self.options.logpath:
151 logdir = self.options.logpath
152 if not os.path.isdir(os.path.dirname(logdir)):
153 raise SystemExit("logpath:%s doesn't exist" % logdir)
154 logfile = os.path.join(logdir, mname.lower()+".log")
155 maxBytes = self.options.maxLogKiloBytes * 1024
156 backupCount = self.options.maxBackupLogs
157 h = logging.handlers.RotatingFileHandler(logfile, maxBytes, backupCount)
158 h.setFormatter(logging.Formatter(
159 "%(asctime)s %(levelname)s %(name)s: %(message)s",
160 "%Y-%m-%d %H:%M:%S"))
161 rlog.addHandler(h)
162 else:
163 logging.basicConfig()
164
165
180
182 """
183 Basic options setup. Other classes should call this before adding
184 more options
185 """
186 self.buildParser()
187 if self.doesLogging:
188 self.parser.add_option('-v', '--logseverity',
189 dest='logseverity',
190 default=20,
191 type='int',
192 help='Logging severity threshold')
193
194 self.parser.add_option('--logpath',dest='logpath',
195 help='Override the default logging path')
196
197 self.parser.add_option('--maxlogsize',
198 dest='maxLogKiloBytes',
199 help='Max size of log file in KB; default 10240',
200 default=10240,
201 type='int')
202
203 self.parser.add_option('--maxbackuplogs',
204 dest='maxBackupLogs',
205 help='Max number of back up log files; default 3',
206 default=3,
207 type='int')
208
209 self.parser.add_option("-C", "--configfile",
210 dest="configfile",
211 help="Use an alternate configuration file" )
212
213 self.parser.add_option("--genconf",
214 action="store_true",
215 default=False,
216 help="Generate a template configuration file" )
217
218 self.parser.add_option("--genxmltable",
219 action="store_true",
220 default=False,
221 help="Generate a Docbook table showing command-line switches." )
222
223 self.parser.add_option("--genxmlconfigs",
224 action="store_true",
225 default=False,
226 help="Generate an XML file containing command-line switches." )
227
228
229
230
284
285
286
288 """
289 Create a configuration file based on the long-form of the option names
290
291 @parameter parser: an optparse parser object which contains defaults, help
292 @parameter options: parsed options list containing actual values
293 """
294
295
296
297
298 unused(options)
299 daemon_name= os.path.basename( sys.argv[0] )
300 daemon_name= daemon_name.replace( '.py', '' )
301
302 print """#
303 # Configuration file for %s
304 #
305 # To enable a particular option, uncomment the desired entry.
306 #
307 # Parameter Setting
308 # --------- -------""" % ( daemon_name )
309
310
311 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' )
312
313
314
315
316
317
318
319 import re
320 for opt in parser.option_list:
321 if opt.help is SUPPRESS_HELP:
322 continue
323
324
325
326
327 option_name= re.sub( r'.*/--', '', "%s" % opt )
328
329
330
331
332 option_name= re.sub( r'^--', '', "%s" % option_name )
333
334
335
336
337 if option_name in options_to_ignore:
338 continue
339
340
341
342
343
344
345 value= getattr( parser.values, opt.dest )
346
347 default_value= parser.defaults.get( opt.dest )
348 if default_value is NO_DEFAULT or default_value is None:
349 default_value= ""
350 default_string= ""
351 if default_value != "":
352 default_string= ", default: " + str( default_value )
353
354 comment= self.pretty_print_config_comment( opt.help + default_string )
355
356
357
358
359
360 print """#
361 # %s
362 #%s %s""" % ( comment, option_name, value )
363
364
365
366
367 print "#"
368 sys.exit( 0 )
369
370
371
373 """
374 Create a Docbook table based on the long-form of the option names
375
376 @parameter parser: an optparse parser object which contains defaults, help
377 @parameter options: parsed options list containing actual values
378 """
379
380
381
382
383 unused(options)
384 daemon_name= os.path.basename( sys.argv[0] )
385 daemon_name= daemon_name.replace( '.py', '' )
386
387 print """<?xml version="1.0" encoding="UTF-8"?>
388
389 <section version="4.0" xmlns="http://docbook.org/ns/docbook"
390 xmlns:xlink="http://www.w3.org/1999/xlink"
391 xmlns:xi="http://www.w3.org/2001/XInclude"
392 xmlns:svg="http://www.w3.org/2000/svg"
393 xmlns:mml="http://www.w3.org/1998/Math/MathML"
394 xmlns:html="http://www.w3.org/1999/xhtml"
395 xmlns:db="http://docbook.org/ns/docbook"
396
397 xml:id="%s.options"
398 >
399
400 <title>%s Options</title>
401 <para />
402 <table frame="all">
403 <caption>%s <indexterm><primary>Daemons</primary><secondary>%s</secondary></indexterm> options</caption>
404 <tgroup cols="2">
405 <colspec colname="option" colwidth="1*" />
406 <colspec colname="description" colwidth="2*" />
407 <thead>
408 <row>
409 <entry> <para>Option</para> </entry>
410 <entry> <para>Description</para> </entry>
411 </row>
412 </thead>
413 <tbody>
414 """ % ( daemon_name, daemon_name, daemon_name, daemon_name )
415
416
417 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' )
418
419
420
421
422
423
424
425 import re
426 for opt in parser.option_list:
427 if opt.help is SUPPRESS_HELP:
428 continue
429
430
431
432
433
434
435 all_options= '<literal>' + re.sub( r'/', '</literal>,</para> <para><literal>', "%s" % opt ) + '</literal>'
436
437
438
439
440 option_name= re.sub( r'.*/--', '', "%s" % opt )
441 option_name= re.sub( r'^--', '', "%s" % option_name )
442 if option_name in options_to_ignore:
443 continue
444
445 default_value= parser.defaults.get( opt.dest )
446 if default_value is NO_DEFAULT or default_value is None:
447 default_value= ""
448 default_string= ""
449 if default_value != "":
450 default_string= "<para> Default: <literal>" + str( default_value ) + "</literal></para>\n"
451
452 comment= self.pretty_print_config_comment( opt.help )
453
454
455
456
457 if opt.action in [ 'store_true', 'store_false' ]:
458 print """<row>
459 <entry> <para>%s</para> </entry>
460 <entry>
461 <para>%s</para>
462 %s</entry>
463 </row>
464 """ % ( all_options, comment, default_string )
465
466 else:
467 target= '=<replaceable>' + opt.dest.lower() + '</replaceable>'
468 all_options= all_options + target
469 all_options= re.sub( r',', target + ',', all_options )
470 print """<row>
471 <entry> <para>%s</para> </entry>
472 <entry>
473 <para>%s</para>
474 %s</entry>
475 </row>
476 """ % ( all_options, comment, default_string )
477
478
479
480
481
482
483 print """</tbody></tgroup>
484 </table>
485 <para />
486 </section>
487 """
488 sys.exit( 0 )
489
490
491
493 """
494 Create an XML file that can be used to create Docbook files
495 as well as used as the basis for GUI-based daemon option
496 configuration.
497 """
498
499
500
501
502 unused(options)
503 daemon_name= os.path.basename( sys.argv[0] )
504 daemon_name= daemon_name.replace( '.py', '' )
505
506 export_date = datetime.datetime.now()
507
508 print """<?xml version="1.0" encoding="UTF-8"?>
509
510 <!-- Default daemon configuration generated on %s -->
511 <configuration id="%s" >
512
513 """ % ( export_date, daemon_name )
514
515 options_to_ignore= (
516 'help', 'version', '', 'genconf', 'genxmltable',
517 'genxmlconfigs',
518 )
519
520
521
522
523
524
525
526 import re
527 for opt in parser.option_list:
528 if opt.help is SUPPRESS_HELP:
529 continue
530
531
532
533
534 option_name= re.sub( r'.*/--', '', "%s" % opt )
535 option_name= re.sub( r'^--', '', "%s" % option_name )
536 if option_name in options_to_ignore:
537 continue
538
539 default_value= parser.defaults.get( opt.dest )
540 if default_value is NO_DEFAULT or default_value is None:
541 default_string= ""
542 else:
543 default_string= str( default_value )
544
545
546
547
548 if opt.action in [ 'store_true', 'store_false' ]:
549 print """ <option id="%s" type="%s" default="%s" help="%s" />
550 """ % ( option_name, "boolean", default_string, quote(opt.help), )
551
552 else:
553 target= opt.dest.lower()
554 print """ <option id="%s" type="%s" default="%s" target="%s" help="%s" />
555 """ % ( option_name, opt.type, quote(default_string), target, quote(opt.help), )
556
557
558
559
560
561 print """
562 </configuration>
563 """
564 sys.exit( 0 )
565
566
567
569 """
570 Uses the optparse parse previously populated and performs common options.
571 """
572
573 if self.noopts:
574 args = []
575 else:
576 import sys
577 args = sys.argv[1:]
578 (self.options, self.args) = self.parser.parse_args(args=args)
579
580 if self.options.genconf:
581 self.generate_configs( self.parser, self.options )
582
583 if self.options.genxmltable:
584 self.generate_xml_table( self.parser, self.options )
585
586 if self.options.genxmlconfigs:
587 self.generate_xml_configs( self.parser, self.options )
588