creole/creole/loader1.py

770 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
#import cjson
import json
import fcntl
import stat
import logging
from os.path import isdir, isfile, join, basename, dirname, splitext
from os import listdir, makedirs, major, minor
from os import stat as os_stat
from distutils.version import StrictVersion
try:
from collections import OrderedDict
except:
from pyeole.odict import OrderedDict
from tiramisu.option import UnicodeOption, OptionDescription, \
IntOption, ChoiceOption, BoolOption, SymLinkOption, IPOption, \
NetworkOption, NetmaskOption
from tiramisu.error import PropertiesOptionError, LeadershipError
from tiramisu.setting import owners
from .config import configeol, eoledirs, dtdfilename, eoleextradico, \
eoleextraconfig, forbiddenextra, VIRTROOT, \
VIRTBASE, VIRTMASTER, templatedir
from .error import ConfigError
from .var_loader import modes_level, CreoleFamily, CreoleConstraint, \
CreoleVarLoader
try:
from .client import CreoleClient, CreoleClientError
client = CreoleClient()
except:
client = None
from pyeole.encode import normalize
try:
from .eosfunc import is_instanciate, get_version
except:
pass
from .i18n import _
log = logging.getLogger(__name__)
class CreoleContainer():
"""
Charge les conteneurs, les fichiers, les packages, services, interfaces
et disknods
"""
def gen_containers(self, paths):
"""
Generate Containers information in tiramisu tree
:paths: paths variables (for added new option in paths's dictionnary)
"""
containers = []
for name, container in self._get_containers().items():
container['path'] = 'container_path_{0}'.format(name)
container['ip'] = 'container_ip_{0}'.format(name)
containers.append(container)
key_type = {'id': IntOption, 'group': UnicodeOption,
'ip': SymLinkOption, 'path': SymLinkOption,
'level': UnicodeOption}
return self._gen_tiramisu_config(paths, "container", containers,
key_type)
def gen_networks(self, paths):
var = []
descr = None
namespace = paths['adresse_ip_br0'].split('.')[0]
for descr_ in self.space:
if descr_._name == namespace:
descr = descr_
break
if descr == None:
raise Exception(_(u'Unable to find namespace: {0}').format(
namespace))
for name in ['adresse_ip_br0', 'adresse_netmask_br0',
'adresse_network_br0', 'adresse_broadcast_br0']:
path = paths[name]
subpath = path.split('.')[1:]
opt = descr
for p in subpath:
opt = getattr(opt, p)
var.append(SymLinkOption(name, opt))
return OptionDescription('network', '', var)
def gen_interfaces(self, paths):
"""Add per container interface linked to inter-containers bridge
Theses interfaces must come before other containers ones as
default gateway.
"""
lxc_net = OrderedDict()
if self.containers_enabled:
interfaces = OrderedDict()
containers = self._get_containers()
for name, container in containers.items():
if name in ['all', 'root']:
continue
lxc_net[name] = {'name': 'containers',
'container': name,
'linkto': 'br0',
'method': 'bridge',
'ip': 'container_ip_{0}'.format(name),
'mask': 'adresse_netmask_br0',
'bcast': 'adresse_broadcast_br0',
'gateway': 'adresse_ip_br0'}
# Insert default interfaces before
self.generic['interfaces'] = lxc_net.values() \
+ self.generic['interfaces']
return self.gen_generic('interfaces', paths, copy_requires='ip')
def gen_service_accesss(self, paths):
return self.__gen_service_access_restriction('service_access', paths)
def gen_service_restrictions(self, paths):
return self.__gen_service_access_restriction('service_restriction', paths)
def __gen_service_access_restriction(self, service_type, paths):
"""Add services requires to service_access/service_restriction
If a service is disabled, we remove, also, access to this service
"""
generic_name = service_type + 's'
list_name = service_type + 'list'
if 'service' in self.requires:
for gen in self.generic[generic_name]:
service_name = gen['service']
requires_name = gen.get(list_name)
if requires_name is None:
requires_name = '___auto_{0}'.format(service_name)
gen[list_name] = requires_name
self.requires[service_type][requires_name] = {'optional': True, 'list': []}
if service_name in self.requires['service']:
service_requires = self.requires['service'][service_name]['list']
if self.requires['service'][service_name]['optional'] is False:
self.requires['service'][service_name]['optional'] = False
self.requires[service_type][requires_name]['list'].extend(service_requires)
return self.gen_generic(generic_name, paths, verify_exists_redefine=False)
def _gen_file(self, fdata, container, containers):
"""Generate one file structure for one container
:param fdata: file informations
:type fdata: `dict`
:param container: container of the file
:type container: `dict`
:return: file information for a container
:rtype: `dict`
"""
file_infos = fdata.copy()
# take care of os.path.join and absolute part after first
# argument.
_file = fdata['name']
if _file[0] == '/':
_file = _file[1:]
file_infos['container'] = container['name']
file_infos['full_name'] = fdata['name']
if self.containers_enabled and container['name'] != VIRTMASTER:
# Prefix the full path with container rootfs
if fdata['container'] == 'all':
cont_grp = container['group']
else:
cont_grp = fdata['container']
cont_name = self.get_real_container_name(containers, cont_grp)
_file = join(VIRTROOT, cont_name, VIRTBASE, _file)
file_infos['full_name'] = _file
source = file_infos.get('source', basename(_file))
source = join(templatedir, source)
file_infos['source'] = source
return file_infos
def gen_files(self, paths):
containers = self._get_containers()
files = []
for fdata in self.generic.get('files', []):
if fdata['container'] == 'all':
# Generate a file per container
for container in containers.values():
if container['name'] in ['all', VIRTMASTER]:
continue
files.append(self._gen_file(fdata, container, containers))
else:
container = containers[fdata['container']]
files.append(self._gen_file(fdata, container, containers))
key_type = {'source': UnicodeOption, 'mode': UnicodeOption,
'full_name': UnicodeOption,
'owner': UnicodeOption, 'group': UnicodeOption,
'mkdir': BoolOption, 'rm': BoolOption,
'del_comment': UnicodeOption,
'level': UnicodeOption}
return self._gen_tiramisu_config(paths, "file", files, key_type,
requires_key='activate')
def gen_disknods(self, paths):
containers = self._get_containers()
disknods = []
for fdata in self.generic.get('disknods', []):
stats = os_stat(fdata['name'])
if stat.S_ISBLK(stats.st_mode):
dev_type = u'b'
device = stats.st_rdev
elif stat.S_ISCHR(stats.st_mode):
dev_type = u'c'
device = stats.st_rdev
elif stat.S_ISDIR(stats.st_mode):
dev_type = u'b'
device = stats.st_dev
else:
dev_type = None
device = None
fdata['type'] = dev_type
if device is not None:
fdata['major'] = major(device)
fdata['minor'] = minor(device)
else:
fdata['major'] = None
fdata['minor'] = None
fdata['mode'] = u'rwm'
fdata['permission'] = 'allow'
disknods.append(fdata)
key_type = {'major': IntOption,
'minor': IntOption,
'name': UnicodeOption,
'permission': UnicodeOption,
'mode': UnicodeOption,
'type': UnicodeOption,
'level': UnicodeOption}
return self._gen_tiramisu_config(paths, "disknod", disknods, key_type)
def gen_packages(self, paths):
# c'est le dernier 'package' qui a raison
# (si présence de deux balises package avec le même nom dans le
# même conteneur)
return self.gen_generic('packages', paths, verify_exists_redefine=False)
class CreoleLoader(CreoleVarLoader, CreoleContainer):
"""
charge les variables + les conteneurs
"""
pass
def _gen_eol_file(namespace, root_path=None):
if namespace == 'creole':
return unicode(configeol)
else:
if root_path is None:
root_path = eoleextraconfig
return unicode(join(root_path, namespace, 'config.eol'))
def _list_extras(extradico=eoleextradico):
extranames = []
if isdir(extradico):
for directory in listdir(extradico):
content = listdir(join(extradico, directory))
if not len(content) == 0:
extensions = [splitext(filename)[1] for filename in content]
if ".xml" in extensions:
extranames.append(directory)
return extranames
def set_mandatory_permissive(config, action):
descr = config.cfgimpl_get_description()
parent = getattr(descr, action, None)
if parent is not None:
for family in parent.impl_getchildren():
for option in family.impl_getchildren():
if 'mandatory' in option.impl_getproperties():
config.cfgimpl_get_settings().setpermissive(('mandatory',), option)
def load_extras(config, load_values=True, mandatory_permissive=False, extradico=eoleextradico,
force_eoleextraconfig=None):
actions = set()
if mandatory_permissive and hasattr(config, 'actions'):
for name, family in config.actions.iter_groups():
for aname, action in family.iter_groups():
actions.add(action.name)
for extraname in _list_extras(extradico=extradico):
if extraname in ['creole', 'containers', 'actions']:
raise Exception(_('extra name {} not allowed').format(extraname))
eol_file = _gen_eol_file(extraname, root_path=force_eoleextraconfig)
config.impl_set_information(extraname, eol_file)
if extraname in actions:
set_mandatory_permissive(config, extraname)
if not load_values:
continue
#if file not exists, create it (for auto_freeze value)
if not isfile(eol_file):
try:
config_save_values(config, extraname, reload_config=False, check_mandatory=False)
except PropertiesOptionError:
pass
if isfile(eol_file):
config_load_values(config, extraname)
def load_config_eol(config, configfile=None, try_upgrade=True, force_load_owner=None,
current_eol_version=None, force_instanciate=None):
if not configfile:
configfile = _gen_eol_file('creole')
config.impl_set_information('creole', configfile)
config_load_values(config, 'creole', force_load_owner=force_load_owner,
force_instanciate=force_instanciate)
load_values(config,
configfile=configfile,
try_upgrade=try_upgrade,
force_load_owner=force_load_owner,
current_eol_version=current_eol_version)
def load_config_store(config, store, unset_default=False,
force_load_owner=None, current_eol_version=None,
force_instanciate=None, remove_unknown_vars=False,
try_upgrade=False):
"""used on Zéphir to upgrade values (2.4.X -> 2.4.X+1) on a configuration
that has already been migrated (2.2/2.3 > 2.4)
"""
config_load_store(config, 'creole', store, force_load_owner=force_load_owner,
unset_default=unset_default, force_instanciate=force_instanciate)
load_values(config,
try_upgrade=try_upgrade,
force_load_owner=force_load_owner,
current_eol_version=current_eol_version,
remove_unknown_vars=remove_unknown_vars)
def load_values(config, configfile=None, try_upgrade=True, force_load_owner=None,
current_eol_version=None, remove_unknown_vars=False):
load_error = config.impl_get_information('load_error', False)
if load_error and try_upgrade:
#Try to upgrade
from .upgrade import upgrade
try:
store_dico, version = upgrade(config, configfile)
config_load_store(config, 'creole', store_dico, unset_default=True, eol_version='1.0')
config.impl_set_information('upgrade', version)
remove_unknown_vars = True
load_error = False
except Exception as e:
log.error(_('Error when trying to upgrade config file: {}').format(e))
config.impl_set_information('load_error', True)
#print "fichier de configuration invalide 2.2 ou 2.3: {0} : {1}".format(configfile, e)
if current_eol_version == None:
current_eol_version = get_version('EOLE_RELEASE')
eol_version = str(config.impl_get_information('eol_version'))
if try_upgrade and not load_error:
if StrictVersion(eol_version) > StrictVersion(current_eol_version):
raise Exception(_('eol_version ({0}) is greater than current version ({1})').format(eol_version, current_eol_version))
if StrictVersion(eol_version) < StrictVersion(current_eol_version):
#can be used to edit lower versions on Zéphir
from .upgrade24 import upgrade2
try:
# 2.4.x (greater than 2.4.0)
if StrictVersion(current_eol_version) >= StrictVersion('2.4.0') and StrictVersion(eol_version) < StrictVersion('2.5.0'):
upgrade2('2.4', eol_version, current_eol_version, config)
# 2.5.x (greater than 2.5.0)
if StrictVersion(current_eol_version) >= StrictVersion('2.5.0') and StrictVersion(eol_version) < StrictVersion('2.6.0'):
upgrade2('2.5', eol_version, current_eol_version, config)
# 2.6.x (greater than 2.6.0)
if StrictVersion(current_eol_version) >= StrictVersion('2.6.0') and StrictVersion(eol_version) < StrictVersion('2.7.0'):
upgrade2('2.6', eol_version, current_eol_version, config)
if config.impl_get_information('upgrade', '') == '':
#set the version only if it is the first upgrade
config.impl_set_information('upgrade', eol_version)
except Exception as e:
log.error(_('Error when trying to upgrade config file: {}').format(normalize(str(e))))
config.impl_set_information('upgrade', False)
config.impl_set_information('load_error', True)
if remove_unknown_vars:
# nettoyage des variables inconnues en dernier (#9858)
config.impl_set_information('unknown_options', {})
def creole_loader(load_values=True, rw=False, namespace='creole',
load_extra=False, reload_config=True, owner=None,
disable_mandatory=False, force_configeol=None,
try_upgrade=True, force_load_creole_owner=None,
force_dirs=None, warnings=None, force_instanciate=None):
"""
charge les dictionnaires Creole et retourne une config Tiramisu
:load_values: boolean. Charge ou non le fichier config.eol (default True)
:rw: boolean. Mode de travail (lecture seule ou lecture/écriture)
:namespace: string. Espace de travail (ex: "creole", "bacula", ...)
:load_extra: boolean. Charge ou non les dictionnaire extra (si namespace='creole')
:reload_config: boolean. Cette option est conservée pour raison de compatibilité
ascendante mais n'a plus de justification, a ne pas utiliser
:owner: string. Owner forcé sur les variables modifiées
:disable_mandatory: boolean.
:force_configeol: string. Force le nom du fichier de configuration utilisé
:try_upgrade: boolean.
:force_dirs: string. Force le nom du réprtoire contenant les dictionnaires
:force_load_creole_owner: Owner forcé pour les variables chargées
:warnings: affiche les warnings de validation
"""
if force_configeol is not None:
if not isfile(force_configeol):
raise ConfigError(_(u"Configuration file unexistent : {0}").format(
force_configeol))
if load_extra:
#if force_configeol, cannot calculated extra configfile name
raise Exception(_(u'Unable to force_configeol with load_extra.'))
if force_dirs is not None and (load_extra is True or namespace != 'creole'):
raise Exception(_(u'If force_dirs is defined, namespace must be set to creole and load_extra must be set to False.'))
if namespace != 'creole' and load_extra:
raise ValueError(_(u'namespace is not creole, so load_extra is forbidden.'))
#should not load value now because create a Config
loader = CreoleLoader()
if force_dirs is not None:
dirs = force_dirs
elif namespace == 'creole':
dirs = eoledirs
else:
dirs = join(eoleextradico, namespace)
#load config
loader.read_dir(dirs, namespace)
if load_extra:
extranames = _list_extras()
if isdir(eoleextradico):
for directory in extranames:
if directory in forbiddenextra:
raise ValueError(
_(u'Namespace {} for extra dictionary not allowed').format(directory))
loader.read_dir(join(eoleextradico, directory), directory)
config = loader.get_config()
if warnings is None:
# warnings is disabled in read-only mode and enabled in read-write mode by default
warnings = rw
if warnings is False:
config.cfgimpl_get_settings().remove('warnings')
if owner is not None:
if owner not in dir(owners):
owners.addowner(owner)
config.cfgimpl_get_settings().setowner(getattr(owners, owner))
#load values
if force_configeol is not None:
configfile = force_configeol
else:
configfile = _gen_eol_file(namespace)
if load_values and isfile(configfile):
disable_mandatory = False
load_config_eol(config, configfile=configfile, try_upgrade=try_upgrade,
force_load_owner=force_load_creole_owner,
force_instanciate=force_instanciate)
else:
config.impl_set_information(namespace, configfile)
if load_extra:
load_extras(config, load_values=load_values)
if rw:
config.read_write()
elif rw is False:
config.read_only()
if disable_mandatory:
config.cfgimpl_get_settings().remove('mandatory')
config.cfgimpl_get_settings().remove('empty')
return config
def valid_store(store):
if not isinstance(store, dict):
raise Exception('store is not a dict: {0}'.format(store))
for key, value in store.items():
if not isinstance(key, unicode):
raise Exception('store key is not an unicode for {0}'.format(key))
if key != '___version___' and (not isinstance(value, dict) or value.keys() != ['owner', 'val']):
raise Exception('store value is not a dict for {0}'.format(key))
def load_store(config, eol_file=configeol):
if not isfile(eol_file):
store = {}
else:
fh = open(eol_file, 'r')
fcntl.lockf(fh, fcntl.LOCK_SH)
try:
store = cjson.decode(fh.read(), all_unicode=True)
except cjson.DecodeError:
config.impl_set_information('load_error', True)
store = {}
fh.close()
try:
valid_store(store)
except Exception as err:
config.impl_set_information('load_error', True)
store = {}
return store
def config_load_store(config, namespace, store, force_instanciate=None,
unset_default=False, force_load_owner=None, eol_version='2.4.0'):
subconfig = getattr(config, namespace)
cache_paths = config.cfgimpl_get_description()._cache_paths[1]
unknown_options = {}
def reorder_store(path1, path2):
"""
sorter function.
sort description : if varname1 is a master and varname 2
is a slave, returns [varname1, varname2]
"""
idx_1 = cache_paths.index(path1)
idx_2 = cache_paths.index(path2)
return cmp(idx_1, idx_2)
def store_path_and_reorder(eol_version):
"""Convenience function to replace varnames with full paths
and to sort an unordered ConfigObj's
:returns: a sorted ordereddict.
"""
store_path = {}
if namespace == 'creole':
paths = {}
for path in subconfig.cfgimpl_get_description().impl_getpaths():
vname = path.split('.')[-1]
paths[vname] = namespace + '.' + path
#variable pas dans Tiramisu
for vname, value in store.items():
if vname == '___version___':
eol_version = value
elif vname not in paths:
unknown_options[vname] = value
if vname not in paths or value == {}:
continue
store_path[paths[vname]] = value
else:
paths = []
subpaths = subconfig.cfgimpl_get_description().impl_getpaths()
for path in subpaths:
paths.append(namespace + '.' + path)
for vname, value in store.items():
if vname == '___version___':
eol_version = value
continue
elif vname not in paths:
continue
store_path[vname] = value
store_order = OrderedDict()
store_key = store_path.keys()
store_key.sort(reorder_store)
for path in store_key:
store_order[path] = store_path[path]
return eol_version, store_order
#don't frozen auto_freeze before instance (or enregistrement_zephir for Zephir)
if force_instanciate is not None:
is_inst = force_instanciate
else:
is_inst = is_instanciate()
eol_version, store = store_path_and_reorder(eol_version)
orig_values = {}
for path, values in store.items():
value = values['val']
option = config.unwrap_from_path(path)
settings = config.cfgimpl_get_settings()
tiramisu_values = config.cfgimpl_get_values()
if force_load_owner is not None:
owner = force_load_owner
else:
owner = values['owner']
if isinstance(owner, dict):
for towner in owner.values():
if towner not in dir(owners):
owners.addowner(towner)
else:
if owner not in dir(owners):
owners.addowner(owner)
try:
#si unset_default, remet à la valeur par défaut si == à la valeur
if unset_default and value == getattr(config, path):
continue
if isinstance(value, tuple):
value = list(value)
values['val'] = value
orig_values[path.split('.')[-1]] = values
if option.impl_is_master_slaves('slave'):
if not isinstance(owner, dict):
new_owner = getattr(owners, owner)
multi = config.getattr(path, force_permissive=True)
if isinstance(value, list):
tval = {}
for idx, val in enumerate(value):
tval[idx] = val
value = tval
for idx, val in value.items():
index = int(idx)
if len(multi) > index:
multi[index] = val
if isinstance(owner, dict):
new_owner = getattr(owners, owner[idx])
tiramisu_values.setowner(option, new_owner, index=index)
else:
log.error(_("master's len is lower than the slave variable ({})").format(path))
else:
if isinstance(owner, str):
owner = unicode(owner)
if not isinstance(owner, unicode):
raise Exception(_('owner must be a string for {}').format(path))
new_owner = getattr(owners, owner)
try:
config.setattr(path, value, force_permissive=True)
except ValueError as e:
if path == 'schedule.schedule.weekday' and 'schedule.schedule.monthday' in store:
settings.remove('validator')
config.setattr(path, value, force_permissive=True)
config.setattr('schedule.schedule.monthday', store['schedule.schedule.monthday'], force_permissive=True)
settings.append('validator')
else:
raise e
tiramisu_values.setowner(option, new_owner)
except ValueError as e:
msg = str(e).decode('utf8')
#msg = unicode(e)
log.error(_('unable to load variable {} with value {}: {}').format(path, value, msg))
settings[option].append('load_error')
config.impl_set_information('error_msg_{}'.format(path), msg)
config.impl_set_information('orig_value_{}'.format(path), value)
except LeadershipError:
# ne pas faire d'erreur #8380
pass
try:
config.impl_get_information('force_store_vars').remove(path)
except (KeyError, ValueError) as err:
pass
path_split = path.split('.')
family_option = config.unwrap_from_path(namespace + '.' + path_split[1])
settings.setpermissive(tuple(modes_level), opt=family_option)
if len(path_split) == 4:
parent_option = config.unwrap_from_path(namespace + '.' + path_split[1] + '.' + path_split[2])
settings.setpermissive(tuple(modes_level), opt=parent_option)
settings.setpermissive(tuple(modes_level), opt=option)
setting = config.cfgimpl_get_settings()
if 'auto_freeze' in setting[option] and is_inst == 'oui' and \
not tiramisu_values.is_default_owner(option):
setting[option].append('frozen')
if namespace == 'creole':
config.impl_set_information('unknown_options', unknown_options)
config.impl_set_information('eol_version', eol_version)
config.impl_set_information('orig_values', orig_values)
def config_load_values(config, namespace, eol_file=None, force_instanciate=None,
force_load_owner=None):
subconfig = getattr(config, namespace, None)
if subconfig is None:
return
if eol_file is None:
try:
eol_file = config.impl_get_information(namespace)
except AttributeError:
raise Exception(_(u'config must have eol_file attribute'))
else:
config.impl_set_information(namespace, eol_file)
if not isfile(eol_file):
raise IOError(_(u'Can not find file {0}').format(
eol_file))
store = load_store(config, eol_file)
config_load_store(config, namespace, store,
force_instanciate=force_instanciate,
force_load_owner=force_load_owner)
def config_get_values(config, namespace, check_mandatory=True, ignore_autofreeze=False):
"""check_mandatory: allows to disable mandatory checking
(i.e : when returning values for partial configuration in Zéphir)
"""
def _get_varname(path):
if namespace == 'creole':
value_name = path.split('.')[-1]
else:
value_name = path
return value_name
subconfig = getattr(config, namespace)
if check_mandatory:
mandatory_errors = list(config.cfgimpl_get_values(
).mandatory_warnings(force_permissive=True))
if mandatory_errors != []:
text = []
for error in mandatory_errors:
if not error.startswith(namespace + '.'):
continue
error = error.split('.')
text.append(_(u"Mandatory variable '{0}' from family '{1}'"
u" is not set !").format(unicode(error[-1]),
unicode(error[1].capitalize())).encode('utf-8'))
if text != []:
raise PropertiesOptionError("\n".join(text), ('mandatory',))
store = {}
opt_values = subconfig.cfgimpl_get_values().get_modified_values()
force_store_values = config.impl_get_information('force_store_values', None)
for path, own_val in opt_values.items():
#for variable not related to current namespace
if not path.startswith(namespace+'.'):
continue
if force_store_values and path in force_store_values:
force_store_values.remove(path)
store[_get_varname(path)] = {'val': own_val[1], 'owner': own_val[0]}
if force_store_values:
for path in force_store_values:
varname = _get_varname(path)
if varname not in store:
try:
store[varname] = {'val': config.getattr(path, force_permissive=True), 'owner': u'forced'}
except PropertiesOptionError:
pass
if namespace == 'creole':
#update with values in store with no known options
store.update(config.impl_get_information('unknown_options', {}))
return store
def add_eol_version(store, eol_version=None):
# on stocke la version passée en paramètre (si >= 2.4.1) ou celle du système le cas échéant
if eol_version:
if StrictVersion(eol_version) >= StrictVersion('2.4.1'):
store['___version___'] = eol_version
else:
store['___version___'] = get_version('EOLE_RELEASE')
def config_save_values(config, namespace, reload_config=True, eol_file=None, check_mandatory=True, eol_version=None):
subconfig = getattr(config, namespace)
if eol_file is not None:
config.impl_set_information(namespace, eol_file)
try:
eol_file = config.impl_get_information(namespace)
except AttributeError:
raise Exception(_(u'config must have eol_file attribute'))
store = config_get_values(config, namespace, check_mandatory)
add_eol_version(store, eol_version)
try:
dirn = dirname(eol_file)
if not isdir(dirn):
makedirs(dirn)
if not isfile(eol_file):
fh = file(eol_file, 'w')
fcntl.lockf(fh, fcntl.LOCK_EX)
else:
fh = file(eol_file, 'r+')
fcntl.lockf(fh, fcntl.LOCK_EX)
fh.truncate() # Here's where the magic happens #7073
fh.write(cjson.encode(store))
fh.close()
except Exception as err:
raise Exception(_(u"Error saving file: {0}").format(err))
if client is not None and reload_config:
try:
client.reload_eol()
#client.reload_config()
except CreoleClientError:
pass
return True