Merge lp:~donkirkby/openobject-server/config-defaults into lp:openobject-server/5.0

Proposed by Don Kirkby
Status: Rejected
Rejected by: Olivier Dony (Odoo)
Proposed branch: lp:~donkirkby/openobject-server/config-defaults
Merge into: lp:openobject-server/5.0
Diff against target: 289 lines (+96/-64)
1 file modified
bin/tools/config.py (+96/-64)
To merge this branch: bzr merge lp:~donkirkby/openobject-server/config-defaults
Reviewer Review Type Date Requested Status
Olivier Dony (Odoo) Needs Resubmitting
Mantavya Gajjar (Open ERP) Pending
Xavier (Open ERP) Pending
Stephane Wirtel (OpenERP) Pending
Jay Vora (Serpent Consulting Services) Pending
Review via email: mp+19066@code.launchpad.net

Commit message

Make all configuration options available on command line and in configuration file as discussed in bug lp:392114

To post a comment you must log in.
Revision history for this message
Don Kirkby (donkirkby) wrote :

This is the work that I proposed in the bug discussion. I have tested it with all the options described in the code, and compared the in-memory values to those generated by

the old code. I also compared the configuration files saved by my new code and the old code.

In addition to adding support for all options in the configuration file and the command line, the following behaviours have changed:

- The reference to the soap option had been commented out from openerp-server.py and it wasn't allowed on the command line, so I removed it from the defaults.
- translate_modules now reads and writes to the configuration file in the same format that it uses on the command line. If it sees the old format, it continues to ignore it

during load, just like the old code did.
- netport and port used to be strings when loaded from a configuration file, and integers when loaded from the command line. They are now always integers.
- demo is no longer saved to the configuration file. (It was ignored during load, so this should be irrelevant.)

I did not get around to adding new names for the command line options that don't match their names in the configuration file.

I tried to break up the changes into smaller revisions to make the review easier. If there's something I can do to make my next proposal easier to review, please let me know.

1954. By Don Kirkby

merge 5.0.9 from 5.0 branch

Revision history for this message
Don Kirkby (donkirkby) wrote :

When I was merging release 5.0.9, I noticed that three people have touched the tools/config.py file recently, so I requested reviews from them for my fix to bug lp:392114 about configurations options not always working in both the config file and the command line.

1955. By Don Kirkby

[MERGE] release 5.0.15 from branch 5.0

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

Hello,

This is interesting, but please resubmit your changes for trunk, as we cannot merge any improvement on 5.0.
Also I'm not sure it makes sense to expose _all_ options as command-line args, because some could be harmful, like the super-admin password or the root_path...

Thanks!

review: Needs Resubmitting

Unmerged revisions

1955. By Don Kirkby

[MERGE] release 5.0.15 from branch 5.0

1954. By Don Kirkby

merge 5.0.9 from 5.0 branch

1953. By Don <don@don-desktop-openerp>

Remove debug code that I added in revision 1949.

1952. By Don <don@don-desktop-openerp>

Remove default values of None. Add some options to the command line that were only available in config files. Convert some parameters to strings for backward compatibility. Save translate_modules as comma separated list without brackets or quotes.

1951. By Don <don@don-desktop-openerp>

copy all options from OptionParser to config.options

1950. By Don <don@don-desktop-openerp>

move all default values from the option definitions to a dictionary called defaults.

1949. By Don <don@don-desktop-openerp>

some debug code to dump the contents of tools.config.options (I'll remove it before making a merge proposal)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/tools/config.py'
--- bin/tools/config.py 2010-09-08 12:55:30 +0000
+++ bin/tools/config.py 2010-11-05 21:37:16 +0000
@@ -40,7 +40,7 @@
40class configmanager(object):40class configmanager(object):
41 def __init__(self, fname=None):41 def __init__(self, fname=None):
42 hasSSL = check_ssl()42 hasSSL = check_ssl()
43 self.options = {43 defaults = {
44 'email_from':False,44 'email_from':False,
45 'interface': '', # this will bind the server to all interfaces45 'interface': '', # this will bind the server to all interfaces
46 'port': 8069,46 'port': 8069,
@@ -55,19 +55,11 @@
55 'reportgz': False,55 'reportgz': False,
56 'netrpc': True,56 'netrpc': True,
57 'xmlrpc': True,57 'xmlrpc': True,
58 'soap': False,58# 'soap': False, #reference is commented out in bin/openerp-server.py
59 'translate_in': None,
60 'translate_out': None,
61 'language': None,
62 'pg_path': None,
63 'admin_passwd': 'admin',59 'admin_passwd': 'admin',
64 'csv_internal_sep': ',',60 'csv_internal_sep': ',',
65 'addons_path': None,
66 'root_path': None,
67 'debug_mode': False,61 'debug_mode': False,
68 'import_partial': "",62 'import_partial': "",
69 'pidfile': None,
70 'logfile': None,
71 'smtp_server': 'localhost',63 'smtp_server': 'localhost',
72 'smtp_user': False,64 'smtp_user': False,
73 'smtp_port':25,65 'smtp_port':25,
@@ -76,14 +68,17 @@
76 'price_accuracy': 2,68 'price_accuracy': 2,
77 'secure' : False,69 'secure' : False,
78 'syslog' : False,70 'syslog' : False,
79 'log_level': logging.INFO,71 'log_level': 'info',
80 'assert_exit_level': logging.WARNING, # level above which a failed assert will be raise72 'assert_exit_level': 'warn', # level above which a failed assert will be raise
81 'cache_timeout': 100000,73 'cache_timeout': 100000,
82 'login_message': False,74 'login_message': False,
83 'list_db': True,75 'list_db': True,
76 'server_actions_allow_code': False,
77 'translate_modules': 'all',
78 'without_demo': False
84 }79 }
85 if hasSSL:80 if hasSSL:
86 self.options.update({81 defaults.update({
87 'secure_cert_file': 'server.cert',82 'secure_cert_file': 'server.cert',
88 'secure_pkey_file': 'server.pkey',83 'secure_pkey_file': 'server.pkey',
89 'smtp_ssl': False,84 'smtp_ssl': False,
@@ -97,7 +92,7 @@
97 parser = optparse.OptionParser(version=version)92 parser = optparse.OptionParser(version=version)
9893
99 parser.add_option("-c", "--config", dest="config", help="specify alternate config file")94 parser.add_option("-c", "--config", dest="config", help="specify alternate config file")
100 parser.add_option("-s", "--save", action="store_true", dest="save", default=False,95 parser.add_option("-s", "--save", action="store_true", dest="save",
101 help="save configuration to ~/.openerp_serverrc")96 help="save configuration to ~/.openerp_serverrc")
102 parser.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")97 parser.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
10398
@@ -109,16 +104,16 @@
109 parser.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", help="disable xmlrpc")104 parser.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", help="disable xmlrpc")
110 parser.add_option("-i", "--init", dest="init", help="init a module (use \"all\" for all modules)")105 parser.add_option("-i", "--init", dest="init", help="init a module (use \"all\" for all modules)")
111 parser.add_option("--without-demo", dest="without_demo",106 parser.add_option("--without-demo", dest="without_demo",
112 help="load demo data for a module (use \"all\" for all modules)", default=False)107 help="load demo data for a module (use \"all\" for all modules)")
113 parser.add_option("-u", "--update", dest="update",108 parser.add_option("-u", "--update", dest="update",
114 help="update a module (use \"all\" for all modules)")109 help="update a module (use \"all\" for all modules)")
115 parser.add_option("--cache-timeout", dest="cache_timeout",110 parser.add_option("--cache-timeout", dest="cache_timeout",
116 help="set the timeout for the cache system", type="int")111 help="set the timeout for the cache system", type="int")
117112
118 # stops the server from launching after initialization113 # stops the server from launching after initialization
119 parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init", default=False,114 parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init",
120 help="stop the server after it initializes")115 help="stop the server after it initializes")
121 parser.add_option('--debug', dest='debug_mode', action='store_true', default=False, help='enable debug mode')116 parser.add_option('--debug', dest='debug_mode', action='store_true', help='enable debug mode')
122 parser.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(),117 parser.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(),
123 help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),))118 help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),))
124 parser.add_option('--price_accuracy', dest='price_accuracy', type='int', help='specify the price accuracy')119 parser.add_option('--price_accuracy', dest='price_accuracy', type='int', help='specify the price accuracy')
@@ -133,11 +128,24 @@
133 help="specify the private key file for the SSL connection")128 help="specify the private key file for the SSL connection")
134 parser.add_option_group(group)129 parser.add_option_group(group)
135130
131 parser.add_option("--admin-passwd",
132 help="set the super administrator password " +
133 "(Warning: it is much better to set this in a " +
134 "configuration file because command line arguments " +
135 "are visible to other users.)")
136 parser.add_option("--csv-internal-sep",
137 help="set the separator character to use in import files")
138 parser.add_option("--login-message",
139 help="set the message displayed to users at log in")
140 parser.add_option("--reportgz",
141 action="store_true",
142 help="return reports in a compressed format")
143
136 # Logging Group144 # Logging Group
137 group = optparse.OptionGroup(parser, "Logging Configuration")145 group = optparse.OptionGroup(parser, "Logging Configuration")
138 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")146 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
139 group.add_option("--syslog", action="store_true", dest="syslog",147 group.add_option("--syslog", action="store_true", dest="syslog",
140 default=False, help="Send the log to the syslog server")148 help="Send the log to the syslog server")
141 group.add_option('--log-level', dest='log_level', type='choice', choices=self._LOGLEVELS.keys(),149 group.add_option('--log-level', dest='log_level', type='choice', choices=self._LOGLEVELS.keys(),
142 help='specify the level of the logging. Accepted values: ' + str(self._LOGLEVELS.keys()))150 help='specify the level of the logging. Accepted values: ' + str(self._LOGLEVELS.keys()))
143 parser.add_option_group(group)151 parser.add_option_group(group)
@@ -163,7 +171,7 @@
163 group.add_option("--db_maxconn", dest="db_maxconn", type='int',171 group.add_option("--db_maxconn", dest="db_maxconn", type='int',
164 help="specify the the maximum number of physical connections to posgresql")172 help="specify the the maximum number of physical connections to posgresql")
165 group.add_option("-P", "--import-partial", dest="import_partial",173 group.add_option("-P", "--import-partial", dest="import_partial",
166 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.", default=False)174 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.")
167 parser.add_option_group(group)175 parser.add_option_group(group)
168176
169 group = optparse.OptionGroup(parser, "Internationalisation options",177 group = optparse.OptionGroup(parser, "Internationalisation options",
@@ -183,12 +191,16 @@
183 group.add_option("--addons-path", dest="addons_path",191 group.add_option("--addons-path", dest="addons_path",
184 help="specify an alternative addons path.",192 help="specify an alternative addons path.",
185 action="callback", callback=self._check_addons_path, nargs=1, type="string")193 action="callback", callback=self._check_addons_path, nargs=1, type="string")
194 group.add_option("--root-path",
195 help="path to the core server code (defaults to the location of this script)",
196 action="callback", callback=self._check_addons_path, nargs=1, type="string")
186 parser.add_option_group(group)197 parser.add_option_group(group)
198 parser.set_defaults(**defaults)
187199
188 security = optparse.OptionGroup(parser, 'Security-related options')200 security = optparse.OptionGroup(parser, 'Security-related options')
189 security.add_option('--no-database-list', action="store_false", dest='list_db', help="disable the ability to return the list of databases")201 security.add_option('--no-database-list', action="store_false", dest='list_db', help="disable the ability to return the list of databases")
190 security.add_option('--enable-code-actions', action='store_true',202 security.add_option('--enable-code-actions', action='store_true',
191 dest='server_actions_allow_code', default=False,203 dest='server_actions_allow_code',
192 help='Enables server actions of state "code". Warning, this is a security risk.')204 help='Enables server actions of state "code". Warning, this is a security risk.')
193 parser.add_option_group(security)205 parser.add_option_group(security)
194206
@@ -199,15 +211,6 @@
199 print msg211 print msg
200 sys.exit(1)212 sys.exit(1)
201213
202 die(bool(opt.syslog) and bool(opt.logfile),
203 "the syslog and logfile options are exclusive")
204
205 die(opt.translate_in and (not opt.language or not opt.db_name),
206 "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
207
208 die(opt.translate_out and (not opt.db_name),
209 "the i18n-export option cannot be used without the database (-d) option")
210
211 # place/search the config file on Win32 near the server installation214 # place/search the config file on Win32 near the server installation
212 # (../etc from the server)215 # (../etc from the server)
213 # 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,216 # 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,
@@ -219,8 +222,34 @@
219222
220 self.rcfile = os.path.abspath(223 self.rcfile = os.path.abspath(
221 fname or opt.config or os.environ.get('OPENERP_SERVER') or rcfilepath)224 fname or opt.config or os.environ.get('OPENERP_SERVER') or rcfilepath)
222 self.load()225 if self.__load_defaults(defaults):
223226 parser.set_defaults(**defaults)
227 (opt, args) = parser.parse_args()
228
229 die(bool(opt.syslog) and bool(opt.logfile),
230 "the syslog and logfile options are exclusive")
231
232 die(opt.translate_in and (not opt.language or not opt.db_name),
233 "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
234
235 die(opt.translate_out and (not opt.db_name),
236 "the i18n-export option cannot be used without the database (-d) option")
237
238 self.options = {}
239 for (arg, value) in opt.__dict__.iteritems():
240 if arg in ('config', 'save'):
241 continue #options are only used during parse process
242 elif arg in ('db_maxconn', 'db_port', 'price_accuracy'):
243 #convert some numeric parameters back to strings just
244 #for backward compatibility
245 #netport and port used to be strings from a config file
246 #and ints from the command line, so now we leave them as ints
247 value = str(value)
248 if value == 'False':
249 value = False
250 elif arg == 'smtp_ssl' and value is None:
251 continue #backward compatibility
252 self.options[arg] = value
224253
225 # Verify that we want to log or not, if not the output will go to stdout254 # Verify that we want to log or not, if not the output will go to stdout
226 if self.options['logfile'] in ('None', 'False'):255 if self.options['logfile'] in ('None', 'False'):
@@ -229,29 +258,6 @@
229 if self.options['pidfile'] in ('None', 'False'):258 if self.options['pidfile'] in ('None', 'False'):
230 self.options['pidfile'] = False259 self.options['pidfile'] = False
231260
232 keys = ['interface', 'port', 'db_name', 'db_user', 'db_password', 'db_host',
233 'db_port', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout',
234 'email_from', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy',
235 'netinterface', 'netport', 'db_maxconn', 'import_partial', 'addons_path']
236
237 if hasSSL:
238 keys.extend(['secure_cert_file', 'secure_pkey_file'])
239
240 for arg in keys:
241 if getattr(opt, arg):
242 self.options[arg] = getattr(opt, arg)
243
244 keys = ['language', 'translate_out', 'translate_in', 'debug_mode',
245 'stop_after_init', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
246 'list_db', 'server_actions_allow_code']
247
248 if hasSSL and not self.options['secure']:
249 keys.extend(['secure', 'smtp_ssl'])
250
251 for arg in keys:
252 if getattr(opt, arg) is not None:
253 self.options[arg] = getattr(opt, arg)
254
255 if opt.assert_exit_level:261 if opt.assert_exit_level:
256 self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level]262 self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level]
257 else:263 else:
@@ -352,33 +358,59 @@
352358
353359
354 setattr(parser.values, option.dest, res)360 setattr(parser.values, option.dest, res)
355361
356 def load(self):362 def load(self):
363 """ Load values from a configuration file specified by self.rcfile
364 into the dictionary self.options.
365
366 Not used in the main code, just here for backward compatibility.
367 """
368 self.__load_defaults(self.options)
369
370 def __load_defaults(self, defaults):
371 """ Load values from a configuration specified by self.rcfile.
372
373 Returns true if the values were successfully loaded. Any reading
374 errors are swallowed and the method returns false.
375 """
357 p = ConfigParser.ConfigParser()376 p = ConfigParser.ConfigParser()
377 isLoaded = False
358 try:378 try:
359 p.read([self.rcfile])379 p.read([self.rcfile])
360 for (name,value) in p.items('options'):380 for (name,value) in p.items('options'):
361 if value=='True' or value=='true':381 if name == 'demo':
382 continue
383 elif name in ('init', 'update', 'translate_modules') and value.startswith(('[', '{')):
384 continue #old config file with invalid format so ignore
385 elif value=='True' or value=='true':
362 value = True386 value = True
363 if value=='False' or value=='false':387 elif value=='False' or value=='false':
364 value = False388 value = False
365 self.options[name] = value389 defaults[name] = value
390 isLoaded = True
366 except IOError:391 except IOError:
367 pass392 pass
368 except ConfigParser.NoSectionError:393 except ConfigParser.NoSectionError:
369 pass394 pass
395 return isLoaded
370396
371 def save(self):397 def save(self):
372 p = ConfigParser.ConfigParser()398 p = ConfigParser.ConfigParser()
373 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))399 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
374 p.add_section('options')400 p.add_section('options')
375 for opt in self.options.keys():401 for (opt, value) in self.options.iteritems():
376 if opt in ('version', 'language', 'translate_out', 'translate_in', 'init', 'update'):402 if opt in ('version', 'language', 'translate_out', 'translate_in', 'init', 'update', 'demo'):
377 continue403 continue
378 if opt in ('log_level', 'assert_exit_level'):404 if opt == 'translate_modules':
379 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))405 if not isinstance(value, list) or len(value) == 0:
380 else:406 continue
381 p.set('options', opt, self.options[opt])407 value.sort()
408 value = ','.join(value)
409 elif opt in ('log_level', 'assert_exit_level'):
410 default = value
411 value = loglevelnames.get(value, default)
412
413 p.set('options', opt, value)
382414
383 # try to create the directories and write the file415 # try to create the directories and write the file
384 try:416 try: