[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 re
import socket import socket
import urlparse import urlparse
import errno
from moulinette.helpers import win_msg, random_password, is_true, validate from moulinette.helpers import win_msg, random_password, is_true, validate
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
@ -55,7 +56,7 @@ def app_listlists():
if '.json' in filename: if '.json' in filename:
list_list.append(filename[:len(filename)-5]) list_list.append(filename[:len(filename)-5])
except OSError: except OSError:
raise MoulinetteError(1, _("No list found")) raise MoulinetteError(1, m18n.n('no_list_found'))
return { 'Lists' : list_list } return { 'Lists' : list_list }
@ -77,12 +78,14 @@ def app_fetchlist(url=None, name=None):
url = 'http://app.yunohost.org/list.json' url = 'http://app.yunohost.org/list.json'
name = 'yunohost' name = 'yunohost'
else: 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) list_file = '%s/%s.json' % (repo_path, name)
if os.system('wget "%s" -O "%s.tmp"' % (url, list_file)) != 0: if os.system('wget "%s" -O "%s.tmp"' % (url, list_file)) != 0:
os.remove('%s.tmp' % list_file) 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 # Rename fetched temp list
os.rename('%s.tmp' % list_file, list_file) 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("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)) 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): def app_removelist(name):
@ -105,9 +108,9 @@ def app_removelist(name):
os.remove('%s/%s.json' % (repo_path, name)) os.remove('%s/%s.json' % (repo_path, name))
os.remove("/etc/cron.d/yunohost-applist-%s" % name) os.remove("/etc/cron.d/yunohost-applist-%s" % name)
except OSError: 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): 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: try:
app_list() app_list()
except MoulinetteError: except MoulinetteError:
raise MoulinetteError(1, _("No app to upgrade")) raise MoulinetteError(errno.ENODATA, m18n.n('app_no_upgrade'))
upgraded_apps = [] upgraded_apps = []
@ -281,7 +284,8 @@ def app_upgrade(auth, app, url=None, file=None):
for app_id in app: for app_id in app:
installed = _is_installed(app_id) installed = _is_installed(app_id)
if not installed: 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: if app_id in upgraded_apps:
continue continue
@ -299,7 +303,8 @@ def app_upgrade(auth, app, url=None, file=None):
elif url: elif url:
manifest = _fetch_app_from_git(url) manifest = _fetch_app_from_git(url)
elif 'lastUpdate' not in new_app_dict or 'git' not in new_app_dict: 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']) \ elif (new_app_dict['lastUpdate'] > current_app_dict['lastUpdate']) \
or ('update_time' not in current_app_dict['settings'] \ or ('update_time' not in current_app_dict['settings'] \
and (new_app_dict['lastUpdate'] > current_app_dict['settings']['install_time'])) \ 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 # Check min version
if 'min_version' in manifest and __version__ < manifest['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 app_setting_path = apps_setting_path +'/'+ app_id
@ -355,12 +361,12 @@ def app_upgrade(auth, app, url=None, file=None):
# So much win # So much win
upgraded_apps.append(app_id) 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: 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): def app_install(auth, app, label=None, args=None):
@ -386,19 +392,21 @@ def app_install(auth, app, label=None, args=None):
# Check ID # Check ID
if 'id' not in manifest or '__' in manifest['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'] app_id = manifest['id']
# Check min version # Check min version
if 'min_version' in manifest and __version__ < manifest['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 # Check if app can be forked
instance_number = _installed_instance_number(app_id, last=True) + 1 instance_number = _installed_instance_number(app_id, last=True) + 1
if instance_number > 1 : if instance_number > 1 :
if 'multi_instance' not in manifest or not is_true(manifest['multi_instance']): 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) 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 root: %s' % app_setting_path)
os.system('chown -R admin: %s/scripts' % app_setting_path) os.system('chown -R admin: %s/scripts' % app_setting_path)
app_ssowatconf(auth) app_ssowatconf(auth)
msignals.display(_("Installation complete"), 'success') msignals.display(m18n.n('installation_complete'), 'success')
else: else:
#TODO: display script fail messages #TODO: display script fail messages
hook_remove(app_id) hook_remove(app_id)
shutil.rmtree(app_setting_path) shutil.rmtree(app_setting_path)
shutil.rmtree(app_tmp_folder) shutil.rmtree(app_tmp_folder)
raise MoulinetteError(1, _("Installation failed")) raise MoulinetteError(errno.EIO, m18n.n('installation_failed'))
except KeyboardInterrupt, EOFError: except KeyboardInterrupt, EOFError:
hook_remove(app_id) hook_remove(app_id)
shutil.rmtree(app_setting_path) shutil.rmtree(app_setting_path)
shutil.rmtree(app_tmp_folder) shutil.rmtree(app_tmp_folder)
raise MoulinetteError(125, _("Interrupted")) raise MoulinetteError(errno.EINTR, m18n.g('operation_interrupted'))
def app_remove(app): def app_remove(app):
@ -492,7 +500,7 @@ def app_remove(app):
from yunohost.hook import hook_exec, hook_remove from yunohost.hook import hook_exec, hook_remove
if not _is_installed(app): 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 app_setting_path = apps_setting_path + app
@ -512,7 +520,7 @@ def app_remove(app):
shutil.rmtree('/tmp/yunohost_remove') shutil.rmtree('/tmp/yunohost_remove')
hook_remove(app) hook_remove(app)
app_ssowatconf() app_ssowatconf()
msignals.display(_("App removed: %s") % app, 'success') msignals.display(m18n.n('app_removed') % app, 'success')
def app_addaccess(auth, apps, users): def app_addaccess(auth, apps, users):
@ -536,7 +544,8 @@ def app_addaccess(auth, apps, users):
for app in apps: for app in apps:
if not _is_installed(app): 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: with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f) app_settings = yaml.load(f)
@ -589,7 +598,8 @@ def app_removeaccess(auth, apps, users):
new_users = '' new_users = ''
if not _is_installed(app): 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: with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f) app_settings = yaml.load(f)
@ -631,7 +641,8 @@ def app_clearaccess(auth, apps):
for app in apps: for app in apps:
if not _is_installed(app): 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: with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f) app_settings = yaml.load(f)
@ -654,7 +665,7 @@ def app_makedefault(app, domain=None):
""" """
if not _is_installed(app): 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: with open(apps_setting_path + app +'/settings.yml') as f:
app_settings = yaml.load(f) app_settings = yaml.load(f)
@ -665,10 +676,11 @@ def app_makedefault(app, domain=None):
if domain is None: if domain is None:
domain = app_domain domain = app_domain
elif domain not in domain_list()['Domains']: 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]: 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: try:
with open('/etc/ssowat/conf.json.persistent') as json_conf: 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') 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.connect(("localhost", int(port)))
s.close() s.close()
except socket.error: except socket.error:
msignals.display(_("Port available: %s") % str(port), 'success') msignals.display(m18n.n('port_available') % int(port), 'success')
else: 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): 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) 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']: 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 domain in apps_map:
if path in apps_map[domain]: 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(): for app_path, v in apps_map[domain].items():
if app_path in path and app_path.count('/') < path.count('/'): 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: if app is not None:
app_setting(app, 'domain', value=domain) 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_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) 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: 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 sql is not None:
if os.system('mysql -u %s -p%s %s < %s' % (user, password, db, sql)) != 0: 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: if not return_pwd:
msignals.display(_("Database initiliazed"), 'success') msignals.display(m18n.n('mysql_db_initialized'), 'success')
def app_ssowatconf(auth): def app_ssowatconf(auth):
@ -951,7 +964,7 @@ def app_ssowatconf(auth):
with open('/etc/ssowat/conf.json', 'w+') as f: with open('/etc/ssowat/conf.json', 'w+') as f:
json.dump(conf_dict, f, sort_keys=True, indent=4) 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): def _extract_app_from_file(path, remove=False):
@ -968,7 +981,7 @@ def _extract_app_from_file(path, remove=False):
""" """
global app_tmp_folder global app_tmp_folder
print(_('Extracting...')) msignals.display(m18n.n('extracting'))
if os.path.exists(app_tmp_folder): shutil.rmtree(app_tmp_folder) if os.path.exists(app_tmp_folder): shutil.rmtree(app_tmp_folder)
os.makedirs(app_tmp_folder) os.makedirs(app_tmp_folder)
@ -988,7 +1001,7 @@ def _extract_app_from_file(path, remove=False):
extract_result = 1 extract_result = 1
if extract_result != 0: if extract_result != 0:
raise MoulinetteError(22, _("Invalid install file")) raise MoulinetteError(errno.EINVAL, m18n.n('app_extraction_failed'))
try: try:
if len(os.listdir(app_tmp_folder)) == 1: 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 = json.loads(str(json_manifest.read()))
manifest['lastUpdate'] = int(time.time()) manifest['lastUpdate'] = int(time.time())
except IOError: 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 return manifest
@ -1018,7 +1031,7 @@ def _fetch_app_from_git(app):
""" """
global app_tmp_folder global app_tmp_folder
print(_('Downloading...')) msignals.display(m18n.n('downloading'))
if ('@' in app) or ('http://' in app) or ('https://' in app): if ('@' in app) or ('http://' in app) or ('https://' in app):
if "github.com" 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 = json.loads(str(json_manifest.read()))
manifest['lastUpdate'] = int(time.time()) manifest['lastUpdate'] = int(time.time())
except IOError: except IOError:
raise MoulinetteError(1, _("Invalid App manifest")) raise MoulinetteError(errno.EIO, m18n.n('app_manifest_invalid'))
else: else:
app_dict = app_list(raw=True) app_dict = app_list(raw=True)
@ -1046,7 +1059,7 @@ def _fetch_app_from_git(app):
app_info['manifest']['lastUpdate'] = app_info['lastUpdate'] app_info['manifest']['lastUpdate'] = app_info['lastUpdate']
manifest = app_info['manifest'] manifest = app_info['manifest']
else: else:
raise MoulinetteError(22, _("App doesn't exists")) raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown'))
if "github.com" in app_info['git']['url']: if "github.com" in app_info['git']['url']:
url = app_info['git']['url'].replace("git@github.com:", "https://github.com/") 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']))) 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: 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 return manifest
@ -1134,4 +1147,3 @@ def _is_installed(app):
continue continue
return False return False

View file

@ -30,6 +30,7 @@ import re
import shutil import shutil
import json import json
import yaml import yaml
import errno
from urllib import urlopen from urllib import urlopen
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
@ -89,7 +90,7 @@ def domain_add(auth, domains, main=False, dyndns=False):
# DynDNS domain # DynDNS domain
if dyndns: if dyndns:
if len(domain.split('.')) < 3: 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 import requests
from yunohost.dyndns import dyndns_subscribe from yunohost.dyndns import dyndns_subscribe
@ -98,10 +99,12 @@ def domain_add(auth, domains, main=False, dyndns=False):
dyndomain = '.'.join(domain.split('.')[1:]) dyndomain = '.'.join(domain.split('.')[1:])
if dyndomain in dyndomains: if dyndomain in dyndomains:
if os.path.exists('/etc/cron.d/yunohost-dyndns'): 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) dyndns_subscribe(domain=domain)
else: else:
raise MoulinetteError(22, _("Unknown DynDNS domain '%s'" % dyndomain)) raise MoulinetteError(errno.EINVAL,
m18n.n('domain_dyndns_root_unknown'))
# Commands # Commands
ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA' 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: for command in command_list:
if os.system(command) != 0: 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: try:
auth.validate_uniqueness({ 'virtualdomain': domain }) auth.validate_uniqueness({ 'virtualdomain': domain })
except MoulinetteError: except MoulinetteError:
raise MoulinetteError(17, _("Domain already created")) raise MoulinetteError(errno.EEXIST, m18n.n('domain_exists'))
attr_dict['virtualdomain'] = domain 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) os.system('chown bind /var/lib/bind/%s.zone' % domain)
else: else:
raise MoulinetteError(17, _("Zone file already exists for %s") % domain) raise MoulinetteError(errno.EEXIST,
m18n.n('domain_zone_exists'))
conf_lines = [ conf_lines = [
'zone "%s" {' % domain, 'zone "%s" {' % domain,
@ -228,12 +233,12 @@ def domain_add(auth, domains, main=False, dyndns=False):
result.append(domain) result.append(domain)
continue continue
else: 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') 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 } return { 'domains': result }
@ -253,7 +258,7 @@ def domain_remove(auth, domains):
for domain in domains: for domain in domains:
if domain not in domains_list: 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 # Check if apps are installed on the domain
for app in os.listdir('/etc/yunohost/apps/'): for app in os.listdir('/etc/yunohost/apps/'):
@ -264,7 +269,8 @@ def domain_remove(auth, domains):
continue continue
else: else:
if app_domain == domain: 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'): if auth.remove('virtualdomain=' + domain + ',ou=domains'):
try: try:
@ -291,13 +297,12 @@ def domain_remove(auth, domains):
result.append(domain) result.append(domain)
continue continue
else: 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('yunohost app ssowatconf > /dev/null 2>&1')
os.system('service nginx reload') os.system('service nginx reload')
os.system('service bind9 reload') os.system('service bind9 reload')
os.system('service metronome restart') os.system('service metronome restart')
msignals.display(_("Domain(s) successfully deleted."), 'success') msignals.display(m18n.n('domain_deleted'), 'success')
return { 'domains': result } return { 'domains': result }

View file

@ -29,6 +29,7 @@ import requests
import json import json
import glob import glob
import base64 import base64
import errno
from moulinette.core import MoulinetteError 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 # Verify if domain is available
if requests.get('http://%s/test/%s' % (subscribe_host, domain)).status_code != 200: 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 key is None:
if len(glob.glob('/etc/yunohost/dyndns/*.key')) == 0: 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: if r.status_code != 201:
try: error = json.loads(r.text)['error'] try: error = json.loads(r.text)['error']
except: error = "Server 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() dyndns_installcron()
@ -134,12 +136,13 @@ def dyndns_update(dyn_host="dynhost.yunohost.org", domain=None, key=None, ip=Non
else: else:
private_key_file = key private_key_file = key
if os.system('/usr/bin/nsupdate -k %s /etc/yunohost/dyndns/zone' % private_key_file) == 0: 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: with open('/etc/yunohost/dyndns/old_ip', 'w') as f:
f.write(new_ip) f.write(new_ip)
else: else:
os.system('rm /etc/yunohost/dyndns/old_ip > /dev/null 2>&1') 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(): def dyndns_installcron():
@ -151,7 +154,7 @@ def dyndns_installcron():
with open('/etc/cron.d/yunohost-dyndns', 'w+') as f: with open('/etc/cron.d/yunohost-dyndns', 'w+') as f:
f.write('*/2 * * * * root yunohost dyndns update >> /dev/null') 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(): def dyndns_removecron():
@ -163,6 +166,6 @@ def dyndns_removecron():
try: try:
os.remove("/etc/cron.d/yunohost-dyndns") os.remove("/etc/cron.d/yunohost-dyndns")
except: 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 os
import sys import sys
import yaml import yaml
import errno
try: try:
import miniupnpc import miniupnpc
except ImportError: 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]: if port not in firewall[ipv][protocol]:
firewall[ipv][protocol].append(port) firewall[ipv][protocol].append(port)
else: 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: with open('/etc/yunohost/firewall.yml', 'w') as f:
yaml.safe_dump(firewall, f, default_flow_style=False) 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]: if port in firewall[ipv][protocol]:
firewall[ipv][protocol].remove(port) firewall[ipv][protocol].remove(port)
else: 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: with open('/etc/yunohost/firewall.yml', 'w') as f:
yaml.safe_dump(firewall, f, default_flow_style=False) yaml.safe_dump(firewall, f, default_flow_style=False)
@ -140,7 +141,7 @@ def firewall_reload():
# IPv4 # IPv4
if os.system("iptables -P INPUT ACCEPT") != 0: 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: if upnp:
try: try:
upnpc = miniupnpc.UPnP() upnpc = miniupnpc.UPnP()
@ -154,9 +155,9 @@ def firewall_reload():
except: pass except: pass
upnpc.addportmapping(port, protocol, upnpc.lanaddr, port, 'yunohost firewall : port %d' % port, '') upnpc.addportmapping(port, protocol, upnpc.lanaddr, port, 'yunohost firewall : port %d' % port, '')
else: else:
raise MoulinetteError(1, _("No uPnP device found")) raise MoulinetteError(errno.ENXIO, m18n.n('upnp_dev_not_found'))
except: 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 -F")
os.system("iptables -X") os.system("iptables -X")
@ -196,7 +197,7 @@ def firewall_reload():
os.system("ip6tables -P INPUT DROP") os.system("ip6tables -P INPUT DROP")
os.system("service fail2ban restart") os.system("service fail2ban restart")
msignals.display(_("Firewall successfully reloaded"), 'success') msignals.display(m18n.n('firewall_reloaded'), 'success')
return firewall_list() return firewall_list()
@ -209,7 +210,6 @@ def firewall_upnp(action=None):
action -- enable/disable action -- enable/disable
""" """
firewall = firewall_list(raw=True) firewall = firewall_list(raw=True)
if action: if action:
@ -221,7 +221,7 @@ def firewall_upnp(action=None):
with open('/etc/cron.d/yunohost-firewall', 'w+') as f: 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') 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': if action == 'disable':
firewall['uPnP']['enabled'] = False firewall['uPnP']['enabled'] = False
@ -242,7 +242,7 @@ def firewall_upnp(action=None):
try: os.remove('/etc/cron.d/yunohost-firewall') try: os.remove('/etc/cron.d/yunohost-firewall')
except: pass except: pass
msignals.display(_("uPnP successfully disabled"), 'success') msignals.display(m18n.n('upnp_disabled'), 'success')
if action: if action:
os.system("cp /etc/yunohost/firewall.yml /etc/yunohost/firewall.yml.old") 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: 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 -F")
os.system("iptables -X") os.system("iptables -X")

View file

@ -27,6 +27,7 @@ import os
import sys import sys
import re import re
import json import json
import errno
from moulinette.helpers import colorize from moulinette.helpers import colorize
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
@ -111,7 +112,7 @@ def hook_check(file):
with open(file[:file.index('scripts/')] + 'manifest.json') as f: with open(file[:file.index('scripts/')] + 'manifest.json') as f:
manifest = json.loads(str(f.read())) manifest = json.loads(str(f.read()))
except: except:
raise MoulinetteError(22, _("Invalid app package")) raise MoulinetteError(errno.EIO, m18n.n('app_manifest_invalid'))
action = file[file.index('scripts/') + 8:] action = file[file.index('scripts/') + 8:]
if 'arguments' in manifest and action in manifest['arguments']: if 'arguments' in manifest and action in manifest['arguments']:
@ -140,7 +141,9 @@ def hook_exec(file, args=None):
for arg in required_args: for arg in required_args:
if arg['name'] in args: if arg['name'] in args:
if 'choices' in arg and args[arg['name']] not in arg['choices']: 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']]) arg_list.append(args[arg['name']])
else: else:
if os.isatty(1) and 'ask' in arg: if os.isatty(1) and 'ask' in arg:
@ -159,7 +162,9 @@ def hook_exec(file, args=None):
elif 'default' in arg: elif 'default' in arg:
arg_list.append(arg['default']) arg_list.append(arg['default'])
else: else:
raise MoulinetteError(22, _("Missing argument : %s") % arg['name']) raise MoulinetteError(errno.EINVAL,
m18n.n('hook_argument_missing')
% arg['name'])
file_path = "./" file_path = "./"
if "/" in file and file[0:2] != 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 subprocess
import xmlrpclib import xmlrpclib
import os.path import os.path
import errno
import cPickle as pickle import cPickle as pickle
from urllib import urlopen from urllib import urlopen
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -74,7 +75,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False):
result_dname = dn result_dname = dn
if len(devices) == 0: if len(devices) == 0:
if mountpoint is not None: if mountpoint is not None:
raise MoulinetteError(1, _("Unknown mountpoint '%s'") % mountpoint) raise MoulinetteError(errno.ENODEV, m18n.n('mountpoint_unknown'))
return result return result
# Retrieve monitoring for unit(s) # Retrieve monitoring for unit(s)
@ -131,7 +132,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False):
for dname in devices_names: for dname in devices_names:
_set(dname, 'not-available') _set(dname, 'not-available')
else: else:
raise MoulinetteError(1, _("Unknown unit '%s'") % u) raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown') % u)
if result_dname is not None: if result_dname is not None:
return result[result_dname] return result[result_dname]
@ -203,7 +204,7 @@ def monitor_network(units=None, human_readable=False):
'gateway': gateway 'gateway': gateway
} }
else: else:
raise MoulinetteError(1, _("Unknown unit '%s'") % u) raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown') % u)
if len(units) == 1: if len(units) == 1:
return result[units[0]] return result[units[0]]
@ -253,7 +254,7 @@ def monitor_system(units=None, human_readable=False):
elif u == 'infos': elif u == 'infos':
result[u] = json.loads(glances.getSystem()) result[u] = json.loads(glances.getSystem())
else: 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: if len(units) == 1 and type(result[units[0]]) is not str:
return result[units[0]] return result[units[0]]
@ -269,7 +270,7 @@ def monitor_update_stats(period):
""" """
if period not in ['day', 'week', 'month']: 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) stats = _retrieve_stats(period)
if not stats: if not stats:
@ -287,7 +288,7 @@ def monitor_update_stats(period):
else: else:
monitor = _monitor_all(p, 0) monitor = _monitor_all(p, 0)
if not monitor: 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()) stats['timestamp'].append(time.time())
@ -352,13 +353,15 @@ def monitor_show_stats(period, date=None):
""" """
if period not in ['day', 'week', 'month']: 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) result = _retrieve_stats(period, date)
if result is False: 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: 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 return result
@ -389,7 +392,7 @@ def monitor_enable(no_stats=False):
os.system("touch %s" % crontab_path) os.system("touch %s" % crontab_path)
os.system("echo '%s' >%s" % (rules, 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(): def monitor_disable():
@ -407,7 +410,7 @@ def monitor_disable():
try: try:
service_disable('glances') service_disable('glances')
except MoulinetteError as e: except MoulinetteError as e:
msignals.display('%s.' % e.strerror, 'warning') msignals.display(e.strerror, 'warning')
# Remove crontab # Remove crontab
try: try:
@ -415,7 +418,7 @@ def monitor_disable():
except: except:
pass pass
msignals.display(_("Server monitoring successfully disabled."), 'success') msignals.display(m18n.n('monitor_disabled'), 'success')
def _get_glances_api(): def _get_glances_api():
@ -434,8 +437,8 @@ def _get_glances_api():
from yunohost.service import service_status from yunohost.service import service_status
if service_status('glances')['status'] != 'running': if service_status('glances')['status'] != 'running':
raise MoulinetteError(1, _("Monitoring is disabled")) raise MoulinetteError(errno.EPERM, m18n.n('monitor_not_enabled'))
raise MoulinetteError(1, _("Connection to Glances server failed")) raise MoulinetteError(errno.EIO, m18n.n('monitor_glances_con_failed'))
def _extract_inet(string, skip_netmask=False, skip_loopback=True): def _extract_inet(string, skip_netmask=False, skip_loopback=True):

View file

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

View file

@ -30,6 +30,7 @@ import re
import getpass import getpass
import requests import requests
import json import json
import errno
import apt import apt
import apt.progress import apt.progress
@ -69,7 +70,7 @@ def tools_ldapinit(auth):
auth.update('cn=admin', admin_dict) 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): def tools_adminpw(old_password, new_password):
@ -83,7 +84,7 @@ def tools_adminpw(old_password, new_password):
""" """
# Validate password length # Validate password length
if len(new_password) < 4: 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('"', '\\"')
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)) 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: if result == 0:
msignals.display(_("Admin password has been changed"), 'success') msignals.display(m18n.n('admin_password_changed'), 'success')
else: 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): 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: for command in command_list:
if os.system(command) != 0: 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) if dyndns: dyndns_subscribe(domain=new_domain)
elif len(new_domain.split('.')) >= 3: 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: if dyndomain in dyndomains:
dyndns_subscribe(domain=new_domain) 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): def tools_postinstall(domain, password, dyndns=False):
@ -194,9 +197,9 @@ def tools_postinstall(domain, password, dyndns=False):
try: try:
with open('/etc/yunohost/installed') as f: pass with open('/etc/yunohost/installed') as f: pass
except IOError: except IOError:
print('Installing YunoHost') msignals.display(m18n.n('yunohost_installing'))
else: else:
raise MoulinetteError(17, _("YunoHost is already installed")) raise MoulinetteError(errno.EPERM, m18n.n('yunohost_already_installed'))
if len(domain.split('.')) >= 3: if len(domain.split('.')) >= 3:
r = requests.get('http://dyndns.yunohost.org/domains') 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: if requests.get('http://dyndns.yunohost.org/test/%s' % domain).status_code == 200:
dyndns=True dyndns=True
else: else:
raise MoulinetteError(17, _("Domain is already taken")) raise MoulinetteError(errno.EEXIST,
m18n.n('dyndns_unavailable'))
# Create required folders # Create required folders
folders_to_create = [ folders_to_create = [
@ -257,7 +261,8 @@ def tools_postinstall(domain, password, dyndns=False):
for command in command_list: for command in command_list:
if os.system(command) != 0: 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 # Initialize YunoHost LDAP base
tools_ldapinit(auth) tools_ldapinit(auth)
@ -277,7 +282,7 @@ def tools_postinstall(domain, password, dyndns=False):
os.system('touch /etc/yunohost/installed') os.system('touch /etc/yunohost/installed')
os.system('service yunohost-api restart &') 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): 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() cache = apt.Cache()
# Update APT cache # Update APT cache
if not cache.update(): 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.open(None)
cache.upgrade(True) cache.upgrade(True)
@ -338,12 +343,11 @@ def tools_update(ignore_apps=False, ignore_packages=False):
}) })
if len(apps) == 0 and len(packages) == 0: 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 } return { 'packages': packages, 'apps': apps }
def tools_upgrade(ignore_apps=False, ignore_packages=False): def tools_upgrade(ignore_apps=False, ignore_packages=False):
""" """
Update apps & package cache, then display changelog Update apps & package cache, then display changelog
@ -380,7 +384,7 @@ def tools_upgrade(ignore_apps=False, ignore_packages=False):
app_upgrade() app_upgrade()
except: pass except: pass
msignals.display(_("System successfully upgraded"), 'success') msignals.display(m18n.n('system_upgraded'), 'success')
# Return API logs if it is an API call # Return API logs if it is an API call
if not os.isatty(1): if not os.isatty(1):

