[IMP] tests: removed the severity concept from `assert` tags.
[odoo/odoo.git] / openerp / tools / config.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #    Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import ConfigParser
24 import optparse
25 import os
26 import sys
27 import openerp
28 import openerp.conf
29 import openerp.loglevels as loglevels
30 import logging
31 import openerp.release as release
32
33 class MyOption (optparse.Option, object):
34     """ optparse Option with two additional attributes.
35
36     The list of command line options (getopt.Option) is used to create the
37     list of the configuration file options. When reading the file, and then
38     reading the command line arguments, we don't want optparse.parse results
39     to override the configuration file values. But if we provide default
40     values to optparse, optparse will return them and we can't know if they
41     were really provided by the user or not. A solution is to not use
42     optparse's default attribute, but use a custom one (that will be copied
43     to create the default values of the configuration file).
44
45     """
46     def __init__(self, *opts, **attrs):
47         self.my_default = attrs.pop('my_default', None)
48         super(MyOption, self).__init__(*opts, **attrs)
49
50 #.apidoc title: Server Configuration Loader
51
52 def check_ssl():
53     try:
54         from OpenSSL import SSL
55         import socket
56
57         return hasattr(socket, 'ssl') and hasattr(SSL, "Connection")
58     except:
59         return False
60
61 class configmanager(object):
62     def __init__(self, fname=None):
63         # Options not exposed on the command line. Command line options will be added
64         # from optparse's parser.
65         self.options = {
66             'admin_passwd': 'admin',
67             'csv_internal_sep': ',',
68             'login_message': False,
69             'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
70             'reportgz': False,
71             'root_path': None,
72         }
73
74         # Not exposed in the configuration file.
75         self.blacklist_for_save = set(
76             ['publisher_warranty_url', 'load_language', 'root_path',
77             'init', 'save', 'config', 'update', 'stop_after_init'])
78
79         # dictionary mapping option destination (keys in self.options) to MyOptions.
80         self.casts = {}
81
82         self.misc = {}
83         self.config_file = fname
84         self.has_ssl = check_ssl()
85
86         self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'TEST', 'DEBUG', 'NOTSET')])
87
88         version = "%s %s" % (release.description, release.version)
89         self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
90
91         # Server startup config
92         group = optparse.OptionGroup(parser, "Common options")
93         group.add_option("-c", "--config", dest="config", help="specify alternate config file")
94         group.add_option("-s", "--save", action="store_true", dest="save", default=False,
95                           help="save configuration to ~/.openerp_serverrc")
96         group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
97         group.add_option("-u", "--update", dest="update",
98                           help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
99         group.add_option("--without-demo", dest="without_demo",
100                           help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
101                           my_default=False)
102         group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
103                         help="Use this for big data importation, if it crashes you will be able to continue at the current state. Provide a filename to store intermediate importation states.")
104         group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
105         group.add_option("--addons-path", dest="addons_path",
106                          help="specify additional addons paths (separated by commas).",
107                          action="callback", callback=self._check_addons_path, nargs=1, type="string")
108         group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
109         parser.add_option_group(group)
110
111         # XML-RPC / HTTP
112         group = optparse.OptionGroup(parser, "XML-RPC Configuration")
113         group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
114                          help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
115         group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
116                          help="specify the TCP port for the XML-RPC protocol", type="int")
117         group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
118                          help="disable the XML-RPC protocol")
119         group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
120                          help="Enable correct behavior when behind a reverse proxy")
121         parser.add_option_group(group)
122
123         # XML-RPC / HTTPS
124         title = "XML-RPC Secure Configuration"
125         if not self.has_ssl:
126             title += " (disabled as ssl is unavailable)"
127
128         group = optparse.OptionGroup(parser, title)
129         group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
130                          help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
131         group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
132                          help="specify the TCP port for the XML-RPC Secure protocol", type="int")
133         group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
134                          help="disable the XML-RPC Secure protocol")
135         group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
136                          help="specify the certificate file for the SSL connection")
137         group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
138                          help="specify the private key file for the SSL connection")
139         parser.add_option_group(group)
140
141         # NET-RPC
142         group = optparse.OptionGroup(parser, "NET-RPC Configuration")
143         group.add_option("--netrpc-interface", dest="netrpc_interface", my_default='',
144                          help="specify the TCP IP address for the NETRPC protocol")
145         group.add_option("--netrpc-port", dest="netrpc_port", my_default=8070,
146                          help="specify the TCP port for the NETRPC protocol", type="int")
147         group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=True,
148                          help="disable the NETRPC protocol")
149         parser.add_option_group(group)
150
151         # WEB
152         # TODO move to web addons after MetaOption merge
153         group = optparse.OptionGroup(parser, "Web interface Configuration")
154         group.add_option("--db-filter", dest="dbfilter", default='.*',
155                          help="Filter listed database", metavar="REGEXP")
156         parser.add_option_group(group)
157
158         # Static HTTP
159         group = optparse.OptionGroup(parser, "Static HTTP service")
160         group.add_option("--static-http-enable", dest="static_http_enable", action="store_true", my_default=False, help="enable static HTTP service for serving plain HTML files")
161         group.add_option("--static-http-document-root", dest="static_http_document_root", help="specify the directory containing your static HTML files (e.g '/var/www/')")
162         group.add_option("--static-http-url-prefix", dest="static_http_url_prefix", help="specify the URL root prefix where you want web browsers to access your static HTML files (e.g '/')")
163         parser.add_option_group(group)
164
165         # Testing Group
166         group = optparse.OptionGroup(parser, "Testing Configuration")
167         group.add_option("--test-file", dest="test_file", my_default=False,
168                          help="Launch a YML test file.")
169         group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
170                          help="If set, will save sample of all reports in this directory.")
171         group.add_option("--test-enable", action="store_true", dest="test_enable",
172                          my_default=False, help="Enable YAML and unit tests.")
173         group.add_option("--test-commit", action="store_true", dest="test_commit",
174                          my_default=False, help="Commit database changes performed by YAML or XML tests.")
175         parser.add_option_group(group)
176
177         # Logging Group
178         group = optparse.OptionGroup(parser, "Logging Configuration")
179         group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
180         group.add_option("--no-logrotate", dest="logrotate", action="store_false", my_default=True, help="do not rotate the logfile")
181         group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
182         group.add_option('--log-handler', action="append", default=[':INFO'], my_default=[':INFO'], metavar="PREFIX:LEVEL", help='setup a handler at LEVEL for a given PREFIX. An empty PREFIX indicates the root logger. This option can be repeated. Example: "openerp.orm:DEBUG" or "werkzeug:CRITICAL" (default: ":INFO")')
183         group.add_option('--log-request', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.request:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.request:DEBUG')
184         group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.response:DEBUG')
185         group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.common.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.common.http:DEBUG')
186         group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG')
187         # For backward-compatibility, map the old log levels to something
188         # quite close.
189         levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
190             'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
191         group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
192             my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
193
194         parser.add_option_group(group)
195
196         # SMTP Group
197         group = optparse.OptionGroup(parser, "SMTP Configuration")
198         group.add_option('--email-from', dest='email_from', my_default=False,
199                          help='specify the SMTP email address for sending email')
200         group.add_option('--smtp', dest='smtp_server', my_default='localhost',
201                          help='specify the SMTP server for sending email')
202         group.add_option('--smtp-port', dest='smtp_port', my_default=25,
203                          help='specify the SMTP port', type="int")
204         group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
205                          help='specify the SMTP server support SSL or not')
206         group.add_option('--smtp-user', dest='smtp_user', my_default=False,
207                          help='specify the SMTP username for sending email')
208         group.add_option('--smtp-password', dest='smtp_password', my_default=False,
209                          help='specify the SMTP password for sending email')
210         parser.add_option_group(group)
211
212         group = optparse.OptionGroup(parser, "Database related options")
213         group.add_option("-d", "--database", dest="db_name", my_default=False,
214                          help="specify the database name")
215         group.add_option("-r", "--db_user", dest="db_user", my_default=False,
216                          help="specify the database user name")
217         group.add_option("-w", "--db_password", dest="db_password", my_default=False,
218                          help="specify the database password")
219         group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
220         group.add_option("--db_host", dest="db_host", my_default=False,
221                          help="specify the database host")
222         group.add_option("--db_port", dest="db_port", my_default=False,
223                          help="specify the database port", type="int")
224         group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
225                          help="specify the the maximum number of physical connections to posgresql")
226         group.add_option("--db-template", dest="db_template", my_default="template0",
227                          help="specify a custom database template to create a new database")
228         parser.add_option_group(group)
229
230         group = optparse.OptionGroup(parser, "Internationalisation options",
231             "Use these options to translate OpenERP to another language."
232             "See i18n section of the user manual. Option '-d' is mandatory."
233             "Option '-l' is mandatory in case of importation"
234             )
235         group.add_option('--load-language', dest="load_language",
236                          help="specifies the languages for the translations you want to be loaded")
237         group.add_option('-l', "--language", dest="language",
238                          help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
239         group.add_option("--i18n-export", dest="translate_out",
240                          help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
241         group.add_option("--i18n-import", dest="translate_in",
242                          help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
243         group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
244                          help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
245         group.add_option("--modules", dest="translate_modules",
246                          help="specify modules to export. Use in combination with --i18n-export")
247         parser.add_option_group(group)
248
249         security = optparse.OptionGroup(parser, 'Security-related options')
250         security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
251                             help="disable the ability to return the list of databases")
252         parser.add_option_group(security)
253
254         # Advanced options
255         group = optparse.OptionGroup(parser, "Advanced options")
256         group.add_option("--cache-timeout", dest="cache_timeout", my_default=100000,
257                           help="set the timeout for the cache system", type="int")
258         group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
259         group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
260                           help="stop the server after its initialization")
261         group.add_option("-t", "--timezone", dest="timezone", my_default=False,
262                          help="specify reference timezone for the server (e.g. Europe/Brussels")
263         group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
264                          help="Force a limit on the maximum number of records kept in the virtual "
265                               "osv_memory tables. The default is False, which means no count-based limit.",
266                          type="int")
267         group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
268                          help="Force a limit on the maximum age of records kept in the virtual "
269                               "osv_memory tables. This is a decimal value expressed in hours, "
270                               "and the default is 1 hour.",
271                          type="float")
272         group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=4,
273                          help="Maximum number of threads processing concurrently cron jobs.",
274                          type="int")
275         # TODO sensible default for the three following limits.
276         group.add_option("--virtual-memory-limit", dest="virtual_memory_limit", my_default=768 * 1024 * 1024,
277                          help="Maximum allowed virtual memory per Gunicorn process. "
278                          "When the limit is reached, any memory allocation will fail.",
279                          type="int")
280         group.add_option("--virtual-memory-reset", dest="virtual_memory_reset", my_default=640 * 1024 * 1024,
281                          help="Maximum allowed virtual memory per Gunicorn process. "
282                          "When the limit is reached, the worker will be reset after "
283                          "the current request.",
284                          type="int")
285         group.add_option("--cpu-time-limit", dest="cpu_time_limit", my_default=60,
286                          help="Maximum allowed CPU time per Gunicorn process. "
287                          "When the limit is reached, an exception is raised.",
288                          type="int")
289         group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
290                          help="Use the unaccent function provided by the database when available.")
291
292         parser.add_option_group(group)
293
294         # Copy all optparse options (i.e. MyOption) into self.options.
295         for group in parser.option_groups:
296             for option in group.option_list:
297                 self.options[option.dest] = option.my_default
298                 self.casts[option.dest] = option
299
300         self.parse_config(None, False)
301
302     def parse_config(self, args=None, complete=True):
303         """ Parse the configuration file (if any) and the command-line
304         arguments.
305
306         This method initializes openerp.tools.config and openerp.conf (the
307         former should be removed in the furture) with library-wide
308         configuration values.
309
310         This method must be called before proper usage of this library can be
311         made.
312
313         Typical usage of this method:
314
315             openerp.tools.config.parse_config(sys.argv[1:])
316
317         :param complete: this is a hack used in __init__(), leave it to True.
318
319         """
320         if args is None:
321             args = []
322         opt, args = self.parser.parse_args(args)
323
324         def die(cond, msg):
325             if cond:
326                 self.parser.error(msg)
327
328         # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
329         die(args, "unrecognized parameters: '%s'" % " ".join(args))
330
331         die(bool(opt.syslog) and bool(opt.logfile),
332             "the syslog and logfile options are exclusive")
333
334         die(opt.translate_in and (not opt.language or not opt.db_name),
335             "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
336
337         die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
338             "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
339
340         die(opt.translate_out and (not opt.db_name),
341             "the i18n-export option cannot be used without the database (-d) option")
342
343         # Check if the config file exists (-c used, but not -s)
344         die(not opt.save and opt.config and not os.path.exists(opt.config),
345             "The config file '%s' selected with -c/--config doesn't exist, "\
346             "use -s/--save if you want to generate it"%(opt.config))
347
348         # place/search the config file on Win32 near the server installation
349         # (../etc from the server)
350         # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write,
351         # else he won't be able to save the configurations, or even to start the server...
352         if os.name == 'nt':
353             rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
354         else:
355             rcfilepath = os.path.expanduser('~/.openerp_serverrc')
356
357         self.rcfile = os.path.abspath(
358             self.config_file or opt.config \
359                 or os.environ.get('OPENERP_SERVER') or rcfilepath)
360         self.load()
361
362
363         # Verify that we want to log or not, if not the output will go to stdout
364         if self.options['logfile'] in ('None', 'False'):
365             self.options['logfile'] = False
366         # the same for the pidfile
367         if self.options['pidfile'] in ('None', 'False'):
368             self.options['pidfile'] = False
369
370         # if defined dont take the configfile value even if the defined value is None
371         keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host',
372                 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout',
373                 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
374                 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path',
375                 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',
376                 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
377                 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
378                 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level'
379                 ]
380
381         for arg in keys:
382             # Copy the command-line argument...
383             if getattr(opt, arg):
384                 self.options[arg] = getattr(opt, arg)
385             # ... or keep, but cast, the config file value.
386             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
387                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
388
389         # if defined but None take the configfile value
390         keys = [
391             'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
392             'debug_mode', 'smtp_ssl', 'load_language',
393             'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
394             'list_db', 'xmlrpcs', 'proxy_mode',
395             'test_file', 'test_enable', 'test_commit', 'test_report_directory',
396             'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads',
397             'virtual_memory_limit', 'virtual_memory_reset', 'cpu_time_limit', 'unaccent',
398         ]
399
400         for arg in keys:
401             # Copy the command-line argument...
402             if getattr(opt, arg) is not None:
403                 self.options[arg] = getattr(opt, arg)
404             # ... or keep, but cast, the config file value.
405             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
406                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
407
408         self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
409         if not self.options['addons_path'] or self.options['addons_path']=='None':
410             self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
411         else:
412             self.options['addons_path'] = ",".join(
413                     os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
414                       for x in self.options['addons_path'].split(','))
415
416         self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
417         self.options["demo"] = not opt.without_demo and self.options['init'] or {}
418         self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
419         self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
420         self.options['translate_modules'].sort()
421
422         # TODO checking the type of the parameters should be done for every
423         # parameters, not just the timezone.
424         # The call to get_server_timezone() sets the timezone; this should
425         # probably done here.
426         if self.options['timezone']:
427             # Prevent the timezone to be True. (The config file parsing changes
428             # the string 'True' to the boolean value True. It would be probably
429             # be better to remove that conversion.)
430             die(not isinstance(self.options['timezone'], basestring),
431                 "Invalid timezone value in configuration or environment: %r.\n"
432                 "Please fix this in your configuration." %(self.options['timezone']))
433
434             # If an explicit TZ was provided in the config, make sure it is known
435             try:
436                 import pytz
437                 pytz.timezone(self.options['timezone'])
438             except pytz.UnknownTimeZoneError:
439                 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
440             except:
441                 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
442                 pass
443
444         if opt.pg_path:
445             self.options['pg_path'] = opt.pg_path
446
447         if self.options.get('language', False):
448             if len(self.options['language']) > 5:
449                 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
450
451         if not self.options['db_user']:
452             try:
453                 import getpass
454                 self.options['db_user'] = getpass.getuser()
455             except:
456                 self.options['db_user'] = None
457
458         die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
459
460         if self.options['db_password']:
461             if sys.platform == 'win32' and not self.options['db_host']:
462                 self.options['db_host'] = 'localhost'
463             #if self.options['db_host']:
464             #    self._generate_pgpassfile()
465
466         if opt.save:
467             self.save()
468
469         openerp.conf.max_cron_threads = self.options['max_cron_threads']
470
471         openerp.conf.addons_paths = self.options['addons_path'].split(',')
472         if opt.server_wide_modules:
473             openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
474         else:
475             openerp.conf.server_wide_modules = ['web']
476         if complete:
477             openerp.modules.module.initialize_sys_path()
478             openerp.modules.loading.open_openerp_namespace()
479
480     def _generate_pgpassfile(self):
481         """
482         Generate the pgpass file with the parameters from the command line (db_host, db_user,
483         db_password)
484
485         Used because pg_dump and pg_restore can not accept the password on the command line.
486         """
487         is_win32 = sys.platform == 'win32'
488         if is_win32:
489             filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
490         else:
491             filename = os.path.join(os.environ['HOME'], '.pgpass')
492
493         text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
494
495         if os.path.exists(filename):
496             content = [x.strip() for x in file(filename, 'r').readlines()]
497             if text_to_add in content:
498                 return
499
500         fp = file(filename, 'a+')
501         fp.write(text_to_add + "\n")
502         fp.close()
503
504         if is_win32:
505             try:
506                 import _winreg
507             except ImportError:
508                 _winreg = None
509             x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
510             y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
511             _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
512             _winreg.CloseKey(y)
513             _winreg.CloseKey(x)
514         else:
515             import stat
516             os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
517
518     def _is_addons_path(self, path):
519         for f in os.listdir(path):
520             modpath = os.path.join(path, f)
521             if os.path.isdir(modpath):
522                 def hasfile(filename):
523                     return os.path.isfile(os.path.join(modpath, filename))
524                 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
525                     return True
526         return False
527
528     def _check_addons_path(self, option, opt, value, parser):
529         ad_paths = []
530         for path in value.split(','):
531             path = path.strip()
532             res = os.path.abspath(os.path.expanduser(path))
533             if not os.path.isdir(res):
534                 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
535             if not self._is_addons_path(res):
536                 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
537             ad_paths.append(res)
538
539         setattr(parser.values, option.dest, ",".join(ad_paths))
540
541     def load(self):
542         p = ConfigParser.ConfigParser()
543         try:
544             p.read([self.rcfile])
545             for (name,value) in p.items('options'):
546                 if value=='True' or value=='true':
547                     value = True
548                 if value=='False' or value=='false':
549                     value = False
550                 self.options[name] = value
551             #parse the other sections, as well
552             for sec in p.sections():
553                 if sec == 'options':
554                     continue
555                 if not self.misc.has_key(sec):
556                     self.misc[sec]= {}
557                 for (name, value) in p.items(sec):
558                     if value=='True' or value=='true':
559                         value = True
560                     if value=='False' or value=='false':
561                         value = False
562                     self.misc[sec][name] = value
563         except IOError:
564             pass
565         except ConfigParser.NoSectionError:
566             pass
567
568     def save(self):
569         p = ConfigParser.ConfigParser()
570         loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
571         p.add_section('options')
572         for opt in sorted(self.options.keys()):
573             if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
574                 continue
575             if opt in self.blacklist_for_save:
576                 continue
577             if opt in ('log_level',):
578                 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
579             else:
580                 p.set('options', opt, self.options[opt])
581
582         for sec in sorted(self.misc.keys()):
583             p.add_section(sec)
584             for opt in sorted(self.misc[sec].keys()):
585                 p.set(sec,opt,self.misc[sec][opt])
586
587         # try to create the directories and write the file
588         try:
589             rc_exists = os.path.exists(self.rcfile)
590             if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
591                 os.makedirs(os.path.dirname(self.rcfile))
592             try:
593                 p.write(file(self.rcfile, 'w'))
594                 if not rc_exists:
595                     os.chmod(self.rcfile, 0600)
596             except IOError:
597                 sys.stderr.write("ERROR: couldn't write the config file\n")
598
599         except OSError:
600             # what to do if impossible?
601             sys.stderr.write("ERROR: couldn't create the config directory\n")
602
603     def get(self, key, default=None):
604         return self.options.get(key, default)
605
606     def get_misc(self, sect, key, default=None):
607         return self.misc.get(sect,{}).get(key, default)
608
609     def __setitem__(self, key, value):
610         self.options[key] = value
611
612     def __getitem__(self, key):
613         return self.options[key]
614
615 config = configmanager()
616
617
618 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: