[enh] Add i18n \o/ 👏

This commit is contained in:
Jérôme Lebleu 2014-05-15 01:00:43 +02:00
parent 8bdb79cfba
commit 24cbd03d47
19 changed files with 615 additions and 216 deletions

View file

@ -33,6 +33,7 @@ import time
import re
import socket
import urlparse
import errno
from moulinette.helpers import win_msg, random_password, is_true, validate
from moulinette.core import MoulinetteError
@ -55,7 +56,7 @@ def app_listlists():
if '.json' in filename:
list_list.append(filename[:len(filename)-5])
except OSError:
raise MoulinetteError(1, _("No list found"))
raise MoulinetteError(1, m18n.n('no_list_found'))
return { 'Lists' : list_list }
@ -77,12 +78,14 @@ def app_fetchlist(url=None, name=None):
url = 'http://app.yunohost.org/list.json'
name = 'yunohost'
else:
if name is None: raise MoulinetteError(22, _("You must indicate a name for your custom list"))
if name is None:
raise MoulinetteError(errno.EINVAL,
m18n.n('custom_list_name_required'))
list_file = '%s/%s.json' % (repo_path, name)
if os.system('wget "%s" -O "%s.tmp"' % (url, list_file)) != 0:
os.remove('%s.tmp' % list_file)
raise MoulinetteError(1, _("List server connection failed"))
raise MoulinetteError(errno.EBADR, m18n.n('list_retrieve_error'))
# Rename fetched temp list
os.rename('%s.tmp' % list_file, list_file)
@ -90,7 +93,7 @@ def app_fetchlist(url=None, name=None):
os.system("touch /etc/cron.d/yunohost-applist-%s" % name)
os.system("echo '00 00 * * * root yunohost app fetchlist -u %s -n %s --no-ldap > /dev/null 2>&1' >/etc/cron.d/yunohost-applist-%s" % (url, name, name))
msignals.display(_("List successfully fetched"), 'success')
msignals.display(m18n.n('list_fetched'), 'success')
def app_removelist(name):
@ -105,9 +108,9 @@ def app_removelist(name):
os.remove('%s/%s.json' % (repo_path, name))
os.remove("/etc/cron.d/yunohost-applist-%s" % name)
except OSError:
raise MoulinetteError(22, _("Unknown list"))
raise MoulinetteError(errno.ENOENT, m18n.n('unknown_list'))
msignals.display(_("List successfully removed"), 'success')
msignals.display(m18n.n('list_removed'), 'success')
def app_list(offset=None, limit=None, filter=None, raw=False):
@ -268,7 +271,7 @@ def app_upgrade(auth, app, url=None, file=None):
try:
app_list()
except MoulinetteError:
raise MoulinetteError(1, _("No app to upgrade"))
raise MoulinetteError(errno.ENODATA, m18n.n('app_no_upgrade'))
upgraded_apps = []
@ -281,7 +284,8 @@ def app_upgrade(auth, app, url=None, file=None):
for app_id in app:
installed = _is_installed(app_id)
if not installed:
raise MoulinetteError(1, _("%s is not installed") % app_id)
raise MoulinetteError(errno.ENOPKG,
m18n.n('app_not_installed') % app_id)
if app_id in upgraded_apps:
continue
@ -299,7 +303,8 @@ def app_upgrade(auth, app, url=None, file=None):
elif url:
manifest = _fetch_app_from_git(url)
elif 'lastUpdate' not in new_app_dict or 'git' not in new_app_dict:
raise MoulinetteError(22, _("%s is a custom app, please provide an URL manually in order to upgrade it") % app_id)
raise MoulinetteError(errno.EDESTADDRREQ,
m18n.n('custom_app_url_required') % app_id)
elif (new_app_dict['lastUpdate'] > current_app_dict['lastUpdate']) \
or ('update_time' not in current_app_dict['settings'] \
and (new_app_dict['lastUpdate'] > current_app_dict['settings']['install_time'])) \
@ -311,7 +316,8 @@ def app_upgrade(auth, app, url=None, file=None):
# Check min version
if 'min_version' in manifest and __version__ < manifest['min_version']:
raise MoulinetteError(1, _("%s requires a more recent version of the moulinette") % app_id)
raise MoulinetteError(errno.EPERM,
m18n.n('app_recent_version_required') % app_id)
app_setting_path = apps_setting_path +'/'+ app_id
@ -355,12 +361,12 @@ def app_upgrade(auth, app, url=None, file=None):
# So much win
upgraded_apps.append(app_id)
msignals.display(_("%s upgraded successfully") % app_id, 'success')
msignals.display(m18n.n('app_upgraded') % app_id, 'success')
if not upgraded_apps:
raise MoulinetteError(1, _("No app to upgrade"))
raise MoulinetteError(errno.ENODATA, m18n.n('no_app_upgrade'))
msignals.display(_("Upgrade complete"), 'success')
msignals.display(m18n.n('upgrade_complete'), 'success')
def app_install(auth, app, label=None, args=None):
@ -386,19 +392,21 @@ def app_install(auth, app, label=None, args=None):
# Check ID
if 'id' not in manifest or '__' in manifest['id']:
raise MoulinetteError(22, _("App id is invalid"))
raise MoulinetteError(errno.EINVAL, m18n.n('app_id_invalid'))
app_id = manifest['id']
# Check min version
if 'min_version' in manifest and __version__ < manifest['min_version']:
raise MoulinetteError(1, _("%s requires a more recent version of the moulinette") % app_id)
raise MoulinetteError(errno.EPERM,
m18n.n('app_recent_version_required') % app_id)
# Check if app can be forked
instance_number = _installed_instance_number(app_id, last=True) + 1
if instance_number > 1 :
if 'multi_instance' not in manifest or not is_true(manifest['multi_instance']):
raise MoulinetteError(1, _("App is already installed"))
raise MoulinetteError(errno.EEXIST,
m18n.n('app_already_installed') % app_id)
app_id_forked = app_id + '__' + str(instance_number)
@ -467,18 +475,18 @@ def app_install(auth, app, label=None, args=None):
os.system('chown -R root: %s' % app_setting_path)
os.system('chown -R admin: %s/scripts' % app_setting_path)
app_ssowatconf(auth)
msignals.display(_("Installation complete"), 'success')
msignals.display(m18n.n('installation_complete'), 'success')
else:
#TODO: display script fail messages
hook_remove(app_id)
shutil.rmtree(app_setting_path)
shutil.rmtree(app_tmp_folder)
raise MoulinetteError(1, _("Installation failed"))
raise MoulinetteError(errno.EIO, m18n.n('installation_failed'))
except KeyboardInterrupt, EOFError:
hook_remove(app_id)
shutil.rmtree(app_setting_path)
shutil.rmtree(app_tmp_folder)
raise MoulinetteError(125, _("Interrupted"))
raise MoulinetteError(errno.EINTR, m18n.g('operation_interrupted'))
def app_remove(app):
@ -492,7 +500,7 @@ def app_remove(app):
from yunohost.hook import hook_exec, hook_remove
if not _is_installed(app):
raise MoulinetteError(22, _("App is not installed"))
raise MoulinetteError(errno.EINVAL, m18n.n('app_not_installed') % app)
app_setting_path = apps_setting_path + app
@ -512,7 +520,7 @@ def app_remove(app):
shutil.rmtree('/tmp/yunohost_remove')
hook_remove(app)
app_ssowatconf()
msignals.display(_("App removed: %s") % app, 'success')
msignals.display(m18n.n('app_removed') % app, 'success')
def app_addaccess(auth, apps, users):
@ -536,7 +544,8 @@ def app_addaccess(auth, apps, users):
for app in apps:
if not _is_installed(app):
raise MoulinetteError(22, _("App is not installed"))
raise MoulinetteError(errno.EINVAL,
m18n.n('app_not_installed') % app)
with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f)
@ -589,7 +598,8 @@ def app_removeaccess(auth, apps, users):
new_users = ''
if not _is_installed(app):
raise MoulinetteError(22, _("App is not installed"))
raise MoulinetteError(errno.EINVAL,
m18n.n('app_not_installed') % app)
with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f)
@ -631,7 +641,8 @@ def app_clearaccess(auth, apps):
for app in apps:
if not _is_installed(app):
raise MoulinetteError(22, _("App is not installed"))
raise MoulinetteError(errno.EINVAL,
m18n.n('app_not_installed') % app)
with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f)
@ -654,7 +665,7 @@ def app_makedefault(app, domain=None):
"""
if not _is_installed(app):
raise MoulinetteError(22, _("App is not installed"))
raise MoulinetteError(errno.EINVAL, m18n.n('app_not_installed') % app)
with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f)
@ -665,10 +676,11 @@ def app_makedefault(app, domain=None):
if domain is None:
domain = app_domain
elif domain not in domain_list()['Domains']:
raise MoulinetteError(22, _("Domain doesn't exists"))
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
if '/' in app_map(raw=True)[domain]:
raise MoulinetteError(1, _("An app is already installed on this location"))
raise MoulinetteError(errno.EEXIST,
m18n.n('app_location_already_used'))
try:
with open('/etc/ssowat/conf.json.persistent') as json_conf:
@ -686,7 +698,7 @@ def app_makedefault(app, domain=None):
os.system('chmod 644 /etc/ssowat/conf.json.persistent')
win_msg('SSOwat persistent configuration has been updated')
msignals.display(m18n.n('ssowat_conf_updated'), 'success')
@ -780,10 +792,10 @@ def app_checkport(port):
s.connect(("localhost", int(port)))
s.close()
except socket.error:
msignals.display(_("Port available: %s") % str(port), 'success')
msignals.display(m18n.n('port_available') % int(port), 'success')
else:
raise MoulinetteError(22, _("Port not available: %s") % str(port))
raise MoulinetteError(errno.EINVAL,
m18n.n('port_unavailable') % int(port))
def app_checkurl(auth, url, app=None):
@ -815,14 +827,15 @@ def app_checkurl(auth, url, app=None):
validate(r'^([a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)(\.[a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)*(\.[a-zA-Z]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)$', domain)
if domain not in domain_list(auth)['domains']:
raise MoulinetteError(22, _("Domain doesn't exists"))
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
if domain in apps_map:
if path in apps_map[domain]:
raise MoulinetteError(1, _("An app is already installed on this location"))
raise MoulinetteError(errno.EINVAL, m18n.n('app_location_already_used'))
for app_path, v in apps_map[domain].items():
if app_path in path and app_path.count('/') < path.count('/'):
raise MoulinetteError(1, _("Unable to install app at this location"))
raise MoulinetteError(errno.EPERM,
m18n.n('app_location_install_failed'))
if app is not None:
app_setting(app, 'domain', value=domain)
@ -852,13 +865,13 @@ def app_initdb(user, password=None, db=None, sql=None):
mysql_root_pwd = open('/etc/yunohost/mysql').read().rstrip()
mysql_command = 'mysql -u root -p%s -e "CREATE DATABASE %s ; GRANT ALL PRIVILEGES ON %s.* TO \'%s\'@localhost IDENTIFIED BY \'%s\';"' % (mysql_root_pwd, db, db, user, password)
if os.system(mysql_command) != 0:
raise MoulinetteError(1, _("MySQL DB creation failed"))
raise MoulinetteError(errno.EIO, m18n.n('mysql_db_creation_failed'))
if sql is not None:
if os.system('mysql -u %s -p%s %s < %s' % (user, password, db, sql)) != 0:
raise MoulinetteError(1, _("MySQL DB init failed"))
raise MoulinetteError(errno.EIO, m18n.n('mysql_db_init_failed'))
if not return_pwd:
msignals.display(_("Database initiliazed"), 'success')
msignals.display(m18n.n('mysql_db_initialized'), 'success')
def app_ssowatconf(auth):
@ -951,7 +964,7 @@ def app_ssowatconf(auth):
with open('/etc/ssowat/conf.json', 'w+') as f:
json.dump(conf_dict, f, sort_keys=True, indent=4)
msignals.display(_('SSOwat configuration generated'), 'success')
msignals.display(m18n.n('ssowat_conf_generated'), 'success')
def _extract_app_from_file(path, remove=False):
@ -968,7 +981,7 @@ def _extract_app_from_file(path, remove=False):
"""
global app_tmp_folder
print(_('Extracting...'))
msignals.display(m18n.n('extracting'))
if os.path.exists(app_tmp_folder): shutil.rmtree(app_tmp_folder)
os.makedirs(app_tmp_folder)
@ -988,7 +1001,7 @@ def _extract_app_from_file(path, remove=False):
extract_result = 1
if extract_result != 0:
raise MoulinetteError(22, _("Invalid install file"))
raise MoulinetteError(errno.EINVAL, m18n.n('app_extraction_failed'))
try:
if len(os.listdir(app_tmp_folder)) == 1:
@ -998,9 +1011,9 @@ def _extract_app_from_file(path, remove=False):
manifest = json.loads(str(json_manifest.read()))
manifest['lastUpdate'] = int(time.time())
except IOError:
raise MoulinetteError(1, _("Invalid App file"))
raise MoulinetteError(errno.EIO, m18n.n('app_install_files_invalid'))
print(_('OK'))
msignals.display(m18n.n('done'))
return manifest
@ -1018,7 +1031,7 @@ def _fetch_app_from_git(app):
"""
global app_tmp_folder
print(_('Downloading...'))
msignals.display(m18n.n('downloading'))
if ('@' in app) or ('http://' in app) or ('https://' in app):
if "github.com" in app:
@ -1036,7 +1049,7 @@ def _fetch_app_from_git(app):
manifest = json.loads(str(json_manifest.read()))
manifest['lastUpdate'] = int(time.time())
except IOError:
raise MoulinetteError(1, _("Invalid App manifest"))
raise MoulinetteError(errno.EIO, m18n.n('app_manifest_invalid'))
else:
app_dict = app_list(raw=True)
@ -1046,7 +1059,7 @@ def _fetch_app_from_git(app):
app_info['manifest']['lastUpdate'] = app_info['lastUpdate']
manifest = app_info['manifest']
else:
raise MoulinetteError(22, _("App doesn't exists"))
raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown'))
if "github.com" in app_info['git']['url']:
url = app_info['git']['url'].replace("git@github.com:", "https://github.com/")
@ -1063,9 +1076,9 @@ def _fetch_app_from_git(app):
git_result_2 = os.system('cd %s && git reset --hard %s' % (app_tmp_folder, str(app_info['git']['revision'])))
if not git_result == git_result_2 == 0:
raise MoulinetteError(22, _("Sources fetching failed"))
raise MoulinetteError(errno.EIO, m18n.n('app_sources_fetch_failed'))
print(_('OK'))
msignals.display(m18n.n('done'))
return manifest
@ -1134,4 +1147,3 @@ def _is_installed(app):
continue
return False

View file

@ -30,6 +30,7 @@ import re
import shutil
import json
import yaml
import errno
from urllib import urlopen
from moulinette.core import MoulinetteError
@ -89,7 +90,7 @@ def domain_add(auth, domains, main=False, dyndns=False):
# DynDNS domain
if dyndns:
if len(domain.split('.')) < 3:
raise MoulinetteError(22, _("Invalid domain '%s' for DynDNS" % domain))
raise MoulinetteError(errno.EINVAL, m18n.n('domain_dyndns_invalid'))
import requests
from yunohost.dyndns import dyndns_subscribe
@ -98,10 +99,12 @@ def domain_add(auth, domains, main=False, dyndns=False):
dyndomain = '.'.join(domain.split('.')[1:])
if dyndomain in dyndomains:
if os.path.exists('/etc/cron.d/yunohost-dyndns'):
raise MoulinetteError(22, _("You already have a DynDNS domain"))
raise MoulinetteError(errno.EPERM,
m18n.n('domain_dyndns_already_subscribed'))
dyndns_subscribe(domain=domain)
else:
raise MoulinetteError(22, _("Unknown DynDNS domain '%s'" % dyndomain))
raise MoulinetteError(errno.EINVAL,
m18n.n('domain_dyndns_root_unknown'))
# Commands
ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
@ -131,12 +134,13 @@ def domain_add(auth, domains, main=False, dyndns=False):
for command in command_list:
if os.system(command) != 0:
raise MoulinetteError(17, _("An error occurred during certificate generation"))
raise MoulinetteError(errno.EIO,
m18n.n('domain_cert_gen_failed'))
try:
auth.validate_uniqueness({ 'virtualdomain': domain })
except MoulinetteError:
raise MoulinetteError(17, _("Domain already created"))
raise MoulinetteError(errno.EEXIST, m18n.n('domain_exists'))
attr_dict['virtualdomain'] = domain
@ -169,7 +173,8 @@ def domain_add(auth, domains, main=False, dyndns=False):
os.system('chown bind /var/lib/bind/%s.zone' % domain)
else:
raise MoulinetteError(17, _("Zone file already exists for %s") % domain)
raise MoulinetteError(errno.EEXIST,
m18n.n('domain_zone_exists'))
conf_lines = [
'zone "%s" {' % domain,
@ -228,12 +233,12 @@ def domain_add(auth, domains, main=False, dyndns=False):
result.append(domain)
continue
else:
raise MoulinetteError(169, _("An error occurred during domain creation"))
raise MoulinetteError(errno.EIO, m18n.n('domain_creation_failed'))
os.system('yunohost app ssowatconf > /dev/null 2>&1')
msignals.display(_("Domain(s) successfully created."), 'success')
msignals.display(m18n.n('domain_created'), 'success')
return { 'domains': result }
@ -253,7 +258,7 @@ def domain_remove(auth, domains):
for domain in domains:
if domain not in domains_list:
raise MoulinetteError(22, _("Unknown domain '%s'") % domain)
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
# Check if apps are installed on the domain
for app in os.listdir('/etc/yunohost/apps/'):
@ -264,7 +269,8 @@ def domain_remove(auth, domains):
continue
else:
if app_domain == domain:
raise MoulinetteError(1, _("One or more apps are installed on this domain, please uninstall them before proceed to domain removal"))
raise MoulinetteError(errno.EPERM,
m18n.n('domain_uninstall_app_first'))
if auth.remove('virtualdomain=' + domain + ',ou=domains'):
try:
@ -291,13 +297,12 @@ def domain_remove(auth, domains):
result.append(domain)
continue
else:
raise MoulinetteError(169, _("An error occurred during domain deletion"))
raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed'))
os.system('yunohost app ssowatconf > /dev/null 2>&1')
os.system('service nginx reload')
os.system('service bind9 reload')
os.system('service metronome restart')
msignals.display(_("Domain(s) successfully deleted."), 'success')
msignals.display(m18n.n('domain_deleted'), 'success')
return { 'domains': result }

View file

@ -29,6 +29,7 @@ import requests
import json
import glob
import base64
import errno
from moulinette.core import MoulinetteError
@ -49,7 +50,7 @@ def dyndns_subscribe(subscribe_host="dyndns.yunohost.org", domain=None, key=None
# Verify if domain is available
if requests.get('http://%s/test/%s' % (subscribe_host, domain)).status_code != 200:
raise MoulinetteError(17, _("DynDNS domain is already taken"))
raise MoulinetteError(errno.EEXIST, m18n.n('dyndns_unavailable'))
if key is None:
if len(glob.glob('/etc/yunohost/dyndns/*.key')) == 0:
@ -67,9 +68,10 @@ def dyndns_subscribe(subscribe_host="dyndns.yunohost.org", domain=None, key=None
if r.status_code != 201:
try: error = json.loads(r.text)['error']
except: error = "Server error"
raise MoulinetteError(1, _("An error occurred during DynDNS registration: %s") % error)
raise MoulinetteError(errno.EPERM,
m18n.n('dyndns_registration_failed') % error)
msignals.display(_("Subscribed to DynDNS."), 'success')
msignals.display(m18n.n('dyndns_registered'), 'success')
dyndns_installcron()
@ -134,12 +136,13 @@ def dyndns_update(dyn_host="dynhost.yunohost.org", domain=None, key=None, ip=Non
else:
private_key_file = key
if os.system('/usr/bin/nsupdate -k %s /etc/yunohost/dyndns/zone' % private_key_file) == 0:
msignals.display(_("IP successfully updated."), 'success')
msignals.display(m18n.n('dyndns_ip_updated'), 'success')
with open('/etc/yunohost/dyndns/old_ip', 'w') as f:
f.write(new_ip)
else:
os.system('rm /etc/yunohost/dyndns/old_ip > /dev/null 2>&1')
raise MoulinetteError(1, _("An error occurred during DynDNS update"))
raise MoulinetteError(errno.EPERM,
m18n.n('dyndns_ip_update_failed'))
def dyndns_installcron():
@ -151,7 +154,7 @@ def dyndns_installcron():
with open('/etc/cron.d/yunohost-dyndns', 'w+') as f:
f.write('*/2 * * * * root yunohost dyndns update >> /dev/null')
msignals.display(_("DynDNS cron installed."), 'success')
msignals.display(m18n.n('dyndns_cron_installed'), 'success')
def dyndns_removecron():
@ -163,6 +166,6 @@ def dyndns_removecron():
try:
os.remove("/etc/cron.d/yunohost-dyndns")
except:
raise MoulinetteError(167,_("DynDNS cron was not installed"))
raise MoulinetteError(errno.EIO, m18n.n('dyndns_cron_remove_failed'))
msignals.display(_("DynDNS cron removed."), 'success')
msignals.display(m18n.n('dyndns_cron_removed'), 'success')

View file

@ -26,6 +26,7 @@
import os
import sys
import yaml
import errno
try:
import miniupnpc
except ImportError:
@ -66,7 +67,7 @@ def firewall_allow(port=None, protocol='TCP', ipv6=False, no_upnp=False):
if port not in firewall[ipv][protocol]:
firewall[ipv][protocol].append(port)
else:
msignals.display(_("Port already openned: %d" % port), 'warning')
msignals.display(m18n.n('port_already_opened') % port, 'warning')
with open('/etc/yunohost/firewall.yml', 'w') as f:
yaml.safe_dump(firewall, f, default_flow_style=False)
@ -102,7 +103,7 @@ def firewall_disallow(port=None, protocol='TCP', ipv6=False):
if port in firewall[ipv][protocol]:
firewall[ipv][protocol].remove(port)
else:
msignals.display(_("Port already closed: %d" % port), 'warning')
msignals.display(m18n.n('port_already_closed') % port, 'warning')
with open('/etc/yunohost/firewall.yml', 'w') as f:
yaml.safe_dump(firewall, f, default_flow_style=False)
@ -140,7 +141,7 @@ def firewall_reload():
# IPv4
if os.system("iptables -P INPUT ACCEPT") != 0:
raise MoulinetteError(1, _("You cannot play with iptables here. You are either in a container or your kernel does not support it."))
raise MoulinetteError(errno.ESRCH, m18n.n('iptables_unavailable'))
if upnp:
try:
upnpc = miniupnpc.UPnP()
@ -154,9 +155,9 @@ def firewall_reload():
except: pass
upnpc.addportmapping(port, protocol, upnpc.lanaddr, port, 'yunohost firewall : port %d' % port, '')
else:
raise MoulinetteError(1, _("No uPnP device found"))
raise MoulinetteError(errno.ENXIO, m18n.n('upnp_dev_not_found'))
except:
msignals.display(_("An error occured during uPnP port openning"), 'warning')
msignals.display(m18n.n('upnp_port_open_failed'), 'warning')
os.system("iptables -F")
os.system("iptables -X")
@ -196,7 +197,7 @@ def firewall_reload():
os.system("ip6tables -P INPUT DROP")
os.system("service fail2ban restart")
msignals.display(_("Firewall successfully reloaded"), 'success')
msignals.display(m18n.n('firewall_reloaded'), 'success')
return firewall_list()
@ -209,7 +210,6 @@ def firewall_upnp(action=None):
action -- enable/disable
"""
firewall = firewall_list(raw=True)
if action:
@ -221,7 +221,7 @@ def firewall_upnp(action=None):
with open('/etc/cron.d/yunohost-firewall', 'w+') as f:
f.write('*/50 * * * * root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin yunohost firewall reload >>/dev/null')
msignals.display(_("uPnP successfully enabled"), 'success')
msignals.display(m18n.n('upnp_enabled'), 'success')
if action == 'disable':
firewall['uPnP']['enabled'] = False
@ -242,7 +242,7 @@ def firewall_upnp(action=None):
try: os.remove('/etc/cron.d/yunohost-firewall')
except: pass
msignals.display(_("uPnP successfully disabled"), 'success')
msignals.display(m18n.n('upnp_disabled'), 'success')
if action:
os.system("cp /etc/yunohost/firewall.yml /etc/yunohost/firewall.yml.old")
@ -260,7 +260,7 @@ def firewall_stop():
"""
if os.system("iptables -P INPUT ACCEPT") != 0:
raise MoulinetteError(1, _("You cannot play with iptables here. You are either in a container or your kernel does not support it."))
raise MoulinetteError(errno.ESRCH, m18n.n('iptables_unavailable'))
os.system("iptables -F")
os.system("iptables -X")

View file

@ -27,6 +27,7 @@ import os
import sys
import re
import json
import errno
from moulinette.helpers import colorize
from moulinette.core import MoulinetteError
@ -111,7 +112,7 @@ def hook_check(file):
with open(file[:file.index('scripts/')] + 'manifest.json') as f:
manifest = json.loads(str(f.read()))
except:
raise MoulinetteError(22, _("Invalid app package"))
raise MoulinetteError(errno.EIO, m18n.n('app_manifest_invalid'))
action = file[file.index('scripts/') + 8:]
if 'arguments' in manifest and action in manifest['arguments']:
@ -140,7 +141,9 @@ def hook_exec(file, args=None):
for arg in required_args:
if arg['name'] in args:
if 'choices' in arg and args[arg['name']] not in arg['choices']:
raise MoulinetteError(22, _("Invalid choice") + ': ' + args[arg['name']])
raise MoulinetteError(errno.EINVAL,
m18n.n('hook_choice_invalid')
% args[arg['name']])
arg_list.append(args[arg['name']])
else:
if os.isatty(1) and 'ask' in arg:
@ -159,7 +162,9 @@ def hook_exec(file, args=None):
elif 'default' in arg:
arg_list.append(arg['default'])
else:
raise MoulinetteError(22, _("Missing argument : %s") % arg['name'])
raise MoulinetteError(errno.EINVAL,
m18n.n('hook_argument_missing')
% arg['name'])
file_path = "./"
if "/" in file and file[0:2] != file_path:

View file

@ -0,0 +1,127 @@
{
"yunohost" : "YunoHost",
"upgrade_complete" : "Upgrade complete",
"installation_complete" : "Installation complete",
"installation_failed" : "Installation failed",
"no_list_found" : "No list found",
"custom_list_name_required" : "You must provide a name for your custom list",
"list_retrieve_error" : "Unable to retrieve the remote list",
"list_feteched" : "List successfully fetched",
"list_unknown" : "Unknown list",
"list_removed" : "List successfully removed",
"app_unknown" : "Unknown app",
"app_no_upgrade" : "No app to upgrade",
"app_not_installed" : "%s is not installed",
"custom_app_url_required" : "You must provide an URL to upgrade your custom app %s",
"app_recent_version_required" : "%s requires a more recent version of the moulinette",
"app_upgraded" : "%s successfully upgraded",
"app_id_invalid" : "Invalid app id",
"app_already_installed" : "%s is already installed",
"app_removed" : "%s successfully removed",
"app_location_already_used" : "An app is already installed on this location",
"app_location_install_failed" : "Unable to install the app on this location",
"app_extraction_failed" : "Unable to extract installation files",
"app_install_files_invalid" : "Invalid installation files",
"app_manifest_invalid" : "Invalid app manifest",
"app_sources_fetch_failed" : "Unable to fetch sources files",
"ssowat_conf_updated" : "SSOwat persistent configuration successfully updated",
"ssowat_conf_generated" : "SSOwat configuration successfully generated",
"mysql_db_creation_failed" : "MySQL database creation failed",
"mysql_db_init_failed" : "MySQL database init failed",
"mysql_db_initialized" : "MySQL database successfully initialized",
"extracting" : "Extracting...",
"downloading" : "Downloading...",
"done" : "Done.",
"domain_unknown" : "Unknown domain",
"domain_dyndns_invalid" : "Invalid domain to use with DynDNS",
"domain_dyndns_already_subscribed" : "You already have subscribed to a DynDNS domain",
"domain_dyndns_root_unknown" : "Unknown DynDNS root domain",
"domain_cert_gen_failed" : "Unable to generate certificate",
"domain_exists" : "Domain already exists",
"domain_zone_exists" : "Zone file already exists",
"domain_creation_failed" : "Unable to create domain",
"domain_created" : "Domain successfully created",
"domain_uninstall_app_first" : "One or more apps are installed on this domain. Please uninstall them before proceed to domain removal.",
"domain_deletion_failed" : "Unable to delete domain",
"domain_deleted" : "Domain successfully deleted",
"dyndns_unavailable" : "Unavailable DynDNS subdomain",
"dyndns_registration_failed" : "Unable to register DynDNS domain: %s",
"dyndns_registered" : "DynDNS domain successfully registered",
"dyndns_ip_update_failed" : "Unable to update IP address on DynDNS",
"dyndns_ip_updated" : "IP address successfully updated on DynDNS",
"dyndns_cron_installed" : "DynDNS cron job successfully installed",
"dyndns_cron_remove_failed" : "Unable to remove DynDNS cron job",
"dyndns_cron_removed" : "DynDNS cron job successfully removed",
"port_available" : "Port %d is available",
"port_unavailable" : "Port %d is not available",
"port_already_opened" : "Port %d is already opened",
"port_already_closed" : "Port %d is already closed",
"iptables_unavailable" : "You cannot play with iptables here. You are either in a container or your kernel does not support it.",
"upnp_dev_not_found" : "No uPnP device found",
"upnp_port_open_failed" : "Unable to open uPnP ports",
"upnp_enabled" : "uPnP successfully enabled",
"upnp_disabled" : "uPnP successfully disabled",
"firewall_reloaded" : "Firewall successfully reloaded",
"hook_choice_invalid" : "Invalid choice '%s'",
"hook_argument_missing" : "Missing argument '%s'",
"mountpoint_unknown" : "Unknown mountpoint",
"unit_unknown" : "Unknown unit '%s'",
"monitor_period_invalid" : "Invalid time period",
"monitor_stats_no_update" : "No monitoring statistics to update",
"monitor_stats_file_not_found" : "Statistics file not found",
"monitor_stats_period_unavailable" : "No available statistics for the period",
"monitor_enabled" : "Server monitoring successfully enabled",
"monitor_disabled" : "Server monitoring successfully disabled",
"monitor_not_enabled" : "Server monitoring is not enabled",
"monitor_glances_con_failed" : "Unable to connect to Glances server",
"service_unknown" : "Unknown service '%s'",
"service_start_failed" : "Unable to start service '%s'",
"service_already_started" : "Service '%s' is already started",
"service_started" : "Service '%s' successfully started",
"service_stop_failed" : "Unable to stop service '%s'",
"service_already_stopped" : "Service '%s' is already stopped",
"service_stopped" : "Service '%s' successfully stopped",
"service_enable_failed" : "Unable to enable service '%s'",
"service_enabled" : "Service '%s' successfully enabled",
"service_disable_failed" : "Unable to disable service '%s'",
"service_disabled" : "Service '%s' successfully disabled",
"service_status_failed" : "Unable to determine status of service '%s'",
"service_no_log" : "No log to display for service '%s'",
"service_cmd_exec_failed" : "Unable to execute command '%s'",
"ldap_initialized" : "LDAP successfully initialized",
"password_too_short" : "Password is too short",
"admin_password_change_failed" : "Unable to change password",
"admin_password_changed" : "Administration password successfully changed",
"maindomain_change_failed" : "Unable to change main domain",
"maindomain_changed" : "Main domain successfully changed",
"yunohost_installing" : "Installing YunoHost...",
"yunohost_already_installed" : "YunoHost is already installed",
"yunohost_ca_creation_failed" : "Unable to create certificate authority",
"yunohost_configured" : "YunoHost successfully configured",
"update_cache_failed" : "Unable to update APT cache",
"system_no_upgrade" : "There is no packages to upgrade",
"system_upgraded" : "System successfully upgraded",
"field_invalid" : "Invalid field '%s'",
"mail_domain_unknown" : "Unknown mail address domain '%s'",
"mail_alias_remove_failed" : "Unable to remove mail alias '%s'",
"mail_forward_remove_failed" : "Unable to remove mail forward '%s'",
"user_unknown" : "Unknown user",
"user_creation_failed" : "Unable to create user",
"user_created" : "User successfully created",
"user_deletion_failed" : "Unable to delete user",
"user_deleted" : "User successfully deleted",
"user_update_failed" : "Unable to update user",
"user_updated" : "User successfully updated",
"user_info_failed" : "Unable to retrieve user information"
}

View file

@ -31,6 +31,7 @@ import calendar
import subprocess
import xmlrpclib
import os.path
import errno
import cPickle as pickle
from urllib import urlopen
from datetime import datetime, timedelta
@ -74,7 +75,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False):
result_dname = dn
if len(devices) == 0:
if mountpoint is not None:
raise MoulinetteError(1, _("Unknown mountpoint '%s'") % mountpoint)
raise MoulinetteError(errno.ENODEV, m18n.n('mountpoint_unknown'))
return result
# Retrieve monitoring for unit(s)
@ -131,7 +132,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False):
for dname in devices_names:
_set(dname, 'not-available')
else:
raise MoulinetteError(1, _("Unknown unit '%s'") % u)
raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown') % u)
if result_dname is not None:
return result[result_dname]
@ -203,7 +204,7 @@ def monitor_network(units=None, human_readable=False):
'gateway': gateway
}
else:
raise MoulinetteError(1, _("Unknown unit '%s'") % u)
raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown') % u)
if len(units) == 1:
return result[units[0]]
@ -253,7 +254,7 @@ def monitor_system(units=None, human_readable=False):
elif u == 'infos':
result[u] = json.loads(glances.getSystem())
else:
raise MoulinetteError(1, _("Unknown unit '%s'") % u)
raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown') % u)
if len(units) == 1 and type(result[units[0]]) is not str:
return result[units[0]]
@ -269,7 +270,7 @@ def monitor_update_stats(period):
"""
if period not in ['day', 'week', 'month']:
raise MoulinetteError(22, _("Invalid period"))
raise MoulinetteError(errno.EINVAL, m18n.n('monitor_period_invalid'))
stats = _retrieve_stats(period)
if not stats:
@ -287,7 +288,7 @@ def monitor_update_stats(period):
else:
monitor = _monitor_all(p, 0)
if not monitor:
raise MoulinetteError(1, _("No monitoring statistics to update"))
raise MoulinetteError(errno.ENODATA, m18n.n('monitor_stats_no_update'))
stats['timestamp'].append(time.time())
@ -352,13 +353,15 @@ def monitor_show_stats(period, date=None):
"""
if period not in ['day', 'week', 'month']:
raise MoulinetteError(22, _("Invalid period"))
raise MoulinetteError(errno.EINVAL, m18n.n('monitor_period_invalid'))
result = _retrieve_stats(period, date)
if result is False:
raise MoulinetteError(167, _("Stats file not found"))
raise MoulinetteError(errno.ENOENT,
m18n.n('monitor_stats_file_not_found'))
elif result is None:
raise MoulinetteError(1, _("No available stats for the given period"))
raise MoulinetteError(errno.EINVAL,
m18n.n('monitor_stats_period_unavailable'))
return result
@ -389,7 +392,7 @@ def monitor_enable(no_stats=False):
os.system("touch %s" % crontab_path)
os.system("echo '%s' >%s" % (rules, crontab_path))
msignals.display(_("Server monitoring successfully enabled."), 'success')
msignals.display(m18n.n('monitor_enabled'), 'success')
def monitor_disable():
@ -407,7 +410,7 @@ def monitor_disable():
try:
service_disable('glances')
except MoulinetteError as e:
msignals.display('%s.' % e.strerror, 'warning')
msignals.display(e.strerror, 'warning')
# Remove crontab
try:
@ -415,7 +418,7 @@ def monitor_disable():
except:
pass
msignals.display(_("Server monitoring successfully disabled."), 'success')
msignals.display(m18n.n('monitor_disabled'), 'success')
def _get_glances_api():
@ -434,8 +437,8 @@ def _get_glances_api():
from yunohost.service import service_status
if service_status('glances')['status'] != 'running':
raise MoulinetteError(1, _("Monitoring is disabled"))
raise MoulinetteError(1, _("Connection to Glances server failed"))
raise MoulinetteError(errno.EPERM, m18n.n('monitor_not_enabled'))
raise MoulinetteError(errno.EIO, m18n.n('monitor_glances_con_failed'))
def _extract_inet(string, skip_netmask=False, skip_loopback=True):

View file

@ -26,6 +26,7 @@
import yaml
import glob
import subprocess
import errno
import os.path
from moulinette.core import MoulinetteError
@ -43,11 +44,12 @@ def service_start(names):
names = [names]
for name in names:
if _run_service_command('start', name):
msignals.display(_("Service '%s' successfully started.") % name, 'success')
msignals.display(m18n.n('service_started') % name, 'success')
else:
if service_status(name)['status'] != 'running':
raise MoulinetteError(1, _("Starting of service '%s' failed") % name)
msignals.display(_("Service '%s' already started.") % name)
raise MoulinetteError(errno.EPERM,
m18n.n('service_start_failed') % name)
msignals.display(m18n.n('service_already_started') % name)
def service_stop(names):
@ -62,11 +64,12 @@ def service_stop(names):
names = [names]
for name in names:
if _run_service_command('stop', name):
msignals.display(_("Service '%s' successfully stopped.") % name, 'success')
msignals.display(m18n.n('service_stopped') % name, 'success')
else:
if service_status(name)['status'] != 'inactive':
raise MoulinetteError(1, _("Stopping of service '%s' failed") % name)
msignals.display(_("Service '%s' already stopped.") % name)
raise MoulinetteError(errno.EPERM,
m18n.n('service_stop_failed') % name)
msignals.display(m18n.n('service_already_stopped') % name)
def service_enable(names):
@ -81,9 +84,10 @@ def service_enable(names):
names = [names]
for name in names:
if _run_service_command('enable', name):
msignals.display(_("Service '%s' successfully enabled.") % name, 'success')
msignals.display(m18n.n('service_enabled') % name, 'success')
else:
raise MoulinetteError(1, _("Enabling of service '%s' failed") % name)
raise MoulinetteError(errno.EPERM,
m18n.n('service_enable_failed') % name)
def service_disable(names):
@ -98,9 +102,10 @@ def service_disable(names):
names = [names]
for name in names:
if _run_service_command('disable', name):
msignals.display(_("Service '%s' successfully disabled.") % name, 'success')
msignals.display(m18n.n('service_disabled') % name, 'success')
else:
raise MoulinetteError(1, _("Disabling of service '%s' failed") % name)
raise MoulinetteError(errno.EPERM,
m18n.n('service_disable_failed') % name)
def service_status(names=[]):
@ -123,7 +128,8 @@ def service_status(names=[]):
for name in names:
if check_names and name not in services.keys():
raise MoulinetteError(1, _("Unknown service '%s'") % name)
raise MoulinetteError(errno.EINVAL,
m18n.n('service_unknown') % name)
status = None
if services[name]['status'] == 'service':
@ -145,8 +151,8 @@ def service_status(names=[]):
result[name]['status'] = 'inactive'
else:
# TODO: Log output?
msignals.display(_("Could not determine status of service '%s'.") % \
name, 'warning')
msignals.display(m18n.n('service_status_failed') % name,
'warning')
else:
result[name]['status'] = 'running'
@ -169,14 +175,14 @@ def service_log(name, number=50):
Log every log files of a service
Keyword argument:
name -- Services name to log
name -- Service name to log
number -- Number of lines to display
"""
services = _get_services()
if name not in services.keys():
raise MoulinetteError(1, _("Unknown service '%s'") % service)
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown') % name)
if 'log' in services[name]:
log_list = services[name]['log']
@ -191,7 +197,7 @@ def service_log(name, number=50):
else:
result[log_path] = _tail(log_path, int(number))
else:
raise MoulinetteError(1, _("Nothing to log for service '%s'") % name)
raise MoulinetteError(errno.EPERM, m18n.n('service_no_log') % name)
return result
@ -206,7 +212,7 @@ def _run_service_command(action, service):
"""
if service not in _get_services().keys():
raise MoulinetteError(1, _("Unknown service '%s'") % service)
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown') % name)
cmd = None
if action in ['start', 'stop']:
@ -215,14 +221,14 @@ def _run_service_command(action, service):
arg = 'defaults' if action == 'enable' else 'remove'
cmd = 'update-rc.d %s %s' % (service, arg)
else:
raise MoulinetteError(1, _("Unknown action '%s'") % action)
raise ValueError("Unknown action '%s'" % action)
try:
ret = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
# TODO: Log output?
msignals.display(_("Execution of command '%s' failed.") % \
' '.join(e.cmd), 'warning')
msignals.display(m18n.n('service_cmd_exec_failed') % ' '.join(e.cmd),
'warning')
return False
return True

View file

@ -30,6 +30,7 @@ import re
import getpass
import requests
import json
import errno
import apt
import apt.progress
@ -69,7 +70,7 @@ def tools_ldapinit(auth):
auth.update('cn=admin', admin_dict)
msignals.display(_("LDAP has been successfully initialized"), 'success')
msignals.display(m18n.n('ldap_ initialized'), 'success')
def tools_adminpw(old_password, new_password):
@ -83,7 +84,7 @@ def tools_adminpw(old_password, new_password):
"""
# Validate password length
if len(new_password) < 4:
raise MoulinetteError(22, _("Password is too short"))
raise MoulinetteError(errno.EINVAL, m18n.n('password_too_short'))
old_password.replace('"', '\\"')
old_password.replace('&', '\\&')
@ -92,9 +93,10 @@ def tools_adminpw(old_password, new_password):
result = os.system('ldappasswd -h localhost -D cn=admin,dc=yunohost,dc=org -w "%s" -a "%s" -s "%s"' % (old_password, old_password, new_password))
if result == 0:
msignals.display(_("Admin password has been changed"), 'success')
msignals.display(m18n.n('admin_password_changed'), 'success')
else:
raise MoulinetteError(22, _("Invalid password"))
raise MoulinetteError(errno.EPERM,
m18n.n('admin_password_change_failed'))
def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False):
@ -165,7 +167,8 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False):
for command in command_list:
if os.system(command) != 0:
raise MoulinetteError(17, _("There were a problem during domain changing"))
raise MoulinetteError(errno.EPERM,
m18n.n('maindomain_change_failed'))
if dyndns: dyndns_subscribe(domain=new_domain)
elif len(new_domain.split('.')) >= 3:
@ -175,7 +178,7 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False):
if dyndomain in dyndomains:
dyndns_subscribe(domain=new_domain)
msignals.display(_("Main domain has been successfully changed"), 'success')
msignals.display(m18n.n('maindomain_changed'), 'success')
def tools_postinstall(domain, password, dyndns=False):
@ -194,9 +197,9 @@ def tools_postinstall(domain, password, dyndns=False):
try:
with open('/etc/yunohost/installed') as f: pass
except IOError:
print('Installing YunoHost')
msignals.display(m18n.n('yunohost_installing'))
else:
raise MoulinetteError(17, _("YunoHost is already installed"))
raise MoulinetteError(errno.EPERM, m18n.n('yunohost_already_installed'))
if len(domain.split('.')) >= 3:
r = requests.get('http://dyndns.yunohost.org/domains')
@ -206,7 +209,8 @@ def tools_postinstall(domain, password, dyndns=False):
if requests.get('http://dyndns.yunohost.org/test/%s' % domain).status_code == 200:
dyndns=True
else:
raise MoulinetteError(17, _("Domain is already taken"))
raise MoulinetteError(errno.EEXIST,
m18n.n('dyndns_unavailable'))
# Create required folders
folders_to_create = [
@ -257,7 +261,8 @@ def tools_postinstall(domain, password, dyndns=False):
for command in command_list:
if os.system(command) != 0:
raise MoulinetteError(17, _("There were a problem during CA creation"))
raise MoulinetteError(errno.EPERM,
m18n.n('yunohost_ca_creation_failed'))
# Initialize YunoHost LDAP base
tools_ldapinit(auth)
@ -277,7 +282,7 @@ def tools_postinstall(domain, password, dyndns=False):
os.system('touch /etc/yunohost/installed')
os.system('service yunohost-api restart &')
msignals.display(_("YunoHost has been successfully configured"), 'success')
msignals.display(m18n.n('yunohost_configured'), 'success')
def tools_update(ignore_apps=False, ignore_packages=False):
@ -296,7 +301,7 @@ def tools_update(ignore_apps=False, ignore_packages=False):
cache = apt.Cache()
# Update APT cache
if not cache.update():
raise MoulinetteError(1, _("An error occured during APT cache update"))
raise MoulinetteError(errno.EPERM, m18n.n('update_cache_failed'))
cache.open(None)
cache.upgrade(True)
@ -338,12 +343,11 @@ def tools_update(ignore_apps=False, ignore_packages=False):
})
if len(apps) == 0 and len(packages) == 0:
msignals.display(_("There is nothing to upgrade right now"), 'success')
msignals.display(m18n.n('system_no_upgrade'), 'success')
return { 'packages': packages, 'apps': apps }
def tools_upgrade(ignore_apps=False, ignore_packages=False):
"""
Update apps & package cache, then display changelog
@ -380,7 +384,7 @@ def tools_upgrade(ignore_apps=False, ignore_packages=False):
app_upgrade()
except: pass
msignals.display(_("System successfully upgraded"), 'success')
msignals.display(m18n.n('system_upgraded'), 'success')
# Return API logs if it is an API call
if not os.isatty(1):

View file

@ -29,6 +29,7 @@ import crypt
import random
import string
import json
import errno
from moulinette.core import MoulinetteError
@ -64,7 +65,8 @@ def user_list(auth, fields=None, filter=None, limit=None, offset=None):
if attr in keys:
attrs.append(attr)
else:
raise MoulinetteError(22, _("Invalid field '%s'") % attr)
raise MoulinetteError(errno.EINVAL,
m18n.n('field_invalid') % attr)
else:
attrs = [ 'uid', 'cn', 'mail' ]
@ -99,7 +101,7 @@ def user_create(auth, username, firstname, lastname, mail, password):
# Validate password length
if len(password) < 4:
raise MoulinetteError(22, _("Password is too short"))
raise MoulinetteError(errno.EINVAL, m18n.n('password_too_short'))
auth.validate_uniqueness({
'uid' : username,
@ -107,10 +109,11 @@ def user_create(auth, username, firstname, lastname, mail, password):
})
if mail[mail.find('@')+1:] not in domain_list(auth)['domains']:
raise MoulinetteError(22, _("Unknown domain '%s'") % mail[mail.find('@')+1:])
raise MoulinetteError(errno.EINVAL,
m18n.n('mail_domain_unknown')
% mail[mail.find('@')+1:])
# Get random UID/GID
uid_check = gid_check = 0
while uid_check == 0 and gid_check == 0:
uid = str(random.randint(200, 99999))
@ -174,12 +177,12 @@ def user_create(auth, username, firstname, lastname, mail, password):
os.system("su - %s -c ''" % username)
os.system('yunohost app ssowatconf > /dev/null 2>&1')
#TODO: Send a welcome mail to user
msignals.display(_("User '%s' successfully created.") % username, 'success')
msignals.display(m18n.n('user_created'), 'success')
hook_callback('post_user_create', [username, mail, password, firstname, lastname])
return { 'fullname' : fullname, 'username' : username, 'mail' : mail }
raise MoulinetteError(169, _("An error occurred during user creation"))
raise MoulinetteError(169, m18n.n('user_creation_failed'))
def user_delete(auth, users, purge=False):
@ -207,10 +210,10 @@ def user_delete(auth, users, purge=False):
deleted.append(user)
continue
else:
raise MoulinetteError(169, _("An error occurred during user deletion"))
raise MoulinetteError(169, m18n.n('user_deletion_failed'))
os.system('yunohost app ssowatconf > /dev/null 2>&1')
msignals.display(_("User(s) successfully deleted."), 'success')
msignals.display(m18n.n('user_deleted'), 'success')
return { 'users': deleted }
@ -239,7 +242,7 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change
# Populate user informations
result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch)
if not result:
raise MoulinetteError(167, _("Unknown username '%s'") % username)
raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown'))
user = result[0]
# Get modifications from arguments
@ -263,7 +266,9 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change
if mail:
auth.validate_uniqueness({ 'mail': mail })
if mail[mail.find('@')+1:] not in domains:
raise MoulinetteError(22, _("Unknown domain '%s'") % mail[mail.find('@')+1:])
raise MoulinetteError(errno.EINVAL,
m18n.n('mail_domain_unknown')
% mail[mail.find('@')+1:])
del user['mail'][0]
new_attr_dict['mail'] = [mail] + user['mail']
@ -273,7 +278,9 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change
for mail in add_mailalias:
auth.validate_uniqueness({ 'mail': mail })
if mail[mail.find('@')+1:] not in domains:
raise MoulinetteError(22, _("Unknown domain '%s'") % mail[mail.find('@')+1:])
raise MoulinetteError(errno.EINVAL,
m18n.n('mail_domain_unknown')
% mail[mail.find('@')+1:])
user['mail'].append(mail)
new_attr_dict['mail'] = user['mail']
@ -284,7 +291,8 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change
if len(user['mail']) > 1 and mail in user['mail'][1:]:
user['mail'].remove(mail)
else:
raise MoulinetteError(22, _("Invalid mail alias '%s'") % mail)
raise MoulinetteError(errno.EINVAL,
m18n.n('mail_alias_remove_failed') % mail)
new_attr_dict['mail'] = user['mail']
if add_mailforward:
@ -303,14 +311,15 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change
if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]:
user['maildrop'].remove(mail)
else:
raise MoulinetteError(22, _("Invalid mail forward '%s'") % mail)
raise MoulinetteError(errno.EINVAL,
m18n.n('mail_forward_remove_failed') % mail)
new_attr_dict['maildrop'] = user['maildrop']
if auth.update('uid=%s,ou=users' % username, new_attr_dict):
msignals.display(_("User '%s' successfully updated.") % username, 'success')
msignals.display(m18n.n('user_updated'), 'success')
return user_info(auth, username)
else:
raise MoulinetteError(169, _("An error occurred during user update"))
raise MoulinetteError(169, m18n.n('user_update_failed'))
def user_info(auth, username):
@ -333,7 +342,7 @@ def user_info(auth, username):
if result:
user = result[0]
else:
raise MoulinetteError(22, _("Unknown username/mail '%s'") % username)
raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown'))
result_dict = {
'username': user['uid'][0],
@ -352,4 +361,4 @@ def user_info(auth, username):
if result:
return result_dict
else:
raise MoulinetteError(167, _("No user found"))
raise MoulinetteError(167, m18n.n('user_info_failed'))

26
locales/en.json Normal file
View file

@ -0,0 +1,26 @@
{
"colon" : "%s: ",
"success" : "Success!",
"warning" : "Warning:",
"error" : "Error:",
"permission_denied" : "Permission denied",
"root_required" : "You must be root to perform this action",
"instance_already_running" : "An instance is already running",
"unable_authenticate" : "Unable to authenticate",
"unable_retrieve_session" : "Unable to retrieve the session",
"error_ldap_operation" : "An error occured during LDAP operation",
"password" : "Password",
"invalid_password" : "Invalid password",
"confirm" : "Confirm",
"values_mismatch" : "Values don't match",
"authentication_required_long" : "Authentication is required to perform this action",
"authentication_required" : "Authentication required",
"authentication_profile_required" : "Authentication to profile '%s' required",
"operation_interrupted" : "Operation interrupted",
"not_logged_in" : "You are not logged in",
"server_already_running" : "A server is already running on that port"
}

26
locales/fr.json Normal file
View file

@ -0,0 +1,26 @@
{
"colon" : "%s : ",
"success" : "Succès !",
"warning" : "Attention :",
"error" : "Erreur :",
"permission_denied" : "Permission refusée",
"root_required" : "Vous devez avoir les droits super-utilisateur pour exécuter cette action",
"instance_already_running" : "Une instance est déjà en cours d'exécution",
"unable_authenticate" : "Impossible de vous authentifier",
"unable_retrieve_session" : "Impossible de récupérer la session",
"error_ldap_operation" : "Une erreur est survenue lors de l'opération LDAP",
"password" : "Mot de passe",
"invalid_password" : "Mot de passe incorrect",
"confirm" : "Confirmez",
"values_mismatch" : "Les valeurs ne correspondent pas",
"authentication_required_long" : "L'authentification est requise pour exécuter cette action",
"authentication_required" : "Authentification requise",
"authentication_profile_required" : "Authentification au profile '%s' requise",
"operation_interrupted" : "Opération interrompue",
"not_logged_in" : "Vous n'êtes pas connecté",
"server_already_running" : "Un server est déjà en cours d'exécution sur ce port"
}

View file

@ -50,13 +50,11 @@ def init(**kwargs):
"""
import sys
import __builtin__
from moulinette.core import Package, MoulinetteSignals, install_i18n
from moulinette.core import Package, Moulinette18n, MoulinetteSignals
__builtin__.__dict__['pkg'] = Package(**kwargs)
__builtin__.__dict__['m18n'] = Moulinette18n(pkg)
__builtin__.__dict__['msignals'] = MoulinetteSignals()
# Initialize internationalization
install_i18n()
# Add library directory to python path
sys.path.insert(0, pkg.libdir)
@ -104,6 +102,6 @@ def cli(namespaces, args, use_cache=True):
'use_cache': use_cache})
moulinette.run(args)
except MoulinetteError as e:
print(_('%s: %s' % (colorize(_('Error'), 'red'), e.strerror)))
print('%s %s' % (colorize(m18n.g('error'), 'red'), e.strerror))
return e.errno
return 0

View file

@ -259,7 +259,7 @@ class ActionsMap(object):
def __init__(self, parser, namespaces=[], use_cache=True):
self.use_cache = use_cache
if not issubclass(parser, BaseActionsMapParser):
raise MoulinetteError(errno.EINVAL, _("Invalid parser class '%s'" % parser.__name__))
raise ValueError("Invalid parser class '%s'" % parser.__name__)
self._parser_class = parser
logging.debug("initializing ActionsMap for the interface '%s'" % parser.interface)
@ -311,7 +311,7 @@ class ActionsMap(object):
try:
auth = self.parser.get_global_conf('authenticator', profile)[1]
except KeyError:
raise MoulinetteError(errno.EINVAL, _("Unknown authenticator profile '%s'") % profile)
raise ValueError("Unknown authenticator profile '%s'" % profile)
else:
return auth()
@ -343,9 +343,11 @@ class ActionsMap(object):
fromlist=[func_name])
func = getattr(mod, func_name)
except (AttributeError, ImportError):
raise MoulinetteError(errno.ENOSYS, _('Function is not defined'))
raise ImportError("Unable to load function %s.%s/%s"
% (namespace, category, func_name))
else:
# Process the action
# Load translation and process the action
m18n.load_namespace(namespace)
return func(**arguments)
@staticmethod

View file

@ -93,7 +93,7 @@ class BaseAuthenticator(object):
s_id, s_hash = token
except TypeError:
if not password:
raise MoulinetteError(errno.EINVAL, _("Invalid format for token"))
raise ValueError("Invalid token format")
else:
# TODO: Log error
store_session = False
@ -108,9 +108,9 @@ class BaseAuthenticator(object):
except MoulinetteError:
raise
except Exception as e:
logging.error("authentication (name: '%s', type: '%s') fails: %s" % \
(self.name, self.vendor, e))
raise MoulinetteError(errno.EACCES, _("Unable to authenticate"))
logging.error("authentication (name: '%s', type: '%s') fails: %s" \
% (self.name, self.vendor, e))
raise MoulinetteError(errno.EACCES, m18n.g('unable_authenticate'))
# Store session
if store_session:
@ -140,8 +140,8 @@ class BaseAuthenticator(object):
with self._open_sessionfile(session_id, 'r') as f:
enc_pwd = f.read()
except IOError:
# TODO: Set proper error code
raise MoulinetteError(167, _("Unable to retrieve session"))
raise MoulinetteError(errno.ENOENT,
m18r.g('unable_retrieve_session'))
else:
gpg = gnupg.GPG()
gpg.encoding = 'utf-8'

View file

@ -2,6 +2,7 @@
# TODO: Use Python3 to remove this fix!
from __future__ import absolute_import
import errno
import ldap
import ldap.modlist as modlist
@ -64,7 +65,7 @@ class Authenticator(BaseAuthenticator):
else:
con.simple_bind_s()
except ldap.INVALID_CREDENTIALS:
raise MoulinetteError(errno.EACCES, _("Invalid password"))
raise MoulinetteError(errno.EACCES, m18n.g('invalid_password'))
else:
self.con = con
@ -93,7 +94,7 @@ class Authenticator(BaseAuthenticator):
try:
result = self.con.search_s(base, ldap.SCOPE_SUBTREE, filter, attrs)
except:
raise MoulinetteError(169, _('An error occured during LDAP search'))
raise MoulinetteError(169, m18n.g('error_ldap_operation'))
result_list = []
if not attrs or 'dn' not in attrs:
@ -122,7 +123,7 @@ class Authenticator(BaseAuthenticator):
try:
self.con.add_s(dn, ldif)
except:
raise MoulinetteError(169, _('An error occured during LDAP entry creation'))
raise MoulinetteError(169, m18n.g('error_ldap_operation'))
else:
return True
@ -141,7 +142,7 @@ class Authenticator(BaseAuthenticator):
try:
self.con.delete_s(dn)
except:
raise MoulinetteError(169, _('An error occured during LDAP entry deletion'))
raise MoulinetteError(169, m18n.g('error_ldap_operation'))
else:
return True
@ -169,7 +170,7 @@ class Authenticator(BaseAuthenticator):
self.con.modify_ext_s(dn, ldif)
except:
raise MoulinetteError(169, _('An error occured during LDAP entry update'))
raise MoulinetteError(169, m18n.g('error_ldap_operation'))
else:
return True
@ -188,5 +189,5 @@ class Authenticator(BaseAuthenticator):
if not self.search(filter=attr + '=' + value):
continue
else:
raise MoulinetteError(17, _('Attribute already exists') + ' "' + attr + '=' + value + '"')
raise MoulinetteError(17, 'Attribute already exists "%s=%s"' % (attr, value))
return True

View file

@ -3,35 +3,14 @@
import os
import sys
import time
import json
import errno
import gettext
import logging
from importlib import import_module
# Package manipulation -------------------------------------------------
def install_i18n(namespace=None):
"""Install internationalization
Install translation based on the package's default gettext domain or
on 'namespace' if provided.
Keyword arguments:
- namespace -- The namespace to initialize i18n for
"""
if namespace:
try:
t = gettext.translation(namespace, pkg.localedir)
except IOError:
# TODO: Log error
return
else:
t.install()
else:
gettext.install('moulinette', pkg.localedir)
class Package(object):
"""Package representation and easy access methods
@ -51,7 +30,7 @@ class Package(object):
# Set local directories
self._datadir = '%s/data' % basedir
self._libdir = '%s/lib' % basedir
self._localedir = '%s/po' % basedir
self._localedir = '%s/locales' % basedir
self._cachedir = '%s/cache' % basedir
else:
import package
@ -130,6 +109,189 @@ class Package(object):
True if mode[0] == 'w' else False)
return open('%s/%s' % (self.get_cachedir(**kwargs), filename), mode)
# Internationalization -------------------------------------------------
class Translator(object):
"""Internationalization class
Provide an internationalization mechanism based on JSON files to
translate a key in the proper locale.
Keyword arguments:
- locale_dir -- The directory where locale files are located
- default_locale -- The default locale to use
"""
def __init__(self, locale_dir, default_locale='en'):
self.locale_dir = locale_dir
self.locale = default_locale
self._translations = {}
# Attempt to load default translations
if not self._load_translations(default_locale):
raise ValueError("Unable to load locale '%s' from '%s'"
% (default_locale, locale_dir))
self.default_locale = default_locale
def get_locales(self):
"""Return a list of the avalaible locales"""
locales = []
for f in os.listdir(self.locale_dir):
if f.endswith('.json'):
# TODO: Validate locale
locales.append(f[:-5])
return locales
def set_locale(self, locale):
"""Set the locale to use
Set the locale to use at first. If the locale is not available,
the default locale is used.
Keyword arguments:
- locale -- The locale to use
Returns:
True if the locale has been set, otherwise False
"""
if locale not in self._translations:
if not self._load_translations(locale):
logging.info("unable to load locale '%s' from '%s'"
% (self.default_locale, self.locale_dir))
# Revert to default locale
self.locale = self.default_locale
return False
# Set current locale
self.locale = locale
return True
def translate(self, key):
"""Retrieve proper translation for a key
Attempt to retrieve translation for a key using the current locale
or the default locale if 'key' is not found.
Keyword arguments:
- key -- The key to translate
"""
try:
value = self._translations[self.locale][key]
except KeyError:
try:
value = self._translations[self.default_locale][key]
logging.info("untranslated key '%s' for locale '%s'" %
(key, self.locale))
except KeyError:
logging.warning("unknown key '%s' for locale '%s'" %
(key, self.default_locale))
return key
return value
def _load_translations(self, locale, overwrite=False):
"""Load translations for a locale
Attempt to load translations for a given locale. If 'overwrite' is
True, translations will be loaded again.
Keyword arguments:
- locale -- The locale to load
- overwrite -- True to overwrite existing translations
Returns:
True if the translations have been loaded, otherwise False
"""
if not overwrite and locale in self._translations:
return True
try:
with open('%s/%s.json' % (self.locale_dir, locale), 'r') as f:
j = json.load(f)
except IOError:
return False
else:
self._translations[locale] = j
return True
class Moulinette18n(object):
"""Internationalization service for the moulinette
Manage internationalization and access to the proper keys translation
used in the moulinette and libraries.
Keyword arguments:
- package -- The current Package instance
- default_locale -- The default locale to use
"""
def __init__(self, package, default_locale='en'):
self.default_locale = default_locale
self.locale = default_locale
self.pkg = package
# Init translators
self._global = Translator(self.pkg.localedir, default_locale)
self._namespace = None
def load_namespace(self, namespace):
"""Load the namespace to use
Load and set translations of a given namespace. Those translations
are accessible with Moulinette18n.n().
Keyword arguments:
- namespace -- The namespace to load
"""
if self._namespace and self._namespace[0] == namespace:
return
self._namespace = (namespace, Translator('%s/%s/locales'
% (self.pkg.libdir, namespace), self.default_locale))
self._namespace[1].set_locale(self.locale)
def set_locale(self, locale):
"""Set the locale to use"""
self.locale = locale
self._global.set_locale(locale)
if self._namespace:
self._namespace[1].set_locale(locale)
def g(self, key):
"""Retrieve proper translation for a moulinette key
Attempt to retrieve value for a key from moulinette translations
using the current locale or the default locale if 'key' is not found.
Keyword arguments:
- key -- The key to translate
"""
return self._global.translate(key)
def n(self, key):
"""Retrieve proper translation for a moulinette key
Attempt to retrieve value for a key from loaded namespace translations
using the current locale or the default locale if 'key' is not found.
Keyword arguments:
- key -- The key to translate
"""
if not self._namespace:
raise RuntimeError("No namespace loaded for translation")
return self._namespace[1].translate(key)
class MoulinetteSignals(object):
"""Signals connector for the moulinette
@ -256,16 +418,16 @@ def init_interface(name, kwargs={}, actionsmap={}):
try:
mod = import_module('moulinette.interfaces.%s' % name)
except ImportError:
except ImportError as e:
# TODO: List available interfaces
raise MoulinetteError(errno.EINVAL, _("Unknown interface '%s'" % name))
raise ImportError("Unable to load interface '%s': %s" % (name, str(e)))
else:
try:
# Retrieve interface classes
parser = mod.ActionsMapParser
interface = mod.Interface
except AttributeError as e:
raise MoulinetteError(errno.EFAULT, _("Invalid interface '%s': %s") % (name, e))
raise ImportError("Invalid interface '%s': %s" % (name, e))
# Instantiate or retrieve ActionsMap
if isinstance(actionsmap, dict):
@ -273,7 +435,7 @@ def init_interface(name, kwargs={}, actionsmap={}):
elif isinstance(actionsmap, ActionsMap):
amap = actionsmap
else:
raise MoulinetteError(errno.EINVAL, _("Invalid actions map '%r'" % actionsmap))
raise ValueError("Invalid actions map '%r'" % actionsmap)
return interface(amap, **kwargs)
@ -293,7 +455,8 @@ def init_authenticator((vendor, name), kwargs={}):
mod = import_module('moulinette.authenticators.%s' % vendor)
except ImportError as e:
# TODO: List available authenticators vendors
raise MoulinetteError(errno.EINVAL, _("Unable to load authenticator vendor '%s': %s") % (vendor, str(e)))
raise ImportError("Unable to load authenticator vendor '%s': %s"
% (vendor, str(e)))
else:
return mod.Authenticator(name, **kwargs)
@ -369,12 +532,13 @@ class MoulinetteLock(object):
try:
(open(self._lockfile, 'w')).close()
except IOError:
raise MoulinetteError(errno.EPERM, _("Permission denied, did you forget 'sudo' ?"))
raise MoulinetteError(errno.EPERM,
'%s. %s.' % (m18n.g('permission_denied'), m18n.g('root_required')))
break
if (time.time() - start_time) > self.timeout:
raise MoulinetteError(errno.EBUSY, _("An instance is already running for '%s'") \
% self.namespace)
raise MoulinetteError(errno.EBUSY,
m18n.g('instance_already_running'))
# Wait before checking again
time.sleep(self.interval)
self._locked = True

View file

@ -122,7 +122,7 @@ class _ActionsMapPlugin(object):
try:
kwargs['password'] = request.POST['password']
except KeyError:
raise HTTPBadRequestResponse(_("Missing password parameter"))
raise HTTPBadRequestResponse("Missing password parameter")
try:
kwargs['profile'] = request.POST['profile']
except KeyError:
@ -235,7 +235,7 @@ class _ActionsMapPlugin(object):
try:
del self.secrets[s_id]
except KeyError:
raise HTTPUnauthorizedResponse(_("You are not logged in"))
raise HTTPUnauthorizedResponse(m18n.g('not_logged_in'))
else:
# TODO: Clean the session for profile only
# Delete cookie and clean the session
@ -278,9 +278,9 @@ class _ActionsMapPlugin(object):
secret=s_secret)[authenticator.name]
except KeyError:
if authenticator.name == 'default':
msg = _("Needing authentication")
msg = m18n.g('authentication_required')
else:
msg = _("Needing authentication to profile '%s'") % authenticator.name
msg = m18n.g('authentication_profile_required') % authenticator.name
raise HTTPUnauthorizedResponse(msg)
else:
return authenticator(token=(s_id, s_hash))
@ -405,7 +405,7 @@ class ActionsMapParser(BaseActionsMapParser):
auth = msignals.authenticate(klass(), **auth_conf)
if not auth.is_authenticated:
# TODO: Set proper error code
raise MoulinetteError(errno.EACCES, _("This action need authentication"))
raise MoulinetteError(errno.EACCES, m18n.g('authentication_required_long'))
if self.get_conf(tid, 'argument_auth') and \
self.get_conf(tid, 'authenticate') == 'all':
ret.auth = auth
@ -466,7 +466,8 @@ class Interface(BaseInterface):
run(self._app, port=_port)
except IOError as e:
if e.args[0] == errno.EADDRINUSE:
raise MoulinetteError(errno.EADDRINUSE, _("A server is already running"))
raise MoulinetteError(errno.EADDRINUSE,
m18n.g('server_already_running'))
raise

View file

@ -137,8 +137,8 @@ class ActionsMapParser(BaseActionsMapParser):
# TODO: Catch errors
auth = msignals.authenticate(klass(), **auth_conf)
if not auth.is_authenticated:
# TODO: Set proper error code
raise MoulinetteError(errno.EACCES, _("This action need authentication"))
raise MoulinetteError(errno.EACCES,
m18n.g('authentication_required_long'))
if self.get_conf(ret._tid, 'argument_auth') and \
self.get_conf(ret._tid, 'authenticate') == 'all':
ret.auth = auth
@ -157,6 +157,12 @@ class Interface(BaseInterface):
"""
def __init__(self, actionsmap):
import locale
# Set user locale
lang = locale.getdefaultlocale()[0]
m18n.set_locale(lang[:2])
# Connect signals to handlers
msignals.set_handler('authenticate', self._do_authenticate)
msignals.set_handler('display', self._do_display)
@ -177,7 +183,7 @@ class Interface(BaseInterface):
try:
ret = self.actionsmap.process(args, timeout=5)
except KeyboardInterrupt, EOFError:
raise MoulinetteError(errno.EINTR, _("Interrupted"))
raise MoulinetteError(errno.EINTR, m18n.g('operation_interrupted'))
if isinstance(ret, dict):
pretty_print_dict(ret)
@ -194,7 +200,7 @@ class Interface(BaseInterface):
"""
# TODO: Allow token authentication?
msg = help or _("Password")
msg = help or m18n.g('password')
return authenticator(password=self._do_prompt(msg, True, False))
def _do_prompt(self, message, is_password, confirm):
@ -204,14 +210,15 @@ class Interface(BaseInterface):
"""
if is_password:
prompt = lambda m: getpass.getpass(colorize(_('%s: ') % m, 'blue'))
prompt = lambda m: getpass.getpass(colorize(m18n.g('colon') % m,
'blue'))
else:
prompt = lambda m: raw_input(colorize(_('%s: ') % m, 'blue'))
prompt = lambda m: raw_input(colorize(m18n.g('colon') % m, 'blue'))
value = prompt(message)
if confirm:
if prompt(_('Retype %s') % message) != value:
raise MoulinetteError(errno.EINVAL, _("Values don't match"))
if prompt('%s %s' % (m18n.g('confirm'), message)) != value:
raise MoulinetteError(errno.EINVAL, m18n.g('values_mismatch'))
return value
@ -222,8 +229,8 @@ class Interface(BaseInterface):
"""
if style == 'success':
print('%s %s' % (colorize(_("Success!"), 'green'), message))
print('%s %s' % (colorize(m18n.g('success'), 'green'), message))
elif style == 'warning':
print('%s %s' % (colorize(_("Warning!"), 'yellow'), message))
print('%s %s' % (colorize(m18n.g('warning'), 'yellow'), message))
else:
print(message)