View file

@ -29,6 +29,7 @@ import crypt
import random import random
import string import string
import json import json
import errno
from moulinette.core import MoulinetteError 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: if attr in keys:
attrs.append(attr) attrs.append(attr)
else: else:
raise MoulinetteError(22, _("Invalid field '%s'") % attr) raise MoulinetteError(errno.EINVAL,
m18n.n('field_invalid') % attr)
else: else:
attrs = [ 'uid', 'cn', 'mail' ] attrs = [ 'uid', 'cn', 'mail' ]
@ -99,7 +101,7 @@ def user_create(auth, username, firstname, lastname, mail, password):
# Validate password length # Validate password length
if len(password) < 4: if len(password) < 4:
raise MoulinetteError(22, _("Password is too short")) raise MoulinetteError(errno.EINVAL, m18n.n('password_too_short'))
auth.validate_uniqueness({ auth.validate_uniqueness({
'uid' : username, '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']: 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 # Get random UID/GID
uid_check = gid_check = 0 uid_check = gid_check = 0
while uid_check == 0 and gid_check == 0: while uid_check == 0 and gid_check == 0:
uid = str(random.randint(200, 99999)) 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("su - %s -c ''" % username)
os.system('yunohost app ssowatconf > /dev/null 2>&1') os.system('yunohost app ssowatconf > /dev/null 2>&1')
#TODO: Send a welcome mail to user #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]) hook_callback('post_user_create', [username, mail, password, firstname, lastname])
return { 'fullname' : fullname, 'username' : username, 'mail' : mail } 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): def user_delete(auth, users, purge=False):
@ -207,10 +210,10 @@ def user_delete(auth, users, purge=False):
deleted.append(user) deleted.append(user)
continue continue
else: 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') 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 } return { 'users': deleted }
@ -239,7 +242,7 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change
# Populate user informations # Populate user informations
result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch) result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch)
if not result: if not result:
raise MoulinetteError(167, _("Unknown username '%s'") % username) raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown'))
user = result[0] user = result[0]
# Get modifications from arguments # Get modifications from arguments
@ -263,7 +266,9 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None, change
if mail: if mail:
auth.validate_uniqueness({ 'mail': mail }) auth.validate_uniqueness({ 'mail': mail })
if mail[mail.find('@')+1:] not in domains: 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] del user['mail'][0]
new_attr_dict['mail'] = [mail] + user['mail'] 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: for mail in add_mailalias:
auth.validate_uniqueness({ 'mail': mail }) auth.validate_uniqueness({ 'mail': mail })
if mail[mail.find('@')+1:] not in domains: 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) user['mail'].append(mail)
new_attr_dict['mail'] = user['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:]: if len(user['mail']) > 1 and mail in user['mail'][1:]:
user['mail'].remove(mail) user['mail'].remove(mail)
else: 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'] new_attr_dict['mail'] = user['mail']
if add_mailforward: 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:]: if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]:
user['maildrop'].remove(mail) user['maildrop'].remove(mail)
else: 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'] new_attr_dict['maildrop'] = user['maildrop']
if auth.update('uid=%s,ou=users' % username, new_attr_dict): 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) return user_info(auth, username)
else: else:
raise MoulinetteError(169, _("An error occurred during user update")) raise MoulinetteError(169, m18n.n('user_update_failed'))
def user_info(auth, username): def user_info(auth, username):
@ -333,7 +342,7 @@ def user_info(auth, username):
if result: if result:
user = result[0] user = result[0]
else: else:
raise MoulinetteError(22, _("Unknown username/mail '%s'") % username) raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown'))
result_dict = { result_dict = {
'username': user['uid'][0], 'username': user['uid'][0],
@ -352,4 +361,4 @@ def user_info(auth, username):
if result: if result:
return result_dict return result_dict
else: 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 sys
import __builtin__ import __builtin__
from moulinette.core import Package, MoulinetteSignals, install_i18n from moulinette.core import Package, Moulinette18n, MoulinetteSignals
__builtin__.__dict__['pkg'] = Package(**kwargs) __builtin__.__dict__['pkg'] = Package(**kwargs)
__builtin__.__dict__['m18n'] = Moulinette18n(pkg)
__builtin__.__dict__['msignals'] = MoulinetteSignals() __builtin__.__dict__['msignals'] = MoulinetteSignals()
# Initialize internationalization
install_i18n()
# Add library directory to python path # Add library directory to python path
sys.path.insert(0, pkg.libdir) sys.path.insert(0, pkg.libdir)
@ -104,6 +102,6 @@ def cli(namespaces, args, use_cache=True):
'use_cache': use_cache}) 'use_cache': use_cache})
moulinette.run(args) moulinette.run(args)
except MoulinetteError as e: 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 e.errno
return 0 return 0

View file

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

View file

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

View file

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

View file

@ -3,35 +3,14 @@
import os import os
import sys import sys
import time import time
import json
import errno import errno
import gettext
import logging import logging
from importlib import import_module from importlib import import_module
# Package manipulation ------------------------------------------------- # 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): class Package(object):
"""Package representation and easy access methods """Package representation and easy access methods
@ -51,7 +30,7 @@ class Package(object):
# Set local directories # Set local directories
self._datadir = '%s/data' % basedir self._datadir = '%s/data' % basedir
self._libdir = '%s/lib' % basedir self._libdir = '%s/lib' % basedir
self._localedir = '%s/po' % basedir self._localedir = '%s/locales' % basedir
self._cachedir = '%s/cache' % basedir self._cachedir = '%s/cache' % basedir
else: else:
import package import package
@ -130,6 +109,189 @@ class Package(object):
True if mode[0] == 'w' else False) True if mode[0] == 'w' else False)
return open('%s/%s' % (self.get_cachedir(**kwargs), filename), mode) 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): class MoulinetteSignals(object):
"""Signals connector for the moulinette """Signals connector for the moulinette
@ -256,16 +418,16 @@ def init_interface(name, kwargs={}, actionsmap={}):
try: try:
mod = import_module('moulinette.interfaces.%s' % name) mod = import_module('moulinette.interfaces.%s' % name)
except ImportError: except ImportError as e:
# TODO: List available interfaces # TODO: List available interfaces
raise MoulinetteError(errno.EINVAL, _("Unknown interface '%s'" % name)) raise ImportError("Unable to load interface '%s': %s" % (name, str(e)))
else: else:
try: try:
# Retrieve interface classes # Retrieve interface classes
parser = mod.ActionsMapParser parser = mod.ActionsMapParser
interface = mod.Interface interface = mod.Interface
except AttributeError as e: 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 # Instantiate or retrieve ActionsMap
if isinstance(actionsmap, dict): if isinstance(actionsmap, dict):
@ -273,7 +435,7 @@ def init_interface(name, kwargs={}, actionsmap={}):
elif isinstance(actionsmap, ActionsMap): elif isinstance(actionsmap, ActionsMap):
amap = actionsmap amap = actionsmap
else: else:
raise MoulinetteError(errno.EINVAL, _("Invalid actions map '%r'" % actionsmap)) raise ValueError("Invalid actions map '%r'" % actionsmap)
return interface(amap, **kwargs) return interface(amap, **kwargs)
@ -293,7 +455,8 @@ def init_authenticator((vendor, name), kwargs={}):
mod = import_module('moulinette.authenticators.%s' % vendor) mod = import_module('moulinette.authenticators.%s' % vendor)
except ImportError as e: except ImportError as e:
# TODO: List available authenticators vendors # 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: else:
return mod.Authenticator(name, **kwargs) return mod.Authenticator(name, **kwargs)
@ -369,12 +532,13 @@ class MoulinetteLock(object):
try: try:
(open(self._lockfile, 'w')).close() (open(self._lockfile, 'w')).close()
except IOError: 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 break
if (time.time() - start_time) > self.timeout: if (time.time() - start_time) > self.timeout:
raise MoulinetteError(errno.EBUSY, _("An instance is already running for '%s'") \ raise MoulinetteError(errno.EBUSY,
% self.namespace) m18n.g('instance_already_running'))
# Wait before checking again # Wait before checking again
time.sleep(self.interval) time.sleep(self.interval)
self._locked = True self._locked = True

View file

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

View file

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