pre-commit, black, isort
This commit is contained in:
parent
d93bdf1b46
commit
40132984fd
|
|
@ -5,18 +5,12 @@
|
||||||
{
|
{
|
||||||
"name": "server configuration environment files",
|
"name": "server configuration environment files",
|
||||||
"version": "13.0.2.0.0",
|
"version": "13.0.2.0.0",
|
||||||
"depends": [
|
"depends": ["base", "base_sparse_field"],
|
||||||
"base",
|
|
||||||
"base_sparse_field",
|
|
||||||
],
|
|
||||||
"author": "Camptocamp,Odoo Community Association (OCA)",
|
"author": "Camptocamp,Odoo Community Association (OCA)",
|
||||||
"summary": "move some configurations out of the database",
|
"summary": "move some configurations out of the database",
|
||||||
"website": "http://github.com/OCA/server-env",
|
"website": "http://github.com/OCA/server-env",
|
||||||
"license": "GPL-3 or any later version",
|
"license": "GPL-3 or any later version",
|
||||||
"category": "Tools",
|
"category": "Tools",
|
||||||
"data": [
|
"data": ["security/res_groups.xml", "serv_config.xml"],
|
||||||
'security/res_groups.xml',
|
"installable": True,
|
||||||
'serv_config.xml',
|
|
||||||
],
|
|
||||||
'installable': True,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
# License GPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License GPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from functools import partialmethod
|
from functools import partialmethod
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
from ..server_env import serv_config
|
from ..server_env import serv_config
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
@ -96,19 +96,20 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
``keychain.backend``.
|
``keychain.backend``.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_name = 'server.env.mixin'
|
|
||||||
_description = 'Mixin to add server environment in existing models'
|
_name = "server.env.mixin"
|
||||||
|
_description = "Mixin to add server environment in existing models"
|
||||||
|
|
||||||
server_env_defaults = fields.Serialized()
|
server_env_defaults = fields.Serialized()
|
||||||
|
|
||||||
_server_env_getter_mapping = {
|
_server_env_getter_mapping = {
|
||||||
'integer': 'getint',
|
"integer": "getint",
|
||||||
'float': 'getfloat',
|
"float": "getfloat",
|
||||||
'monetary': 'getfloat',
|
"monetary": "getfloat",
|
||||||
'boolean': 'getboolean',
|
"boolean": "getboolean",
|
||||||
'char': 'get',
|
"char": "get",
|
||||||
'selection': 'get',
|
"selection": "get",
|
||||||
'text': 'get',
|
"text": "get",
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -180,16 +181,13 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
# _server_env_has_key_defined so we are sure that the value is
|
# _server_env_has_key_defined so we are sure that the value is
|
||||||
# either in the global or the record config
|
# either in the global or the record config
|
||||||
getter = getattr(serv_config, config_getter)
|
getter = getattr(serv_config, config_getter)
|
||||||
if (section_name in serv_config
|
if section_name in serv_config and field_name in serv_config[section_name]:
|
||||||
and field_name in serv_config[section_name]):
|
|
||||||
value = getter(section_name, field_name)
|
value = getter(section_name, field_name)
|
||||||
else:
|
else:
|
||||||
value = getter(global_section_name, field_name)
|
value = getter(global_section_name, field_name)
|
||||||
except Exception:
|
except Exception:
|
||||||
_logger.exception(
|
_logger.exception(
|
||||||
"error trying to read field %s in section %s",
|
"error trying to read field %s in section %s", field_name, section_name
|
||||||
field_name,
|
|
||||||
section_name,
|
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
return value
|
return value
|
||||||
|
|
@ -203,34 +201,31 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
and field_name in serv_config[global_section_name]
|
and field_name in serv_config[global_section_name]
|
||||||
)
|
)
|
||||||
has_config = (
|
has_config = (
|
||||||
section_name in serv_config
|
section_name in serv_config and field_name in serv_config[section_name]
|
||||||
and field_name in serv_config[section_name]
|
|
||||||
)
|
)
|
||||||
return has_global_config or has_config
|
return has_global_config or has_config
|
||||||
|
|
||||||
def _compute_server_env_from_config(self, field_name, options):
|
def _compute_server_env_from_config(self, field_name, options):
|
||||||
getter_name = options.get('getter') if options else None
|
getter_name = options.get("getter") if options else None
|
||||||
if not getter_name:
|
if not getter_name:
|
||||||
field_type = self._fields[field_name].type
|
field_type = self._fields[field_name].type
|
||||||
getter_name = self._server_env_getter_mapping.get(field_type)
|
getter_name = self._server_env_getter_mapping.get(field_type)
|
||||||
if not getter_name:
|
if not getter_name:
|
||||||
# if you get this message and the field is working as expected,
|
# if you get this message and the field is working as expected,
|
||||||
# you may want to add the type in _server_env_getter_mapping
|
# you may want to add the type in _server_env_getter_mapping
|
||||||
_logger.warning('server.env.mixin is used on a field of type %s, '
|
_logger.warning(
|
||||||
'which may not be supported properly')
|
"server.env.mixin is used on a field of type %s, "
|
||||||
getter_name = 'get'
|
"which may not be supported properly"
|
||||||
value = self._server_env_read_from_config(
|
|
||||||
field_name, getter_name
|
|
||||||
)
|
)
|
||||||
|
getter_name = "get"
|
||||||
|
value = self._server_env_read_from_config(field_name, getter_name)
|
||||||
self[field_name] = value
|
self[field_name] = value
|
||||||
|
|
||||||
def _compute_server_env_from_default(self, field_name, options):
|
def _compute_server_env_from_default(self, field_name, options):
|
||||||
if options and options.get('compute_default'):
|
if options and options.get("compute_default"):
|
||||||
getattr(self, options['compute_default'])()
|
getattr(self, options["compute_default"])()
|
||||||
else:
|
else:
|
||||||
default_field = self._server_env_default_fieldname(
|
default_field = self._server_env_default_fieldname(field_name)
|
||||||
field_name
|
|
||||||
)
|
|
||||||
if default_field:
|
if default_field:
|
||||||
self[field_name] = self[default_field]
|
self[field_name] = self[default_field]
|
||||||
else:
|
else:
|
||||||
|
|
@ -248,9 +243,7 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
record._compute_server_env_from_config(field_name, options)
|
record._compute_server_env_from_config(field_name, options)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
record._compute_server_env_from_default(
|
record._compute_server_env_from_default(field_name, options)
|
||||||
field_name, options
|
|
||||||
)
|
|
||||||
|
|
||||||
def _inverse_server_env(self, field_name):
|
def _inverse_server_env(self, field_name):
|
||||||
options = self._server_env_fields[field_name]
|
options = self._server_env_fields[field_name]
|
||||||
|
|
@ -262,8 +255,8 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
# we update the default value in database
|
# we update the default value in database
|
||||||
|
|
||||||
if record[is_editable_field]:
|
if record[is_editable_field]:
|
||||||
if options and options.get('inverse_default'):
|
if options and options.get("inverse_default"):
|
||||||
getattr(record, options['inverse_default'])()
|
getattr(record, options["inverse_default"])()
|
||||||
elif default_field:
|
elif default_field:
|
||||||
record[default_field] = record[field_name]
|
record[default_field] = record[field_name]
|
||||||
|
|
||||||
|
|
@ -278,12 +271,8 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
# in ``_inverse_server_env`` it would reset the value of the field
|
# in ``_inverse_server_env`` it would reset the value of the field
|
||||||
for record in self:
|
for record in self:
|
||||||
for field_name in self._server_env_fields:
|
for field_name in self._server_env_fields:
|
||||||
is_editable_field = self._server_env_is_editable_fieldname(
|
is_editable_field = self._server_env_is_editable_fieldname(field_name)
|
||||||
field_name
|
is_editable = not record._server_env_has_key_defined(field_name)
|
||||||
)
|
|
||||||
is_editable = not record._server_env_has_key_defined(
|
|
||||||
field_name
|
|
||||||
)
|
|
||||||
record[is_editable_field] = is_editable
|
record[is_editable_field] = is_editable
|
||||||
|
|
||||||
def _server_env_view_set_readonly(self, view_arch):
|
def _server_env_view_set_readonly(self, view_arch):
|
||||||
|
|
@ -293,37 +282,32 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
for elem in view_arch.findall(field_xpath % field):
|
for elem in view_arch.findall(field_xpath % field):
|
||||||
# set env-computed fields to readonly if the configuration
|
# set env-computed fields to readonly if the configuration
|
||||||
# files have a key set for this field
|
# files have a key set for this field
|
||||||
elem.set('attrs',
|
elem.set("attrs", str({"readonly": [(is_editable_field, "=", False)]}))
|
||||||
str({'readonly': [(is_editable_field, '=', False)]}))
|
|
||||||
if not view_arch.findall(field_xpath % is_editable_field):
|
if not view_arch.findall(field_xpath % is_editable_field):
|
||||||
# add the _is_editable fields in the view for the 'attrs'
|
# add the _is_editable fields in the view for the 'attrs'
|
||||||
# domain
|
# domain
|
||||||
view_arch.append(
|
view_arch.append(
|
||||||
etree.Element(
|
etree.Element("field", name=is_editable_field, invisible="1")
|
||||||
'field',
|
|
||||||
name=is_editable_field,
|
|
||||||
invisible="1"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return view_arch
|
return view_arch
|
||||||
|
|
||||||
def _fields_view_get(self, view_id=None, view_type='form', toolbar=False,
|
def _fields_view_get(
|
||||||
submenu=False):
|
self, view_id=None, view_type="form", toolbar=False, submenu=False
|
||||||
|
):
|
||||||
view_data = super()._fields_view_get(
|
view_data = super()._fields_view_get(
|
||||||
view_id=view_id, view_type=view_type,
|
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
|
||||||
toolbar=toolbar, submenu=submenu
|
|
||||||
)
|
)
|
||||||
view_arch = etree.fromstring(view_data['arch'].encode('utf-8'))
|
view_arch = etree.fromstring(view_data["arch"].encode("utf-8"))
|
||||||
view_arch = self._server_env_view_set_readonly(view_arch)
|
view_arch = self._server_env_view_set_readonly(view_arch)
|
||||||
view_data['arch'] = etree.tostring(view_arch, encoding='unicode')
|
view_data["arch"] = etree.tostring(view_arch, encoding="unicode")
|
||||||
return view_data
|
return view_data
|
||||||
|
|
||||||
def _server_env_default_fieldname(self, base_field_name):
|
def _server_env_default_fieldname(self, base_field_name):
|
||||||
"""Return the name of the field with default value"""
|
"""Return the name of the field with default value"""
|
||||||
options = self._server_env_fields[base_field_name]
|
options = self._server_env_fields[base_field_name]
|
||||||
if options and options.get('no_default_field'):
|
if options and options.get("no_default_field"):
|
||||||
return ''
|
return ""
|
||||||
return '%s_env_default' % (base_field_name,)
|
return "{}_env_default".format(base_field_name)
|
||||||
|
|
||||||
def _server_env_is_editable_fieldname(self, base_field_name):
|
def _server_env_is_editable_fieldname(self, base_field_name):
|
||||||
"""Return the name of the field for "is editable"
|
"""Return the name of the field for "is editable"
|
||||||
|
|
@ -331,16 +315,14 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
This is the field used to tell if the env-computed field can
|
This is the field used to tell if the env-computed field can
|
||||||
be edited.
|
be edited.
|
||||||
"""
|
"""
|
||||||
return '%s_env_is_editable' % (base_field_name,)
|
return "{}_env_is_editable".format(base_field_name)
|
||||||
|
|
||||||
def _server_env_transform_field_to_read_from_env(self, field):
|
def _server_env_transform_field_to_read_from_env(self, field):
|
||||||
"""Transform the original field in a computed field"""
|
"""Transform the original field in a computed field"""
|
||||||
field.compute = '_compute_server_env'
|
field.compute = "_compute_server_env"
|
||||||
|
|
||||||
inverse_method_name = '_inverse_server_env_%s' % field.name
|
inverse_method_name = "_inverse_server_env_%s" % field.name
|
||||||
inverse_method = partialmethod(
|
inverse_method = partialmethod(type(self)._inverse_server_env, field.name)
|
||||||
type(self)._inverse_server_env, field.name
|
|
||||||
)
|
|
||||||
setattr(type(self), inverse_method_name, inverse_method)
|
setattr(type(self), inverse_method_name, inverse_method)
|
||||||
field.inverse = inverse_method_name
|
field.inverse = inverse_method_name
|
||||||
field.store = False
|
field.store = False
|
||||||
|
|
@ -360,7 +342,7 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
# (inherits), we want to override it with a new one
|
# (inherits), we want to override it with a new one
|
||||||
if fieldname not in self._fields or self._fields[fieldname].inherited:
|
if fieldname not in self._fields or self._fields[fieldname].inherited:
|
||||||
field = fields.Boolean(
|
field = fields.Boolean(
|
||||||
compute='_compute_server_env_is_editable',
|
compute="_compute_server_env_is_editable",
|
||||||
automatic=True,
|
automatic=True,
|
||||||
# this is required to be able to edit fields
|
# this is required to be able to edit fields
|
||||||
# on new records
|
# on new records
|
||||||
|
|
@ -385,14 +367,11 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
if fieldname not in self._fields or self._fields[fieldname].inherited:
|
if fieldname not in self._fields or self._fields[fieldname].inherited:
|
||||||
base_field_cls = base_field.__class__
|
base_field_cls = base_field.__class__
|
||||||
field_args = base_field.args.copy()
|
field_args = base_field.args.copy()
|
||||||
field_args.pop('_sequence', None)
|
field_args.pop("_sequence", None)
|
||||||
field_args.update({
|
field_args.update({"sparse": "server_env_defaults", "automatic": True})
|
||||||
'sparse': 'server_env_defaults',
|
|
||||||
'automatic': True,
|
|
||||||
})
|
|
||||||
|
|
||||||
if hasattr(base_field, 'selection'):
|
if hasattr(base_field, "selection"):
|
||||||
field_args['selection'] = base_field.selection
|
field_args["selection"] = base_field.selection
|
||||||
field = base_field_cls(**field_args)
|
field = base_field_cls(**field_args)
|
||||||
self._add_field(fieldname, field)
|
self._add_field(fieldname, field)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,14 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
import configparser
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import configparser
|
|
||||||
from lxml import etree
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from odoo import api, models, fields
|
from lxml import etree
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
from odoo.tools.config import config as system_base_config
|
from odoo.tools.config import config as system_base_config
|
||||||
|
|
||||||
from .system_info import get_server_environment
|
from .system_info import get_server_environment
|
||||||
|
|
@ -33,19 +34,29 @@ _logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from odoo.addons import server_environment_files
|
from odoo.addons import server_environment_files
|
||||||
|
|
||||||
_dir = os.path.dirname(server_environment_files.__file__)
|
_dir = os.path.dirname(server_environment_files.__file__)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_logger.info('not using server_environment_files for configuration,'
|
_logger.info(
|
||||||
' no directory found')
|
"not using server_environment_files for configuration," " no directory found"
|
||||||
|
)
|
||||||
_dir = None
|
_dir = None
|
||||||
|
|
||||||
ENV_VAR_NAMES = ('SERVER_ENV_CONFIG', 'SERVER_ENV_CONFIG_SECRET')
|
ENV_VAR_NAMES = ("SERVER_ENV_CONFIG", "SERVER_ENV_CONFIG_SECRET")
|
||||||
|
|
||||||
# Same dict as RawConfigParser._boolean_states
|
# Same dict as RawConfigParser._boolean_states
|
||||||
_boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
|
_boolean_states = {
|
||||||
'0': False, 'no': False, 'false': False, 'off': False}
|
"1": True,
|
||||||
|
"yes": True,
|
||||||
|
"true": True,
|
||||||
|
"on": True,
|
||||||
|
"0": False,
|
||||||
|
"no": False,
|
||||||
|
"false": False,
|
||||||
|
"off": False,
|
||||||
|
}
|
||||||
|
|
||||||
if not system_base_config.get('running_env', False):
|
if not system_base_config.get("running_env", False):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"The parameter 'running_env' has not be set neither in base config "
|
"The parameter 'running_env' has not be set neither in base config "
|
||||||
"file option -c or in openerprc.\n"
|
"file option -c or in openerprc.\n"
|
||||||
|
|
@ -56,7 +67,7 @@ if not system_base_config.get('running_env', False):
|
||||||
|
|
||||||
ck_path = None
|
ck_path = None
|
||||||
if _dir:
|
if _dir:
|
||||||
ck_path = os.path.join(_dir, system_base_config['running_env'])
|
ck_path = os.path.join(_dir, system_base_config["running_env"])
|
||||||
|
|
||||||
if not os.path.exists(ck_path):
|
if not os.path.exists(ck_path):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
|
|
@ -77,25 +88,29 @@ def setboolean(obj, attr, _bool=None):
|
||||||
# Borrowed from MarkupSafe
|
# Borrowed from MarkupSafe
|
||||||
def _escape(s):
|
def _escape(s):
|
||||||
"""Convert the characters &<>'" in string s to HTML-safe sequences."""
|
"""Convert the characters &<>'" in string s to HTML-safe sequences."""
|
||||||
return (str(s).replace('&', '&')
|
return (
|
||||||
.replace('>', '>')
|
str(s)
|
||||||
.replace('<', '<')
|
.replace("&", "&")
|
||||||
.replace("'", ''')
|
.replace(">", ">")
|
||||||
.replace('"', '"'))
|
.replace("<", "<")
|
||||||
|
.replace("'", "'")
|
||||||
|
.replace('"', """)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _listconf(env_path):
|
def _listconf(env_path):
|
||||||
"""List configuration files in a folder."""
|
"""List configuration files in a folder."""
|
||||||
files = [os.path.join(env_path, name)
|
files = [
|
||||||
|
os.path.join(env_path, name)
|
||||||
for name in sorted(os.listdir(env_path))
|
for name in sorted(os.listdir(env_path))
|
||||||
if name.endswith('.conf')]
|
if name.endswith(".conf")
|
||||||
|
]
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
|
||||||
def _load_config_from_server_env_files(config_p):
|
def _load_config_from_server_env_files(config_p):
|
||||||
default = os.path.join(_dir, 'default')
|
default = os.path.join(_dir, "default")
|
||||||
running_env = os.path.join(_dir,
|
running_env = os.path.join(_dir, system_base_config["running_env"])
|
||||||
system_base_config['running_env'])
|
|
||||||
if os.path.isdir(default):
|
if os.path.isdir(default):
|
||||||
conf_files = _listconf(default) + _listconf(running_env)
|
conf_files = _listconf(default) + _listconf(running_env)
|
||||||
else:
|
else:
|
||||||
|
|
@ -104,12 +119,12 @@ def _load_config_from_server_env_files(config_p):
|
||||||
try:
|
try:
|
||||||
config_p.read(conf_files)
|
config_p.read(conf_files)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception('Cannot read config files "%s": %s' % (conf_files, e))
|
raise Exception('Cannot read config files "{}": {}'.format(conf_files, e))
|
||||||
|
|
||||||
|
|
||||||
def _load_config_from_rcfile(config_p):
|
def _load_config_from_rcfile(config_p):
|
||||||
config_p.read(system_base_config.rcfile)
|
config_p.read(system_base_config.rcfile)
|
||||||
config_p.remove_section('options')
|
config_p.remove_section("options")
|
||||||
|
|
||||||
|
|
||||||
def _load_config_from_env(config_p):
|
def _load_config_from_env(config_p):
|
||||||
|
|
@ -120,8 +135,7 @@ def _load_config_from_env(config_p):
|
||||||
config_p.read_string(env_config)
|
config_p.read_string(env_config)
|
||||||
except configparser.Error as err:
|
except configparser.Error as err:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'%s content could not be parsed: %s'
|
"{} content could not be parsed: {}".format(varname, err)
|
||||||
% (varname, err,)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -147,13 +161,15 @@ class _Defaults(dict):
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
def func(*a):
|
def func(*a):
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
return dict.__setitem__(self, key, func)
|
return dict.__setitem__(self, key, func)
|
||||||
|
|
||||||
|
|
||||||
class ServerConfiguration(models.TransientModel):
|
class ServerConfiguration(models.TransientModel):
|
||||||
"""Display server configuration."""
|
"""Display server configuration."""
|
||||||
_name = 'server.config'
|
|
||||||
_description = 'Display server configuration'
|
_name = "server.config"
|
||||||
|
_description = "Display server configuration"
|
||||||
_conf_defaults = _Defaults()
|
_conf_defaults = _Defaults()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -164,20 +180,20 @@ class ServerConfiguration(models.TransientModel):
|
||||||
"""
|
"""
|
||||||
ModelClass = super(ServerConfiguration, cls)._build_model(pool, cr)
|
ModelClass = super(ServerConfiguration, cls)._build_model(pool, cr)
|
||||||
ModelClass._add_columns()
|
ModelClass._add_columns()
|
||||||
ModelClass.running_env = system_base_config['running_env']
|
ModelClass.running_env = system_base_config["running_env"]
|
||||||
# Only show passwords in development
|
# Only show passwords in development
|
||||||
ModelClass.show_passwords = ModelClass.running_env in ('dev',)
|
ModelClass.show_passwords = ModelClass.running_env in ("dev",)
|
||||||
ModelClass._arch = None
|
ModelClass._arch = None
|
||||||
ModelClass._build_osv()
|
ModelClass._build_osv()
|
||||||
return ModelClass
|
return ModelClass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _format_key(cls, section, key):
|
def _format_key(cls, section, key):
|
||||||
return '%s_I_%s' % (section, key)
|
return "{}_I_{}".format(section, key)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _format_key_display_name(cls, key_name):
|
def _format_key_display_name(cls, key_name):
|
||||||
return key_name.replace('_I_', ' | ')
|
return key_name.replace("_I_", " | ")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _add_columns(cls):
|
def _add_columns(cls):
|
||||||
|
|
@ -185,15 +201,16 @@ class ServerConfiguration(models.TransientModel):
|
||||||
cols = chain(
|
cols = chain(
|
||||||
list(cls._get_base_cols().items()),
|
list(cls._get_base_cols().items()),
|
||||||
list(cls._get_env_cols().items()),
|
list(cls._get_env_cols().items()),
|
||||||
list(cls._get_system_cols().items())
|
list(cls._get_system_cols().items()),
|
||||||
)
|
)
|
||||||
for col, value in cols:
|
for col, value in cols:
|
||||||
col_name = col.replace('.', '_')
|
col_name = col.replace(".", "_")
|
||||||
setattr(ServerConfiguration,
|
setattr(
|
||||||
|
ServerConfiguration,
|
||||||
col_name,
|
col_name,
|
||||||
fields.Char(
|
fields.Char(
|
||||||
string=cls._format_key_display_name(col_name),
|
string=cls._format_key_display_name(col_name), readonly=True
|
||||||
readonly=True)
|
),
|
||||||
)
|
)
|
||||||
cls._conf_defaults[col_name] = value
|
cls._conf_defaults[col_name] = value
|
||||||
|
|
||||||
|
|
@ -202,7 +219,7 @@ class ServerConfiguration(models.TransientModel):
|
||||||
""" Compute base fields"""
|
""" Compute base fields"""
|
||||||
res = {}
|
res = {}
|
||||||
for col, item in list(system_base_config.options.items()):
|
for col, item in list(system_base_config.options.items()):
|
||||||
key = cls._format_key('odoo', col)
|
key = cls._format_key("odoo", col)
|
||||||
res[key] = item
|
res[key] = item
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
@ -222,7 +239,7 @@ class ServerConfiguration(models.TransientModel):
|
||||||
""" Compute system fields"""
|
""" Compute system fields"""
|
||||||
res = {}
|
res = {}
|
||||||
for col, item in get_server_environment():
|
for col, item in get_server_environment():
|
||||||
key = cls._format_key('system', col)
|
key = cls._format_key("system", col)
|
||||||
res[key] = item
|
res[key] = item
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
@ -232,17 +249,19 @@ class ServerConfiguration(models.TransientModel):
|
||||||
names = []
|
names = []
|
||||||
|
|
||||||
for key in sorted(items):
|
for key in sorted(items):
|
||||||
names.append(key.replace('.', '_'))
|
names.append(key.replace(".", "_"))
|
||||||
return ('<group col="2" colspan="4">' +
|
return (
|
||||||
''.join(['<field name="%s" readonly="1"/>' %
|
'<group col="2" colspan="4">'
|
||||||
_escape(name) for name in names]) +
|
+ "".join(
|
||||||
'</group>')
|
['<field name="%s" readonly="1"/>' % _escape(name) for name in names]
|
||||||
|
)
|
||||||
|
+ "</group>"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _build_osv(cls):
|
def _build_osv(cls):
|
||||||
"""Build the view for the current configuration."""
|
"""Build the view for the current configuration."""
|
||||||
arch = ('<form string="Configuration Form">'
|
arch = '<form string="Configuration Form">' '<notebook colspan="4">'
|
||||||
'<notebook colspan="4">')
|
|
||||||
|
|
||||||
# Odoo server configuration
|
# Odoo server configuration
|
||||||
rcfile = system_base_config.rcfile
|
rcfile = system_base_config.rcfile
|
||||||
|
|
@ -265,23 +284,23 @@ class ServerConfiguration(models.TransientModel):
|
||||||
arch += cls._group(cls._get_system_cols())
|
arch += cls._group(cls._get_system_cols())
|
||||||
arch += '<separator colspan="4"/></page>'
|
arch += '<separator colspan="4"/></page>'
|
||||||
|
|
||||||
arch += '</notebook></form>'
|
arch += "</notebook></form>"
|
||||||
cls._arch = etree.fromstring(arch)
|
cls._arch = etree.fromstring(arch)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def fields_view_get(self, view_id=None, view_type='form', toolbar=False,
|
def fields_view_get(
|
||||||
submenu=False):
|
self, view_id=None, view_type="form", toolbar=False, submenu=False
|
||||||
|
):
|
||||||
"""Overwrite the default method to render the custom view."""
|
"""Overwrite the default method to render the custom view."""
|
||||||
res = super(ServerConfiguration, self).fields_view_get(view_id,
|
res = super(ServerConfiguration, self).fields_view_get(
|
||||||
view_type,
|
view_id, view_type, toolbar
|
||||||
toolbar)
|
)
|
||||||
View = self.env['ir.ui.view']
|
View = self.env["ir.ui.view"]
|
||||||
if view_type == 'form':
|
if view_type == "form":
|
||||||
arch_node = self._arch
|
arch_node = self._arch
|
||||||
xarch, xfields = View.postprocess_and_fields(
|
xarch, xfields = View.postprocess_and_fields(self._name, arch_node, view_id)
|
||||||
self._name, arch_node, view_id)
|
res["arch"] = xarch
|
||||||
res['arch'] = xarch
|
res["fields"] = xfields
|
||||||
res['fields'] = xfields
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
|
|
@ -291,18 +310,19 @@ class ServerConfiguration(models.TransientModel):
|
||||||
should be secret.
|
should be secret.
|
||||||
:return: list of secret keywords
|
:return: list of secret keywords
|
||||||
"""
|
"""
|
||||||
secret_keys = ['passw', 'key', 'secret', 'token']
|
secret_keys = ["passw", "key", "secret", "token"]
|
||||||
return any(secret_key in key for secret_key in secret_keys)
|
return any(secret_key in key for secret_key in secret_keys)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def default_get(self, fields_list):
|
def default_get(self, fields_list):
|
||||||
res = {}
|
res = {}
|
||||||
if not self.env.user.has_group(
|
if not self.env.user.has_group(
|
||||||
'server_environment.has_server_configuration_access'):
|
"server_environment.has_server_configuration_access"
|
||||||
|
):
|
||||||
return res
|
return res
|
||||||
for key in self._conf_defaults:
|
for key in self._conf_defaults:
|
||||||
if not self.show_passwords and self._is_secret(key=key):
|
if not self.show_passwords and self._is_secret(key=key):
|
||||||
res[key] = '**********'
|
res[key] = "**********"
|
||||||
else:
|
else:
|
||||||
res[key] = self._conf_defaults[key]()
|
res[key] = self._conf_defaults[key]()
|
||||||
return res
|
return res
|
||||||
|
|
|
||||||
|
|
@ -28,38 +28,39 @@ from odoo.tools.config import config
|
||||||
|
|
||||||
|
|
||||||
def _get_output(cmd):
|
def _get_output(cmd):
|
||||||
bindir = config['root_path']
|
bindir = config["root_path"]
|
||||||
p = subprocess.Popen(cmd, shell=True, cwd=bindir, stdout=subprocess.PIPE,
|
p = subprocess.Popen(
|
||||||
stderr=subprocess.STDOUT)
|
cmd, shell=True, cwd=bindir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||||
|
)
|
||||||
return p.communicate()[0].rstrip()
|
return p.communicate()[0].rstrip()
|
||||||
|
|
||||||
|
|
||||||
def get_server_environment():
|
def get_server_environment():
|
||||||
# inspired by server/bin/service/web_services.py
|
# inspired by server/bin/service/web_services.py
|
||||||
try:
|
try:
|
||||||
rev_id = 'git:%s' % _get_output('git rev-parse HEAD')
|
rev_id = "git:%s" % _get_output("git rev-parse HEAD")
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
rev_id = 'bzr: %s' % _get_output('bzr revision-info')
|
rev_id = "bzr: %s" % _get_output("bzr revision-info")
|
||||||
except Exception:
|
except Exception:
|
||||||
rev_id = 'Can not retrieve revison from git or bzr'
|
rev_id = "Can not retrieve revison from git or bzr"
|
||||||
|
|
||||||
os_lang = '.'.join([x for x in locale.getdefaultlocale() if x])
|
os_lang = ".".join([x for x in locale.getdefaultlocale() if x])
|
||||||
if not os_lang:
|
if not os_lang:
|
||||||
os_lang = 'NOT SET'
|
os_lang = "NOT SET"
|
||||||
if os.name == 'posix' and platform.system() == 'Linux':
|
if os.name == "posix" and platform.system() == "Linux":
|
||||||
lsbinfo = _get_output('lsb_release -a')
|
lsbinfo = _get_output("lsb_release -a")
|
||||||
else:
|
else:
|
||||||
lsbinfo = 'not lsb compliant'
|
lsbinfo = "not lsb compliant"
|
||||||
return (
|
return (
|
||||||
('platform', platform.platform()),
|
("platform", platform.platform()),
|
||||||
('os.name', os.name),
|
("os.name", os.name),
|
||||||
('lsb_release', lsbinfo),
|
("lsb_release", lsbinfo),
|
||||||
('release', platform.release()),
|
("release", platform.release()),
|
||||||
('version', platform.version()),
|
("version", platform.version()),
|
||||||
('architecture', platform.architecture()[0]),
|
("architecture", platform.architecture()[0]),
|
||||||
('locale', os_lang),
|
("locale", os_lang),
|
||||||
('python', platform.python_version()),
|
("python", platform.python_version()),
|
||||||
('odoo', release.version),
|
("odoo", release.version),
|
||||||
('revision', rev_id),
|
("revision", rev_id),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,27 @@ from contextlib import contextmanager
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from odoo.tests import common
|
from odoo.tests import common
|
||||||
from odoo.addons.server_environment import server_env
|
|
||||||
from odoo.tools.config import config
|
from odoo.tools.config import config
|
||||||
|
|
||||||
import odoo.addons.server_environment.models.server_env_mixin as \
|
import odoo.addons.server_environment.models.server_env_mixin as server_env_mixin
|
||||||
server_env_mixin
|
from odoo.addons.server_environment import server_env
|
||||||
|
|
||||||
|
|
||||||
class ServerEnvironmentCase(common.SavepointCase):
|
class ServerEnvironmentCase(common.SavepointCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self._original_running_env = config.get('running_env')
|
self._original_running_env = config.get("running_env")
|
||||||
config['running_env'] = 'testing'
|
config["running_env"] = "testing"
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
config['running_env'] = self._original_running_env
|
config["running_env"] = self._original_running_env
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def set_config_dir(self, path):
|
def set_config_dir(self, path):
|
||||||
original_dir = server_env._dir
|
original_dir = server_env._dir
|
||||||
if path and not os.path.isabs(path):
|
if path and not os.path.isabs(path):
|
||||||
path = os.path.join(os.path.dirname(__file__,), path)
|
path = os.path.join(os.path.dirname(__file__), path)
|
||||||
server_env._dir = path
|
server_env._dir = path
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
|
|
@ -39,18 +37,17 @@ class ServerEnvironmentCase(common.SavepointCase):
|
||||||
def set_env_variables(self, public=None, secret=None):
|
def set_env_variables(self, public=None, secret=None):
|
||||||
newkeys = {}
|
newkeys = {}
|
||||||
if public:
|
if public:
|
||||||
newkeys['SERVER_ENV_CONFIG'] = public
|
newkeys["SERVER_ENV_CONFIG"] = public
|
||||||
if secret:
|
if secret:
|
||||||
newkeys['SERVER_ENV_CONFIG_SECRET'] = secret
|
newkeys["SERVER_ENV_CONFIG_SECRET"] = secret
|
||||||
with patch.dict('os.environ', newkeys):
|
with patch.dict("os.environ", newkeys):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def load_config(self, public=None, secret=None):
|
def load_config(self, public=None, secret=None):
|
||||||
original_serv_config = server_env_mixin.serv_config
|
original_serv_config = server_env_mixin.serv_config
|
||||||
try:
|
try:
|
||||||
with self.set_config_dir(None), \
|
with self.set_config_dir(None), self.set_env_variables(public, secret):
|
||||||
self.set_env_variables(public, secret):
|
|
||||||
parser = server_env._load_config()
|
parser = server_env._load_config()
|
||||||
server_env_mixin.serv_config = parser
|
server_env_mixin.serv_config = parser
|
||||||
yield
|
yield
|
||||||
|
|
|
||||||
|
|
@ -3,43 +3,25 @@
|
||||||
|
|
||||||
|
|
||||||
from odoo.addons.server_environment import server_env
|
from odoo.addons.server_environment import server_env
|
||||||
|
|
||||||
from .common import ServerEnvironmentCase
|
from .common import ServerEnvironmentCase
|
||||||
|
|
||||||
|
|
||||||
class TestEnvironmentVariables(ServerEnvironmentCase):
|
class TestEnvironmentVariables(ServerEnvironmentCase):
|
||||||
|
|
||||||
def test_env_variables(self):
|
def test_env_variables(self):
|
||||||
public = (
|
public = "[section]\n" "foo=bar\n" "bar=baz\n"
|
||||||
"[section]\n"
|
secret = "[section]\n" "bar=foo\n" "alice=bob\n"
|
||||||
"foo=bar\n"
|
with self.set_config_dir(None), self.set_env_variables(public, secret):
|
||||||
"bar=baz\n"
|
|
||||||
)
|
|
||||||
secret = (
|
|
||||||
"[section]\n"
|
|
||||||
"bar=foo\n"
|
|
||||||
"alice=bob\n"
|
|
||||||
)
|
|
||||||
with self.set_config_dir(None), \
|
|
||||||
self.set_env_variables(public, secret):
|
|
||||||
parser = server_env._load_config()
|
parser = server_env._load_config()
|
||||||
self.assertEqual(
|
self.assertEqual(list(parser.keys()), ["DEFAULT", "section"])
|
||||||
list(parser.keys()),
|
|
||||||
['DEFAULT', 'section']
|
|
||||||
)
|
|
||||||
self.assertDictEqual(
|
self.assertDictEqual(
|
||||||
dict(parser['section'].items()),
|
dict(parser["section"].items()),
|
||||||
{'alice': 'bob',
|
{"alice": "bob", "bar": "foo", "foo": "bar"},
|
||||||
'bar': 'foo',
|
|
||||||
'foo': 'bar'}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_env_variables_override(self):
|
def test_env_variables_override(self):
|
||||||
public = (
|
public = "[external_service.ftp]\n" "user=foo\n"
|
||||||
"[external_service.ftp]\n"
|
with self.set_config_dir("testfiles"), self.set_env_variables(public):
|
||||||
"user=foo\n"
|
|
||||||
)
|
|
||||||
with self.set_config_dir('testfiles'), \
|
|
||||||
self.set_env_variables(public):
|
|
||||||
parser = server_env._load_config()
|
parser = server_env._load_config()
|
||||||
val = parser.get('external_service.ftp', 'user')
|
val = parser.get("external_service.ftp", "user")
|
||||||
self.assertEqual(val, 'foo')
|
self.assertEqual(val, "foo")
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,33 @@
|
||||||
# Copyright 2018 Camptocamp (https://www.camptocamp.com).
|
# Copyright 2018 Camptocamp (https://www.camptocamp.com).
|
||||||
# License GPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License GPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
from odoo.addons.server_environment import server_env
|
from odoo.addons.server_environment import server_env
|
||||||
|
|
||||||
from . import common
|
from . import common
|
||||||
|
|
||||||
|
|
||||||
class TestEnv(common.ServerEnvironmentCase):
|
class TestEnv(common.ServerEnvironmentCase):
|
||||||
|
|
||||||
def test_view(self):
|
def test_view(self):
|
||||||
model = self.env['server.config']
|
model = self.env["server.config"]
|
||||||
view = model.fields_view_get()
|
view = model.fields_view_get()
|
||||||
self.assertTrue(view)
|
self.assertTrue(view)
|
||||||
|
|
||||||
def test_default(self):
|
def test_default(self):
|
||||||
model = self.env['server.config']
|
model = self.env["server.config"]
|
||||||
rec = model.create({})
|
rec = model.create({})
|
||||||
defaults = rec.default_get([])
|
defaults = rec.default_get([])
|
||||||
self.assertTrue(defaults)
|
self.assertTrue(defaults)
|
||||||
self.assertIsInstance(defaults, dict)
|
self.assertIsInstance(defaults, dict)
|
||||||
pass_checked = False
|
pass_checked = False
|
||||||
for default in defaults:
|
for default in defaults:
|
||||||
if 'passw' in default:
|
if "passw" in default:
|
||||||
self.assertNotEqual(defaults[default],
|
self.assertNotEqual(defaults[default], "**********")
|
||||||
'**********')
|
|
||||||
pass_checked = True
|
pass_checked = True
|
||||||
self.assertTrue(pass_checked)
|
self.assertTrue(pass_checked)
|
||||||
|
|
||||||
def test_value_retrival(self):
|
def test_value_retrival(self):
|
||||||
with self.set_config_dir('testfiles'):
|
with self.set_config_dir("testfiles"):
|
||||||
parser = server_env._load_config()
|
parser = server_env._load_config()
|
||||||
val = parser.get('external_service.ftp', 'user')
|
val = parser.get("external_service.ftp", "user")
|
||||||
self.assertEqual(val, 'testing')
|
self.assertEqual(val, "testing")
|
||||||
val = parser.get('external_service.ftp', 'host')
|
val = parser.get("external_service.ftp", "host")
|
||||||
self.assertEqual(val, 'sftp.example.com')
|
self.assertEqual(val, "sftp.example.com")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue