[enh] Regenerate configuration via hook scripts

This commit is contained in:
kload 2015-09-28 16:53:36 +00:00
parent 442e578f0e
commit b6c77c9e43
5 changed files with 140 additions and 107 deletions

View file

@ -243,6 +243,10 @@ domain:
authenticate: all authenticate: all
authenticator: ldap-anonymous authenticator: ldap-anonymous
arguments: arguments:
-r:
full: --raw
help: Return domains as a bash-usable list instead of JSON
action: store_true
-f: -f:
full: --filter full: --filter
help: LDAP filter used to search help: LDAP filter used to search
@ -879,18 +883,47 @@ service:
Prints the differences between files if any. Prints the differences between files if any.
api: PUT /services/regenconf api: PUT /services/regenconf
arguments: arguments:
-n: -s:
full: --name full: --service
help: Regenerate configuration for a specfic service help: Regenerate configuration for a specfic service
-f: -f:
full: --force full: --force
help: Override the current configuration with the newly generated one, even if it has been modified help: Override the current configuration with the newly generated one, even if it has been modified
action: store_true action: store_true
-k:
full: --keep ### service_safecopy()
help: Save the current configuration to avoid further notifications safecopy:
action_help: >
Check if the specific file has been modified and display differences.
Stores the file hash in the services.yml file
api: PUT /services/safecopy
arguments:
service:
help: Service name attached to the conf file
new_conf_file:
help: Path to the desired conf file
conf_file:
help: Path to the targeted conf file
-f:
full: --force
help: Override the current configuration with the newly generated one, even if it has been modified
action: store_true action: store_true
### service_saferemove()
saferemove:
action_help: >
Check if the specific file has been modified before removing it.
Backup the file in /home/yunohost.backup
api: PUT /services/safecopy
arguments:
service:
help: Service name attached to the conf file
conf_file:
help: Path to the targeted conf file
-f:
full: --force
help: Force file deletion
action: store_true
############################# #############################
# Firewall # # Firewall #

View file

@ -0,0 +1,57 @@
#!/bin/bash
set -e
force=$1
function safe_copy () {
if $force; then
sudo yunohost service safecopy \
-s nginx \
--force \
$1 $2
else
sudo yunohost service safecopy \
-s nginx \
$1 $2
fi
}
cd /usr/share/yunohost/templates/nginx
# Copy plain single configuration files
$files="ssowat.conf
yunohost_admin.conf
yunohost_admin.conf.inc
yunohost_api.conf.inc
yunohost_panel.conf.inc"
for file in files; do
safe_copy $file /etc/nginx/conf.d/$file
done
# Copy 'yunohost.local' to the main domain conf directory
main_domain=$(sudo yunohost tools maindomain)
safe_copy yunohost_local.conf \
/etc/nginx/conf.d/$main_domain.d/yunohost_local.conf
need_restart=0
# Copy a configuration file for each YunoHost domain
for domain in $(sudo yunohost domain list --raw); do
sudo mkdir /etc/nginx/conf.d/$domain.d
cat server.conf.sed \
| sed "s/{{ domain }}/$domain/g" \
| sudo tee $domain.conf
if $(safe_copy $domain.conf /etc/nginx/conf.d/$domain.conf); then
need_restart=1
fi
done
# Restart if need be
if $need_restart; then
service nginx restart
else
service nginx reload
fi

View file

