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 import zope.component
26 from zope.traversing.adapters import DefaultTraversable
27 from Products.Five import zcml
28
29 from logging import handlers
30 from optparse import OptionParser, SUPPRESS_HELP, NO_DEFAULT
31 from urllib import quote
32
33
34
35
36 from Products.ZenUtils.PkgResources import pkg_resources
37
38 from Products.ZenUtils.Utils import unused
39 unused(pkg_resources)
40
42
44 """
45 Class used for all Zenoss commands
46 """
47
48 doesLogging = True
49
51
52
53
54 zope.component.provideAdapter(DefaultTraversable, (None,))
55 import Products
56 try:
57 zcml.load_config('meta.zcml', Products.Five)
58 zcml.load_config('indexing.zcml', Products.ZenModel)
59 zcml.load_config('configure.zcml', Products.ZenRelations)
60 zcml.load_config('scriptmessaging.zcml', Products.ZenWidgets)
61 except AttributeError:
62
63
64 pass
65 import Products.ZenossStartup
66 unused(Products.ZenossStartup)
67
68 self.usage = "%prog [options]"
69 self.noopts = noopts
70 self.args = []
71 self.parser = None
72 self.buildParser()
73 self.buildOptions()
74
75 self.parseOptions()
76 if self.options.configfile:
77 self.getConfigFileDefaults( self.options.configfile )
78
79
80
81
82 self.parseOptions()
83 if self.doesLogging:
84 self.setupLogging()
85
86
88 """
89 Parse a config file which has key-value pairs delimited by white space,
90 and update the parser's option defaults with these values.
91
92 @parameter filename: name of configuration file
93 @type filename: string
94 """
95 outlines = []
96
97 try:
98 configFile = open(filename)
99 lines = configFile.readlines()
100 configFile.close()
101 except:
102 import traceback
103 print >>sys.stderr, "WARN: unable to read config file %s -- skipping" % \
104 filename
105 traceback.print_exc(0)
106 return
107
108 lineno = 0
109 modified = False
110 for line in lines:
111 outlines.append(line)
112 lineno += 1
113 if line.lstrip().startswith('#'): continue
114 if line.strip() == '': continue
115
116 try:
117 key, value = line.strip().split(None, 1)
118 except ValueError:
119 print >>sys.stderr, "WARN: missing value on line %d" % lineno
120 continue
121 flag= "--%s" % key
122 option= self.parser.get_option( flag )
123 if option is None:
124 print >>sys.stderr, "INFO: Commenting out unknown option '%s' found " \
125 "on line %d in config file" % (key, lineno)
126
127 outlines = outlines[:-1]
128 outlines.append('## %s' % line)
129 modified = True
130 continue
131
132
133
134 try:
135 if option.action in [ "store_true", "store_false" ]:
136 if value in ['True', 'true']:
137 value = True
138 else:
139 value = False
140 self.parser.set_default( option.dest, value )
141 else:
142 self.parser.set_default( option.dest, type(option.type)(value) )
143 except:
144 print >>sys.stderr, "Bad configuration value for" \
145 " %s at line %s, value = %s (type %s)" % (
146 option.dest, lineno, value, option.type )
147
148
149
150 if modified:
151 configFile = file(filename, 'w')
152 configFile.writelines(outlines)
153 configFile.close()
154
156 """
157 Validate the logpath is valid
158 """
159 if not self.options.logpath:
160 return None
161 else:
162 logdir = self.options.logpath
163 if not os.path.exists(logdir):
164
165 try:
166 os.makedirs(logdir)
167 except OSError, ex:
168 raise SystemExit("logpath:%s doesn't exist and cannot be created" % logdir)
169 elif not os.path.isdir(logdir):
170 raise SystemExit("logpath:%s exists but is not a directory" % logdir)
171 return logdir
172
174 """
175 Set common logging options
176 """
177 rlog = logging.getLogger()
178 rlog.setLevel(logging.WARN)
179 mname = self.__class__.__name__
180 self.log = logging.getLogger("zen."+ mname)
181 zlog = logging.getLogger("zen")
182 zlog.setLevel(self.options.logseverity)
183 logdir = self.checkLogpath()
184 if logdir:
185 logfile = os.path.join(logdir, mname.lower()+".log")
186 maxBytes = self.options.maxLogKiloBytes * 1024
187 backupCount = self.options.maxBackupLogs
188 h = logging.handlers.RotatingFileHandler(logfile, maxBytes, backupCount)
189 h.setFormatter(logging.Formatter(
190 "%(asctime)s %(levelname)s %(name)s: %(message)s",
191 "%Y-%m-%d %H:%M:%S"))
192 rlog.addHandler(h)
193 else:
194 logging.basicConfig()
195
196
211
213 """
214 Basic options setup. Other classes should call this before adding
215 more options
216 """
217 self.buildParser()
218 if self.doesLogging:
219 self.parser.add_option('-v', '--logseverity',
220 dest='logseverity',
221 default=20,
222 type='int',
223 help='Logging severity threshold')
224
225 self.parser.add_option('--logpath',dest='logpath',
226 help='Override the default logging path')
227
228 self.parser.add_option('--maxlogsize',
229 dest='maxLogKiloBytes',
230 help='Max size of log file in KB; default 10240',
231 default=10240,
232 type='int')
233
234 self.parser.add_option('--maxbackuplogs',
235 dest='maxBackupLogs',
236 help='Max number of back up log files; default 3',
237 default=3,
238 type='int')
239
240 self.parser.add_option("-C", "--configfile",
241 dest="configfile",
242 help="Use an alternate configuration file" )
243
244 self.parser.add_option("--genconf",
245 action="store_true",
246 default=False,
247 help="Generate a template configuration file" )
248
249 self.parser.add_option("--genxmltable",
250 action="store_true",
251 default=False,
252 help="Generate a Docbook table showing command-line switches." )
253
254 self.parser.add_option("--genxmlconfigs",
255 action="store_true",
256 default=False,
257 help="Generate an XML file containing command-line switches." )
258
259
260
261
315
316
317
319 """
320 Create a configuration file based on the long-form of the option names
321
322 @parameter parser: an optparse parser object which contains defaults, help
323 @parameter options: parsed options list containing actual values
324 """
325
326
327
328
329 unused(options)
330 daemon_name= os.path.basename( sys.argv[0] )
331 daemon_name= daemon_name.replace( '.py', '' )
332
333 print """#
334 # Configuration file for %s
335 #
336 # To enable a particular option, uncomment the desired entry.
337 #
338 # Parameter Setting
339 # --------- -------""" % ( daemon_name )
340
341
342 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' )
343
344
345
346
347
348
349
350 import re
351 for opt in parser.option_list:
352 if opt.help is SUPPRESS_HELP:
353 continue
354
355
356
357
358 option_name= re.sub( r'.*/--', '', "%s" % opt )
359
360
361
362
363 option_name= re.sub( r'^--', '', "%s" % option_name )
364
365
366
367
368 if option_name in options_to_ignore:
369 continue
370
371
372
373
374
375
376 value= getattr( parser.values, opt.dest )
377
378 default_value= parser.defaults.get( opt.dest )
379 if default_value is NO_DEFAULT or default_value is None:
380 default_value= ""
381 default_string= ""
382 if default_value != "":
383 default_string= ", default: " + str( default_value )
384
385 comment= self.pretty_print_config_comment( opt.help + default_string )
386
387
388
389
390
391 print """#
392 # %s
393 #%s %s""" % ( comment, option_name, value )
394
395
396
397
398 print "#"
399 sys.exit( 0 )
400
401
402
404 """
405 Create a Docbook table based on the long-form of the option names
406
407 @parameter parser: an optparse parser object which contains defaults, help
408 @parameter options: parsed options list containing actual values
409 """
410
411
412
413
414 unused(options)
415 daemon_name= os.path.basename( sys.argv[0] )
416 daemon_name= daemon_name.replace( '.py', '' )
417
418 print """<?xml version="1.0" encoding="UTF-8"?>
419
420 <section version="4.0" xmlns="http://docbook.org/ns/docbook"
421 xmlns:xlink="http://www.w3.org/1999/xlink"
422 xmlns:xi="http://www.w3.org/2001/XInclude"
423 xmlns:svg="http://www.w3.org/2000/svg"
424 xmlns:mml="http://www.w3.org/1998/Math/MathML"
425 xmlns:html="http://www.w3.org/1999/xhtml"
426 xmlns:db="http://docbook.org/ns/docbook"
427
428 xml:id="%s.options"
429 >
430
431 <title>%s Options</title>
432 <para />
433 <table frame="all">
434 <caption>%s <indexterm><primary>Daemons</primary><secondary>%s</secondary></indexterm> options</caption>
435 <tgroup cols="2">
436 <colspec colname="option" colwidth="1*" />
437 <colspec colname="description" colwidth="2*" />
438 <thead>
439 <row>
440 <entry> <para>Option</para> </entry>
441 <entry> <para>Description</para> </entry>
442 </row>
443 </thead>
444 <tbody>
445 """ % ( daemon_name, daemon_name, daemon_name, daemon_name )
446
447
448 options_to_ignore= ( 'help', 'version', '', 'genconf', 'genxmltable' )
449
450
451
452
453
454
455
456 import re
457 for opt in parser.option_list:
458 if opt.help is SUPPRESS_HELP:
459 continue
460
461
462
463
464
465
466 all_options= '<literal>' + re.sub( r'/', '</literal>,</para> <para><literal>', "%s" % opt ) + '</literal>'
467
468
469
470
471 option_name= re.sub( r'.*/--', '', "%s" % opt )
472 option_name= re.sub( r'^--', '', "%s" % option_name )
473 if option_name in options_to_ignore:
474 continue
475
476 default_value= parser.defaults.get( opt.dest )
477 if default_value is NO_DEFAULT or default_value is None:
478 default_value= ""
479 default_string= ""
480 if default_value != "":
481 default_string= "<para> Default: <literal>" + str( default_value ) + "</literal></para>\n"
482
483 comment= self.pretty_print_config_comment( opt.help )
484
485
486
487
488 if opt.action in [ 'store_true', 'store_false' ]:
489 print """<row>
490 <entry> <para>%s</para> </entry>
491 <entry>
492 <para>%s</para>
493 %s</entry>
494 </row>
495 """ % ( all_options, comment, default_string )
496
497 else:
498 target= '=<replaceable>' + opt.dest.lower() + '</replaceable>'
499 all_options= all_options + target
500 all_options= re.sub( r',', target + ',', all_options )
501 print """<row>
502 <entry> <para>%s</para> </entry>
503 <entry>
504 <para>%s</para>
505 %s</entry>
506 </row>
507 """ % ( all_options, comment, default_string )
508
509
510
511
512
513
514 print """</tbody></tgroup>
515 </table>
516 <para />
517 </section>
518 """
519 sys.exit( 0 )
520
521
522
524 """
525 Create an XML file that can be used to create Docbook files
526 as well as used as the basis for GUI-based daemon option
527 configuration.
528 """
529
530
531
532
533 unused(options)
534 daemon_name= os.path.basename( sys.argv[0] )
535 daemon_name= daemon_name.replace( '.py', '' )
536
537 export_date = datetime.datetime.now()
538
539 print """<?xml version="1.0" encoding="UTF-8"?>
540
541 <!-- Default daemon configuration generated on %s -->
542 <configuration id="%s" >
543
544 """ % ( export_date, daemon_name )
545
546 options_to_ignore= (
547 'help', 'version', '', 'genconf', 'genxmltable',
548 'genxmlconfigs',
549 )
550
551
552
553
554
555
556
557 import re
558 for opt in parser.option_list:
559 if opt.help is SUPPRESS_HELP:
560 continue
561
562
563
564
565 option_name= re.sub( r'.*/--', '', "%s" % opt )
566 option_name= re.sub( r'^--', '', "%s" % option_name )
567 if option_name in options_to_ignore:
568 continue
569
570 default_value= parser.defaults.get( opt.dest )
571 if default_value is NO_DEFAULT or default_value is None:
572 default_string= ""
573 else:
574 default_string= str( default_value )
575
576
577
578
579 if opt.action in [ 'store_true', 'store_false' ]:
580 print """ <option id="%s" type="%s" default="%s" help="%s" />
581 """ % ( option_name, "boolean", default_string, quote(opt.help), )
582
583 else:
584 target= opt.dest.lower()
585 print """ <option id="%s" type="%s" default="%s" target="%s" help="%s" />
586 """ % ( option_name, opt.type, quote(default_string), target, quote(opt.help), )
587
588
589
590
591
592 print """
593 </configuration>
594 """
595 sys.exit( 0 )
596
597
598
600 """
601 Uses the optparse parse previously populated and performs common options.
602 """
603
604 if self.noopts:
605 args = []
606 else:
607 import sys
608 args = sys.argv[1:]
609 (self.options, self.args) = self.parser.parse_args(args=args)
610
611 if self.options.genconf:
612 self.generate_configs( self.parser, self.options )
613
614 if self.options.genxmltable:
615 self.generate_xml_table( self.parser, self.options )
616
617 if self.options.genxmlconfigs:
618 self.generate_xml_configs( self.parser, self.options )
619