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 Validate the logpath is valid
143 """
144 if not self.options.logpath:
145 return None
146 else:
147 logdir = self.options.logpath
148 if not os.path.exists(logdir):
149
150 try:
151 os.makedirs(logdir)
152 except OSError, ex:
153 raise SystemExit("logpath:%s doesn't exist and cannot be created" % logdir)
154 elif not os.path.isdir(logdir):
155 raise SystemExit("logpath:%s exists but is not a directory" % logdir)
156 return logdir
157
159 """
160 Set common logging options
161 """
162 rlog = logging.getLogger()
163 rlog.setLevel(logging.WARN)
164 mname = self.__class__.__name__
165 self.log = logging.getLogger("zen."+ mname)
166 zlog = logging.getLogger("zen")
167 zlog.setLevel(self.options.logseverity)
168 logdir = self.checkLogpath()
169 if logdir:
170 logfile = os.path.join(logdir, mname.lower()+".log")
171 maxBytes = self.options.maxLogKiloBytes * 1024
172 backupCount = self.options.maxBackupLogs
173 h = logging.handlers.RotatingFileHandler(logfile, maxBytes, backupCount)
174 h.setFormatter(logging.Formatter(
175 "%(asctime)s %(levelname)s %(name)s: %(message)s",
176 "%Y-%m-%d %H:%M:%S"))
177 rlog.addHandler(h)
178 else:
179 logging.basicConfig()
180
181
196
198 """
199 Basic options setup. Other classes should call this before adding
200 more options
201 """
202 self.buildParser()
203 if self.doesLogging:
204 self.parser.add_option('-v', '--logseverity',
205 dest='logseverity',
206 default=20,
207 type='int',
208 help='Logging severity threshold')
209
210 self.parser.add_option('--logpath',dest='logpath',
211 help='Override the default logging path')
212
213 self.parser.add_option('--maxlogsize',
214 dest='maxLogKiloBytes',
215 help='Max size of log file in KB; default 10240',
216 default=10240,
217 type='int')
218
219 self.parser.add_option('--maxbackuplogs',
220 dest='maxBackupLogs',
221 help='Max number of back up log files; default 3',
222 default=3,
223 type='int')
224
225 self.parser.add_option("-C", "--configfile",
226 dest="configfile",
227 help="Use an alternate configuration file" )
228
229 self.parser.add_option("--genconf",
230 action="store_true",
231 default=False,
232 help="Generate a template configuration file" )
233
234 self.parser.add_option("--genxmltable",
235 action="store_true",
236 default=False,
237 help="Generate a Docbook table showing command-line switches." )
238
239 self.parser.add_option("--genxmlconfigs",
240 action="store_true",
241 default=False,
242 help="Generate an XML file containing command-line switches." )
243
244
245
246
300
301
302
304 """
305 Create a configuration file based on the long-form of the option names
306
307 @parameter parser: an optparse parser object which contains defaults, help
308 @parameter options: parsed options list containing actual values
309 """
310
311
312
313
314 unused(options)
315 daemon_name= os.path.basename( sys.argv[0] )
316 daemon_name= daemon_name.replace( '.py', '' )
317
318 print """#
319 # Configuration file for %s
320 #
321 # To enable a particular option, uncomment the desired entry.
322 #
323 # Parameter Setting
324 # --------- -------""" % ( daemon_name )
325
326
327 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' )
328
329
330
331
332
333
334
335 import re
336 for opt in parser.option_list:
337 if opt.help is SUPPRESS_HELP:
338 continue
339
340
341
342
343 option_name= re.sub( r'.*/--', '', "%s" % opt )
344
345
346
347
348 option_name= re.sub( r'^--', '', "%s" % option_name )
349
350
351
352
353 if option_name in options_to_ignore:
354 continue
355
356
357
358
359
360
361 value= getattr( parser.values, opt.dest )
362
363 default_value= parser.defaults.get( opt.dest )
364 if default_value is NO_DEFAULT or default_value is None:
365 default_value= ""
366 default_string= ""
367 if default_value != "":
368 default_string= ", default: " + str( default_value )
369
370 comment= self.pretty_print_config_comment( opt.help + default_string )
371
372
373
374
375
376 print """#
377 # %s
378 #%s %s""" % ( comment, option_name, value )
379
380
381
382
383 print "#"
384 sys.exit( 0 )
385
386
387
389 """
390 Create a Docbook table based on the long-form of the option names
391
392 @parameter parser: an optparse parser object which contains defaults, help
393 @parameter options: parsed options list containing actual values
394 """
395
396
397
398
399 unused(options)
400 daemon_name= os.path.basename( sys.argv[0] )
401 daemon_name= daemon_name.replace( '.py', '' )
402
403 print """<?xml version="1.0" encoding="UTF-8"?>
404
405 <section version="4.0" xmlns="http://docbook.org/ns/docbook"
406 xmlns:xlink="http://www.w3.org/1999/xlink"
407 xmlns:xi="http://www.w3.org/2001/XInclude"
408 xmlns:svg="http://www.w3.org/2000/svg"
409 xmlns:mml="http://www.w3.org/1998/Math/MathML"
410 xmlns:html="http://www.w3.org/1999/xhtml"
411 xmlns:db="http://docbook.org/ns/docbook"
412
413 xml:id="%s.options"
414 >
415
416 <title>%s Options</title>
417 <para />
418 <table frame="all">
419 <caption>%s <indexterm><primary>Daemons</primary><secondary>%s</secondary></indexterm> options</caption>
420 <tgroup cols="2">
421 <colspec colname="option" colwidth="1*" />
422 <colspec colname="description" colwidth="2*" />
423 <thead>
424 <row>
425 <entry> <para>Option</para> </entry>
426 <entry> <para>Description</para> </entry>
427 </row>
428 </thead>
429 <tbody>
430 """ % ( daemon_name, daemon_name, daemon_name, daemon_name )
431
432
433 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' )
434
435
436
437
438
439
440
441 import re
442 for opt in parser.option_list:
443 if opt.help is SUPPRESS_HELP:
444 continue
445
446
447
448
449
450
451 all_options= '<literal>' + re.sub( r'/', '</literal>,</para> <para><literal>', "%s" % opt ) + '</literal>'
452
453
454
455
456 option_name= re.sub( r'.*/--', '', "%s" % opt )
457 option_name= re.sub( r'^--', '', "%s" % option_name )
458 if option_name in options_to_ignore:
459 continue
460
461 default_value= parser.defaults.get( opt.dest )
462 if default_value is NO_DEFAULT or default_value is None:
463 default_value= ""
464 default_string= ""
465 if default_value != "":
466 default_string= "<para> Default: <literal>" + str( default_value ) + "</literal></para>\n"
467
468 comment= self.pretty_print_config_comment( opt.help )
469
470
471
472
473 if opt.action in [ 'store_true', 'store_false' ]:
474 print """<row>
475 <entry> <para>%s</para> </entry>
476 <entry>
477 <para>%s</para>
478 %s</entry>
479 </row>
480 """ % ( all_options, comment, default_string )
481
482 else:
483 target= '=<replaceable>' + opt.dest.lower() + '</replaceable>'
484 all_options= all_options + target
485 all_options= re.sub( r',', target + ',', all_options )
486 print """<row>
487 <entry> <para>%s</para> </entry>
488 <entry>
489 <para>%s</para>
490 %s</entry>
491 </row>
492 """ % ( all_options, comment, default_string )
493
494
495
496
497
498
499 print """</tbody></tgroup>
500 </table>
501 <para />
502 </section>
503 """
504 sys.exit( 0 )
505
506
507
509 """
510 Create an XML file that can be used to create Docbook files
511 as well as used as the basis for GUI-based daemon option
512 configuration.
513 """
514
515
516
517
518 unused(options)
519 daemon_name= os.path.basename( sys.argv[0] )
520 daemon_name= daemon_name.replace( '.py', '' )
521
522 export_date = datetime.datetime.now()
523
524 print """<?xml version="1.0" encoding="UTF-8"?>
525
526 <!-- Default daemon configuration generated on %s -->
527 <configuration id="%s" >
528
529 """ % ( export_date, daemon_name )
530
531 options_to_ignore= (
532 'help', 'version', '', 'genconf', 'genxmltable',
533 'genxmlconfigs',
534 )
535
536
537
538
539
540
541
542 import re
543 for opt in parser.option_list:
544 if opt.help is SUPPRESS_HELP:
545 continue
546
547
548
549
550 option_name= re.sub( r'.*/--', '', "%s" % opt )
551 option_name= re.sub( r'^--', '', "%s" % option_name )
552 if option_name in options_to_ignore:
553 continue
554
555 default_value= parser.defaults.get( opt.dest )
556 if default_value is NO_DEFAULT or default_value is None:
557 default_string= ""
558 else:
559 default_string= str( default_value )
560
561
562
563
564 if opt.action in [ 'store_true', 'store_false' ]:
565 print """ <option id="%s" type="%s" default="%s" help="%s" />
566 """ % ( option_name, "boolean", default_string, quote(opt.help), )
567
568 else:
569 target= opt.dest.lower()
570 print """ <option id="%s" type="%s" default="%s" target="%s" help="%s" />
571 """ % ( option_name, opt.type, quote(default_string), target, quote(opt.help), )
572
573
574
575
576
577 print """
578 </configuration>
579 """
580 sys.exit( 0 )
581
582
583
585 """
586 Uses the optparse parse previously populated and performs common options.
587 """
588
589 if self.noopts:
590 args = []
591 else:
592 import sys
593 args = sys.argv[1:]
594 (self.options, self.args) = self.parser.parse_args(args=args)
595
596 if self.options.genconf:
597 self.generate_configs( self.parser, self.options )
598
599 if self.options.genxmltable:
600 self.generate_xml_table( self.parser, self.options )
601
602 if self.options.genxmlconfigs:
603 self.generate_xml_configs( self.parser, self.options )
604