@ -36,11 +36,12 @@ from urllib import urlopen
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
def domain_list(auth, filter=None, limit=None, offset=None): def domain_list(auth, raw=False, filter=None, limit=None, offset=None):
""" """
List domains List domains
Keyword argument: Keyword argument:
raw -- Return domains as a bash-usable list instead of JSON
filter -- LDAP filter used to search filter -- LDAP filter used to search
offset -- Starting number for domain fetching offset -- Starting number for domain fetching
limit -- Maximum number of domain fetched limit -- Maximum number of domain fetched
@ -61,7 +62,12 @@ def domain_list(auth, filter=None, limit=None, offset=None):
if len(result) > offset and limit > 0: if len(result) > offset and limit > 0:
for domain in result[offset:offset+limit]: for domain in result[offset:offset+limit]:
result_list.append(domain['virtualdomain'][0]) result_list.append(domain['virtualdomain'][0])
return { 'domains': result_list }
if raw:
for domain in result_list:
print domain
else:
return { 'domains': result_list }
def domain_add(auth, domain, dyndns=False): def domain_add(auth, domain, dyndns=False):

View file

@ -185,6 +185,16 @@ def hook_callback(action, hooks=[], args=None):
else: else:
hooks_names = hook_list(action, list_by='name', hooks_names = hook_list(action, list_by='name',
show_info=True)['hooks'] show_info=True)['hooks']
# Add similar hooks to the list
# For example: Having a 16-postfix hook in the list will execute a
# xx-postfix_dkim as well
for n in hooks:
for key in hooks.keys():
if key.startswith("%s_" % n) \
and n not in hooks:
hooks.append(n)
# Iterate over given hooks names list # Iterate over given hooks names list
for n in hooks: for n in hooks:
try: try:

View file

