This commit is contained in:
Laurent Peuch 2016-05-13 11:55:58 +00:00
commit 1c41c5e442
10 changed files with 214 additions and 235 deletions

View file

@ -24,13 +24,13 @@
Manage apps Manage apps
""" """
import os import os
import sys
import json import json
import shutil import shutil
import stat
import yaml import yaml
import time import time
import re import re
import string
import random
import socket import socket
import urlparse import urlparse
import errno import errno
@ -42,6 +42,10 @@ from moulinette.utils.log import getActionLogger
from yunohost.service import service_log from yunohost.service import service_log
from yunohost.utils import packages from yunohost.utils import packages
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
from yunohost.user import user_list, user_info
from yunohost.domain import domain_list
logger = getActionLogger('yunohost.app') logger = getActionLogger('yunohost.app')
@ -333,8 +337,6 @@ def app_upgrade(auth, app=[], url=None, file=None):
url -- Git url to fetch for upgrade url -- Git url to fetch for upgrade
""" """
from yunohost.hook import hook_add, hook_remove, hook_exec
try: try:
app_list() app_list()
except MoulinetteError: except MoulinetteError:
@ -445,8 +447,6 @@ def app_install(auth, app, label=None, args=None):
args -- Serialize arguments for app installation args -- Serialize arguments for app installation
""" """
from yunohost.hook import hook_add, hook_remove, hook_exec
# Fetch or extract sources # Fetch or extract sources
try: os.listdir(install_tmp) try: os.listdir(install_tmp)
except OSError: os.makedirs(install_tmp) except OSError: os.makedirs(install_tmp)
@ -588,8 +588,6 @@ def app_remove(auth, app):
app -- App(s) to delete app -- App(s) to delete
""" """
from yunohost.hook import hook_exec, hook_remove
if not _is_installed(app): if not _is_installed(app):
raise MoulinetteError(errno.EINVAL, raise MoulinetteError(errno.EINVAL,
m18n.n('app_not_installed', app=app)) m18n.n('app_not_installed', app=app))
@ -631,9 +629,6 @@ def app_addaccess(auth, apps, users=[]):
apps apps
""" """
from yunohost.user import user_list, user_info
from yunohost.hook import hook_callback
result = {} result = {}
if not users: if not users:
@ -686,9 +681,6 @@ def app_removeaccess(auth, apps, users=[]):
apps apps
""" """
from yunohost.user import user_list
from yunohost.hook import hook_callback
result = {} result = {}
remove_all = False remove_all = False
@ -736,8 +728,6 @@ def app_clearaccess(auth, apps):
apps apps
""" """
from yunohost.hook import hook_callback
if not isinstance(apps, list): apps = [apps] if not isinstance(apps, list): apps = [apps]
for app in apps: for app in apps:
@ -788,8 +778,6 @@ def app_makedefault(auth, app, domain=None):
domain domain
""" """
from yunohost.domain import domain_list
app_settings = _get_app_settings(app) app_settings = _get_app_settings(app)
app_domain = app_settings['domain'] app_domain = app_settings['domain']
app_path = app_settings['path'] app_path = app_settings['path']
@ -881,8 +869,6 @@ def app_checkurl(auth, url, app=None):
app -- Write domain & path to app settings for further checks app -- Write domain & path to app settings for further checks
""" """
from yunohost.domain import domain_list
if "https://" == url[:8]: if "https://" == url[:8]:
url = url[8:] url = url[8:]
elif "http://" == url[:7]: elif "http://" == url[:7]:
@ -965,9 +951,6 @@ def app_ssowatconf(auth):
""" """
from yunohost.domain import domain_list
from yunohost.user import user_list
with open('/etc/yunohost/current_host', 'r') as f: with open('/etc/yunohost/current_host', 'r') as f:
main_domain = f.readline().rstrip() main_domain = f.readline().rstrip()
@ -1404,11 +1387,11 @@ def _value_for_locale(values):
if not isinstance(values, dict): if not isinstance(values, dict):
return values return values
for lang in [m18n.locale, m18n.default_locale]: if m18n.locale in values:
try: return _encode_string(values[m18n.locale])
return _encode_string(values[lang])
except KeyError: if m18n.default_locale in values:
continue return _encode_string(values[m18n.default_locale])
# Fallback to first value # Fallback to first value
return _encode_string(values.values()[0]) return _encode_string(values.values()[0])
@ -1466,6 +1449,7 @@ def _check_manifest_requirements(manifest):
pkgname=pkgname, version=version, pkgname=pkgname, version=version,
spec=spec)) spec=spec))
def _parse_args_from_manifest(manifest, action, args={}, auth=None): def _parse_args_from_manifest(manifest, action, args={}, auth=None):
"""Parse arguments needed for an action from the manifest """Parse arguments needed for an action from the manifest
@ -1480,9 +1464,6 @@ def _parse_args_from_manifest(manifest, action, args={}, auth=None):
args -- A dictionnary of arguments to parse args -- A dictionnary of arguments to parse
""" """
from yunohost.domain import domain_list
from yunohost.user import user_info
args_list = OrderedDict() args_list = OrderedDict()
try: try:
action_args = manifest['arguments'][action] action_args = manifest['arguments'][action]
@ -1577,6 +1558,7 @@ def _parse_args_from_manifest(manifest, action, args={}, auth=None):
args_list[arg_name] = arg_value args_list[arg_name] = arg_value
return args_list return args_list
def _make_environment_dict(args_dict): def _make_environment_dict(args_dict):
""" """
Convert a dictionnary containing manifest arguments Convert a dictionnary containing manifest arguments
@ -1591,6 +1573,7 @@ def _make_environment_dict(args_dict):
env_dict[ "YNH_APP_ARG_%s" % arg_name.upper() ] = arg_value env_dict[ "YNH_APP_ARG_%s" % arg_name.upper() ] = arg_value
return env_dict return env_dict
def _parse_app_instance_name(app_instance_name): def _parse_app_instance_name(app_instance_name):
""" """
Parse a Yunohost app instance name and extracts the original appid Parse a Yunohost app instance name and extracts the original appid
@ -1618,6 +1601,7 @@ def _parse_app_instance_name(app_instance_name):
app_instance_nb = int(match.groupdict().get('appinstancenb')) if match.groupdict().get('appinstancenb') is not None else 1 app_instance_nb = int(match.groupdict().get('appinstancenb')) if match.groupdict().get('appinstancenb') is not None else 1
return (appid, app_instance_nb) return (appid, app_instance_nb)
def is_true(arg): def is_true(arg):
""" """
Convert a string into a boolean Convert a string into a boolean
@ -1650,7 +1634,5 @@ def random_password(length=8):
length -- The string length to generate length -- The string length to generate
""" """
import string, random
char_set = string.ascii_uppercase + string.digits + string.ascii_lowercase char_set = string.ascii_uppercase + string.digits + string.ascii_lowercase
return ''.join(random.sample(char_set, length)) return ''.join(random.sample(char_set, length))

View file

@ -25,7 +25,6 @@
""" """
import os import os
import re import re
import sys
import json import json
import errno import errno
import time import time

View file

@ -2,7 +2,7 @@
""" License """ License
Copyright (C) 2013 YunoHost Copyright (C) 2013-2016 YunoHost
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@ -24,20 +24,20 @@
Manage domains Manage domains
""" """
import os import os
import sys
import datetime
import re import re
import shutil
import json import json
import yaml import yaml
import errno import errno
import shutil
import requests import requests
from urllib import urlopen
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.service import service_regen_conf from yunohost.service import service_regen_conf
from yunohost.hook import hook_callback
from yunohost.dyndns import dyndns_subscribe
from yunohost.app import app_ssowatconf
logger = getActionLogger('yunohost.domain') logger = getActionLogger('yunohost.domain')
@ -80,13 +80,9 @@ def domain_add(auth, domain, dyndns=False):
dyndns -- Subscribe to DynDNS dyndns -- Subscribe to DynDNS
""" """
from yunohost.hook import hook_callback
attr_dict = { 'objectClass' : ['mailDomain', 'top'] } attr_dict = { 'objectClass' : ['mailDomain', 'top'] }
now = datetime.datetime.now()
timestamp = str(now.year) + str(now.month) + str(now.day)
if domain in domain_list(auth)['domains']: if domain in domain_list(auth)['domains']:
raise MoulinetteError(errno.EEXIST, m18n.n('domain_exists')) raise MoulinetteError(errno.EEXIST, m18n.n('domain_exists'))
@ -94,7 +90,6 @@ def domain_add(auth, domain, dyndns=False):
if dyndns: if dyndns:
if len(domain.split('.')) < 3: if len(domain.split('.')) < 3:
raise MoulinetteError(errno.EINVAL, m18n.n('domain_dyndns_invalid')) raise MoulinetteError(errno.EINVAL, m18n.n('domain_dyndns_invalid'))
from yunohost.dyndns import dyndns_subscribe
try: try:
r = requests.get('https://dyndns.yunohost.org/domains') r = requests.get('https://dyndns.yunohost.org/domains')
@ -160,12 +155,14 @@ def domain_add(auth, domain, dyndns=False):
with open('/etc/yunohost/installed', 'r') as f: with open('/etc/yunohost/installed', 'r') as f:
service_regen_conf(names=[ service_regen_conf(names=[
'nginx', 'metronome', 'dnsmasq', 'rmilter']) 'nginx', 'metronome', 'dnsmasq', 'rmilter'])
os.system('yunohost app ssowatconf > /dev/null 2>&1') app_ssowatconf(auth)
except IOError: pass except IOError: pass
except: except:
# Force domain removal silently # Force domain removal silently
try: domain_remove(auth, domain, True) try:
except: pass domain_remove(auth, domain, True)
except:
pass
raise raise
hook_callback('post_domain_add', args=[domain]) hook_callback('post_domain_add', args=[domain])
@ -182,8 +179,6 @@ def domain_remove(auth, domain, force=False):
force -- Force the domain removal force -- Force the domain removal
""" """
from yunohost.hook import hook_callback
if not force and domain not in domain_list(auth)['domains']: if not force and domain not in domain_list(auth)['domains']:
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown')) raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
@ -200,12 +195,12 @@ def domain_remove(auth, domain, force=False):
m18n.n('domain_uninstall_app_first')) m18n.n('domain_uninstall_app_first'))
if auth.remove('virtualdomain=' + domain + ',ou=domains') or force: if auth.remove('virtualdomain=' + domain + ',ou=domains') or force:
os.system('rm -rf /etc/yunohost/certs/%s' % domain) shutil.rmtree('/etc/yunohost/certs/%s' % domain)
else: else:
raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed')) raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed'))
service_regen_conf(names=['nginx', 'metronome', 'dnsmasq']) service_regen_conf(names=['nginx', 'metronome', 'dnsmasq'])
os.system('yunohost app ssowatconf > /dev/null 2>&1') app_ssowatconf(auth)
hook_callback('post_domain_remove', args=[domain]) hook_callback('post_domain_remove', args=[domain])
@ -296,7 +291,7 @@ def get_public_ip(protocol=4):
else: else:
raise ValueError("invalid protocol version") raise ValueError("invalid protocol version")
try: try:
return urlopen(url).read().strip() return requests.get(url).content.strip()
except IOError: except IOError:
logger.debug('cannot retrieve public IPv%d' % protocol, exc_info=1) logger.debug('cannot retrieve public IPv%d' % protocol, exc_info=1)
raise MoulinetteError(errno.ENETUNREACH, raise MoulinetteError(errno.ENETUNREACH,

View file

@ -40,6 +40,10 @@ from yunohost.domain import get_public_ip
logger = getActionLogger('yunohost.dyndns') logger = getActionLogger('yunohost.dyndns')
RE_DYNDNS_PRIVATE_KEY = re.compile(
r'.*/K(?P<domain>[^\s\+]+)\.\+157.+\.private$'
)
class IPRouteLine(object): class IPRouteLine(object):
""" Utility class to parse an ip route output line """ Utility class to parse an ip route output line
@ -62,10 +66,6 @@ class IPRouteLine(object):
for k, v in self.m.groupdict().items(): for k, v in self.m.groupdict().items():
setattr(self, k, v) setattr(self, k, v)
re_dyndns_private_key = re.compile(
r'.*/K(?P<domain>[^\s\+]+)\.\+157.+\.private$'
)
def dyndns_subscribe(subscribe_host="dyndns.yunohost.org", domain=None, key=None): def dyndns_subscribe(subscribe_host="dyndns.yunohost.org", domain=None, key=None):
""" """
@ -171,7 +171,7 @@ def dyndns_update(dyn_host="dyndns.yunohost.org", domain=None, key=None,
if domain is None: if domain is None:
# Retrieve the first registered domain # Retrieve the first registered domain
for path in glob.iglob('/etc/yunohost/dyndns/K*.private'): for path in glob.iglob('/etc/yunohost/dyndns/K*.private'):
match = re_dyndns_private_key.match(path) match = RE_DYNDNS_PRIVATE_KEY.match(path)
if not match: if not match:
continue continue
_domain = match.group('domain') _domain = match.group('domain')

View file

@ -36,7 +36,9 @@ except ImportError:
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils import process from moulinette.utils import process
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.text import prependlines from moulinette.utils.text import prependlines, searchf
from yunohost.hook import hook_callback
firewall_file = '/etc/yunohost/firewall.yml' firewall_file = '/etc/yunohost/firewall.yml'
upnp_cron_job = '/etc/cron.d/yunohost-firewall-upnp' upnp_cron_job = '/etc/cron.d/yunohost-firewall-upnp'
@ -178,7 +180,7 @@ def firewall_list(raw=False, by_ip_version=False, list_forwarded=False):
ports = sorted(set(ports['ipv4']) | set(ports['ipv6'])) ports = sorted(set(ports['ipv4']) | set(ports['ipv6']))
# Format returned dict # Format returned dict
ret = { "opened_ports": ports } ret = {"opened_ports": ports}
if list_forwarded: if list_forwarded:
# Combine TCP and UDP forwarded ports # Combine TCP and UDP forwarded ports
ret['forwarded_ports'] = sorted( ret['forwarded_ports'] = sorted(
@ -194,8 +196,6 @@ def firewall_reload(skip_upnp=False):
skip_upnp -- Do not refresh port forwarding using UPnP skip_upnp -- Do not refresh port forwarding using UPnP
""" """
from yunohost.hook import hook_callback
reloaded = False reloaded = False
errors = False errors = False
@ -224,7 +224,7 @@ def firewall_reload(skip_upnp=False):
# Iterate over ports and add rule # Iterate over ports and add rule
for protocol in ['TCP', 'UDP']: for protocol in ['TCP', 'UDP']:
for port in firewall['ipv4'][protocol]: for port in firewall['ipv4'][protocol]:
rules.append("iptables -A INPUT -p %s --dport %s -j ACCEPT" \ rules.append("iptables -A INPUT -p %s --dport %s -j ACCEPT"
% (protocol, process.quote(str(port)))) % (protocol, process.quote(str(port))))
rules += [ rules += [
"iptables -A INPUT -i lo -j ACCEPT", "iptables -A INPUT -i lo -j ACCEPT",
@ -253,7 +253,7 @@ def firewall_reload(skip_upnp=False):
# Iterate over ports and add rule # Iterate over ports and add rule
for protocol in ['TCP', 'UDP']: for protocol in ['TCP', 'UDP']:
for port in firewall['ipv6'][protocol]: for port in firewall['ipv6'][protocol]:
rules.append("ip6tables -A INPUT -p %s --dport %s -j ACCEPT" \ rules.append("ip6tables -A INPUT -p %s --dport %s -j ACCEPT"
% (protocol, process.quote(str(port)))) % (protocol, process.quote(str(port))))
rules += [ rules += [
"ip6tables -A INPUT -i lo -j ACCEPT", "ip6tables -A INPUT -i lo -j ACCEPT",
@ -308,13 +308,14 @@ def firewall_upnp(action='status', no_refresh=False):
try: try:
# Remove old cron job # Remove old cron job
os.remove('/etc/cron.d/yunohost-firewall') os.remove('/etc/cron.d/yunohost-firewall')
except: pass except:
pass
action = 'status' action = 'status'
no_refresh = False no_refresh = False
if action == 'status' and no_refresh: if action == 'status' and no_refresh:
# Only return current state # Only return current state
return { 'enabled': enabled } return {'enabled': enabled}
elif action == 'enable' or (enabled and action == 'status'): elif action == 'enable' or (enabled and action == 'status'):
# Add cron job # Add cron job
with open(upnp_cron_job, 'w+') as f: with open(upnp_cron_job, 'w+') as f:
@ -330,7 +331,8 @@ def firewall_upnp(action='status', no_refresh=False):
try: try:
# Remove cron job # Remove cron job
os.remove(upnp_cron_job) os.remove(upnp_cron_job)
except: pass except:
pass
enabled = False enabled = False
if action == 'status': if action == 'status':
no_refresh = True no_refresh = True
@ -364,7 +366,8 @@ def firewall_upnp(action='status', no_refresh=False):
if upnpc.getspecificportmapping(port, protocol): if upnpc.getspecificportmapping(port, protocol):
try: try:
upnpc.deleteportmapping(port, protocol) upnpc.deleteportmapping(port, protocol)
except: pass except:
pass
if not enabled: if not enabled:
continue continue
try: try:
@ -403,7 +406,7 @@ def firewall_upnp(action='status', no_refresh=False):
if action == 'enable' and not enabled: if action == 'enable' and not enabled:
raise MoulinetteError(errno.ENXIO, m18n.n('upnp_port_open_failed')) raise MoulinetteError(errno.ENXIO, m18n.n('upnp_port_open_failed'))
return { 'enabled': enabled } return {'enabled': enabled}
def firewall_stop(): def firewall_stop():
@ -434,7 +437,6 @@ def _get_ssh_port(default=22):
Retrieve the SSH port from the sshd_config file or used the default Retrieve the SSH port from the sshd_config file or used the default
one if it's not defined. one if it's not defined.
""" """
from moulinette.utils.text import searchf
try: try:
m = searchf(r'^Port[ \t]+([0-9]+)$', m = searchf(r'^Port[ \t]+([0-9]+)$',
'/etc/ssh/sshd_config', count=-1) '/etc/ssh/sshd_config', count=-1)
@ -444,12 +446,14 @@ def _get_ssh_port(default=22):
pass pass
return default return default
def _update_firewall_file(rules): def _update_firewall_file(rules):
"""Make a backup and write new rules to firewall file""" """Make a backup and write new rules to firewall file"""
os.system("cp {0} {0}.old".format(firewall_file)) os.system("cp {0} {0}.old".format(firewall_file))
with open(firewall_file, 'w') as f: with open(firewall_file, 'w') as f:
yaml.safe_dump(rules, f, default_flow_style=False) yaml.safe_dump(rules, f, default_flow_style=False)
def _on_rule_command_error(returncode, cmd, output): def _on_rule_command_error(returncode, cmd, output):
"""Callback for rules commands error""" """Callback for rules commands error"""
# Log error and continue commands execution # Log error and continue commands execution

View file

@ -24,15 +24,12 @@
Manage hooks Manage hooks
""" """
import os import os
import sys
import re
import json
import errno import errno
import subprocess
from glob import iglob from glob import iglob
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils import log from moulinette.utils import log
from moulinette.utils.process import call_async_output
hook_folder = '/usr/share/yunohost/hooks/' hook_folder = '/usr/share/yunohost/hooks/'
custom_hook_folder = '/etc/yunohost/hooks.d/' custom_hook_folder = '/etc/yunohost/hooks.d/'
@ -52,14 +49,16 @@ def hook_add(app, file):
path, filename = os.path.split(file) path, filename = os.path.split(file)
priority, action = _extract_filename_parts(filename) priority, action = _extract_filename_parts(filename)
try: os.listdir(custom_hook_folder + action) try:
except OSError: os.makedirs(custom_hook_folder + action) os.listdir(custom_hook_folder + action)
except OSError:
os.makedirs(custom_hook_folder + action)
finalpath = custom_hook_folder + action +'/'+ priority +'-'+ app finalpath = custom_hook_folder + action + '/' + priority + '-' + app
os.system('cp %s %s' % (file, finalpath)) os.system('cp %s %s' % (file, finalpath))
os.system('chown -hR admin: %s' % hook_folder) os.system('chown -hR admin: %s' % hook_folder)
return { 'hook': finalpath } return {'hook': finalpath}
def hook_remove(app): def hook_remove(app):
@ -74,8 +73,9 @@ def hook_remove(app):
for action in os.listdir(custom_hook_folder): for action in os.listdir(custom_hook_folder):
for script in os.listdir(custom_hook_folder + action): for script in os.listdir(custom_hook_folder + action):
if script.endswith(app): if script.endswith(app):
os.remove(custom_hook_folder + action +'/'+ script) os.remove(custom_hook_folder + action + '/' + script)
except OSError: pass except OSError:
pass
def hook_info(action, name): def hook_info(action, name):
@ -136,11 +136,11 @@ def hook_list(action, list_by='name', show_info=False):
def _append_hook(d, priority, name, path): def _append_hook(d, priority, name, path):
# Use the priority as key and a dict of hooks names # Use the priority as key and a dict of hooks names
# with their info as value # with their info as value
value = { 'path': path } value = {'path': path}
try: try:
d[priority][name] = value d[priority][name] = value
except KeyError: except KeyError:
d[priority] = { name: value } d[priority] = {name: value}
else: else:
def _append_hook(d, priority, name, path): def _append_hook(d, priority, name, path):
# Use the priority as key and the name as value # Use the priority as key and the name as value
@ -162,11 +162,12 @@ def hook_list(action, list_by='name', show_info=False):
if h['path'] != path: if h['path'] != path:
h['path'] = path h['path'] = path
return return
l.append({ 'priority': priority, 'path': path }) l.append({'priority': priority, 'path': path})
d[name] = l d[name] = l
else: else:
if list_by == 'name': if list_by == 'name':
result = set() result = set()
def _append_hook(d, priority, name, path): def _append_hook(d, priority, name, path):
# Add only the name # Add only the name
d.add(name) d.add(name)
@ -204,7 +205,7 @@ def hook_list(action, list_by='name', show_info=False):
logger.debug("custom hook folder not found for action '%s' in %s", logger.debug("custom hook folder not found for action '%s' in %s",
action, custom_hook_folder) action, custom_hook_folder)
return { 'hooks': result } return {'hooks': result}
def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None,
@ -226,7 +227,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None,
(name, priority, path, succeed) as arguments (name, priority, path, succeed) as arguments
""" """
result = { 'succeed': {}, 'failed': {} } result = {'succeed': {}, 'failed': {}}
hooks_dict = {} hooks_dict = {}
# Retrieve hooks # Retrieve hooks
@ -258,7 +259,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None,
for h in hl: for h in hl:
# Update hooks dict # Update hooks dict
d = hooks_dict.get(h['priority'], dict()) d = hooks_dict.get(h['priority'], dict())
d.update({ n: { 'path': h['path'] }}) d.update({n: {'path': h['path']}})
hooks_dict[h['priority']] = d hooks_dict[h['priority']] = d
if not hooks_dict: if not hooks_dict:
return result return result
@ -308,9 +309,6 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
env -- Dictionnary of environment variables to export env -- Dictionnary of environment variables to export
""" """
from moulinette.utils.process import call_async_output
from yunohost.app import _value_for_locale
# Validate hook path # Validate hook path
if path[0] != '/': if path[0] != '/':
path = os.path.realpath(path) path = os.path.realpath(path)

View file

@ -35,12 +35,15 @@ import errno
import os import os
import dns.resolver import dns.resolver
import cPickle as pickle import cPickle as pickle
from datetime import datetime, timedelta from datetime import datetime
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.domain import get_public_ip from yunohost.domain import get_public_ip
from yunohost.service import (service_status, service_enable,
service_start, service_disable, service_stop)
logger = getActionLogger('yunohost.monitor') logger = getActionLogger('yunohost.monitor')
@ -406,9 +409,6 @@ def monitor_enable(no_stats=False):
no_stats -- Disable monitoring statistics no_stats -- Disable monitoring statistics
""" """
from yunohost.service import (service_status, service_enable,
service_start)
glances = service_status('glances') glances = service_status('glances')
if glances['status'] != 'running': if glances['status'] != 'running':
service_start('glances') service_start('glances')
@ -433,9 +433,6 @@ def monitor_disable():
Disable server monitoring Disable server monitoring
""" """
from yunohost.service import (service_status, service_disable,
service_stop)
glances = service_status('glances') glances = service_status('glances')
if glances['status'] != 'inactive': if glances['status'] != 'inactive':
service_stop('glances') service_stop('glances')
@ -467,8 +464,6 @@ def _get_glances_api():
else: else:
return p return p
from yunohost.service import service_status
if service_status('glances')['status'] != 'running': if service_status('glances')['status'] != 'running':
raise MoulinetteError(errno.EPERM, m18n.n('monitor_not_enabled')) raise MoulinetteError(errno.EPERM, m18n.n('monitor_not_enabled'))
raise MoulinetteError(errno.EIO, m18n.n('monitor_glances_con_failed')) raise MoulinetteError(errno.EIO, m18n.n('monitor_glances_con_failed'))
@ -722,22 +717,26 @@ def _append_to_stats(stats, monitor, statics=[]):
statics = [statics] statics = [statics]
# Appending function # Appending function
def _append(s, m, st): def _append(_stats, _monitor, _statics):
for k, v in m.items(): for key, value in _monitor.items():
if k in st: if key in _statics:
s[k] = v _stats[key] = value
elif isinstance(v, dict):
if k not in s: elif isinstance(value, dict):
s[k] = {} if key not in _stats:
s[k] = _append(s[k], v, st) _stats[key] = {}
_stats[key] = _append(_stats[key], value, _statics)
else: else:
if k not in s: if key not in _stats:
s[k] = [] _stats[key] = []
if isinstance(v, list):
s[k].extend(v) if isinstance(value, list):
_stats[key].extend(value)
else: else:
s[k].append(v) _stats[key].append(value)
return s
return _stats
stats = _append(stats, monitor, statics) stats = _append(stats, monitor, statics)
return stats return stats

View file

@ -36,7 +36,7 @@ from difflib import unified_diff
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils import log, filesystem from moulinette.utils import log, filesystem
from yunohost.hook import hook_list, hook_callback from yunohost.hook import hook_callback
base_conf_path = '/home/yunohost.conf' base_conf_path = '/home/yunohost.conf'

View file

@ -24,10 +24,7 @@
Specific tools Specific tools
""" """
import os import os
import sys
import yaml import yaml
import re
import getpass
import requests import requests
import json import json
import errno import errno
@ -42,12 +39,12 @@ from moulinette.utils.log import getActionLogger
from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, app_list from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, app_list
from yunohost.domain import domain_add, domain_list, get_public_ip from yunohost.domain import domain_add, domain_list, get_public_ip
from yunohost.dyndns import dyndns_subscribe from yunohost.dyndns import dyndns_subscribe
from yunohost.firewall import firewall_upnp, firewall_reload from yunohost.firewall import firewall_upnp
from yunohost.service import service_status, service_regen_conf, service_log from yunohost.service import service_status, service_regen_conf, service_log
from yunohost.monitor import monitor_disk, monitor_network, monitor_system from yunohost.monitor import monitor_disk, monitor_system
from yunohost.utils.packages import ynh_packages_version from yunohost.utils.packages import ynh_packages_version
apps_setting_path= '/etc/yunohost/apps/' apps_setting_path = '/etc/yunohost/apps/'
logger = getActionLogger('yunohost.tools') logger = getActionLogger('yunohost.tools')
@ -62,12 +59,16 @@ def tools_ldapinit(auth):
ldap_map = yaml.load(f) ldap_map = yaml.load(f)
for rdn, attr_dict in ldap_map['parents'].items(): for rdn, attr_dict in ldap_map['parents'].items():
try: auth.add(rdn, attr_dict) try:
except: pass auth.add(rdn, attr_dict)
except:
pass
for rdn, attr_dict in ldap_map['children'].items(): for rdn, attr_dict in ldap_map['children'].items():
try: auth.add(rdn, attr_dict) try:
except: pass auth.add(rdn, attr_dict)
except:
pass
admin_dict = { admin_dict = {
'cn': 'admin', 'cn': 'admin',
@ -118,7 +119,7 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False):
old_domain = f.readline().rstrip() old_domain = f.readline().rstrip()
if not new_domain: if not new_domain:
return { 'current_main_domain': old_domain } return {'current_main_domain': old_domain}
if not new_domain: if not new_domain:
raise MoulinetteError(errno.EINVAL, m18n.n('new_domain_required')) raise MoulinetteError(errno.EINVAL, m18n.n('new_domain_required'))
@ -153,7 +154,8 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False):
try: try:
with open('/etc/yunohost/installed', 'r') as f: with open('/etc/yunohost/installed', 'r') as f:
service_regen_conf() service_regen_conf()
except IOError: pass except IOError:
pass
logger.success(m18n.n('maindomain_changed')) logger.success(m18n.n('maindomain_changed'))
@ -195,7 +197,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
auth = init_authenticator(('ldap', 'default'), auth = init_authenticator(('ldap', 'default'),
{'uri': "ldap://localhost:389", {'uri': "ldap://localhost:389",
'base_dn': "dc=yunohost,dc=org", 'base_dn': "dc=yunohost,dc=org",
'user_rdn': "cn=admin" }) 'user_rdn': "cn=admin"})
auth.authenticate('yunohost') auth.authenticate('yunohost')
# Initialize LDAP for YunoHost # Initialize LDAP for YunoHost
@ -212,8 +214,10 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
] ]
for folder in folders_to_create: for folder in folders_to_create:
try: os.listdir(folder) try:
except OSError: os.makedirs(folder) os.listdir(folder)
except OSError:
os.makedirs(folder)
# Change folders permissions # Change folders permissions
os.system('chmod 755 /home/yunohost.app') os.system('chmod 755 /home/yunohost.app')
@ -232,7 +236,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
if 'redirected_urls' not in ssowat_conf: if 'redirected_urls' not in ssowat_conf:
ssowat_conf['redirected_urls'] = {} ssowat_conf['redirected_urls'] = {}
ssowat_conf['redirected_urls']['/'] = domain +'/yunohost/admin' ssowat_conf['redirected_urls']['/'] = domain + '/yunohost/admin'
with open('/etc/ssowat/conf.json.persistent', 'w+') as f: with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
json.dump(ssowat_conf, f, sort_keys=True, indent=4) json.dump(ssowat_conf, f, sort_keys=True, indent=4)
@ -317,9 +321,8 @@ def tools_update(ignore_apps=False, ignore_packages=False):
app_fetchlist() app_fetchlist()
except MoulinetteError: except MoulinetteError:
pass pass
app_list = os.listdir(apps_setting_path)
if len(app_list) > 0: for app_id in os.listdir(apps_setting_path):
for app_id in app_list:
if '__' in app_id: if '__' in app_id:
original_app_id = app_id[:app_id.index('__')] original_app_id = app_id[:app_id.index('__')]
else: else:
@ -345,7 +348,7 @@ 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:
logger.info(m18n.n('packages_no_upgrade')) logger.info(m18n.n('packages_no_upgrade'))
return { 'packages': packages, 'apps': apps } return {'packages': packages, 'apps': apps}
def tools_upgrade(auth, ignore_apps=False, ignore_packages=False): def tools_upgrade(auth, ignore_apps=False, ignore_packages=False):
@ -414,7 +417,7 @@ def tools_upgrade(auth, ignore_apps=False, ignore_packages=False):
# Return API logs if it is an API call # Return API logs if it is an API call
if is_api: if is_api:
return { "log": service_log('yunohost-api', number="100").values()[0] } return {"log": service_log('yunohost-api', number="100").values()[0]}
def tools_diagnosis(auth, private=False): def tools_diagnosis(auth, private=False):
@ -422,7 +425,7 @@ def tools_diagnosis(auth, private=False):
Return global info about current yunohost instance to help debugging Return global info about current yunohost instance to help debugging
""" """
diagnosis = OrderedDict(); diagnosis = OrderedDict()
# Debian release # Debian release
try: try:
@ -466,8 +469,8 @@ def tools_diagnosis(auth, private=False):
logger.warning(m18n.n('diagnosis_monitor_system_error', error=format(e)), exc_info=1) logger.warning(m18n.n('diagnosis_monitor_system_error', error=format(e)), exc_info=1)
else: else:
diagnosis['system']['memory'] = { diagnosis['system']['memory'] = {
'ram' : '%s (%s free)' % (system['memory']['ram']['total'], system['memory']['ram']['free']), 'ram': '%s (%s free)' % (system['memory']['ram']['total'], system['memory']['ram']['free']),
'swap' : '%s (%s free)' % (system['memory']['swap']['total'], system['memory']['swap']['free']), 'swap': '%s (%s free)' % (system['memory']['swap']['total'], system['memory']['swap']['free']),
} }
# Services status # Services status

View file

@ -30,12 +30,17 @@ import string
import json import json
import errno import errno
import subprocess import subprocess
import math
import re import re
import pwd
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.domain import domain_list
from yunohost.hook import hook_callback
from yunohost.app import app_ssowatconf
logger = getActionLogger('yunohost.user') logger = getActionLogger('yunohost.user')
@ -50,12 +55,12 @@ def user_list(auth, fields=None, filter=None, limit=None, offset=None):
fields -- fields to fetch fields -- fields to fetch
""" """
user_attrs = { 'uid': 'username', user_attrs = {'uid': 'username',
'cn': 'fullname', 'cn': 'fullname',
'mail': 'mail', 'mail': 'mail',
'maildrop': 'mail-forward', 'maildrop': 'mail-forward',
'mailuserquota': 'mailbox-quota' } 'mailuserquota': 'mailbox-quota'}
attrs = [ 'uid' ] attrs = ['uid']
users = {} users = {}
# Set default arguments values # Set default arguments values
@ -74,12 +79,12 @@ def user_list(auth, fields=None, filter=None, limit=None, offset=None):
raise MoulinetteError(errno.EINVAL, raise MoulinetteError(errno.EINVAL,
m18n.n('field_invalid', attr)) m18n.n('field_invalid', attr))
else: else:
attrs = [ 'uid', 'cn', 'mail', 'mailuserquota' ] attrs = ['uid', 'cn', 'mail', 'mailuserquota']
result = auth.search('ou=users,dc=yunohost,dc=org', filter, attrs) result = auth.search('ou=users,dc=yunohost,dc=org', filter, attrs)
if len(result) > offset and limit > 0: if len(result) > offset and limit > 0:
for user in result[offset:offset+limit]: for user in result[offset:offset + limit]:
entry = {} entry = {}
for attr, values in user.items(): for attr, values in user.items():
try: try:
@ -88,7 +93,7 @@ def user_list(auth, fields=None, filter=None, limit=None, offset=None):
pass pass
uid = entry[user_attrs['uid']] uid = entry[user_attrs['uid']]
users[uid] = entry users[uid] = entry
return { 'users' : users } return {'users': users}
def user_create(auth, username, firstname, lastname, mail, password, def user_create(auth, username, firstname, lastname, mail, password,
@ -105,15 +110,10 @@ def user_create(auth, username, firstname, lastname, mail, password,
mailbox_quota -- Mailbox size quota mailbox_quota -- Mailbox size quota
""" """
import pwd
from yunohost.domain import domain_list
from yunohost.hook import hook_callback
from yunohost.app import app_ssowatconf
# Validate uniqueness of username and mail in LDAP # Validate uniqueness of username and mail in LDAP
auth.validate_uniqueness({ auth.validate_uniqueness({
'uid' : username, 'uid': username,
'mail' : mail 'mail': mail
}) })
# Validate uniqueness of username in system users # Validate uniqueness of username in system users
@ -125,10 +125,10 @@ def user_create(auth, username, firstname, lastname, mail, password,
raise MoulinetteError(errno.EEXIST, m18n.n('system_username_exists')) raise MoulinetteError(errno.EEXIST, m18n.n('system_username_exists'))
# Check that the mail domain exists # Check that the mail domain exists
if mail[mail.find('@')+1:] not in domain_list(auth)['domains']: if mail[mail.find('@') + 1:] not in domain_list(auth)['domains']:
raise MoulinetteError(errno.EINVAL, raise MoulinetteError(errno.EINVAL,
m18n.n('mail_domain_unknown', m18n.n('mail_domain_unknown',
domain=mail[mail.find('@')+1:])) domain=mail[mail.find('@') + 1:]))
# Get random UID/GID # Get random UID/GID
uid_check = gid_check = 0 uid_check = gid_check = 0
@ -141,7 +141,7 @@ def user_create(auth, username, firstname, lastname, mail, password,
fullname = '%s %s' % (firstname, lastname) fullname = '%s %s' % (firstname, lastname)
rdn = 'uid=%s,ou=users' % username rdn = 'uid=%s,ou=users' % username
char_set = string.ascii_uppercase + string.digits char_set = string.ascii_uppercase + string.digits
salt = ''.join(random.sample(char_set,8)) salt = ''.join(random.sample(char_set, 8))
salt = '$1$' + salt + '$' salt = '$1$' + salt + '$'
user_pwd = '{CRYPT}' + crypt.crypt(str(password), salt) user_pwd = '{CRYPT}' + crypt.crypt(str(password), salt)
attr_dict = { attr_dict = {
@ -166,12 +166,12 @@ def user_create(auth, username, firstname, lastname, mail, password,
with open('/etc/yunohost/current_host') as f: with open('/etc/yunohost/current_host') as f:
main_domain = f.readline().rstrip() main_domain = f.readline().rstrip()
aliases = [ aliases = [
'root@'+ main_domain, 'root@' + main_domain,
'admin@'+ main_domain, 'admin@' + main_domain,
'webmaster@'+ main_domain, 'webmaster@' + main_domain,
'postmaster@'+ main_domain, 'postmaster@' + main_domain,
] ]
attr_dict['mail'] = [ attr_dict['mail'] ] + aliases attr_dict['mail'] = [attr_dict['mail']] + aliases
# If exists, remove the redirection from the SSO # If exists, remove the redirection from the SSO
try: try:
@ -184,8 +184,8 @@ def user_create(auth, username, firstname, lastname, mail, password,
with open('/etc/ssowat/conf.json.persistent', 'w+') as f: with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
json.dump(ssowat_conf, f, sort_keys=True, indent=4) json.dump(ssowat_conf, f, sort_keys=True, indent=4)
except IOError: pass except IOError:
pass
if auth.add(rdn, attr_dict): if auth.add(rdn, attr_dict):
# Invalidate passwd to take user creation into account # Invalidate passwd to take user creation into account
@ -194,7 +194,7 @@ def user_create(auth, username, firstname, lastname, mail, password,
# Update SFTP user group # Update SFTP user group
memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid']
memberlist.append(username) memberlist.append(username)
if auth.update('cn=sftpusers,ou=groups', { 'memberUid': memberlist }): if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}):
try: try:
# Attempt to create user home folder # Attempt to create user home folder
subprocess.check_call( subprocess.check_call(
@ -204,12 +204,12 @@ def user_create(auth, username, firstname, lastname, mail, password,
logger.warning(m18n.n('user_home_creation_failed'), logger.warning(m18n.n('user_home_creation_failed'),
exc_info=1) exc_info=1)
app_ssowatconf(auth) app_ssowatconf(auth)
#TODO: Send a welcome mail to user # TODO: Send a welcome mail to user
logger.success(m18n.n('user_created')) logger.success(m18n.n('user_created'))
hook_callback('post_user_create', hook_callback('post_user_create',
args=[username, mail, password, firstname, lastname]) args=[username, mail, password, firstname, lastname])
return { 'fullname' : fullname, 'username' : username, 'mail' : mail } return {'fullname': fullname, 'username': username, 'mail': mail}
raise MoulinetteError(169, m18n.n('user_creation_failed')) raise MoulinetteError(169, m18n.n('user_creation_failed'))
@ -223,18 +223,17 @@ def user_delete(auth, username, purge=False):
purge purge
""" """
from yunohost.app import app_ssowatconf
from yunohost.hook import hook_callback
if auth.remove('uid=%s,ou=users' % username): if auth.remove('uid=%s,ou=users' % username):
# Invalidate passwd to take user deletion into account # Invalidate passwd to take user deletion into account
subprocess.call(['nscd', '-i', 'passwd']) subprocess.call(['nscd', '-i', 'passwd'])
# Update SFTP user group # Update SFTP user group
memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid']
try: memberlist.remove(username) try:
except: pass memberlist.remove(username)
if auth.update('cn=sftpusers,ou=groups', { 'memberUid': memberlist }): except:
pass
if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}):
if purge: if purge:
subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
else: else:
@ -265,9 +264,6 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None,
remove_mailalias -- Mail aliases to remove remove_mailalias -- Mail aliases to remove
""" """
from yunohost.domain import domain_list
from yunohost.app import app_ssowatconf
attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop'] attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop']
new_attr_dict = {} new_attr_dict = {}
domains = domain_list(auth)['domains'] domains = domain_list(auth)['domains']
@ -292,34 +288,34 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None,
if change_password: if change_password:
char_set = string.ascii_uppercase + string.digits char_set = string.ascii_uppercase + string.digits
salt = ''.join(random.sample(char_set,8)) salt = ''.join(random.sample(char_set, 8))
salt = '$1$' + salt + '$' salt = '$1$' + salt + '$'
new_attr_dict['userPassword'] = '{CRYPT}' + crypt.crypt(str(change_password), salt) new_attr_dict['userPassword'] = '{CRYPT}' + crypt.crypt(str(change_password), salt)
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(errno.EINVAL, raise MoulinetteError(errno.EINVAL,
m18n.n('mail_domain_unknown', m18n.n('mail_domain_unknown',
domain=mail[mail.find('@')+1:])) domain=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']
if add_mailalias: if add_mailalias:
if not isinstance(add_mailalias, list): if not isinstance(add_mailalias, list):
add_mailalias = [ add_mailalias ] add_mailalias = [add_mailalias]
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(errno.EINVAL, raise MoulinetteError(errno.EINVAL,
m18n.n('mail_domain_unknown', m18n.n('mail_domain_unknown',
domain=mail[mail.find('@')+1:])) domain=mail[mail.find('@') + 1:]))
user['mail'].append(mail) user['mail'].append(mail)
new_attr_dict['mail'] = user['mail'] new_attr_dict['mail'] = user['mail']
if remove_mailalias: if remove_mailalias:
if not isinstance(remove_mailalias, list): if not isinstance(remove_mailalias, list):
remove_mailalias = [ remove_mailalias ] remove_mailalias = [remove_mailalias]
for mail in remove_mailalias: for mail in remove_mailalias:
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)
@ -330,7 +326,7 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None,
if add_mailforward: if add_mailforward:
if not isinstance(add_mailforward, list): if not isinstance(add_mailforward, list):
add_mailforward = [ add_mailforward ] add_mailforward = [add_mailforward]
for mail in add_mailforward: for mail in add_mailforward:
if mail in user['maildrop'][1:]: if mail in user['maildrop'][1:]:
continue continue
@ -339,7 +335,7 @@ def user_update(auth, username, firstname=None, lastname=None, mail=None,
if remove_mailforward: if remove_mailforward:
if not isinstance(remove_mailforward, list): if not isinstance(remove_mailforward, list):
remove_mailforward = [ remove_mailforward ] remove_mailforward = [remove_mailforward]
for mail in remove_mailforward: for mail in remove_mailforward:
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)
@ -372,9 +368,9 @@ def user_info(auth, username):
] ]
if len(username.split('@')) is 2: if len(username.split('@')) is 2:
filter = 'mail='+ username filter = 'mail=' + username
else: else:
filter = 'uid='+ username filter = 'uid=' + username
result = auth.search('ou=users,dc=yunohost,dc=org', filter, user_attrs) result = auth.search('ou=users,dc=yunohost,dc=org', filter, user_attrs)
@ -400,14 +396,14 @@ def user_info(auth, username):
if 'mailuserquota' in user: if 'mailuserquota' in user:
if user['mailuserquota'][0] != '0': if user['mailuserquota'][0] != '0':
cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0] cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0]
userquota = subprocess.check_output(cmd,stderr=subprocess.STDOUT, userquota = subprocess.check_output(cmd, stderr=subprocess.STDOUT,
shell=True) shell=True)
quotavalue = re.findall(r'\d+', userquota) quotavalue = re.findall(r'\d+', userquota)
result = '%s (%s%s)' % ( _convertSize(eval(quotavalue[0])), result = '%s (%s%s)' % (_convertSize(eval(quotavalue[0])),
quotavalue[2], '%') quotavalue[2], '%')
result_dict['mailbox-quota'] = { result_dict['mailbox-quota'] = {
'limit' : user['mailuserquota'][0], 'limit': user['mailuserquota'][0],
'use' : result 'use': result
} }
else: else:
result_dict['mailbox-quota'] = m18n.n('unlimit') result_dict['mailbox-quota'] = m18n.n('unlimit')
@ -417,9 +413,12 @@ def user_info(auth, username):
else: else:
raise MoulinetteError(167, m18n.n('user_info_failed')) raise MoulinetteError(167, m18n.n('user_info_failed'))
def _convertSize(num, suffix=''): def _convertSize(num, suffix=''):
for unit in ['K','M','G','T','P','E','Z']: for unit in ['K', 'M', 'G', 'T', 'P', 'E', 'Z']:
if abs(num) < 1024.0: if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix) return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0 num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix) return "%.1f%s%s" % (num, 'Yi', suffix)