@ -30,7 +30,6 @@ import glob
import subprocess import subprocess
import errno import errno
import shutil import shutil
import jinja2
import difflib import difflib
import hashlib import hashlib
@ -270,26 +269,35 @@ def service_log(name, number=50):
return result return result
def service_regenconf(auth, name=None, force=False, keep=False): def service_regenconf(service=None, force=False):
""" """
Regenerate the configuration file(s) for a service and compare the result Regenerate the configuration file(s) for a service and compare the result
with the existing configuration file. with the existing configuration file.
Prints the differences between files if any. Prints the differences between files if any.
Keyword argument: Keyword argument:
name -- Regenerate configuration for a specfic service service -- Regenerate configuration for a specfic service
force -- Override the current configuration with the newly generated force -- Override the current configuration with the newly generated
one, even if it has been modified one, even if it has been modified
keep -- Save the current configuration to avoid further notifications
""" """
if name is not None: if force:
_regenerate_configuration_for(auth, name, force, keep) arg_force = 1
else:
arg_force = 0
#TODO: Raise error when force + keep
#TODO: Loop through all the services if service is None:
#TODO: Win message with regenerated configurations # Regen ALL THE CONFIGURATIONS
hook_callback('conf_regen', args=[arg_force])
msignals.display(m18n.n('services_configured'), 'success')
else:
if service not in _get_services().keys():
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service))
hook_callback('conf_regen', [service] , args=[arg_force])
msignals.display(m18n.n('service_configured', service), 'success')
def _run_service_command(action, service): def _run_service_command(action, service):
@ -417,16 +425,15 @@ def _hash(filename):
return 'no hash yet' return 'no hash yet'
def _safe_remove(conf_file, service=None, force=False, keep=False): def service_saferemove(service, conf_file, force=False):
""" """
Check if the specific file has been modified before removing it. Check if the specific file has been modified before removing it.
Backup the file in /home/yunohost.backup Backup the file in /home/yunohost.backup
Keyword argument: Keyword argument:
conf_file -- The file to write
service -- Service name of the file to delete service -- Service name of the file to delete
conf_file -- The file to write
force -- Force file deletion force -- Force file deletion
keep -- Keep the current file and save its hash
""" """
deleted = False deleted = False
@ -487,24 +494,23 @@ def _safe_remove(conf_file, service=None, force=False, keep=False):
return deleted return deleted
def _safe_write(conf_file, new_conf='', service=None, force=False, keep=False): def service_safecopy(service, new_conf_file, conf_file, force=False):
""" """
Check if the specific file has been modified and display differences. Check if the specific file has been modified and display differences.
Stores the file hash in the services.yml file Stores the file hash in the services.yml file
Keyword argument: Keyword argument:
conf_file -- The file to write service -- Service name attached to the conf file
new_conf -- String containing the new content new_conf_file -- Path to the desired conf file
service -- Service name of the file to write conf_file -- Path to the targeted conf file
force -- Force file overriding force -- Force file overriding
keep -- Keep the current file and save its hash
""" """
regenerated = False regenerated = False
services = _get_services() services = _get_services()
if os.path.exists(new_conf): if os.path.exists(new_conf_file):
filename = new_conf filename = new_conf_file
with open(filename, 'r') as f: with open(filename, 'r') as f:
new_conf = ''.join(f.readlines()).rstrip() new_conf = ''.join(f.readlines()).rstrip()
@ -537,8 +543,6 @@ def _safe_write(conf_file, new_conf='', service=None, force=False, keep=False):
with open(conf_file, 'w') as f: f.write(new_conf) with open(conf_file, 'w') as f: f.write(new_conf)
regenerated = True regenerated = True
new_hash = _hash(conf_file) new_hash = _hash(conf_file)
elif keep:
new_hash = previous_hash[0:32] + ', but keep ' + current_hash
elif len(diff) == 0: elif len(diff) == 0:
new_hash = _hash(conf_file) new_hash = _hash(conf_file)
else: else:
@ -565,80 +569,3 @@ def _safe_write(conf_file, new_conf='', service=None, force=False, keep=False):
_save_services(services) _save_services(services)
return regenerated return regenerated
def _regenerate_configuration_for(auth, service, force=False, keep=False):
"""
Handle all the different services' configurations of YunoHost
Keyword argument:
service -- Service name to take care of
force -- Force configuration overriding
keep -- Keep the current configuration and save its hash
"""
from yunohost.domain import domain_list
if service not in _get_services().keys() \
or not os.path.isdir("%s/%s" % (template_dir, service)):
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service))
# Set the service's template directory as Jinja Environment in order
# to ease the template loading
env = jinja2.Environment(
loader=jinja2.FileSystemLoader("%s/%s" % (template_dir, service))
)
domains = domain_list(auth)['domains']
with open('/etc/yunohost/current_host', 'r') as f:
main_domain = f.readline().rstrip()
if service == 'nginx':
need_restart = False
# Copy plain files
for filename in [
'ssowat.conf',
'yunohost_admin.conf',
'yunohost_admin.conf.inc',
'yunohost_api.conf.inc',
'yunohost_panel.conf.inc',
]:
conf_file = '/etc/nginx/conf.d/%s' % filename
new_conf = '%s/%s/%s' % (template_dir, service, filename)
_safe_write(conf_file, new_conf, service, force, keep)
# We need one file and one folder per virtualhost
for domain in domains:
conf_file = '/etc/nginx/conf.d/%s.conf' % domain
new_conf = env.get_template('server.conf.j2').render(domain=domain)
need_restart = _safe_write(conf_file, new_conf, service, force, keep) \
or need_restart
try:
os.makedirs('/etc/nginx/conf.d/%s.d' % domain)
except OSError: pass
# Copy yunohost_local.conf for the main domain
filename = 'yunohost_local.conf'
conf_file = '/etc/nginx/conf.d/%s.d/%s' % (main_domain, filename)
new_conf = '%s/%s/%s' % (template_dir, service, filename)
_safe_write(conf_file, new_conf, service, force, keep)
# Backup and remove configuration for unexisting domains
for conf_file in os.listdir('/etc/nginx/conf.d/'):
if conf_file.endswith('.conf') and len(conf_file.split('.')) > 2 \
and conf_file.replace('.conf', '') not in domains:
_safe_remove('/etc/nginx/conf.d/'+ conf_file, service, force, keep)
# Restart Nginx
if need_restart:
_run_service_command('restart', service)
else:
_run_service_command('reload', service)
msignals.display(m18n.n('service_configured', service), 'success')
if service == 'postfix':
pass