mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge branch 'stretch-unstable' of github.com:eauchat/yunohost into stretch-unstable
This commit is contained in:
commit
06522b1fa8
43 changed files with 1192 additions and 984 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -32,3 +32,6 @@ pip-log.txt
|
|||
|
||||
# moulinette lib
|
||||
src/yunohost/locales
|
||||
|
||||
# Test
|
||||
src/yunohost/tests/apps
|
||||
|
|
|
@ -5,7 +5,7 @@ ip=$(hostname --all-ip-address)
|
|||
|
||||
# Fetch SSH fingerprints
|
||||
i=0
|
||||
for key in /etc/ssh/ssh_host_*_key.pub ; do
|
||||
for key in $(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key.pub 2> /dev/null) ; do
|
||||
output=$(ssh-keygen -l -f $key)
|
||||
fingerprint[$i]=" - $(echo $output | cut -d' ' -f2) $(echo $output| cut -d' ' -f4)"
|
||||
i=$(($i + 1))
|
||||
|
|
|
@ -558,6 +558,10 @@ app:
|
|||
full: --no-remove-on-failure
|
||||
help: Debug option to avoid removing the app on a failed installation
|
||||
action: store_true
|
||||
-f:
|
||||
full: --force
|
||||
help: Do not ask confirmation if the app is not safe to use (low quality, experimental or 3rd party)
|
||||
action: store_true
|
||||
|
||||
### app_remove() TODO: Write help
|
||||
remove:
|
||||
|
|
|
@ -10,12 +10,13 @@ CAN_BIND=${CAN_BIND:-1}
|
|||
#
|
||||
# If DEST is ended by a slash it complete this path with the basename of SRC.
|
||||
#
|
||||
# usage: ynh_backup src [dest [is_big [arg]]]
|
||||
# usage: ynh_backup src [dest [is_big [not_mandatory [arg]]]]
|
||||
# | arg: src - file or directory to bind or symlink or copy. it shouldn't be in
|
||||
# the backup dir.
|
||||
# | arg: dest - destination file or directory inside the
|
||||
# backup dir
|
||||
# | arg: is_big - 1 to indicate data are big (mail, video, image ...)
|
||||
# | arg: not_mandatory - 1 to indicate that if the file is missing, the backup can ignore it.
|
||||
# | arg: arg - Deprecated arg
|
||||
#
|
||||
# example:
|
||||
|
@ -46,6 +47,7 @@ ynh_backup() {
|
|||
local SRC_PATH="$1"
|
||||
local DEST_PATH="${2:-}"
|
||||
local IS_BIG="${3:-0}"
|
||||
local NOT_MANDATORY="${4:-0}"
|
||||
BACKUP_CORE_ONLY=${BACKUP_CORE_ONLY:-0}
|
||||
|
||||
# If backing up core only (used by ynh_backup_before_upgrade),
|
||||
|
@ -60,15 +62,18 @@ ynh_backup() {
|
|||
# ==============================================================================
|
||||
# Be sure the source path is not empty
|
||||
[[ -e "${SRC_PATH}" ]] || {
|
||||
echo "!!! Source path '${SRC_PATH}' does not exist !!!" >&2
|
||||
|
||||
# This is a temporary fix for fail2ban config files missing after the migration to stretch.
|
||||
if echo "${SRC_PATH}" | grep --quiet "/etc/fail2ban"
|
||||
if [ "$NOT_MANDATORY" == "0" ]
|
||||
then
|
||||
touch "${SRC_PATH}"
|
||||
echo "The missing file will be replaced by a dummy one for the backup !!!" >&2
|
||||
# This is a temporary fix for fail2ban config files missing after the migration to stretch.
|
||||
if echo "${SRC_PATH}" | grep --quiet "/etc/fail2ban"
|
||||
then
|
||||
touch "${SRC_PATH}"
|
||||
echo "The missing file will be replaced by a dummy one for the backup !!!" >&2
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
return 1
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -176,12 +181,13 @@ with open(sys.argv[1], 'r') as backup_file:
|
|||
# Use the registered path in backup_list by ynh_backup to restore the file at
|
||||
# the good place.
|
||||
#
|
||||
# usage: ynh_restore_file ORIGIN_PATH [ DEST_PATH ]
|
||||
# usage: ynh_restore_file ORIGIN_PATH [ DEST_PATH [NOT_MANDATORY]]
|
||||
# | arg: ORIGIN_PATH - Path where was located the file or the directory before
|
||||
# to be backuped or relative path to $YNH_CWD where it is located in the backup archive
|
||||
# | arg: DEST_PATH - Path where restore the file or the dir, if unspecified,
|
||||
# the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in
|
||||
# the archive, the destination will be searched into backup.csv
|
||||
# | arg: NOT_MANDATORY - 1 to indicate that if the file is missing, the restore process can ignore it.
|
||||
#
|
||||
# If DEST_PATH already exists and is lighter than 500 Mo, a backup will be made in
|
||||
# /home/yunohost.conf/backup/. Otherwise, the existing file is removed.
|
||||
|
@ -201,10 +207,16 @@ ynh_restore_file () {
|
|||
local ARCHIVE_PATH="$YNH_CWD${ORIGIN_PATH}"
|
||||
# Default value for DEST_PATH = /$ORIGIN_PATH
|
||||
local DEST_PATH="${2:-$ORIGIN_PATH}"
|
||||
local NOT_MANDATORY="${3:-0}"
|
||||
|
||||
# If ARCHIVE_PATH doesn't exist, search for a corresponding path in CSV
|
||||
if [ ! -d "$ARCHIVE_PATH" ] && [ ! -f "$ARCHIVE_PATH" ] && [ ! -L "$ARCHIVE_PATH" ]; then
|
||||
ARCHIVE_PATH="$YNH_BACKUP_DIR/$(_get_archive_path \"$ORIGIN_PATH\")"
|
||||
if [ "$NOT_MANDATORY" == "0" ]
|
||||
then
|
||||
ARCHIVE_PATH="$YNH_BACKUP_DIR/$(_get_archive_path \"$ORIGIN_PATH\")"
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Move the old directory if it already exists
|
||||
|
|
|
@ -272,6 +272,7 @@ ynh_local_curl () {
|
|||
ynh_render_template() {
|
||||
local template_path=$1
|
||||
local output_path=$2
|
||||
mkdir -p "$(dirname $output_path)"
|
||||
# Taken from https://stackoverflow.com/a/35009576
|
||||
python2.7 -c 'import os, sys, jinja2; sys.stdout.write(
|
||||
jinja2.Template(sys.stdin.read()
|
||||
|
|
|
@ -2,28 +2,53 @@
|
|||
|
||||
set -e
|
||||
|
||||
. /usr/share/yunohost/helpers.d/utils
|
||||
|
||||
do_pre_regen() {
|
||||
pending_dir=$1
|
||||
pending_dir=$1
|
||||
|
||||
cd /usr/share/yunohost/templates/ssh
|
||||
# If the (legacy) 'from_script' flag is here,
|
||||
# we won't touch anything in the ssh config.
|
||||
[[ ! -f /etc/yunohost/from_script ]] || return 0
|
||||
|
||||
# only overwrite SSH configuration on an ISO installation
|
||||
if [[ ! -f /etc/yunohost/from_script ]]; then
|
||||
# do not listen to IPv6 if unavailable
|
||||
[[ -f /proc/net/if_inet6 ]] \
|
||||
|| sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config
|
||||
cd /usr/share/yunohost/templates/ssh
|
||||
|
||||
install -D -m 644 sshd_config "${pending_dir}/etc/ssh/sshd_config"
|
||||
fi
|
||||
# do not listen to IPv6 if unavailable
|
||||
[[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false
|
||||
|
||||
# Support legacy setting (this setting might be disabled by a user during a migration)
|
||||
ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null)
|
||||
if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then
|
||||
ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)"
|
||||
fi
|
||||
|
||||
ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null)
|
||||
|
||||
# Support legacy setting (this setting might be disabled by a user during a migration)
|
||||
if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then
|
||||
ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)"
|
||||
fi
|
||||
|
||||
export ssh_keys
|
||||
export ipv6_enabled
|
||||
ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config"
|
||||
}
|
||||
|
||||
do_post_regen() {
|
||||
regen_conf_files=$1
|
||||
regen_conf_files=$1
|
||||
|
||||
if [[ ! -f /etc/yunohost/from_script ]]; then
|
||||
[[ -z "$regen_conf_files" ]] \
|
||||
|| sudo service ssh restart
|
||||
fi
|
||||
# If the (legacy) 'from_script' flag is here,
|
||||
# we won't touch anything in the ssh config.
|
||||
[[ ! -f /etc/yunohost/from_script ]] || return 0
|
||||
|
||||
# If no file changed, there's nothing to do
|
||||
[[ -n "$regen_conf_files" ]] || return 0
|
||||
|
||||
# Enforce permissions for /etc/ssh/sshd_config
|
||||
chown root:root "/etc/ssh/sshd_config"
|
||||
chmod 644 "/etc/ssh/sshd_config"
|
||||
|
||||
systemctl restart ssh
|
||||
}
|
||||
|
||||
FORCE=${2:-0}
|
||||
|
|
|
@ -1,96 +1,78 @@
|
|||
# Package generated configuration file
|
||||
# See the sshd_config(5) manpage for details
|
||||
# This configuration has been automatically generated
|
||||
# by YunoHost
|
||||
|
||||
# What ports, IPs and protocols we listen for
|
||||
Port 22
|
||||
# Use these options to restrict which interfaces/protocols sshd will bind to
|
||||
ListenAddress ::
|
||||
ListenAddress 0.0.0.0
|
||||
Protocol 2
|
||||
# HostKeys for protocol version 2
|
||||
HostKey /etc/ssh/ssh_host_rsa_key
|
||||
HostKey /etc/ssh/ssh_host_dsa_key
|
||||
#Privilege Separation is turned on for security
|
||||
UsePrivilegeSeparation yes
|
||||
Port 22
|
||||
|
||||
# Lifetime and size of ephemeral version 1 server key
|
||||
KeyRegenerationInterval 3600
|
||||
ServerKeyBits 768
|
||||
{% if ipv6_enabled == "true" %}ListenAddress ::{% endif %}
|
||||
ListenAddress 0.0.0.0
|
||||
|
||||
# Logging
|
||||
{% for key in ssh_keys.split() %}
|
||||
HostKey {{ key }}{% endfor %}
|
||||
|
||||
# ##############################################
|
||||
# Stuff recommended by Mozilla "modern" compat'
|
||||
# https://infosec.mozilla.org/guidelines/openssh
|
||||
# ##############################################
|
||||
|
||||
# Keys, ciphers and MACS
|
||||
KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
|
||||
|
||||
# Use kernel sandbox mechanisms where possible in unprivileged processes
|
||||
UsePrivilegeSeparation sandbox
|
||||
|
||||
# LogLevel VERBOSE logs user's key fingerprint on login.
|
||||
# Needed to have a clear audit track of which key was using to log in.
|
||||
SyslogFacility AUTH
|
||||
LogLevel INFO
|
||||
LogLevel VERBOSE
|
||||
|
||||
# #######################
|
||||
# Authentication settings
|
||||
# #######################
|
||||
|
||||
# Comment from Mozilla about the motivation behind disabling root login
|
||||
#
|
||||
# Root login is not allowed for auditing reasons. This is because it's difficult to track which process belongs to which root user:
|
||||
#
|
||||
# On Linux, user sessions are tracking using a kernel-side session id, however, this session id is not recorded by OpenSSH.
|
||||
# Additionally, only tools such as systemd and auditd record the process session id.
|
||||
# On other OSes, the user session id is not necessarily recorded at all kernel-side.
|
||||
# Using regular users in combination with /bin/su or /usr/bin/sudo ensure a clear audit track.
|
||||
|
||||
# Authentication:
|
||||
LoginGraceTime 120
|
||||
PermitRootLogin no
|
||||
StrictModes yes
|
||||
|
||||
RSAAuthentication yes
|
||||
PubkeyAuthentication yes
|
||||
#AuthorizedKeysFile %h/.ssh/authorized_keys
|
||||
|
||||
# Don't read the user's ~/.rhosts and ~/.shosts files
|
||||
IgnoreRhosts yes
|
||||
# For this to work you will also need host keys in /etc/ssh_known_hosts
|
||||
RhostsRSAAuthentication no
|
||||
# similar for protocol version 2
|
||||
HostbasedAuthentication no
|
||||
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
|
||||
#IgnoreUserKnownHosts yes
|
||||
|
||||
# To enable empty passwords, change to yes (NOT RECOMMENDED)
|
||||
PermitEmptyPasswords no
|
||||
|
||||
# Change to yes to enable challenge-response passwords (beware issues with
|
||||
# some PAM modules and threads)
|
||||
ChallengeResponseAuthentication no
|
||||
|
||||
# Change to no to disable tunnelled clear text passwords
|
||||
#PasswordAuthentication yes
|
||||
|
||||
# Kerberos options
|
||||
#KerberosAuthentication no
|
||||
#KerberosGetAFSToken no
|
||||
#KerberosOrLocalPasswd yes
|
||||
#KerberosTicketCleanup yes
|
||||
|
||||
# GSSAPI options
|
||||
#GSSAPIAuthentication no
|
||||
#GSSAPICleanupCredentials yes
|
||||
|
||||
X11Forwarding yes
|
||||
X11DisplayOffset 10
|
||||
PrintMotd no
|
||||
PrintLastLog yes
|
||||
TCPKeepAlive yes
|
||||
#UseLogin no
|
||||
|
||||
# keep ssh sessions fresh
|
||||
ClientAliveInterval 60
|
||||
|
||||
#MaxStartups 10:30:60
|
||||
Banner /etc/issue.net
|
||||
|
||||
# Allow client to pass locale environment variables
|
||||
AcceptEnv LANG LC_*
|
||||
|
||||
Subsystem sftp internal-sftp
|
||||
|
||||
# Set this to 'yes' to enable PAM authentication, account processing,
|
||||
# and session processing. If this is enabled, PAM authentication will
|
||||
# be allowed through the ChallengeResponseAuthentication and
|
||||
# PasswordAuthentication. Depending on your PAM configuration,
|
||||
# PAM authentication via ChallengeResponseAuthentication may bypass
|
||||
# the setting of "PermitRootLogin without-password".
|
||||
# If you just want the PAM account and session checks to run without
|
||||
# PAM authentication, then enable this but set PasswordAuthentication
|
||||
# and ChallengeResponseAuthentication to 'no'.
|
||||
UsePAM yes
|
||||
|
||||
# Change to no to disable tunnelled clear text passwords
|
||||
# (i.e. everybody will need to authenticate using ssh keys)
|
||||
#PasswordAuthentication yes
|
||||
|
||||
# Post-login stuff
|
||||
Banner /etc/issue.net
|
||||
PrintMotd no
|
||||
PrintLastLog yes
|
||||
ClientAliveInterval 60
|
||||
AcceptEnv LANG LC_*
|
||||
|
||||
# SFTP stuff
|
||||
Subsystem sftp internal-sftp
|
||||
Match User sftpusers
|
||||
ForceCommand internal-sftp
|
||||
ChrootDirectory /home/%u
|
||||
AllowTcpForwarding no
|
||||
GatewayPorts no
|
||||
X11Forwarding no
|
||||
|
||||
# root login is allowed on local networks
|
||||
# It's meant to be a backup solution in case LDAP is down and
|
||||
# user admin can't be used...
|
||||
# If the server is a VPS, it's expected that the owner of the
|
||||
# server has access to a web console through which to log in.
|
||||
Match Address 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,169.254.0.0/16,fe80::/10,fd00::/8
|
||||
PermitRootLogin yes
|
||||
|
|
18
debian/changelog
vendored
18
debian/changelog
vendored
|
@ -1,17 +1,19 @@
|
|||
yunohost (3.3.3) stable; urgency=low
|
||||
|
||||
* [fix] ynh_wait_dpkg_free displaying a warning despite everything being okay (#593)
|
||||
* [fix] Quotes for recommended CAA DNS record (#596)
|
||||
* [fix] Manual migration and disclaimer behaviors (#594)
|
||||
* [fix] Explicit root password change each time admin password is changed
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 09 Dec 2018 20:58:00 +0000
|
||||
|
||||
yunohost (3.3.2) stable; urgency=low
|
||||
|
||||
* [fix] Regen nginx conf to be sure it integrates OCSP Stapling (#588)
|
||||
* [fix] Broken new settings and options to control passwords checks / constrains (#589)
|
||||
* [fix] Log dyndns update only if we really update something (#591)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 02 Dev 2018 17:23:00 +0000
|
||||
|
||||
yunohost (3.3.2) stable; urgency=low
|
||||
|
||||
* [fix] Log dyndns update only if we really update something (#591)
|
||||
* [fix] Broken new settings and options to control passwords checks / constrains (#589)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 02 Dev 2018 17:17:00 +0000
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 02 Dec 2018 17:23:00 +0000
|
||||
|
||||
yunohost (3.3.1) stable; urgency=low
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"aborting": "Aborting.",
|
||||
"action_invalid": "Invalid action '{action:s}'",
|
||||
"admin_password": "Administration password",
|
||||
"admin_password_change_failed": "Unable to change password",
|
||||
|
@ -132,6 +133,9 @@
|
|||
"certmanager_no_cert_file": "Unable to read certificate file for domain {domain:s} (file: {file:s})",
|
||||
"certmanager_self_ca_conf_file_not_found": "Configuration file not found for self-signing authority (file: {file:s})",
|
||||
"certmanager_unable_to_parse_self_CA_name": "Unable to parse name of self-signing authority (file: {file:s})",
|
||||
"confirm_app_install_warning": "Warning: this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ",
|
||||
"confirm_app_install_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ",
|
||||
"confirm_app_install_thirdparty": "WARNING! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ",
|
||||
"custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}",
|
||||
"custom_appslist_name_required": "You must provide a name for your custom app list",
|
||||
"diagnosis_debian_version_error": "Can't retrieve the Debian version: {error}",
|
||||
|
@ -194,7 +198,10 @@
|
|||
"global_settings_setting_example_enum": "Example enum option",
|
||||
"global_settings_setting_example_int": "Example int option",
|
||||
"global_settings_setting_example_string": "Example string option",
|
||||
"global_settings_setting_security_password_admin_strength": "Admin password strength",
|
||||
"global_settings_setting_security_password_user_strength": "User password strength",
|
||||
"global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discarding it and save it in /etc/yunohost/unkown_settings.json",
|
||||
"global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration",
|
||||
"global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.",
|
||||
"good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).",
|
||||
"good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).",
|
||||
|
@ -211,7 +218,7 @@
|
|||
"log_category_404": "The log category '{category}' does not exist",
|
||||
"log_link_to_log": "Full log of this operation: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\">{desc}</a>'",
|
||||
"log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log display {name}'",
|
||||
"log_link_to_failed_log": "The operation '{desc}' has failed ! To get help, please <a href=\"#/tools/logs/{name}\">provide the full log of this operation</a>",
|
||||
"log_link_to_failed_log": "The operation '{desc}' has failed ! To get help, please <a href=\"#/tools/logs/{name}\">provide the full log of this operation by clicking here</a>",
|
||||
"log_help_to_get_failed_log": "The operation '{desc}' has failed ! To get help, please share the full log of this operation using the command 'yunohost log display {name} --share'",
|
||||
"log_category_404": "The log category '{category}' does not exist",
|
||||
"log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list to see all available operation logs'",
|
||||
|
@ -274,6 +281,8 @@
|
|||
"migration_description_0004_php5_to_php7_pools": "Reconfigure the PHP pools to use PHP 7 instead of 5",
|
||||
"migration_description_0005_postgresql_9p4_to_9p6": "Migrate databases from postgresql 9.4 to 9.6",
|
||||
"migration_description_0006_sync_admin_and_root_passwords": "Synchronize admin and root passwords",
|
||||
"migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)",
|
||||
"migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)",
|
||||
"migration_0003_backward_impossible": "The stretch migration cannot be reverted.",
|
||||
"migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.",
|
||||
"migration_0003_patching_sources_list": "Patching the sources.lists ...",
|
||||
|
@ -291,7 +300,14 @@
|
|||
"migration_0005_postgresql_96_not_installed": "Postgresql 9.4 has been found to be installed, but not postgresql 9.6 !? Something weird might have happened on your system :( ...",
|
||||
"migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.",
|
||||
"migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.",
|
||||
"migration_0006_done": "Your root password have been replaced by your admin password.",
|
||||
"migration_0007_cancelled": "YunoHost has failed to improve the way your SSH conf is managed.",
|
||||
"migration_0007_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.",
|
||||
"migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:",
|
||||
"migration_0008_port": " - you will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it ;",
|
||||
"migration_0008_root": " - you will not be able to connect as root through SSH. Instead you should use the admin user ;",
|
||||
"migration_0008_dsa": " - the DSA key will be disabled. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server ;",
|
||||
"migration_0008_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.",
|
||||
"migration_0008_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;) ! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.",
|
||||
"migrations_backward": "Migrating backward.",
|
||||
"migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}",
|
||||
"migrations_cant_reach_migration_file": "Can't access migrations files at path %s",
|
||||
|
@ -305,6 +321,7 @@
|
|||
"migrations_show_currently_running_migration": "Running migration {number} {name}...",
|
||||
"migrations_show_last_migration": "Last ran migration is {}",
|
||||
"migrations_skip_migration": "Skipping migration {number} {name}...",
|
||||
"migrations_success": "Successfully ran migration {number} {name}!",
|
||||
"migrations_to_be_ran_manually": "Migration {number} {name} has to be ran manually. Please go to Tools > Migrations on the webadmin, or run `yunohost tools migrations migrate`.",
|
||||
"migrations_need_to_accept_disclaimer": "To run the migration {number} {name}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option --accept-disclaimer.",
|
||||
"monitor_disabled": "The server monitoring has been disabled",
|
||||
|
@ -375,6 +392,7 @@
|
|||
"restore_running_hooks": "Running restoration hooks...",
|
||||
"restore_system_part_failed": "Unable to restore the '{part:s}' system part",
|
||||
"root_password_desynchronized": "The admin password has been changed, but YunoHost was unable to propagate this on the root password !",
|
||||
"root_password_replaced_by_admin_password": "Your root password have been replaced by your admin password.",
|
||||
"server_shutdown": "The server will shutdown",
|
||||
"server_shutdown_confirm": "The server will shutdown immediatly, are you sure? [{answers:s}]",
|
||||
"server_reboot": "The server will reboot",
|
||||
|
@ -392,7 +410,7 @@
|
|||
"service_conf_file_remove_failed": "Unable to remove the configuration file '{conf}'",
|
||||
"service_conf_file_removed": "The configuration file '{conf}' has been removed",
|
||||
"service_conf_file_updated": "The configuration file '{conf}' has been updated",
|
||||
"service_conf_new_managed_file": "The configuration file '{conf}' is now managed by the service {service}.",
|
||||
"service_conf_now_managed_by_yunohost": "The configuration file '{conf}' is now managed by YunoHost.",
|
||||
"service_conf_up_to_date": "The configuration is already up-to-date for service '{service}'",
|
||||
"service_conf_updated": "The configuration has been updated for service '{service}'",
|
||||
"service_conf_would_be_updated": "The configuration would have been updated for service '{service}'",
|
||||
|
|
|
@ -30,15 +30,15 @@ import yaml
|
|||
import time
|
||||
import re
|
||||
import urlparse
|
||||
import errno
|
||||
import subprocess
|
||||
import glob
|
||||
import pwd
|
||||
import grp
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
|
||||
from moulinette import msignals, m18n, msettings
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_json
|
||||
|
||||
|
@ -80,6 +80,12 @@ def app_listlists():
|
|||
# Get the list
|
||||
appslist_list = _read_appslist_list()
|
||||
|
||||
# Convert 'lastUpdate' timestamp to datetime
|
||||
for name, infos in appslist_list.items():
|
||||
if infos["lastUpdate"] is None:
|
||||
infos["lastUpdate"] = 0
|
||||
infos["lastUpdate"] = datetime.utcfromtimestamp(infos["lastUpdate"])
|
||||
|
||||
return appslist_list
|
||||
|
||||
|
||||
|
@ -118,14 +124,12 @@ def app_fetchlist(url=None, name=None):
|
|||
appslists_to_be_fetched = [name]
|
||||
operation_logger.success()
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('custom_appslist_name_required'))
|
||||
raise YunohostError('custom_appslist_name_required')
|
||||
|
||||
# If a name is given, look for an appslist with that name and fetch it
|
||||
elif name is not None:
|
||||
if name not in appslists.keys():
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('appslist_unknown', appslist=name))
|
||||
raise YunohostError('appslist_unknown', appslist=name)
|
||||
else:
|
||||
appslists_to_be_fetched = [name]
|
||||
|
||||
|
@ -133,7 +137,7 @@ def app_fetchlist(url=None, name=None):
|
|||
else:
|
||||
appslists_to_be_fetched = appslists.keys()
|
||||
|
||||
import requests # lazy loading this module for performance reasons
|
||||
import requests # lazy loading this module for performance reasons
|
||||
# Fetch all appslists to be fetched
|
||||
for name in appslists_to_be_fetched:
|
||||
|
||||
|
@ -168,7 +172,7 @@ def app_fetchlist(url=None, name=None):
|
|||
appslist = appslist_request.text
|
||||
try:
|
||||
json.loads(appslist)
|
||||
except ValueError, e:
|
||||
except ValueError as e:
|
||||
logger.error(m18n.n('appslist_retrieve_bad_format',
|
||||
appslist=name))
|
||||
continue
|
||||
|
@ -179,9 +183,7 @@ def app_fetchlist(url=None, name=None):
|
|||
with open(list_file, "w") as f:
|
||||
f.write(appslist)
|
||||
except Exception as e:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
"Error while writing appslist %s: %s" %
|
||||
(name, str(e)))
|
||||
raise YunohostError("Error while writing appslist %s: %s" % (name, str(e)), raw_msg=True)
|
||||
|
||||
now = int(time.time())
|
||||
appslists[name]["lastUpdate"] = now
|
||||
|
@ -205,7 +207,7 @@ def app_removelist(operation_logger, name):
|
|||
|
||||
# Make sure we know this appslist
|
||||
if name not in appslists.keys():
|
||||
raise MoulinetteError(errno.ENOENT, m18n.n('appslist_unknown', appslist=name))
|
||||
raise YunohostError('appslist_unknown', appslist=name)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -336,8 +338,7 @@ def app_info(app, show_status=False, raw=False):
|
|||
|
||||
"""
|
||||
if not _is_installed(app):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_not_installed', app=app))
|
||||
raise YunohostError('app_not_installed', app=app)
|
||||
|
||||
app_setting_path = APPS_SETTING_PATH + app
|
||||
|
||||
|
@ -395,8 +396,7 @@ def app_map(app=None, raw=False, user=None):
|
|||
|
||||
if app is not None:
|
||||
if not _is_installed(app):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_not_installed', app=app))
|
||||
raise YunohostError('app_not_installed', app=app)
|
||||
apps = [app, ]
|
||||
else:
|
||||
apps = os.listdir(APPS_SETTING_PATH)
|
||||
|
@ -448,11 +448,10 @@ def app_change_url(operation_logger, auth, app, domain, path):
|
|||
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
raise MoulinetteError(errno.ENOPKG,
|
||||
m18n.n('app_not_installed', app=app))
|
||||
raise YunohostError('app_not_installed', app=app)
|
||||
|
||||
if not os.path.exists(os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url")):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n("app_change_no_change_url_script", app_name=app))
|
||||
raise YunohostError("app_change_no_change_url_script", app_name=app)
|
||||
|
||||
old_domain = app_setting(app, "domain")
|
||||
old_path = app_setting(app, "path")
|
||||
|
@ -464,7 +463,7 @@ def app_change_url(operation_logger, auth, app, domain, path):
|
|||
path = normalize_url_path(path)
|
||||
|
||||
if (domain, path) == (old_domain, old_path):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n("app_change_url_identical_domains", domain=domain, path=path))
|
||||
raise YunohostError("app_change_url_identical_domains", domain=domain, path=path)
|
||||
|
||||
# WARNING / FIXME : checkurl will modify the settings
|
||||
# (this is a non intuitive behavior that should be changed)
|
||||
|
@ -540,10 +539,10 @@ def app_change_url(operation_logger, auth, app, domain, path):
|
|||
stderr=subprocess.STDOUT,
|
||||
shell=True).rstrip()
|
||||
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors))
|
||||
raise YunohostError("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors)
|
||||
|
||||
logger.success(m18n.n("app_change_url_success",
|
||||
app=app, domain=domain, path=path))
|
||||
app=app, domain=domain, path=path))
|
||||
|
||||
hook_callback('post_app_change_url', args=args_list, env=env_dict)
|
||||
|
||||
|
@ -565,8 +564,8 @@ def app_upgrade(auth, app=[], url=None, file=None):
|
|||
|
||||
try:
|
||||
app_list()
|
||||
except MoulinetteError:
|
||||
raise MoulinetteError(errno.ENODATA, m18n.n('app_no_upgrade'))
|
||||
except YunohostError:
|
||||
raise YunohostError('app_no_upgrade')
|
||||
|
||||
upgraded_apps = []
|
||||
|
||||
|
@ -586,8 +585,7 @@ def app_upgrade(auth, app=[], url=None, file=None):
|
|||
logger.info(m18n.n('app_upgrade_app_name', app=app_instance_name))
|
||||
installed = _is_installed(app_instance_name)
|
||||
if not installed:
|
||||
raise MoulinetteError(errno.ENOPKG,
|
||||
m18n.n('app_not_installed', app=app_instance_name))
|
||||
raise YunohostError('app_not_installed', app=app_instance_name)
|
||||
|
||||
if app_instance_name in upgraded_apps:
|
||||
continue
|
||||
|
@ -677,7 +675,7 @@ def app_upgrade(auth, app=[], url=None, file=None):
|
|||
operation_logger.success()
|
||||
|
||||
if not upgraded_apps:
|
||||
raise MoulinetteError(errno.ENODATA, m18n.n('app_no_upgrade'))
|
||||
raise YunohostError('app_no_upgrade')
|
||||
|
||||
app_ssowatconf(auth)
|
||||
|
||||
|
@ -689,7 +687,7 @@ def app_upgrade(auth, app=[], url=None, file=None):
|
|||
|
||||
|
||||
@is_unit_operation()
|
||||
def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on_failure=False):
|
||||
def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on_failure=False, force=False):
|
||||
"""
|
||||
Install apps
|
||||
|
||||
|
@ -698,12 +696,11 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on
|
|||
label -- Custom name for the app
|
||||
args -- Serialize arguments for app installation
|
||||
no_remove_on_failure -- Debug option to avoid removing the app on a failed installation
|
||||
|
||||
force -- Do not ask for confirmation when installing experimental / low-quality apps
|
||||
"""
|
||||
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
||||
from yunohost.log import OperationLogger
|
||||
|
||||
|
||||
# Fetch or extract sources
|
||||
try:
|
||||
os.listdir(INSTALL_TMP)
|
||||
|
@ -718,17 +715,46 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on
|
|||
},
|
||||
}
|
||||
|
||||
if app in app_list(raw=True) or ('@' in app) or ('http://' in app) or ('https://' in app):
|
||||
def confirm_install(confirm):
|
||||
|
||||
# Ignore if there's nothing for confirm (good quality app), if --force is used
|
||||
# or if request on the API (confirm already implemented on the API side)
|
||||
if confirm is None or force or msettings.get('interface') == 'api':
|
||||
return
|
||||
|
||||
answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm,
|
||||
answers='Y/N'))
|
||||
if answer.upper() != "Y":
|
||||
raise YunohostError("aborting")
|
||||
|
||||
|
||||
raw_app_list = app_list(raw=True)
|
||||
if app in raw_app_list or ('@' in app) or ('http://' in app) or ('https://' in app):
|
||||
if app in raw_app_list:
|
||||
state = raw_app_list[app].get("state", "notworking")
|
||||
level = raw_app_list[app].get("level", None)
|
||||
confirm = "danger"
|
||||
if state in ["working", "validated"]:
|
||||
if isinstance(level, int) and level >= 3:
|
||||
confirm = None
|
||||
elif isinstance(level, int) and level > 0:
|
||||
confirm = "warning"
|
||||
else:
|
||||
confirm = "thirdparty"
|
||||
|
||||
confirm_install(confirm)
|
||||
|
||||
manifest, extracted_app_folder = _fetch_app_from_git(app)
|
||||
elif os.path.exists(app):
|
||||
confirm_install("thirdparty")
|
||||
manifest, extracted_app_folder = _extract_app_from_file(app)
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown'))
|
||||
raise YunohostError('app_unknown')
|
||||
status['remote'] = manifest.get('remote', {})
|
||||
|
||||
# Check ID
|
||||
if 'id' not in manifest or '__' in manifest['id']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('app_id_invalid'))
|
||||
raise YunohostError('app_id_invalid')
|
||||
|
||||
app_id = manifest['id']
|
||||
|
||||
|
@ -739,8 +765,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on
|
|||
instance_number = _installed_instance_number(app_id, last=True) + 1
|
||||
if instance_number > 1:
|
||||
if 'multi_instance' not in manifest or not is_true(manifest['multi_instance']):
|
||||
raise MoulinetteError(errno.EEXIST,
|
||||
m18n.n('app_already_installed', app=app_id))
|
||||
raise YunohostError('app_already_installed', app=app_id)
|
||||
|
||||
# Change app_id to the forked app id
|
||||
app_instance_name = app_id + '__' + str(instance_number)
|
||||
|
@ -761,7 +786,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on
|
|||
env_dict["YNH_APP_INSTANCE_NUMBER"] = str(instance_number)
|
||||
|
||||
# Start register change on system
|
||||
operation_logger.extra.update({'env':env_dict})
|
||||
operation_logger.extra.update({'env': env_dict})
|
||||
operation_logger.related_to = [s for s in operation_logger.related_to if s[0] != "app"]
|
||||
operation_logger.related_to.append(("app", app_id))
|
||||
operation_logger.start()
|
||||
|
@ -819,8 +844,8 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on
|
|||
|
||||
# Execute remove script
|
||||
operation_logger_remove = OperationLogger('remove_on_failed_install',
|
||||
[('app', app_instance_name)],
|
||||
env=env_dict_remove)
|
||||
[('app', app_instance_name)],
|
||||
env=env_dict_remove)
|
||||
operation_logger_remove.start()
|
||||
|
||||
remove_retcode = hook_exec(
|
||||
|
@ -843,9 +868,9 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on
|
|||
|
||||
if install_retcode == -1:
|
||||
msg = m18n.n('operation_interrupted') + " " + error_msg
|
||||
raise MoulinetteError(errno.EINTR, msg)
|
||||
raise YunohostError(msg, raw_msg=True)
|
||||
msg = error_msg
|
||||
raise MoulinetteError(errno.EIO, msg)
|
||||
raise YunohostError(msg, raw_msg=True)
|
||||
|
||||
# Clean hooks and add new ones
|
||||
hook_remove(app_instance_name)
|
||||
|
@ -881,8 +906,7 @@ def app_remove(operation_logger, auth, app):
|
|||
"""
|
||||
from yunohost.hook import hook_exec, hook_remove, hook_callback
|
||||
if not _is_installed(app):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_not_installed', app=app))
|
||||
raise YunohostError('app_not_installed', app=app)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -948,7 +972,6 @@ def app_addaccess(auth, apps, users=[]):
|
|||
|
||||
for app in apps:
|
||||
|
||||
|
||||
app_settings = _get_app_settings(app)
|
||||
if not app_settings:
|
||||
continue
|
||||
|
@ -961,7 +984,7 @@ def app_addaccess(auth, apps, users=[]):
|
|||
|
||||
# Start register change on system
|
||||
related_to = [('app', app)]
|
||||
operation_logger= OperationLogger('app_addaccess', related_to)
|
||||
operation_logger = OperationLogger('app_addaccess', related_to)
|
||||
operation_logger.start()
|
||||
|
||||
allowed_users = set()
|
||||
|
@ -972,7 +995,7 @@ def app_addaccess(auth, apps, users=[]):
|
|||
if allowed_user not in allowed_users:
|
||||
try:
|
||||
user_info(auth, allowed_user)
|
||||
except MoulinetteError:
|
||||
except YunohostError:
|
||||
logger.warning(m18n.n('user_unknown', user=allowed_user))
|
||||
continue
|
||||
allowed_users.add(allowed_user)
|
||||
|
@ -1024,7 +1047,7 @@ def app_removeaccess(auth, apps, users=[]):
|
|||
|
||||
# Start register change on system
|
||||
related_to = [('app', app)]
|
||||
operation_logger= OperationLogger('app_removeaccess', related_to)
|
||||
operation_logger = OperationLogger('app_removeaccess', related_to)
|
||||
operation_logger.start()
|
||||
|
||||
if remove_all:
|
||||
|
@ -1038,7 +1061,7 @@ def app_removeaccess(auth, apps, users=[]):
|
|||
if allowed_user not in users:
|
||||
allowed_users.add(allowed_user)
|
||||
|
||||
operation_logger.related_to += [ ('user', x) for x in allowed_users ]
|
||||
operation_logger.related_to += [('user', x) for x in allowed_users]
|
||||
operation_logger.flush()
|
||||
new_users = ','.join(allowed_users)
|
||||
app_setting(app, 'allowed_users', new_users)
|
||||
|
@ -1073,7 +1096,7 @@ def app_clearaccess(auth, apps):
|
|||
|
||||
# Start register change on system
|
||||
related_to = [('app', app)]
|
||||
operation_logger= OperationLogger('app_clearaccess', related_to)
|
||||
operation_logger = OperationLogger('app_clearaccess', related_to)
|
||||
operation_logger.start()
|
||||
|
||||
if 'mode' in app_settings:
|
||||
|
@ -1130,23 +1153,19 @@ def app_makedefault(operation_logger, auth, app, domain=None):
|
|||
|
||||
if domain is None:
|
||||
domain = app_domain
|
||||
operation_logger.related_to.append(('domain',domain))
|
||||
operation_logger.related_to.append(('domain', domain))
|
||||
elif domain not in domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
|
||||
raise YunohostError('domain_unknown')
|
||||
|
||||
operation_logger.start()
|
||||
if '/' in app_map(raw=True)[domain]:
|
||||
raise MoulinetteError(errno.EEXIST,
|
||||
m18n.n('app_make_default_location_already_used',
|
||||
app=app, domain=app_domain,
|
||||
other_app=app_map(raw=True)[domain]["/"]["id"]))
|
||||
raise YunohostError('app_make_default_location_already_used', app=app, domain=app_domain, other_app=app_map(raw=True)[domain]["/"]["id"])
|
||||
|
||||
try:
|
||||
with open('/etc/ssowat/conf.json.persistent') as json_conf:
|
||||
ssowat_conf = json.loads(str(json_conf.read()))
|
||||
except ValueError as e:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('ssowat_persistent_conf_read_error', error=e.strerror))
|
||||
raise YunohostError('ssowat_persistent_conf_read_error', error=e.strerror)
|
||||
except IOError:
|
||||
ssowat_conf = {}
|
||||
|
||||
|
@ -1159,8 +1178,7 @@ def app_makedefault(operation_logger, auth, app, domain=None):
|
|||
with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
|
||||
json.dump(ssowat_conf, f, sort_keys=True, indent=4)
|
||||
except IOError as e:
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('ssowat_persistent_conf_write_error', error=e.strerror))
|
||||
raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror)
|
||||
|
||||
os.system('chmod 644 /etc/ssowat/conf.json.persistent')
|
||||
|
||||
|
@ -1212,8 +1230,7 @@ def app_checkport(port):
|
|||
if tools_port_available(port):
|
||||
logger.success(m18n.n('port_available', port=int(port)))
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('port_unavailable', port=int(port)))
|
||||
raise YunohostError('port_unavailable', port=int(port))
|
||||
|
||||
|
||||
def app_register_url(auth, app, domain, path):
|
||||
|
@ -1228,7 +1245,7 @@ def app_register_url(auth, app, domain, path):
|
|||
|
||||
# This line can't be moved on top of file, otherwise it creates an infinite
|
||||
# loop of import with tools.py...
|
||||
from domain import _get_conflicting_apps, _normalize_domain_path
|
||||
from .domain import _get_conflicting_apps, _normalize_domain_path
|
||||
|
||||
domain, path = _normalize_domain_path(domain, path)
|
||||
|
||||
|
@ -1240,8 +1257,7 @@ def app_register_url(auth, app, domain, path):
|
|||
if installed:
|
||||
settings = _get_app_settings(app)
|
||||
if "path" in settings.keys() and "domain" in settings.keys():
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_already_installed_cant_change_url'))
|
||||
raise YunohostError('app_already_installed_cant_change_url')
|
||||
|
||||
# Check the url is available
|
||||
conflicts = _get_conflicting_apps(auth, domain, path)
|
||||
|
@ -1255,7 +1271,7 @@ def app_register_url(auth, app, domain, path):
|
|||
app_label=app_label,
|
||||
))
|
||||
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('app_location_unavailable', apps="\n".join(apps)))
|
||||
raise YunohostError('app_location_unavailable', apps="\n".join(apps))
|
||||
|
||||
app_setting(app, 'domain', value=domain)
|
||||
app_setting(app, 'path', value=path)
|
||||
|
@ -1293,7 +1309,7 @@ def app_checkurl(auth, url, app=None):
|
|||
apps_map = app_map(raw=True)
|
||||
|
||||
if domain not in domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
|
||||
raise YunohostError('domain_unknown')
|
||||
|
||||
if domain in apps_map:
|
||||
# Loop through apps
|
||||
|
@ -1303,14 +1319,10 @@ def app_checkurl(auth, url, app=None):
|
|||
installed = True
|
||||
continue
|
||||
if path == p:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_location_already_used',
|
||||
app=a["id"], path=path))
|
||||
raise YunohostError('app_location_already_used', app=a["id"], path=path)
|
||||
# can't install "/a/b/" if "/a/" exists
|
||||
elif path.startswith(p) or p.startswith(path):
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('app_location_install_failed',
|
||||
other_path=p, other_app=a['id']))
|
||||
raise YunohostError('app_location_install_failed', other_path=p, other_app=a['id'])
|
||||
|
||||
if app is not None and not installed:
|
||||
app_setting(app, 'domain', value=domain)
|
||||
|
@ -1342,10 +1354,10 @@ def app_initdb(user, password=None, db=None, sql=None):
|
|||
mysql_root_pwd = open('/etc/yunohost/mysql').read().rstrip()
|
||||
mysql_command = 'mysql -u root -p%s -e "CREATE DATABASE %s ; GRANT ALL PRIVILEGES ON %s.* TO \'%s\'@localhost IDENTIFIED BY \'%s\';"' % (mysql_root_pwd, db, db, user, password)
|
||||
if os.system(mysql_command) != 0:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('mysql_db_creation_failed'))
|
||||
raise YunohostError('mysql_db_creation_failed')
|
||||
if sql is not None:
|
||||
if os.system('mysql -u %s -p%s %s < %s' % (user, password, db, sql)) != 0:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('mysql_db_init_failed'))
|
||||
raise YunohostError('mysql_db_init_failed')
|
||||
|
||||
if return_pwd:
|
||||
return password
|
||||
|
@ -1451,8 +1463,7 @@ def app_ssowatconf(auth):
|
|||
def app_change_label(auth, app, new_label):
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
raise MoulinetteError(errno.ENOPKG,
|
||||
m18n.n('app_not_installed', app=app))
|
||||
raise YunohostError('app_not_installed', app=app)
|
||||
|
||||
app_setting(app, "label", value=new_label)
|
||||
|
||||
|
@ -1488,7 +1499,7 @@ def app_action_run(app, action, args=None):
|
|||
actions = {x["id"]: x for x in actions}
|
||||
|
||||
if action not in actions:
|
||||
raise MoulinetteError(errno.EINVAL, "action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys())))
|
||||
raise YunohostError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys())), raw_msg=True)
|
||||
|
||||
action_declaration = actions[action]
|
||||
|
||||
|
@ -1526,7 +1537,7 @@ def app_action_run(app, action, args=None):
|
|||
)
|
||||
|
||||
if retcode not in action_declaration.get("accepted_return_codes", [0]):
|
||||
raise MoulinetteError(retcode, "Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode))
|
||||
raise YunohostError("Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode), raw_msg=True)
|
||||
|
||||
os.remove(path)
|
||||
|
||||
|
@ -1585,10 +1596,10 @@ def app_config_show_panel(app):
|
|||
parsed_values[key] = value
|
||||
|
||||
return_code = hook_exec(config_script,
|
||||
args=["show"],
|
||||
env=env,
|
||||
stdout_callback=parse_stdout,
|
||||
)
|
||||
args=["show"],
|
||||
env=env,
|
||||
stdout_callback=parse_stdout,
|
||||
)
|
||||
|
||||
if return_code != 0:
|
||||
raise Exception("script/config show return value code: %s (considered as an error)", return_code)
|
||||
|
@ -1633,8 +1644,7 @@ def app_config_apply(app, args):
|
|||
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
raise MoulinetteError(errno.ENOPKG,
|
||||
m18n.n('app_not_installed', app=app))
|
||||
raise YunohostError('app_not_installed', app=app)
|
||||
|
||||
config_panel = os.path.join(APPS_SETTING_PATH, app, 'config_panel.json')
|
||||
config_script = os.path.join(APPS_SETTING_PATH, app, 'scripts', 'config')
|
||||
|
@ -1673,9 +1683,9 @@ def app_config_apply(app, args):
|
|||
logger.warning("Ignore key '%s' from arguments because it is not in the config", key)
|
||||
|
||||
return_code = hook_exec(config_script,
|
||||
args=["apply"],
|
||||
env=env,
|
||||
)
|
||||
args=["apply"],
|
||||
env=env,
|
||||
)
|
||||
|
||||
if return_code != 0:
|
||||
raise Exception("'script/config apply' return value code: %s (considered as an error)", return_code)
|
||||
|
@ -1692,8 +1702,7 @@ def _get_app_settings(app_id):
|
|||
|
||||
"""
|
||||
if not _is_installed(app_id):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_not_installed', app=app_id))
|
||||
raise YunohostError('app_not_installed', app=app_id)
|
||||
try:
|
||||
with open(os.path.join(
|
||||
APPS_SETTING_PATH, app_id, 'settings.yml')) as f:
|
||||
|
@ -1731,7 +1740,7 @@ def _get_app_status(app_id, format_date=False):
|
|||
"""
|
||||
app_setting_path = APPS_SETTING_PATH + app_id
|
||||
if not os.path.isdir(app_setting_path):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown'))
|
||||
raise YunohostError('app_unknown')
|
||||
status = {}
|
||||
|
||||
try:
|
||||
|
@ -1755,8 +1764,7 @@ def _get_app_status(app_id, format_date=False):
|
|||
if not v:
|
||||
status[f] = '-'
|
||||
else:
|
||||
status[f] = time.strftime(m18n.n('format_datetime_short'),
|
||||
time.gmtime(v))
|
||||
status[f] = datetime.utcfromtimestamp(v)
|
||||
return status
|
||||
|
||||
|
||||
|
@ -1797,7 +1805,7 @@ def _extract_app_from_file(path, remove=False):
|
|||
extract_result = 1
|
||||
|
||||
if extract_result != 0:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('app_extraction_failed'))
|
||||
raise YunohostError('app_extraction_failed')
|
||||
|
||||
try:
|
||||
extracted_app_folder = APP_TMP_FOLDER
|
||||
|
@ -1808,10 +1816,9 @@ def _extract_app_from_file(path, remove=False):
|
|||
manifest = json.loads(str(json_manifest.read()))
|
||||
manifest['lastUpdate'] = int(time.time())
|
||||
except IOError:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('app_install_files_invalid'))
|
||||
raise YunohostError('app_install_files_invalid')
|
||||
except ValueError as e:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_manifest_invalid', error=e.strerror))
|
||||
raise YunohostError('app_manifest_invalid', error=e.strerror)
|
||||
|
||||
logger.debug(m18n.n('done'))
|
||||
|
||||
|
@ -1879,8 +1886,7 @@ def _fetch_app_from_git(app):
|
|||
'wget', '-qO', app_tmp_archive, tarball_url])
|
||||
except subprocess.CalledProcessError:
|
||||
logger.exception('unable to download %s', tarball_url)
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('app_sources_fetch_failed'))
|
||||
raise YunohostError('app_sources_fetch_failed')
|
||||
else:
|
||||
manifest, extracted_app_folder = _extract_app_from_file(
|
||||
app_tmp_archive, remove=True)
|
||||
|
@ -1903,11 +1909,9 @@ def _fetch_app_from_git(app):
|
|||
with open(extracted_app_folder + '/manifest.json') as f:
|
||||
manifest = json.loads(str(f.read()))
|
||||
except subprocess.CalledProcessError:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('app_sources_fetch_failed'))
|
||||
raise YunohostError('app_sources_fetch_failed')
|
||||
except ValueError as e:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('app_manifest_invalid', error=e.strerror))
|
||||
raise YunohostError('app_manifest_invalid', error=e.strerror)
|
||||
else:
|
||||
logger.debug(m18n.n('done'))
|
||||
|
||||
|
@ -1927,11 +1931,10 @@ def _fetch_app_from_git(app):
|
|||
app_info['manifest']['lastUpdate'] = app_info['lastUpdate']
|
||||
manifest = app_info['manifest']
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown'))
|
||||
raise YunohostError('app_unknown')
|
||||
|
||||
if 'git' not in app_info:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_unsupported_remote_type'))
|
||||
raise YunohostError('app_unsupported_remote_type')
|
||||
url = app_info['git']['url']
|
||||
|
||||
if 'github.com' in url:
|
||||
|
@ -1943,8 +1946,7 @@ def _fetch_app_from_git(app):
|
|||
'wget', '-qO', app_tmp_archive, tarball_url])
|
||||
except subprocess.CalledProcessError:
|
||||
logger.exception('unable to download %s', tarball_url)
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('app_sources_fetch_failed'))
|
||||
raise YunohostError('app_sources_fetch_failed')
|
||||
else:
|
||||
manifest, extracted_app_folder = _extract_app_from_file(
|
||||
app_tmp_archive, remove=True)
|
||||
|
@ -1960,11 +1962,9 @@ def _fetch_app_from_git(app):
|
|||
with open(extracted_app_folder + '/manifest.json') as f:
|
||||
manifest = json.loads(str(f.read()))
|
||||
except subprocess.CalledProcessError:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('app_sources_fetch_failed'))
|
||||
raise YunohostError('app_sources_fetch_failed')
|
||||
except ValueError as e:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('app_manifest_invalid', error=e.strerror))
|
||||
raise YunohostError('app_manifest_invalid', error=e.strerror)
|
||||
else:
|
||||
logger.debug(m18n.n('done'))
|
||||
|
||||
|
@ -2083,7 +2083,7 @@ def _check_manifest_requirements(manifest, app_instance_name):
|
|||
yunohost_req = requirements.get('yunohost', None)
|
||||
if (not yunohost_req or
|
||||
not packages.SpecifierSet(yunohost_req) & '>= 2.3.6'):
|
||||
raise MoulinetteError(errno.EINVAL, '{0}{1}'.format(
|
||||
raise YunohostError('{0}{1}'.format(
|
||||
m18n.g('colon', m18n.n('app_incompatible'), app=app_instance_name),
|
||||
m18n.n('app_package_need_update', app=app_instance_name)))
|
||||
elif not requirements:
|
||||
|
@ -2096,18 +2096,15 @@ def _check_manifest_requirements(manifest, app_instance_name):
|
|||
versions = packages.get_installed_version(
|
||||
*requirements.keys(), strict=True, as_dict=True)
|
||||
except packages.PackageException as e:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_requirements_failed',
|
||||
error=str(e), app=app_instance_name))
|
||||
raise YunohostError('app_requirements_failed', error=str(e), app=app_instance_name)
|
||||
|
||||
# Iterate over requirements
|
||||
for pkgname, spec in requirements.items():
|
||||
version = versions[pkgname]
|
||||
if version not in packages.SpecifierSet(spec):
|
||||
raise MoulinetteError(
|
||||
errno.EINVAL, m18n.n('app_requirements_unmeet',
|
||||
pkgname=pkgname, version=version,
|
||||
spec=spec, app=app_instance_name))
|
||||
raise YunohostError('app_requirements_unmeet',
|
||||
pkgname=pkgname, version=version,
|
||||
spec=spec, app=app_instance_name)
|
||||
|
||||
|
||||
def _parse_args_from_manifest(manifest, action, args={}, auth=None):
|
||||
|
@ -2215,7 +2212,6 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None):
|
|||
elif arg_type == 'password':
|
||||
msignals.display(m18n.n('good_practices_about_user_password'))
|
||||
|
||||
|
||||
try:
|
||||
input_string = msignals.prompt(ask_string, is_password)
|
||||
except NotImplementedError:
|
||||
|
@ -2231,36 +2227,27 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None):
|
|||
# Validate argument value
|
||||
if (arg_value is None or arg_value == '') \
|
||||
and not arg.get('optional', False):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_argument_required', name=arg_name))
|
||||
raise YunohostError('app_argument_required', name=arg_name)
|
||||
elif arg_value is None:
|
||||
args_dict[arg_name] = ''
|
||||
continue
|
||||
|
||||
# Validate argument choice
|
||||
if arg_choices and arg_value not in arg_choices:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_argument_choice_invalid',
|
||||
name=arg_name, choices=', '.join(arg_choices)))
|
||||
raise YunohostError('app_argument_choice_invalid', name=arg_name, choices=', '.join(arg_choices))
|
||||
|
||||
# Validate argument type
|
||||
if arg_type == 'domain':
|
||||
if arg_value not in domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_argument_invalid',
|
||||
name=arg_name, error=m18n.n('domain_unknown')))
|
||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown'))
|
||||
elif arg_type == 'user':
|
||||
try:
|
||||
user_info(auth, arg_value)
|
||||
except MoulinetteError as e:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_argument_invalid',
|
||||
name=arg_name, error=e.strerror))
|
||||
except YunohostError as e:
|
||||
raise YunohostError('app_argument_invalid', name=arg_name, error=e.strerror)
|
||||
elif arg_type == 'app':
|
||||
if not _is_installed(arg_value):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_argument_invalid',
|
||||
name=arg_name, error=m18n.n('app_unknown')))
|
||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('app_unknown'))
|
||||
elif arg_type == 'boolean':
|
||||
if isinstance(arg_value, bool):
|
||||
arg_value = 1 if arg_value else 0
|
||||
|
@ -2270,9 +2257,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None):
|
|||
elif str(arg_value).lower() in ["0", "no", "n"]:
|
||||
arg_value = 0
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('app_argument_choice_invalid',
|
||||
name=arg_name, choices='yes, no, y, n, 1, 0'))
|
||||
raise YunohostError('app_argument_choice_invalid', name=arg_name, choices='yes, no, y, n, 1, 0')
|
||||
elif arg_type == 'password':
|
||||
from yunohost.utils.password import assert_password_is_strong_enough
|
||||
assert_password_is_strong_enough('user', arg_value)
|
||||
|
@ -2306,7 +2291,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None):
|
|||
app_label=app_label,
|
||||
))
|
||||
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('app_location_unavailable', apps="\n".join(apps)))
|
||||
raise YunohostError('app_location_unavailable', apps="\n".join(apps))
|
||||
|
||||
# (We save this normalized path so that the install script have a
|
||||
# standard path format to deal with no matter what the user inputted)
|
||||
|
@ -2426,7 +2411,7 @@ def _install_appslist_fetch_cron():
|
|||
with open(cron_job_file, "w") as f:
|
||||
f.write('\n'.join(cron_job))
|
||||
|
||||
_set_permissions(cron_job_file, "root", "root", 0755)
|
||||
_set_permissions(cron_job_file, "root", "root", 0o755)
|
||||
|
||||
|
||||
# FIXME - Duplicate from certificate.py, should be moved into a common helper
|
||||
|
@ -2456,8 +2441,7 @@ def _read_appslist_list():
|
|||
try:
|
||||
appslists = json.loads(appslists_json)
|
||||
except ValueError:
|
||||
raise MoulinetteError(errno.EBADR,
|
||||
m18n.n('appslist_corrupted_json', filename=APPSLISTS_JSON))
|
||||
raise YunohostError('appslist_corrupted_json', filename=APPSLISTS_JSON)
|
||||
|
||||
return appslists
|
||||
|
||||
|
@ -2472,9 +2456,8 @@ def _write_appslist_list(appslist_lists):
|
|||
with open(APPSLISTS_JSON, "w") as f:
|
||||
json.dump(appslist_lists, f)
|
||||
except Exception as e:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
"Error while writing list of appslist %s: %s" %
|
||||
(APPSLISTS_JSON, str(e)))
|
||||
raise YunohostError("Error while writing list of appslist %s: %s" %
|
||||
(APPSLISTS_JSON, str(e)), raw_msg=True)
|
||||
|
||||
|
||||
def _register_new_appslist(url, name):
|
||||
|
@ -2487,15 +2470,13 @@ def _register_new_appslist(url, name):
|
|||
|
||||
# Check if name conflicts with an existing list
|
||||
if name in appslist_list:
|
||||
raise MoulinetteError(errno.EEXIST,
|
||||
m18n.n('appslist_name_already_tracked', name=name))
|
||||
raise YunohostError('appslist_name_already_tracked', name=name)
|
||||
|
||||
# Check if url conflicts with an existing list
|
||||
known_appslist_urls = [appslist["url"] for _, appslist in appslist_list.items()]
|
||||
|
||||
if url in known_appslist_urls:
|
||||
raise MoulinetteError(errno.EEXIST,
|
||||
m18n.n('appslist_url_already_tracked', url=url))
|
||||
raise YunohostError('appslist_url_already_tracked', url=url)
|
||||
|
||||
logger.debug("Registering new appslist %s at %s" % (name, url))
|
||||
|
||||
|
@ -2586,7 +2567,7 @@ def _patch_php5(app_folder):
|
|||
continue
|
||||
|
||||
c = "sed -i -e 's@/etc/php5@/etc/php/7.0@g' " \
|
||||
"-e 's@/var/run/php5-fpm@/var/run/php/php7.0-fpm@g' " \
|
||||
"-e 's@php5@php7.0@g' " \
|
||||
"%s" % filename
|
||||
"-e 's@/var/run/php5-fpm@/var/run/php/php7.0-fpm@g' " \
|
||||
"-e 's@php5@php7.0@g' " \
|
||||
"%s" % filename
|
||||
os.system(c)
|
||||
|
|
|
@ -26,18 +26,18 @@
|
|||
import os
|
||||
import re
|
||||
import json
|
||||
import errno
|
||||
import time
|
||||
import tarfile
|
||||
import shutil
|
||||
import subprocess
|
||||
import csv
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from glob import glob
|
||||
from collections import OrderedDict
|
||||
|
||||
from moulinette import msignals, m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils import filesystem
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_file
|
||||
|
@ -52,6 +52,7 @@ from yunohost.monitor import binary_to_human
|
|||
from yunohost.tools import tools_postinstall
|
||||
from yunohost.service import service_regen_conf
|
||||
from yunohost.log import OperationLogger
|
||||
from functools import reduce
|
||||
|
||||
BACKUP_PATH = '/home/yunohost.backup'
|
||||
ARCHIVES_PATH = '%s/archives' % BACKUP_PATH
|
||||
|
@ -63,6 +64,7 @@ logger = getActionLogger('yunohost.backup')
|
|||
|
||||
|
||||
class BackupRestoreTargetsManager(object):
|
||||
|
||||
"""
|
||||
BackupRestoreTargetsManager manage the targets
|
||||
in BackupManager and RestoreManager
|
||||
|
@ -176,6 +178,7 @@ class BackupRestoreTargetsManager(object):
|
|||
|
||||
|
||||
class BackupManager():
|
||||
|
||||
"""
|
||||
This class collect files to backup in a list and apply one or several
|
||||
backup method on it.
|
||||
|
@ -267,9 +270,9 @@ class BackupManager():
|
|||
self.work_dir = os.path.join(BACKUP_PATH, 'tmp', name)
|
||||
self._init_work_dir()
|
||||
|
||||
###########################################################################
|
||||
# Misc helpers #
|
||||
###########################################################################
|
||||
#
|
||||
# Misc helpers #
|
||||
#
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
|
@ -299,7 +302,7 @@ class BackupManager():
|
|||
(string) A backup name created from current date 'YYMMDD-HHMMSS'
|
||||
"""
|
||||
# FIXME: case where this name already exist
|
||||
return time.strftime('%Y%m%d-%H%M%S')
|
||||
return time.strftime('%Y%m%d-%H%M%S', time.gmtime())
|
||||
|
||||
def _init_work_dir(self):
|
||||
"""Initialize preparation directory
|
||||
|
@ -307,31 +310,30 @@ class BackupManager():
|
|||
Ensure the working directory exists and is empty
|
||||
|
||||
exception:
|
||||
backup_output_directory_not_empty -- (MoulinetteError) Raised if the
|
||||
backup_output_directory_not_empty -- (YunohostError) Raised if the
|
||||
directory was given by the user and isn't empty
|
||||
|
||||
(TODO) backup_cant_clean_tmp_working_directory -- (MoulinetteError)
|
||||
(TODO) backup_cant_clean_tmp_working_directory -- (YunohostError)
|
||||
Raised if the working directory isn't empty, is temporary and can't
|
||||
be automaticcaly cleaned
|
||||
|
||||
(TODO) backup_cant_create_working_directory -- (MoulinetteError) Raised
|
||||
(TODO) backup_cant_create_working_directory -- (YunohostError) Raised
|
||||
if iyunohost can't create the working directory
|
||||
"""
|
||||
|
||||
# FIXME replace isdir by exists ? manage better the case where the path
|
||||
# exists
|
||||
if not os.path.isdir(self.work_dir):
|
||||
filesystem.mkdir(self.work_dir, 0750, parents=True, uid='admin')
|
||||
filesystem.mkdir(self.work_dir, 0o750, parents=True, uid='admin')
|
||||
elif self.is_tmp_work_dir:
|
||||
logger.debug("temporary directory for backup '%s' already exists",
|
||||
self.work_dir)
|
||||
# FIXME May be we should clean the workdir here
|
||||
raise MoulinetteError(
|
||||
errno.EIO, m18n.n('backup_output_directory_not_empty'))
|
||||
raise YunohostError('backup_output_directory_not_empty')
|
||||
|
||||
###########################################################################
|
||||
# Backup target management #
|
||||
###########################################################################
|
||||
#
|
||||
# Backup target management #
|
||||
#
|
||||
|
||||
def set_system_targets(self, system_parts=[]):
|
||||
"""
|
||||
|
@ -381,9 +383,9 @@ class BackupManager():
|
|||
logger.warning(m18n.n('backup_with_no_restore_script_for_app', app=app))
|
||||
self.targets.set_result("apps", app, "Warning")
|
||||
|
||||
###########################################################################
|
||||
# Management of files to backup / "The CSV" #
|
||||
###########################################################################
|
||||
#
|
||||
# Management of files to backup / "The CSV" #
|
||||
#
|
||||
|
||||
def _import_to_list_to_backup(self, tmp_csv):
|
||||
"""
|
||||
|
@ -466,9 +468,9 @@ class BackupManager():
|
|||
logger.error(m18n.n('backup_csv_addition_failed'))
|
||||
self.csv_file.close()
|
||||
|
||||
###########################################################################
|
||||
# File collection from system parts and apps #
|
||||
###########################################################################
|
||||
#
|
||||
# File collection from system parts and apps #
|
||||
#
|
||||
|
||||
def collect_files(self):
|
||||
"""
|
||||
|
@ -493,7 +495,7 @@ class BackupManager():
|
|||
copied here
|
||||
|
||||
Exceptions:
|
||||
"backup_nothings_done" -- (MoulinetteError) This exception is raised if
|
||||
"backup_nothings_done" -- (YunohostError) This exception is raised if
|
||||
nothing has been listed.
|
||||
"""
|
||||
|
||||
|
@ -506,7 +508,7 @@ class BackupManager():
|
|||
|
||||
if not successfull_apps and not successfull_system:
|
||||
filesystem.rm(self.work_dir, True, True)
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('backup_nothings_done'))
|
||||
raise YunohostError('backup_nothings_done')
|
||||
|
||||
# Add unlisted files from backup tmp dir
|
||||
self._add_to_list_to_backup('backup.csv')
|
||||
|
@ -603,7 +605,7 @@ class BackupManager():
|
|||
|
||||
restore_hooks_dir = os.path.join(self.work_dir, "hooks", "restore")
|
||||
if not os.path.exists(restore_hooks_dir):
|
||||
filesystem.mkdir(restore_hooks_dir, mode=0750,
|
||||
filesystem.mkdir(restore_hooks_dir, mode=0o750,
|
||||
parents=True, uid='admin')
|
||||
|
||||
restore_hooks = hook_list("restore")["hooks"]
|
||||
|
@ -669,7 +671,7 @@ class BackupManager():
|
|||
logger.debug(m18n.n('backup_running_app_script', app=app))
|
||||
try:
|
||||
# Prepare backup directory for the app
|
||||
filesystem.mkdir(tmp_app_bkp_dir, 0750, True, uid='admin')
|
||||
filesystem.mkdir(tmp_app_bkp_dir, 0o750, True, uid='admin')
|
||||
|
||||
# Copy the app settings to be able to call _common.sh
|
||||
shutil.copytree(app_setting_path, settings_dir)
|
||||
|
@ -703,9 +705,9 @@ class BackupManager():
|
|||
filesystem.rm(tmp_script, force=True)
|
||||
filesystem.rm(env_dict["YNH_BACKUP_CSV"], force=True)
|
||||
|
||||
###########################################################################
|
||||
# Actual backup archive creation / method management #
|
||||
###########################################################################
|
||||
#
|
||||
# Actual backup archive creation / method management #
|
||||
#
|
||||
|
||||
def add(self, method):
|
||||
"""
|
||||
|
@ -777,6 +779,7 @@ class BackupManager():
|
|||
|
||||
|
||||
class RestoreManager():
|
||||
|
||||
"""
|
||||
RestoreManager allow to restore a past backup archive
|
||||
|
||||
|
@ -825,9 +828,9 @@ class RestoreManager():
|
|||
self.method = BackupMethod.create(method)
|
||||
self.targets = BackupRestoreTargetsManager()
|
||||
|
||||
###########################################################################
|
||||
# Misc helpers #
|
||||
###########################################################################
|
||||
#
|
||||
# Misc helpers #
|
||||
#
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
|
@ -856,10 +859,10 @@ class RestoreManager():
|
|||
self.info["system"] = self.info["hooks"]
|
||||
except IOError:
|
||||
logger.debug("unable to load '%s'", info_file, exc_info=1)
|
||||
raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))
|
||||
raise YunohostError('backup_invalid_archive')
|
||||
else:
|
||||
logger.debug("restoring from backup '%s' created on %s", self.name,
|
||||
time.ctime(self.info['created_at']))
|
||||
datetime.utcfromtimestamp(self.info['created_at']))
|
||||
|
||||
def _postinstall_if_needed(self):
|
||||
"""
|
||||
|
@ -877,10 +880,9 @@ class RestoreManager():
|
|||
domain = f.readline().rstrip()
|
||||
except IOError:
|
||||
logger.debug("unable to retrieve current_host from the backup",
|
||||
exc_info=1)
|
||||
exc_info=1)
|
||||
# FIXME include the current_host by default ?
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_invalid_archive'))
|
||||
raise YunohostError('backup_invalid_archive')
|
||||
|
||||
logger.debug("executing the post-install...")
|
||||
tools_postinstall(domain, 'yunohost', True)
|
||||
|
@ -904,9 +906,9 @@ class RestoreManager():
|
|||
logger.warning(m18n.n('restore_cleaning_failed'))
|
||||
filesystem.rm(self.work_dir, True, True)
|
||||
|
||||
###########################################################################
|
||||
# Restore target manangement #
|
||||
###########################################################################
|
||||
#
|
||||
# Restore target manangement #
|
||||
#
|
||||
|
||||
def set_system_targets(self, system_parts=[]):
|
||||
"""
|
||||
|
@ -982,9 +984,9 @@ class RestoreManager():
|
|||
self.info['apps'].keys(),
|
||||
unknown_error)
|
||||
|
||||
###########################################################################
|
||||
# Archive mounting #
|
||||
###########################################################################
|
||||
#
|
||||
# Archive mounting #
|
||||
#
|
||||
|
||||
def mount(self):
|
||||
"""
|
||||
|
@ -1009,8 +1011,7 @@ class RestoreManager():
|
|||
subprocess.call(['rmdir', self.work_dir])
|
||||
logger.debug("Unmount dir: {}".format(self.work_dir))
|
||||
else:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('restore_removing_tmp_dir_failed'))
|
||||
raise YunohostError('restore_removing_tmp_dir_failed')
|
||||
elif os.path.isdir(self.work_dir):
|
||||
logger.debug("temporary restore directory '%s' already exists",
|
||||
self.work_dir)
|
||||
|
@ -1018,8 +1019,7 @@ class RestoreManager():
|
|||
if ret == 0:
|
||||
logger.debug("Delete dir: {}".format(self.work_dir))
|
||||
else:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('restore_removing_tmp_dir_failed'))
|
||||
raise YunohostError('restore_removing_tmp_dir_failed')
|
||||
|
||||
filesystem.mkdir(self.work_dir, parents=True)
|
||||
|
||||
|
@ -1027,9 +1027,9 @@ class RestoreManager():
|
|||
|
||||
self._read_info_files()
|
||||
|
||||
###########################################################################
|
||||
# Space computation / checks #
|
||||
###########################################################################
|
||||
#
|
||||
# Space computation / checks #
|
||||
#
|
||||
|
||||
def _compute_needed_space(self):
|
||||
"""
|
||||
|
@ -1086,21 +1086,13 @@ class RestoreManager():
|
|||
return True
|
||||
elif free_space > needed_space:
|
||||
# TODO Add --force options to avoid the error raising
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('restore_may_be_not_enough_disk_space',
|
||||
free_space=free_space,
|
||||
needed_space=needed_space,
|
||||
margin=margin))
|
||||
raise YunohostError('restore_may_be_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin)
|
||||
else:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('restore_not_enough_disk_space',
|
||||
free_space=free_space,
|
||||
needed_space=needed_space,
|
||||
margin=margin))
|
||||
raise YunohostError('restore_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin)
|
||||
|
||||
###########################################################################
|
||||
# "Actual restore" (reverse step of the backup collect part) #
|
||||
###########################################################################
|
||||
#
|
||||
# "Actual restore" (reverse step of the backup collect part) #
|
||||
#
|
||||
|
||||
def restore(self):
|
||||
"""
|
||||
|
@ -1116,7 +1108,6 @@ class RestoreManager():
|
|||
# Apply dirty patch to redirect php5 file on php7
|
||||
self._patch_backup_csv_file()
|
||||
|
||||
|
||||
self._restore_system()
|
||||
self._restore_apps()
|
||||
finally:
|
||||
|
@ -1142,13 +1133,11 @@ class RestoreManager():
|
|||
contains_php5 = True
|
||||
row['source'] = row['source'].replace('/etc/php5', '/etc/php/7.0') \
|
||||
.replace('/var/run/php5-fpm', '/var/run/php/php7.0-fpm') \
|
||||
.replace('php5','php7')
|
||||
.replace('php5', 'php7')
|
||||
|
||||
newlines.append(row)
|
||||
except (IOError, OSError, csv.Error) as e:
|
||||
raise MoulinetteError(errno.EIO,m18n.n('error_reading_file',
|
||||
file=backup_csv,
|
||||
error=str(e)))
|
||||
raise YunohostError('error_reading_file', file=backup_csv, error=str(e))
|
||||
|
||||
if not contains_php5:
|
||||
return
|
||||
|
@ -1274,7 +1263,7 @@ class RestoreManager():
|
|||
|
||||
# Check if the app has a restore script
|
||||
app_restore_script_in_archive = os.path.join(app_scripts_in_archive,
|
||||
'restore')
|
||||
'restore')
|
||||
if not os.path.isfile(app_restore_script_in_archive):
|
||||
logger.warning(m18n.n('unrestore_app', app=app_instance_name))
|
||||
self.targets.set_result("apps", app_instance_name, "Warning")
|
||||
|
@ -1287,7 +1276,7 @@ class RestoreManager():
|
|||
app_instance_name)
|
||||
app_scripts_new_path = os.path.join(app_settings_new_path, 'scripts')
|
||||
shutil.copytree(app_settings_in_archive, app_settings_new_path)
|
||||
filesystem.chmod(app_settings_new_path, 0400, 0400, True)
|
||||
filesystem.chmod(app_settings_new_path, 0o400, 0o400, True)
|
||||
filesystem.chown(app_scripts_new_path, 'admin', None, True)
|
||||
|
||||
# Copy the app scripts to a writable temporary folder
|
||||
|
@ -1295,7 +1284,7 @@ class RestoreManager():
|
|||
# in the backup method ?
|
||||
tmp_folder_for_app_restore = tempfile.mkdtemp(prefix='restore')
|
||||
copytree(app_scripts_in_archive, tmp_folder_for_app_restore)
|
||||
filesystem.chmod(tmp_folder_for_app_restore, 0550, 0550, True)
|
||||
filesystem.chmod(tmp_folder_for_app_restore, 0o550, 0o550, True)
|
||||
filesystem.chown(tmp_folder_for_app_restore, 'admin', None, True)
|
||||
restore_script = os.path.join(tmp_folder_for_app_restore, 'restore')
|
||||
|
||||
|
@ -1312,7 +1301,7 @@ class RestoreManager():
|
|||
raise_on_error=True,
|
||||
env=env_dict)
|
||||
except:
|
||||
msg = m18n.n('restore_app_failed',app=app_instance_name)
|
||||
msg = m18n.n('restore_app_failed', app=app_instance_name)
|
||||
logger.exception(msg)
|
||||
operation_logger.error(msg)
|
||||
|
||||
|
@ -1328,8 +1317,8 @@ class RestoreManager():
|
|||
env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)
|
||||
|
||||
operation_logger = OperationLogger('remove_on_failed_restore',
|
||||
[('app', app_instance_name)],
|
||||
env=env_dict_remove)
|
||||
[('app', app_instance_name)],
|
||||
env=env_dict_remove)
|
||||
operation_logger.start()
|
||||
|
||||
# Execute remove script
|
||||
|
@ -1373,12 +1362,13 @@ class RestoreManager():
|
|||
|
||||
return env_var
|
||||
|
||||
###############################################################################
|
||||
# Backup methods #
|
||||
###############################################################################
|
||||
#
|
||||
# Backup methods #
|
||||
#
|
||||
|
||||
|
||||
class BackupMethod(object):
|
||||
|
||||
"""
|
||||
BackupMethod is an abstract class that represents a way to backup and
|
||||
restore a list of files.
|
||||
|
@ -1441,7 +1431,7 @@ class BackupMethod(object):
|
|||
@property
|
||||
def method_name(self):
|
||||
"""Return the string name of a BackupMethod (eg "tar" or "copy")"""
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('backup_abstract_method'))
|
||||
raise YunohostError('backup_abstract_method')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -1523,8 +1513,7 @@ class BackupMethod(object):
|
|||
"""
|
||||
if self.need_mount():
|
||||
if self._recursive_umount(self.work_dir) > 0:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('backup_cleaning_failed'))
|
||||
raise YunohostError('backup_cleaning_failed')
|
||||
|
||||
if self.manager.is_tmp_work_dir:
|
||||
filesystem.rm(self.work_dir, True, True)
|
||||
|
@ -1566,8 +1555,7 @@ class BackupMethod(object):
|
|||
if free_space < backup_size:
|
||||
logger.debug('Not enough space at %s (free: %s / needed: %d)',
|
||||
self.repo, free_space, backup_size)
|
||||
raise MoulinetteError(errno.EIO, m18n.n(
|
||||
'not_enough_disk_space', path=self.repo))
|
||||
raise YunohostError('not_enough_disk_space', path=self.repo)
|
||||
|
||||
def _organize_files(self):
|
||||
"""
|
||||
|
@ -1653,18 +1641,16 @@ class BackupMethod(object):
|
|||
if size > MB_ALLOWED_TO_ORGANIZE:
|
||||
try:
|
||||
i = msignals.prompt(m18n.n('backup_ask_for_copying_if_needed',
|
||||
answers='y/N', size=str(size)))
|
||||
answers='y/N', size=str(size)))
|
||||
except NotImplemented:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_unable_to_organize_files'))
|
||||
raise YunohostError('backup_unable_to_organize_files')
|
||||
else:
|
||||
if i != 'y' and i != 'Y':
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_unable_to_organize_files'))
|
||||
raise YunohostError('backup_unable_to_organize_files')
|
||||
|
||||
# Copy unbinded path
|
||||
logger.debug(m18n.n('backup_copying_to_organize_the_archive',
|
||||
size=str(size)))
|
||||
size=str(size)))
|
||||
for path in paths_needed_to_be_copied:
|
||||
dest = os.path.join(self.work_dir, path['dest'])
|
||||
if os.path.isdir(path['source']):
|
||||
|
@ -1704,6 +1690,7 @@ class BackupMethod(object):
|
|||
|
||||
|
||||
class CopyBackupMethod(BackupMethod):
|
||||
|
||||
"""
|
||||
This class just do an uncompress copy of each file in a location, and
|
||||
could be the inverse for restoring
|
||||
|
@ -1730,7 +1717,7 @@ class CopyBackupMethod(BackupMethod):
|
|||
|
||||
dest_parent = os.path.dirname(dest)
|
||||
if not os.path.exists(dest_parent):
|
||||
filesystem.mkdir(dest_parent, 0750, True, uid='admin')
|
||||
filesystem.mkdir(dest_parent, 0o750, True, uid='admin')
|
||||
|
||||
if os.path.isdir(source):
|
||||
shutil.copytree(source, dest)
|
||||
|
@ -1750,8 +1737,7 @@ class CopyBackupMethod(BackupMethod):
|
|||
super(CopyBackupMethod, self).mount()
|
||||
|
||||
if not os.path.isdir(self.repo):
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_no_uncompress_archive_dir'))
|
||||
raise YunohostError('backup_no_uncompress_archive_dir')
|
||||
|
||||
filesystem.mkdir(self.work_dir, parent=True)
|
||||
ret = subprocess.call(["mount", "-r", "--rbind", self.repo,
|
||||
|
@ -1762,11 +1748,11 @@ class CopyBackupMethod(BackupMethod):
|
|||
logger.warning(m18n.n("bind_mouting_disable"))
|
||||
subprocess.call(["mountpoint", "-q", dest,
|
||||
"&&", "umount", "-R", dest])
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_cant_mount_uncompress_archive'))
|
||||
raise YunohostError('backup_cant_mount_uncompress_archive')
|
||||
|
||||
|
||||
class TarBackupMethod(BackupMethod):
|
||||
|
||||
"""
|
||||
This class compress all files to backup in archive.
|
||||
"""
|
||||
|
@ -1797,7 +1783,7 @@ class TarBackupMethod(BackupMethod):
|
|||
"""
|
||||
|
||||
if not os.path.exists(self.repo):
|
||||
filesystem.mkdir(self.repo, 0750, parents=True, uid='admin')
|
||||
filesystem.mkdir(self.repo, 0o750, parents=True, uid='admin')
|
||||
|
||||
# Check free space in output
|
||||
self._check_is_enough_free_space()
|
||||
|
@ -1808,8 +1794,7 @@ class TarBackupMethod(BackupMethod):
|
|||
except:
|
||||
logger.debug("unable to open '%s' for writing",
|
||||
self._archive_file, exc_info=1)
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_archive_open_failed'))
|
||||
raise YunohostError('backup_archive_open_failed')
|
||||
|
||||
# Add files to the archive
|
||||
try:
|
||||
|
@ -1820,8 +1805,7 @@ class TarBackupMethod(BackupMethod):
|
|||
tar.close()
|
||||
except IOError:
|
||||
logger.error(m18n.n('backup_archive_writing_error'), exc_info=1)
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_creation_failed'))
|
||||
raise YunohostError('backup_creation_failed')
|
||||
|
||||
# Move info file
|
||||
shutil.copy(os.path.join(self.work_dir, 'info.json'),
|
||||
|
@ -1849,8 +1833,7 @@ class TarBackupMethod(BackupMethod):
|
|||
except:
|
||||
logger.debug("cannot open backup archive '%s'",
|
||||
self._archive_file, exc_info=1)
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_archive_open_failed'))
|
||||
raise YunohostError('backup_archive_open_failed')
|
||||
tar.close()
|
||||
|
||||
# Mount the tarball
|
||||
|
@ -1911,15 +1894,14 @@ class BorgBackupMethod(BackupMethod):
|
|||
super(CopyBackupMethod, self).backup()
|
||||
|
||||
# TODO run borg create command
|
||||
raise MoulinetteError(
|
||||
errno.EIO, m18n.n('backup_borg_not_implemented'))
|
||||
raise YunohostError('backup_borg_not_implemented')
|
||||
|
||||
def mount(self, mnt_path):
|
||||
raise MoulinetteError(
|
||||
errno.EIO, m18n.n('backup_borg_not_implemented'))
|
||||
raise YunohostError('backup_borg_not_implemented')
|
||||
|
||||
|
||||
class CustomBackupMethod(BackupMethod):
|
||||
|
||||
"""
|
||||
This class use a bash script/hook "backup_method" to do the
|
||||
backup/restore operations. A user can add his own hook inside
|
||||
|
@ -1962,8 +1944,7 @@ class CustomBackupMethod(BackupMethod):
|
|||
ret = hook_callback('backup_method', [self.method],
|
||||
args=self._get_args('backup'))
|
||||
if ret['failed']:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_custom_backup_error'))
|
||||
raise YunohostError('backup_custom_backup_error')
|
||||
|
||||
def mount(self, restore_manager):
|
||||
"""
|
||||
|
@ -1976,8 +1957,7 @@ class CustomBackupMethod(BackupMethod):
|
|||
ret = hook_callback('backup_method', [self.method],
|
||||
args=self._get_args('mount'))
|
||||
if ret['failed']:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_custom_mount_error'))
|
||||
raise YunohostError('backup_custom_mount_error')
|
||||
|
||||
def _get_args(self, action):
|
||||
"""Return the arguments to give to the custom script"""
|
||||
|
@ -1985,9 +1965,9 @@ class CustomBackupMethod(BackupMethod):
|
|||
self.manager.description]
|
||||
|
||||
|
||||
###############################################################################
|
||||
# "Front-end" #
|
||||
###############################################################################
|
||||
#
|
||||
# "Front-end" #
|
||||
#
|
||||
|
||||
def backup_create(name=None, description=None, methods=[],
|
||||
output_directory=None, no_compress=False,
|
||||
|
@ -2007,14 +1987,13 @@ def backup_create(name=None, description=None, methods=[],
|
|||
|
||||
# TODO: Add a 'clean' argument to clean output directory
|
||||
|
||||
###########################################################################
|
||||
# Validate / parse arguments #
|
||||
###########################################################################
|
||||
#
|
||||
# Validate / parse arguments #
|
||||
#
|
||||
|
||||
# Validate there is no archive with the same name
|
||||
if name and name in backup_list()['archives']:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('backup_archive_name_exists'))
|
||||
raise YunohostError('backup_archive_name_exists')
|
||||
|
||||
# Validate output_directory option
|
||||
if output_directory:
|
||||
|
@ -2023,18 +2002,15 @@ def backup_create(name=None, description=None, methods=[],
|
|||
# Check for forbidden folders
|
||||
if output_directory.startswith(ARCHIVES_PATH) or \
|
||||
re.match(r'^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$',
|
||||
output_directory):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('backup_output_directory_forbidden'))
|
||||
output_directory):
|
||||
raise YunohostError('backup_output_directory_forbidden')
|
||||
|
||||
# Check that output directory is empty
|
||||
if os.path.isdir(output_directory) and no_compress and \
|
||||
os.listdir(output_directory):
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_output_directory_not_empty'))
|
||||
raise YunohostError('backup_output_directory_not_empty')
|
||||
elif no_compress:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('backup_output_directory_required'))
|
||||
raise YunohostError('backup_output_directory_required')
|
||||
|
||||
# Define methods (retro-compat)
|
||||
if not methods:
|
||||
|
@ -2048,9 +2024,9 @@ def backup_create(name=None, description=None, methods=[],
|
|||
system = []
|
||||
apps = []
|
||||
|
||||
###########################################################################
|
||||
# Intialize #
|
||||
###########################################################################
|
||||
#
|
||||
# Intialize #
|
||||
#
|
||||
|
||||
# Create yunohost archives directory if it does not exists
|
||||
_create_archive_dir()
|
||||
|
@ -2075,9 +2051,9 @@ def backup_create(name=None, description=None, methods=[],
|
|||
backup_manager.set_system_targets(system)
|
||||
backup_manager.set_apps_targets(apps)
|
||||
|
||||
###########################################################################
|
||||
# Collect files and put them in the archive #
|
||||
###########################################################################
|
||||
#
|
||||
# Collect files and put them in the archive #
|
||||
#
|
||||
|
||||
# Collect files to be backup (by calling app backup script / system hooks)
|
||||
backup_manager.collect_files()
|
||||
|
@ -2105,9 +2081,9 @@ def backup_restore(auth, name, system=[], apps=[], force=False):
|
|||
apps -- List of application names to restore
|
||||
"""
|
||||
|
||||
###########################################################################
|
||||
# Validate / parse arguments #
|
||||
###########################################################################
|
||||
#
|
||||
# Validate / parse arguments #
|
||||
#
|
||||
|
||||
# If no --system or --apps given, restore everything
|
||||
if system is None and apps is None:
|
||||
|
@ -2131,14 +2107,14 @@ def backup_restore(auth, name, system=[], apps=[], force=False):
|
|||
if i == 'y' or i == 'Y':
|
||||
force = True
|
||||
if not force:
|
||||
raise MoulinetteError(errno.EEXIST, m18n.n('restore_failed'))
|
||||
raise YunohostError('restore_failed')
|
||||
|
||||
# TODO Partial app restore could not work if ldap is not restored before
|
||||
# TODO repair mysql if broken and it's a complete restore
|
||||
|
||||
###########################################################################
|
||||
# Initialize #
|
||||
###########################################################################
|
||||
#
|
||||
# Initialize #
|
||||
#
|
||||
|
||||
restore_manager = RestoreManager(name)
|
||||
|
||||
|
@ -2147,9 +2123,9 @@ def backup_restore(auth, name, system=[], apps=[], force=False):
|
|||
|
||||
restore_manager.assert_enough_free_space()
|
||||
|
||||
###########################################################################
|
||||
# Mount the archive then call the restore for each system part / app #
|
||||
###########################################################################
|
||||
#
|
||||
# Mount the archive then call the restore for each system part / app #
|
||||
#
|
||||
|
||||
restore_manager.mount()
|
||||
restore_manager.restore()
|
||||
|
@ -2158,7 +2134,7 @@ def backup_restore(auth, name, system=[], apps=[], force=False):
|
|||
if restore_manager.success:
|
||||
logger.success(m18n.n('restore_complete'))
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('restore_nothings_done'))
|
||||
raise YunohostError('restore_nothings_done')
|
||||
|
||||
return restore_manager.targets.results
|
||||
|
||||
|
@ -2187,14 +2163,14 @@ def backup_list(with_info=False, human_readable=False):
|
|||
except ValueError:
|
||||
continue
|
||||
result.append(name)
|
||||
result.sort(key=lambda x: os.path.getctime(os.path.join(ARCHIVES_PATH, x+".tar.gz")))
|
||||
result.sort(key=lambda x: os.path.getctime(os.path.join(ARCHIVES_PATH, x + ".tar.gz")))
|
||||
|
||||
if result and with_info:
|
||||
d = OrderedDict()
|
||||
for a in result:
|
||||
try:
|
||||
d[a] = backup_info(a, human_readable=human_readable)
|
||||
except MoulinetteError, e:
|
||||
except YunohostError as e:
|
||||
logger.warning('%s: %s' % (a, e.strerror))
|
||||
|
||||
result = d
|
||||
|
@ -2216,8 +2192,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
|
||||
# Check file exist (even if it's a broken symlink)
|
||||
if not os.path.lexists(archive_file):
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_archive_name_unknown', name=name))
|
||||
raise YunohostError('backup_archive_name_unknown', name=name)
|
||||
|
||||
# If symlink, retrieve the real path
|
||||
if os.path.islink(archive_file):
|
||||
|
@ -2225,9 +2200,8 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
|
||||
# Raise exception if link is broken (e.g. on unmounted external storage)
|
||||
if not os.path.exists(archive_file):
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('backup_archive_broken_link',
|
||||
path=archive_file))
|
||||
raise YunohostError('backup_archive_broken_link',
|
||||
path=archive_file)
|
||||
|
||||
info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name)
|
||||
|
||||
|
@ -2239,7 +2213,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
except KeyError:
|
||||
logger.debug("unable to retrieve '%s' inside the archive",
|
||||
info_file, exc_info=1)
|
||||
raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))
|
||||
raise YunohostError('backup_invalid_archive')
|
||||
else:
|
||||
shutil.move(os.path.join(info_dir, 'info.json'), info_file)
|
||||
finally:
|
||||
|
@ -2252,7 +2226,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
info = json.load(f)
|
||||
except:
|
||||
logger.debug("unable to load '%s'", info_file, exc_info=1)
|
||||
raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))
|
||||
raise YunohostError('backup_invalid_archive')
|
||||
|
||||
# Retrieve backup size
|
||||
size = info.get('size', 0)
|
||||
|
@ -2266,8 +2240,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
|
||||
result = {
|
||||
'path': archive_file,
|
||||
'created_at': time.strftime(m18n.n('format_datetime_short'),
|
||||
time.gmtime(info['created_at'])),
|
||||
'created_at': datetime.utcfromtimestamp(info['created_at']),
|
||||
'description': info['description'],
|
||||
'size': size,
|
||||
}
|
||||
|
@ -2292,8 +2265,8 @@ def backup_delete(name):
|
|||
|
||||
"""
|
||||
if name not in backup_list()["archives"]:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('backup_archive_name_unknown',
|
||||
name=name))
|
||||
raise YunohostError('backup_archive_name_unknown',
|
||||
name=name)
|
||||
|
||||
hook_callback('pre_backup_delete', args=[name])
|
||||
|
||||
|
@ -2311,20 +2284,18 @@ def backup_delete(name):
|
|||
|
||||
logger.success(m18n.n('backup_deleted'))
|
||||
|
||||
###############################################################################
|
||||
# Misc helpers #
|
||||
###############################################################################
|
||||
#
|
||||
# Misc helpers #
|
||||
#
|
||||
|
||||
|
||||
def _create_archive_dir():
|
||||
""" Create the YunoHost archives directory if doesn't exist """
|
||||
if not os.path.isdir(ARCHIVES_PATH):
|
||||
if os.path.lexists(ARCHIVES_PATH):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('backup_output_symlink_dir_broken',
|
||||
path=ARCHIVES_PATH))
|
||||
raise YunohostError('backup_output_symlink_dir_broken', path=ARCHIVES_PATH)
|
||||
|
||||
os.mkdir(ARCHIVES_PATH, 0750)
|
||||
os.mkdir(ARCHIVES_PATH, 0o750)
|
||||
|
||||
|
||||
def _call_for_each_path(self, callback, csv_path=None):
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import errno
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
|
@ -37,7 +36,7 @@ from datetime import datetime
|
|||
|
||||
from yunohost.vendor.acme_tiny.acme_tiny import get_crt as sign_certificate
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.utils.network import get_public_ip
|
||||
|
@ -81,9 +80,9 @@ DNS_RESOLVERS = [
|
|||
"80.67.188.188" # LDN
|
||||
]
|
||||
|
||||
###############################################################################
|
||||
# Front-end stuff #
|
||||
###############################################################################
|
||||
#
|
||||
# Front-end stuff #
|
||||
#
|
||||
|
||||
|
||||
def certificate_status(auth, domain_list, full=False):
|
||||
|
@ -106,8 +105,7 @@ def certificate_status(auth, domain_list, full=False):
|
|||
for domain in domain_list:
|
||||
# Is it in Yunohost domain list?
|
||||
if domain not in yunohost_domains_list:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_domain_unknown', domain=domain))
|
||||
raise YunohostError('certmanager_domain_unknown', domain=domain)
|
||||
|
||||
certificates = {}
|
||||
|
||||
|
@ -151,10 +149,10 @@ def _certificate_install_selfsigned(domain_list, force=False):
|
|||
for domain in domain_list:
|
||||
|
||||
operation_logger = OperationLogger('selfsigned_cert_install', [('domain', domain)],
|
||||
args={'force': force})
|
||||
args={'force': force})
|
||||
|
||||
# Paths of files and folder we'll need
|
||||
date_tag = datetime.now().strftime("%Y%m%d.%H%M%S")
|
||||
date_tag = datetime.utcnow().strftime("%Y%m%d.%H%M%S")
|
||||
new_cert_folder = "%s/%s-history/%s-selfsigned" % (
|
||||
CERT_FOLDER, domain, date_tag)
|
||||
|
||||
|
@ -172,8 +170,7 @@ def _certificate_install_selfsigned(domain_list, force=False):
|
|||
status = _get_status(domain)
|
||||
|
||||
if status["summary"]["code"] in ('good', 'great'):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_attempt_to_replace_valid_cert', domain=domain))
|
||||
raise YunohostError('certmanager_attempt_to_replace_valid_cert', domain=domain)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -203,8 +200,7 @@ def _certificate_install_selfsigned(domain_list, force=False):
|
|||
|
||||
if p.returncode != 0:
|
||||
logger.warning(out)
|
||||
raise MoulinetteError(
|
||||
errno.EIO, m18n.n('domain_cert_gen_failed'))
|
||||
raise YunohostError('domain_cert_gen_failed')
|
||||
else:
|
||||
logger.debug(out)
|
||||
|
||||
|
@ -219,10 +215,10 @@ def _certificate_install_selfsigned(domain_list, force=False):
|
|||
crt_pem.write(ca_pem.read())
|
||||
|
||||
# Set appropriate permissions
|
||||
_set_permissions(new_cert_folder, "root", "root", 0755)
|
||||
_set_permissions(key_file, "root", "ssl-cert", 0640)
|
||||
_set_permissions(crt_file, "root", "ssl-cert", 0640)
|
||||
_set_permissions(conf_file, "root", "root", 0600)
|
||||
_set_permissions(new_cert_folder, "root", "root", 0o755)
|
||||
_set_permissions(key_file, "root", "ssl-cert", 0o640)
|
||||
_set_permissions(crt_file, "root", "ssl-cert", 0o640)
|
||||
_set_permissions(conf_file, "root", "root", 0o600)
|
||||
|
||||
# Actually enable the certificate we created
|
||||
_enable_certificate(domain, new_cert_folder)
|
||||
|
@ -262,14 +258,12 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F
|
|||
for domain in domain_list:
|
||||
yunohost_domains_list = yunohost.domain.domain_list(auth)['domains']
|
||||
if domain not in yunohost_domains_list:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_domain_unknown', domain=domain))
|
||||
raise YunohostError('certmanager_domain_unknown', domain=domain)
|
||||
|
||||
# Is it self-signed?
|
||||
status = _get_status(domain)
|
||||
if not force and status["CA_type"]["code"] != "self-signed":
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_domain_cert_not_selfsigned', domain=domain))
|
||||
raise YunohostError('certmanager_domain_cert_not_selfsigned', domain=domain)
|
||||
|
||||
if staging:
|
||||
logger.warning(
|
||||
|
@ -279,8 +273,8 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F
|
|||
for domain in domain_list:
|
||||
|
||||
operation_logger = OperationLogger('letsencrypt_cert_install', [('domain', domain)],
|
||||
args={'force': force, 'no_checks': no_checks,
|
||||
'staging': staging})
|
||||
args={'force': force, 'no_checks': no_checks,
|
||||
'staging': staging})
|
||||
logger.info(
|
||||
"Now attempting install of certificate for domain %s!", domain)
|
||||
|
||||
|
@ -304,6 +298,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F
|
|||
logger.error(msg)
|
||||
operation_logger.error(msg)
|
||||
|
||||
|
||||
def certificate_renew(auth, domain_list, force=False, no_checks=False, email=False, staging=False):
|
||||
"""
|
||||
Renew Let's Encrypt certificate for given domains (all by default)
|
||||
|
@ -349,25 +344,21 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal
|
|||
|
||||
# Is it in Yunohost dmomain list?
|
||||
if domain not in yunohost.domain.domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_domain_unknown', domain=domain))
|
||||
raise YunohostError('certmanager_domain_unknown', domain=domain)
|
||||
|
||||
status = _get_status(domain)
|
||||
|
||||
# Does it expire soon?
|
||||
if status["validity"] > VALIDITY_LIMIT and not force:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_attempt_to_renew_valid_cert', domain=domain))
|
||||
raise YunohostError('certmanager_attempt_to_renew_valid_cert', domain=domain)
|
||||
|
||||
# Does it have a Let's Encrypt cert?
|
||||
if status["CA_type"]["code"] != "lets-encrypt":
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_attempt_to_renew_nonLE_cert', domain=domain))
|
||||
raise YunohostError('certmanager_attempt_to_renew_nonLE_cert', domain=domain)
|
||||
|
||||
# Check ACME challenge configured for given domain
|
||||
if not _check_acme_challenge_configuration(domain):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_acme_not_configured_for_domain', domain=domain))
|
||||
raise YunohostError('certmanager_acme_not_configured_for_domain', domain=domain)
|
||||
|
||||
if staging:
|
||||
logger.warning(
|
||||
|
@ -377,8 +368,8 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal
|
|||
for domain in domain_list:
|
||||
|
||||
operation_logger = OperationLogger('letsencrypt_cert_renew', [('domain', domain)],
|
||||
args={'force': force, 'no_checks': no_checks,
|
||||
'staging': staging, 'email': email})
|
||||
args={'force': force, 'no_checks': no_checks,
|
||||
'staging': staging, 'email': email})
|
||||
|
||||
logger.info(
|
||||
"Now attempting renewing of certificate for domain %s !", domain)
|
||||
|
@ -411,9 +402,10 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal
|
|||
logger.error("Sending email with details to root ...")
|
||||
_email_renewing_failed(domain, e, stack.getvalue())
|
||||
|
||||
###############################################################################
|
||||
# Back-end stuff #
|
||||
###############################################################################
|
||||
#
|
||||
# Back-end stuff #
|
||||
#
|
||||
|
||||
|
||||
def _install_cron():
|
||||
cron_job_file = "/etc/cron.daily/yunohost-certificate-renew"
|
||||
|
@ -422,7 +414,7 @@ def _install_cron():
|
|||
f.write("#!/bin/bash\n")
|
||||
f.write("yunohost domain cert-renew --email\n")
|
||||
|
||||
_set_permissions(cron_job_file, "root", "root", 0755)
|
||||
_set_permissions(cron_job_file, "root", "root", 0o755)
|
||||
|
||||
|
||||
def _email_renewing_failed(domain, exception_message, stack):
|
||||
|
@ -484,8 +476,7 @@ location ^~ '/.well-known/acme-challenge/'
|
|||
contents = f.read()
|
||||
|
||||
if '/.well-known/acme-challenge' in contents:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_conflicting_nginx_file', filepath=path))
|
||||
raise YunohostError('certmanager_conflicting_nginx_file', filepath=path)
|
||||
|
||||
# Write the conf
|
||||
if os.path.exists(nginx_conf_file):
|
||||
|
@ -528,8 +519,8 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False):
|
|||
if not os.path.exists(TMP_FOLDER):
|
||||
os.makedirs(TMP_FOLDER)
|
||||
|
||||
_set_permissions(WEBROOT_FOLDER, "root", "www-data", 0650)
|
||||
_set_permissions(TMP_FOLDER, "root", "root", 0640)
|
||||
_set_permissions(WEBROOT_FOLDER, "root", "www-data", 0o650)
|
||||
_set_permissions(TMP_FOLDER, "root", "root", 0o640)
|
||||
|
||||
# Regen conf for dnsmasq if needed
|
||||
_regen_dnsmasq_if_needed()
|
||||
|
@ -540,7 +531,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False):
|
|||
|
||||
domain_key_file = "%s/%s.pem" % (TMP_FOLDER, domain)
|
||||
_generate_key(domain_key_file)
|
||||
_set_permissions(domain_key_file, "root", "ssl-cert", 0640)
|
||||
_set_permissions(domain_key_file, "root", "ssl-cert", 0o640)
|
||||
|
||||
_prepare_certificate_signing_request(domain, domain_key_file, TMP_FOLDER)
|
||||
|
||||
|
@ -563,31 +554,28 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False):
|
|||
CA=certification_authority)
|
||||
except ValueError as e:
|
||||
if "urn:acme:error:rateLimited" in str(e):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_hit_rate_limit', domain=domain))
|
||||
raise YunohostError('certmanager_hit_rate_limit', domain=domain)
|
||||
else:
|
||||
logger.error(str(e))
|
||||
_display_debug_information(domain)
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_cert_signing_failed'))
|
||||
raise YunohostError('certmanager_cert_signing_failed')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_cert_signing_failed'))
|
||||
raise YunohostError('certmanager_cert_signing_failed')
|
||||
|
||||
import requests # lazy loading this module for performance reasons
|
||||
import requests # lazy loading this module for performance reasons
|
||||
try:
|
||||
intermediate_certificate = requests.get(INTERMEDIATE_CERTIFICATE_URL, timeout=30).text
|
||||
except requests.exceptions.Timeout as e:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('certmanager_couldnt_fetch_intermediate_cert'))
|
||||
raise YunohostError('certmanager_couldnt_fetch_intermediate_cert')
|
||||
|
||||
# Now save the key and signed certificate
|
||||
logger.debug("Saving the key and signed certificate...")
|
||||
|
||||
# Create corresponding directory
|
||||
date_tag = datetime.now().strftime("%Y%m%d.%H%M%S")
|
||||
date_tag = datetime.utcnow().strftime("%Y%m%d.%H%M%S")
|
||||
|
||||
if staging:
|
||||
folder_flag = "staging"
|
||||
|
@ -599,12 +587,12 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False):
|
|||
|
||||
os.makedirs(new_cert_folder)
|
||||
|
||||
_set_permissions(new_cert_folder, "root", "root", 0655)
|
||||
_set_permissions(new_cert_folder, "root", "root", 0o655)
|
||||
|
||||
# Move the private key
|
||||
domain_key_file_finaldest = os.path.join(new_cert_folder, "key.pem")
|
||||
shutil.move(domain_key_file, domain_key_file_finaldest)
|
||||
_set_permissions(domain_key_file_finaldest, "root", "ssl-cert", 0640)
|
||||
_set_permissions(domain_key_file_finaldest, "root", "ssl-cert", 0o640)
|
||||
|
||||
# Write the cert
|
||||
domain_cert_file = os.path.join(new_cert_folder, "crt.pem")
|
||||
|
@ -613,7 +601,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False):
|
|||
f.write(signed_certificate)
|
||||
f.write(intermediate_certificate)
|
||||
|
||||
_set_permissions(domain_cert_file, "root", "ssl-cert", 0640)
|
||||
_set_permissions(domain_cert_file, "root", "ssl-cert", 0o640)
|
||||
|
||||
if staging:
|
||||
return
|
||||
|
@ -624,12 +612,11 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False):
|
|||
status_summary = _get_status(domain)["summary"]
|
||||
|
||||
if status_summary["code"] != "great":
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_certificate_fetching_or_enabling_failed', domain=domain))
|
||||
raise YunohostError('certmanager_certificate_fetching_or_enabling_failed', domain=domain)
|
||||
|
||||
|
||||
def _prepare_certificate_signing_request(domain, key_file, output_folder):
|
||||
from OpenSSL import crypto # lazy loading this module for performance reasons
|
||||
from OpenSSL import crypto # lazy loading this module for performance reasons
|
||||
# Init a request
|
||||
csr = crypto.X509Req()
|
||||
|
||||
|
@ -658,23 +645,21 @@ def _get_status(domain):
|
|||
cert_file = os.path.join(CERT_FOLDER, domain, "crt.pem")
|
||||
|
||||
if not os.path.isfile(cert_file):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_no_cert_file', domain=domain, file=cert_file))
|
||||
raise YunohostError('certmanager_no_cert_file', domain=domain, file=cert_file)
|
||||
|
||||
from OpenSSL import crypto # lazy loading this module for performance reasons
|
||||
from OpenSSL import crypto # lazy loading this module for performance reasons
|
||||
try:
|
||||
cert = crypto.load_certificate(
|
||||
crypto.FILETYPE_PEM, open(cert_file).read())
|
||||
except Exception as exception:
|
||||
import traceback
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_cannot_read_cert', domain=domain, file=cert_file, reason=exception))
|
||||
raise YunohostError('certmanager_cannot_read_cert', domain=domain, file=cert_file, reason=exception)
|
||||
|
||||
cert_subject = cert.get_subject().CN
|
||||
cert_issuer = cert.get_issuer().CN
|
||||
valid_up_to = datetime.strptime(cert.get_notAfter(), "%Y%m%d%H%M%SZ")
|
||||
days_remaining = (valid_up_to - datetime.now()).days
|
||||
days_remaining = (valid_up_to - datetime.utcnow()).days
|
||||
|
||||
if cert_issuer == _name_self_CA():
|
||||
CA_type = {
|
||||
|
@ -752,19 +737,19 @@ def _get_status(domain):
|
|||
"ACME_eligible": ACME_eligible
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Misc small stuff ... #
|
||||
###############################################################################
|
||||
#
|
||||
# Misc small stuff ... #
|
||||
#
|
||||
|
||||
|
||||
def _generate_account_key():
|
||||
logger.debug("Generating account key ...")
|
||||
_generate_key(ACCOUNT_KEY_FILE)
|
||||
_set_permissions(ACCOUNT_KEY_FILE, "root", "root", 0400)
|
||||
_set_permissions(ACCOUNT_KEY_FILE, "root", "root", 0o400)
|
||||
|
||||
|
||||
def _generate_key(destination_path):
|
||||
from OpenSSL import crypto # lazy loading this module for performance reasons
|
||||
from OpenSSL import crypto # lazy loading this module for performance reasons
|
||||
k = crypto.PKey()
|
||||
k.generate_key(crypto.TYPE_RSA, KEY_SIZE)
|
||||
|
||||
|
@ -810,13 +795,16 @@ def _enable_certificate(domain, new_cert_folder):
|
|||
|
||||
_run_service_command("reload", "nginx")
|
||||
|
||||
from yunohost.hook import hook_callback
|
||||
hook_callback('post_cert_update', args=[domain])
|
||||
|
||||
|
||||
def _backup_current_cert(domain):
|
||||
logger.debug("Backuping existing certificate for domain %s", domain)
|
||||
|
||||
cert_folder_domain = os.path.join(CERT_FOLDER, domain)
|
||||
|
||||
date_tag = datetime.now().strftime("%Y%m%d.%H%M%S")
|
||||
date_tag = datetime.utcnow().strftime("%Y%m%d.%H%M%S")
|
||||
backup_folder = "%s-backups/%s" % (cert_folder_domain, date_tag)
|
||||
|
||||
shutil.copytree(cert_folder_domain, backup_folder)
|
||||
|
@ -827,13 +815,11 @@ def _check_domain_is_ready_for_ACME(domain):
|
|||
|
||||
# Check if IP from DNS matches public IP
|
||||
if not _dns_ip_match_public_ip(public_ip, domain):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_domain_dns_ip_differs_from_public_ip', domain=domain))
|
||||
raise YunohostError('certmanager_domain_dns_ip_differs_from_public_ip', domain=domain)
|
||||
|
||||
# Check if domain seems to be accessible through HTTP?
|
||||
if not _domain_is_accessible_through_HTTP(public_ip, domain):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_domain_http_not_working', domain=domain))
|
||||
raise YunohostError('certmanager_domain_http_not_working', domain=domain)
|
||||
|
||||
|
||||
def _get_dns_ip(domain):
|
||||
|
@ -842,8 +828,7 @@ def _get_dns_ip(domain):
|
|||
resolver.nameservers = DNS_RESOLVERS
|
||||
answers = resolver.query(domain, "A")
|
||||
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'certmanager_error_no_A_record', domain=domain))
|
||||
raise YunohostError('certmanager_error_no_A_record', domain=domain)
|
||||
|
||||
return str(answers[0])
|
||||
|
||||
|
@ -853,7 +838,7 @@ def _dns_ip_match_public_ip(public_ip, domain):
|
|||
|
||||
|
||||
def _domain_is_accessible_through_HTTP(ip, domain):
|
||||
import requests # lazy loading this module for performance reasons
|
||||
import requests # lazy loading this module for performance reasons
|
||||
try:
|
||||
requests.head("http://" + ip, headers={"Host": domain}, timeout=10)
|
||||
except requests.exceptions.Timeout as e:
|
||||
|
|
|
@ -3,7 +3,9 @@ import glob
|
|||
from yunohost.tools import Migration
|
||||
from moulinette.utils.filesystem import chown
|
||||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Change certificates group permissions from 'metronome' to 'ssl-cert'"
|
||||
|
||||
all_certificate_files = glob.glob("/etc/yunohost/certs/*/*.pem")
|
||||
|
|
|
@ -4,10 +4,9 @@ import requests
|
|||
import base64
|
||||
import time
|
||||
import json
|
||||
import errno
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.tools import Migration
|
||||
|
@ -17,6 +16,7 @@ logger = getActionLogger('yunohost.migration')
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate Dyndns stuff from MD5 TSIG to SHA512 TSIG"
|
||||
|
||||
def backward(self):
|
||||
|
@ -29,7 +29,7 @@ class MyMigration(Migration):
|
|||
try:
|
||||
(domain, private_key_path) = _guess_current_dyndns_domain(dyn_host)
|
||||
assert "+157" in private_key_path
|
||||
except (MoulinetteError, AssertionError):
|
||||
except (YunohostError, AssertionError):
|
||||
logger.info(m18n.n("migrate_tsig_not_needed"))
|
||||
return
|
||||
|
||||
|
@ -52,7 +52,7 @@ class MyMigration(Migration):
|
|||
'public_key_sha512': base64.b64encode(public_key_sha512),
|
||||
}, timeout=30)
|
||||
except requests.ConnectionError:
|
||||
raise MoulinetteError(errno.ENETUNREACH, m18n.n('no_internet_connection'))
|
||||
raise YunohostError('no_internet_connection')
|
||||
|
||||
if r.status_code != 201:
|
||||
try:
|
||||
|
@ -70,8 +70,8 @@ class MyMigration(Migration):
|
|||
# Migration didn't succeed, so we rollback and raise an exception
|
||||
os.system("mv /etc/yunohost/dyndns/*+165* /tmp")
|
||||
|
||||
raise MoulinetteError(m18n.n('migrate_tsig_failed', domain=domain,
|
||||
error_code=str(r.status_code), error=error))
|
||||
raise YunohostError('migrate_tsig_failed', domain=domain,
|
||||
error_code=str(r.status_code), error=error)
|
||||
|
||||
# remove old certificates
|
||||
os.system("mv /etc/yunohost/dyndns/*+157* /tmp")
|
||||
|
@ -88,4 +88,3 @@ class MyMigration(Migration):
|
|||
|
||||
logger.info(m18n.n('migrate_tsig_end'))
|
||||
return
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import os
|
|||
from shutil import copy2
|
||||
|
||||
from moulinette import m18n, msettings
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.process import check_output, call_async_output
|
||||
from moulinette.utils.filesystem import read_file
|
||||
|
@ -24,13 +24,14 @@ YUNOHOST_PACKAGES = ["yunohost", "yunohost-admin", "moulinette", "ssowat"]
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Upgrade the system to Debian Stretch and Yunohost 3.0"
|
||||
|
||||
mode = "manual"
|
||||
|
||||
def backward(self):
|
||||
|
||||
raise MoulinetteError(m18n.n("migration_0003_backward_impossible"))
|
||||
raise YunohostError("migration_0003_backward_impossible")
|
||||
|
||||
def migrate(self):
|
||||
|
||||
|
@ -57,7 +58,7 @@ class MyMigration(Migration):
|
|||
self.apt_dist_upgrade(conf_flags=["old", "miss", "def"])
|
||||
_run_service_command("start", "mysql")
|
||||
if self.debian_major_version() == 8:
|
||||
raise MoulinetteError(m18n.n("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile))
|
||||
raise YunohostError("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile)
|
||||
|
||||
# Specific upgrade for fail2ban...
|
||||
logger.info(m18n.n("migration_0003_fail2ban_upgrade"))
|
||||
|
@ -107,11 +108,11 @@ class MyMigration(Migration):
|
|||
# would still be in 2.x...
|
||||
if not self.debian_major_version() == 8 \
|
||||
and not self.yunohost_major_version() == 2:
|
||||
raise MoulinetteError(m18n.n("migration_0003_not_jessie"))
|
||||
raise YunohostError("migration_0003_not_jessie")
|
||||
|
||||
# Have > 1 Go free space on /var/ ?
|
||||
if free_space_in_directory("/var/") / (1024**3) < 1.0:
|
||||
raise MoulinetteError(m18n.n("migration_0003_not_enough_free_space"))
|
||||
raise YunohostError("migration_0003_not_enough_free_space")
|
||||
|
||||
# Check system is up to date
|
||||
# (but we don't if 'stretch' is already in the sources.list ...
|
||||
|
@ -120,7 +121,7 @@ class MyMigration(Migration):
|
|||
self.apt_update()
|
||||
apt_list_upgradable = check_output("apt list --upgradable -a")
|
||||
if "upgradable" in apt_list_upgradable:
|
||||
raise MoulinetteError(m18n.n("migration_0003_system_not_fully_up_to_date"))
|
||||
raise YunohostError("migration_0003_system_not_fully_up_to_date")
|
||||
|
||||
@property
|
||||
def disclaimer(self):
|
||||
|
@ -168,11 +169,11 @@ class MyMigration(Migration):
|
|||
# - switch yunohost's repo to forge
|
||||
for f in sources_list:
|
||||
command = "sed -i -e 's@ jessie @ stretch @g' " \
|
||||
"-e '/backports/ s@^#*@#@' " \
|
||||
"-e 's@ jessie/updates @ stretch/updates @g' " \
|
||||
"-e 's@ jessie-updates @ stretch-updates @g' " \
|
||||
"-e 's@repo.yunohost@forge.yunohost@g' " \
|
||||
"{}".format(f)
|
||||
"-e '/backports/ s@^#*@#@' " \
|
||||
"-e 's@ jessie/updates @ stretch/updates @g' " \
|
||||
"-e 's@ jessie-updates @ stretch-updates @g' " \
|
||||
"-e 's@repo.yunohost@forge.yunohost@g' " \
|
||||
"{}".format(f)
|
||||
os.system(command)
|
||||
|
||||
def get_apps_equivs_packages(self):
|
||||
|
@ -286,7 +287,7 @@ class MyMigration(Migration):
|
|||
# Create tmp directory if it does not exists
|
||||
tmp_dir = os.path.join("/tmp/", self.name)
|
||||
if not os.path.exists(tmp_dir):
|
||||
os.mkdir(tmp_dir, 0700)
|
||||
os.mkdir(tmp_dir, 0o700)
|
||||
|
||||
for f in self.files_to_keep:
|
||||
dest_file = f.strip('/').replace("/", "_")
|
||||
|
|
|
@ -19,6 +19,7 @@ MIGRATION_COMMENT = "; YunoHost note : this file was automatically moved from {}
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate php5-fpm 'pool' conf files to php7 stuff"
|
||||
|
||||
def migrate(self):
|
||||
|
@ -58,7 +59,7 @@ class MyMigration(Migration):
|
|||
_run_service_command("enable", "php7.0-fpm")
|
||||
os.system("systemctl stop php5-fpm")
|
||||
os.system("systemctl disable php5-fpm")
|
||||
os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy
|
||||
os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy
|
||||
|
||||
# Get list of nginx conf file
|
||||
nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import subprocess
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.tools import Migration
|
||||
|
@ -11,6 +11,7 @@ logger = getActionLogger('yunohost.migration')
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate DBs from Postgresql 9.4 to 9.6 after migrating to Stretch"
|
||||
|
||||
def migrate(self):
|
||||
|
@ -20,10 +21,10 @@ class MyMigration(Migration):
|
|||
return
|
||||
|
||||
if not self.package_is_installed("postgresql-9.6"):
|
||||
raise MoulinetteError(m18n.n("migration_0005_postgresql_96_not_installed"))
|
||||
raise YunohostError("migration_0005_postgresql_96_not_installed")
|
||||
|
||||
if not space_used_by_directory("/var/lib/postgresql/9.4") > free_space_in_directory("/var/lib/postgresql"):
|
||||
raise MoulinetteError(m18n.n("migration_0005_not_enough_space", path="/var/lib/postgresql/"))
|
||||
raise YunohostError("migration_0005_not_enough_space", path="/var/lib/postgresql/")
|
||||
|
||||
subprocess.check_call("service postgresql stop", shell=True)
|
||||
subprocess.check_call("pg_dropcluster --stop 9.6 main", shell=True)
|
||||
|
|
|
@ -5,7 +5,7 @@ import string
|
|||
import subprocess
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.process import run_commands, check_output
|
||||
from moulinette.utils.filesystem import append_to_file
|
||||
|
@ -15,7 +15,9 @@ from yunohost.tools import Migration
|
|||
logger = getActionLogger('yunohost.migration')
|
||||
SMALL_PWD_LIST = ["yunohost", "olinuxino", "olinux", "raspberry", "admin", "root", "test", "rpi"]
|
||||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Synchronize admin and root passwords"
|
||||
|
||||
def migrate(self):
|
||||
|
@ -23,7 +25,7 @@ class MyMigration(Migration):
|
|||
new_hash = self._get_admin_hash()
|
||||
self._replace_root_hash(new_hash)
|
||||
|
||||
logger.info(m18n.n("migration_0006_done"))
|
||||
logger.info(m18n.n("root_password_replaced_by_admin_password"))
|
||||
|
||||
def backward(self):
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
from shutil import copyfile
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import mkdir, rm
|
||||
|
||||
from yunohost.tools import Migration
|
||||
from yunohost.service import service_regen_conf, \
|
||||
_get_conf_hashes, \
|
||||
_calculate_hash, \
|
||||
_run_service_command
|
||||
from yunohost.settings import settings_set
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
logger = getActionLogger('yunohost.migration')
|
||||
|
||||
SSHD_CONF = '/etc/ssh/sshd_config'
|
||||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"""
|
||||
This is the first step of a couple of migrations that ensure SSH conf is
|
||||
managed by YunoHost (even if the "from_script" flag is present, which was
|
||||
previously preventing it from being managed by YunoHost)
|
||||
|
||||
The goal of this first (automatic) migration is to make sure that the
|
||||
sshd_config is managed by the regen-conf mechanism.
|
||||
|
||||
If the from_script flag exists, then we keep the current SSH conf such that it
|
||||
will appear as "manually modified" to the regenconf.
|
||||
|
||||
In step 2 (manual), the admin will be able to choose wether or not to actually
|
||||
use the recommended configuration, with an appropriate disclaimer.
|
||||
"""
|
||||
|
||||
def migrate(self):
|
||||
|
||||
# Check if deprecated DSA Host Key is in config
|
||||
dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$'
|
||||
dsa = False
|
||||
for line in open(SSHD_CONF):
|
||||
if re.match(dsa_rgx, line) is not None:
|
||||
dsa = True
|
||||
break
|
||||
if dsa:
|
||||
settings_set("service.ssh.allow_deprecated_dsa_hostkey", True)
|
||||
|
||||
# Create sshd_config.d dir
|
||||
if not os.path.exists(SSHD_CONF + '.d'):
|
||||
mkdir(SSHD_CONF + '.d', 0o755, uid='root', gid='root')
|
||||
|
||||
# Here, we make it so that /etc/ssh/sshd_config is managed
|
||||
# by the regen conf (in particular in the case where the
|
||||
# from_script flag is present - in which case it was *not*
|
||||
# managed by the regenconf)
|
||||
# But because we can't be sure the user wants to use the
|
||||
# recommended conf, we backup then restore the /etc/ssh/sshd_config
|
||||
# right after the regenconf, such that it will appear as
|
||||
# "manually modified".
|
||||
if os.path.exists('/etc/yunohost/from_script'):
|
||||
rm('/etc/yunohost/from_script')
|
||||
copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp')
|
||||
service_regen_conf(names=['ssh'], force=True)
|
||||
copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF)
|
||||
|
||||
# Restart ssh and backward if it fail
|
||||
if not _run_service_command('restart', 'ssh'):
|
||||
self.backward()
|
||||
raise YunohostError("migration_0007_cancel")
|
||||
|
||||
def backward(self):
|
||||
|
||||
# We don't backward completely but it should be enough
|
||||
copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF)
|
||||
if not _run_service_command('restart', 'ssh'):
|
||||
raise YunohostError("migration_0007_cannot_restart")
|
|
@ -0,0 +1,98 @@
|
|||
import re
|
||||
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.tools import Migration
|
||||
from yunohost.service import service_regen_conf, \
|
||||
_get_conf_hashes, \
|
||||
_calculate_hash
|
||||
from yunohost.settings import settings_set, settings_get
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
logger = getActionLogger('yunohost.migration')
|
||||
|
||||
SSHD_CONF = '/etc/ssh/sshd_config'
|
||||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"""
|
||||
In this second step, the admin is asked if it's okay to use
|
||||
the recommended SSH configuration - which also implies
|
||||
disabling deprecated DSA key.
|
||||
|
||||
This has important implications in the way the user may connect
|
||||
to its server (key change, and a spooky warning might be given
|
||||
by SSH later)
|
||||
|
||||
A disclaimer explaining the various things to be aware of is
|
||||
shown - and the user may also choose to skip this migration.
|
||||
"""
|
||||
|
||||
def migrate(self):
|
||||
settings_set("service.ssh.allow_deprecated_dsa_hostkey", False)
|
||||
service_regen_conf(names=['ssh'], force=True)
|
||||
|
||||
def backward(self):
|
||||
|
||||
raise YunohostError("migration_0008_backward_impossible")
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
|
||||
# If the conf is already up to date
|
||||
# and no DSA key is used, then we're good to go
|
||||
# and the migration can be done automatically
|
||||
# (basically nothing shall change)
|
||||
ynh_hash = _get_conf_hashes('ssh').get(SSHD_CONF, None)
|
||||
current_hash = _calculate_hash(SSHD_CONF)
|
||||
dsa = settings_get("service.ssh.allow_deprecated_dsa_hostkey")
|
||||
if ynh_hash == current_hash and not dsa:
|
||||
return "auto"
|
||||
|
||||
return "manual"
|
||||
|
||||
@property
|
||||
def disclaimer(self):
|
||||
|
||||
if self.mode == "auto":
|
||||
return None
|
||||
|
||||
# Detect key things to be aware of before enabling the
|
||||
# recommended configuration
|
||||
dsa_key_enabled = False
|
||||
ports = []
|
||||
root_login = []
|
||||
port_rgx = r'^[ \t]*Port[ \t]+(\d+)[ \t]*(?:#.*)?$'
|
||||
root_rgx = r'^[ \t]*PermitRootLogin[ \t]([^# \t]*)[ \t]*(?:#.*)?$'
|
||||
dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$'
|
||||
for line in open(SSHD_CONF):
|
||||
|
||||
ports = ports + re.findall(port_rgx, line)
|
||||
|
||||
root_login = root_login + re.findall(root_rgx, line)
|
||||
|
||||
if not dsa_key_enabled and re.match(dsa_rgx, line) is not None:
|
||||
dsa_key_enabled = True
|
||||
|
||||
custom_port = ports != ['22'] and ports != []
|
||||
root_login_enabled = root_login and root_login[-1] != 'no'
|
||||
|
||||
# Build message
|
||||
message = m18n.n("migration_0008_general_disclaimer")
|
||||
|
||||
if custom_port:
|
||||
message += "\n\n" + m18n.n("migration_0008_port")
|
||||
|
||||
if root_login_enabled:
|
||||
message += "\n\n" + m18n.n("migration_0008_root")
|
||||
|
||||
if dsa_key_enabled:
|
||||
message += "\n\n" + m18n.n("migration_0008_dsa")
|
||||
|
||||
if custom_port or root_login_enabled or dsa_key_enabled:
|
||||
message += "\n\n" + m18n.n("migration_0008_warning")
|
||||
else:
|
||||
message += "\n\n" + m18n.n("migration_0008_no_warning")
|
||||
|
||||
return message
|
|
@ -25,12 +25,11 @@
|
|||
"""
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import yaml
|
||||
import errno
|
||||
|
||||
from moulinette import m18n, msettings
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
import yunohost.certificate
|
||||
|
@ -78,7 +77,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False):
|
|||
try:
|
||||
auth.validate_uniqueness({'virtualdomain': domain})
|
||||
except MoulinetteError:
|
||||
raise MoulinetteError(errno.EEXIST, m18n.n('domain_exists'))
|
||||
raise YunohostError('domain_exists')
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -87,16 +86,14 @@ def domain_add(operation_logger, auth, domain, dyndns=False):
|
|||
|
||||
# Do not allow to subscribe to multiple dyndns domains...
|
||||
if os.path.exists('/etc/cron.d/yunohost-dyndns'):
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('domain_dyndns_already_subscribed'))
|
||||
raise YunohostError('domain_dyndns_already_subscribed')
|
||||
|
||||
from yunohost.dyndns import dyndns_subscribe, _dyndns_provides
|
||||
|
||||
# Check that this domain can effectively be provided by
|
||||
# dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st)
|
||||
if not _dyndns_provides("dyndns.yunohost.org", domain):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('domain_dyndns_root_unknown'))
|
||||
raise YunohostError('domain_dyndns_root_unknown')
|
||||
|
||||
# Actually subscribe
|
||||
dyndns_subscribe(domain=domain)
|
||||
|
@ -110,23 +107,20 @@ def domain_add(operation_logger, auth, domain, dyndns=False):
|
|||
}
|
||||
|
||||
if not auth.add('virtualdomain=%s,ou=domains' % domain, attr_dict):
|
||||
raise MoulinetteError(errno.EIO, m18n.n('domain_creation_failed'))
|
||||
raise YunohostError('domain_creation_failed')
|
||||
|
||||
# Don't regen these conf if we're still in postinstall
|
||||
if os.path.exists('/etc/yunohost/installed'):
|
||||
service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix'])
|
||||
app_ssowatconf(auth)
|
||||
|
||||
except Exception, e:
|
||||
from sys import exc_info;
|
||||
t, v, tb = exc_info()
|
||||
|
||||
except Exception:
|
||||
# Force domain removal silently
|
||||
try:
|
||||
domain_remove(auth, domain, True)
|
||||
except:
|
||||
pass
|
||||
raise t, v, tb
|
||||
raise
|
||||
|
||||
hook_callback('post_domain_add', args=[domain])
|
||||
|
||||
|
@ -147,11 +141,11 @@ def domain_remove(operation_logger, auth, domain, force=False):
|
|||
from yunohost.app import app_ssowatconf
|
||||
|
||||
if not force and domain not in domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
|
||||
raise YunohostError('domain_unknown')
|
||||
|
||||
# Check domain is not the main domain
|
||||
if domain == _get_maindomain():
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('domain_cannot_remove_main'))
|
||||
raise YunohostError('domain_cannot_remove_main')
|
||||
|
||||
# Check if apps are installed on the domain
|
||||
for app in os.listdir('/etc/yunohost/apps/'):
|
||||
|
@ -162,14 +156,13 @@ def domain_remove(operation_logger, auth, domain, force=False):
|
|||
continue
|
||||
else:
|
||||
if app_domain == domain:
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('domain_uninstall_app_first'))
|
||||
raise YunohostError('domain_uninstall_app_first')
|
||||
|
||||
operation_logger.start()
|
||||
if auth.remove('virtualdomain=' + domain + ',ou=domains') or force:
|
||||
os.system('rm -rf /etc/yunohost/certs/%s' % domain)
|
||||
else:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed'))
|
||||
raise YunohostError('domain_deletion_failed')
|
||||
|
||||
service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix'])
|
||||
app_ssowatconf(auth)
|
||||
|
@ -246,7 +239,7 @@ def _get_conflicting_apps(auth, domain, path):
|
|||
|
||||
# Abort if domain is unknown
|
||||
if domain not in domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
|
||||
raise YunohostError('domain_unknown')
|
||||
|
||||
# This import cannot be put on top of file because it would create a
|
||||
# recursive import...
|
||||
|
@ -340,7 +333,7 @@ def _build_dns_conf(domain, ttl=3600):
|
|||
{"type": "TXT", "name": "_dmarc", "value": "\"v=DMARC1; p=none\"", "ttl": 3600}
|
||||
],
|
||||
"extra": [
|
||||
{"type": "CAA", "name": "@", "value": "128 issue 'letsencrypt.org'", "ttl": 3600},
|
||||
{"type": "CAA", "name": "@", "value": "128 issue \"letsencrypt.org\"", "ttl": 3600},
|
||||
],
|
||||
}
|
||||
"""
|
||||
|
@ -397,7 +390,7 @@ def _build_dns_conf(domain, ttl=3600):
|
|||
|
||||
# Extra
|
||||
extra = [
|
||||
["@", ttl, "CAA", "128 issue 'letsencrypt.org'"]
|
||||
["@", ttl, "CAA", '128 issue "letsencrypt.org"']
|
||||
]
|
||||
|
||||
return {
|
||||
|
|
|
@ -27,19 +27,17 @@ import os
|
|||
import re
|
||||
import json
|
||||
import glob
|
||||
import time
|
||||
import base64
|
||||
import errno
|
||||
import subprocess
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_file, write_to_file, rm
|
||||
from moulinette.utils.filesystem import write_to_file
|
||||
from moulinette.utils.network import download_json
|
||||
from moulinette.utils.process import check_output
|
||||
|
||||
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.domain import _get_maindomain, _build_dns_conf
|
||||
from yunohost.utils.network import get_public_ip
|
||||
from yunohost.log import is_unit_operation
|
||||
|
@ -77,9 +75,7 @@ def _dyndns_provides(provider, domain):
|
|||
dyndomains = download_json('https://%s/domains' % provider, timeout=30)
|
||||
except MoulinetteError as e:
|
||||
logger.error(str(e))
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('dyndns_could_not_check_provide',
|
||||
domain=domain, provider=provider))
|
||||
raise YunohostError('dyndns_could_not_check_provide', domain=domain, provider=provider)
|
||||
|
||||
# Extract 'dyndomain' from 'domain', e.g. 'nohost.me' from 'foo.nohost.me'
|
||||
dyndomain = '.'.join(domain.split('.')[1:])
|
||||
|
@ -106,9 +102,8 @@ def _dyndns_available(provider, domain):
|
|||
expected_status_code=None)
|
||||
except MoulinetteError as e:
|
||||
logger.error(str(e))
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('dyndns_could_not_check_available',
|
||||
domain=domain, provider=provider))
|
||||
raise YunohostError('dyndns_could_not_check_available',
|
||||
domain=domain, provider=provider)
|
||||
|
||||
return r == u"Domain %s is available" % domain
|
||||
|
||||
|
@ -130,14 +125,11 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom
|
|||
|
||||
# Verify if domain is provided by subscribe_host
|
||||
if not _dyndns_provides(subscribe_host, domain):
|
||||
raise MoulinetteError(errno.ENOENT,
|
||||
m18n.n('dyndns_domain_not_provided',
|
||||
domain=domain, provider=subscribe_host))
|
||||
raise YunohostError('dyndns_domain_not_provided', domain=domain, provider=subscribe_host)
|
||||
|
||||
# Verify if domain is available
|
||||
if not _dyndns_available(subscribe_host, domain):
|
||||
raise MoulinetteError(errno.ENOENT,
|
||||
m18n.n('dyndns_unavailable', domain=domain))
|
||||
raise YunohostError('dyndns_unavailable', domain=domain)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -156,19 +148,18 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom
|
|||
with open(key_file) as f:
|
||||
key = f.readline().strip().split(' ', 6)[-1]
|
||||
|
||||
import requests # lazy loading this module for performance reasons
|
||||
import requests # lazy loading this module for performance reasons
|
||||
# Send subscription
|
||||
try:
|
||||
r = requests.post('https://%s/key/%s?key_algo=hmac-sha512' % (subscribe_host, base64.b64encode(key)), data={'subdomain': domain}, timeout=30)
|
||||
except requests.ConnectionError:
|
||||
raise MoulinetteError(errno.ENETUNREACH, m18n.n('no_internet_connection'))
|
||||
raise YunohostError('no_internet_connection')
|
||||
if r.status_code != 201:
|
||||
try:
|
||||
error = json.loads(r.text)['error']
|
||||
except:
|
||||
error = "Server error, code: %s. (Message: \"%s\")" % (r.status_code, r.text)
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('dyndns_registration_failed', error=error))
|
||||
raise YunohostError('dyndns_registration_failed', error=error)
|
||||
|
||||
logger.success(m18n.n('dyndns_registered'))
|
||||
|
||||
|
@ -202,7 +193,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
|
|||
keys = glob.glob('/etc/yunohost/dyndns/K{0}.+*.private'.format(domain))
|
||||
|
||||
if not keys:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('dyndns_key_not_found'))
|
||||
raise YunohostError('dyndns_key_not_found')
|
||||
|
||||
key = keys[0]
|
||||
|
||||
|
@ -219,7 +210,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
|
|||
exception=e,
|
||||
number=migration.number,
|
||||
name=migration.name),
|
||||
exc_info=1)
|
||||
exc_info=1)
|
||||
return
|
||||
|
||||
# Extract 'host', e.g. 'nohost.me' from 'foo.nohost.me'
|
||||
|
@ -233,7 +224,6 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
|
|||
'zone %s' % host,
|
||||
]
|
||||
|
||||
|
||||
old_ipv4 = check_output("dig @%s +short %s" % (dyn_host, domain)).strip() or None
|
||||
old_ipv6 = check_output("dig @%s +short aaaa %s" % (dyn_host, domain)).strip() or None
|
||||
|
||||
|
@ -260,7 +250,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
|
|||
logger.info("Updated needed, going on...")
|
||||
|
||||
dns_conf = _build_dns_conf(domain)
|
||||
del dns_conf["extra"] # Ignore records from the 'extra' category
|
||||
del dns_conf["extra"] # Ignore records from the 'extra' category
|
||||
|
||||
# Delete the old records for all domain/subdomains
|
||||
|
||||
|
@ -281,7 +271,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
|
|||
# should be muc.the.domain.tld. or the.domain.tld
|
||||
if record["value"] == "@":
|
||||
record["value"] = domain
|
||||
record["value"] = record["value"].replace(";","\;")
|
||||
record["value"] = record["value"].replace(";", "\;")
|
||||
|
||||
action = "update add {name}.{domain}. {ttl} {type} {value}".format(domain=domain, **record)
|
||||
action = action.replace(" @.", " ")
|
||||
|
@ -302,8 +292,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
|
|||
command = ["/usr/bin/nsupdate", "-k", key, DYNDNS_ZONE]
|
||||
subprocess.check_call(command)
|
||||
except subprocess.CalledProcessError:
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('dyndns_ip_update_failed'))
|
||||
raise YunohostError('dyndns_ip_update_failed')
|
||||
|
||||
logger.success(m18n.n('dyndns_ip_updated'))
|
||||
|
||||
|
@ -329,7 +318,7 @@ def dyndns_removecron():
|
|||
try:
|
||||
os.remove("/etc/cron.d/yunohost-dyndns")
|
||||
except:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('dyndns_cron_remove_failed'))
|
||||
raise YunohostError('dyndns_cron_remove_failed')
|
||||
|
||||
logger.success(m18n.n('dyndns_cron_removed'))
|
||||
|
||||
|
@ -359,5 +348,4 @@ def _guess_current_dyndns_domain(dyn_host):
|
|||
else:
|
||||
return (_domain, path)
|
||||
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('dyndns_no_domain_registered'))
|
||||
raise YunohostError('dyndns_no_domain_registered')
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import errno
|
||||
try:
|
||||
import miniupnpc
|
||||
except ImportError:
|
||||
|
@ -34,7 +33,7 @@ except ImportError:
|
|||
sys.exit(1)
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils import process
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.text import prependlines
|
||||
|
@ -268,7 +267,7 @@ def firewall_reload(skip_upnp=False):
|
|||
reloaded = True
|
||||
|
||||
if not reloaded:
|
||||
raise MoulinetteError(errno.ESRCH, m18n.n('firewall_reload_failed'))
|
||||
raise YunohostError('firewall_reload_failed')
|
||||
|
||||
hook_callback('post_iptable_rules',
|
||||
args=[upnp, os.path.exists("/proc/net/if_inet6")])
|
||||
|
@ -338,7 +337,7 @@ def firewall_upnp(action='status', no_refresh=False):
|
|||
if action == 'status':
|
||||
no_refresh = True
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('action_invalid', action=action))
|
||||
raise YunohostError('action_invalid', action=action)
|
||||
|
||||
# Refresh port mapping using UPnP
|
||||
if not no_refresh:
|
||||
|
@ -375,10 +374,10 @@ def firewall_upnp(action='status', no_refresh=False):
|
|||
try:
|
||||
# Add new port mapping
|
||||
upnpc.addportmapping(port, protocol, upnpc.lanaddr,
|
||||
port, 'yunohost firewall: port %d' % port, '')
|
||||
port, 'yunohost firewall: port %d' % port, '')
|
||||
except:
|
||||
logger.debug('unable to add port %d using UPnP',
|
||||
port, exc_info=1)
|
||||
port, exc_info=1)
|
||||
enabled = False
|
||||
|
||||
if enabled != firewall['uPnP']['enabled']:
|
||||
|
@ -407,7 +406,7 @@ def firewall_upnp(action='status', no_refresh=False):
|
|||
firewall_reload(skip_upnp=True)
|
||||
|
||||
if action == 'enable' and not enabled:
|
||||
raise MoulinetteError(errno.ENXIO, m18n.n('upnp_port_open_failed'))
|
||||
raise YunohostError('upnp_port_open_failed')
|
||||
return {'enabled': enabled}
|
||||
|
||||
|
||||
|
@ -419,7 +418,7 @@ def firewall_stop():
|
|||
"""
|
||||
|
||||
if os.system("iptables -w -P INPUT ACCEPT") != 0:
|
||||
raise MoulinetteError(errno.ESRCH, m18n.n('iptables_unavailable'))
|
||||
raise YunohostError('iptables_unavailable')
|
||||
|
||||
os.system("iptables -w -F")
|
||||
os.system("iptables -w -X")
|
||||
|
|
|
@ -25,12 +25,11 @@
|
|||
"""
|
||||
import os
|
||||
import re
|
||||
import errno
|
||||
import tempfile
|
||||
from glob import iglob
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils import log
|
||||
|
||||
HOOK_FOLDER = '/usr/share/yunohost/hooks/'
|
||||
|
@ -112,7 +111,7 @@ def hook_info(action, name):
|
|||
})
|
||||
|
||||
if not hooks:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('hook_name_unknown', name=name))
|
||||
raise YunohostError('hook_name_unknown', name=name)
|
||||
return {
|
||||
'action': action,
|
||||
'name': name,
|
||||
|
@ -174,7 +173,7 @@ def hook_list(action, list_by='name', show_info=False):
|
|||
# Add only the name
|
||||
d.add(name)
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('hook_list_by_invalid'))
|
||||
raise YunohostError('hook_list_by_invalid')
|
||||
|
||||
def _append_folder(d, folder):
|
||||
# Iterate over and add hook from a folder
|
||||
|
@ -255,8 +254,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None,
|
|||
try:
|
||||
hl = hooks_names[n]
|
||||
except KeyError:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('hook_name_unknown', n))
|
||||
raise YunohostError('hook_name_unknown', n)
|
||||
# Iterate over hooks with this name
|
||||
for h in hl:
|
||||
# Update hooks dict
|
||||
|
@ -282,7 +280,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None,
|
|||
path=path, args=args)
|
||||
hook_exec(path, args=hook_args, chdir=chdir, env=env,
|
||||
no_trace=no_trace, raise_on_error=True)
|
||||
except MoulinetteError as e:
|
||||
except YunohostError as e:
|
||||
state = 'failed'
|
||||
logger.error(e.strerror, exc_info=1)
|
||||
post_callback(name=name, priority=priority, path=path,
|
||||
|
@ -319,7 +317,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
|
|||
if path[0] != '/':
|
||||
path = os.path.realpath(path)
|
||||
if not os.path.isfile(path):
|
||||
raise MoulinetteError(errno.EIO, m18n.g('file_not_exist', path=path))
|
||||
raise YunohostError('file_not_exist', path=path)
|
||||
|
||||
# Construct command variables
|
||||
cmd_args = ''
|
||||
|
@ -356,7 +354,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
|
|||
# prepend environment variables
|
||||
cmd = '{0} {1}'.format(
|
||||
' '.join(['{0}={1}'.format(k, shell_quote(v))
|
||||
for k, v in env.items()]), cmd)
|
||||
for k, v in env.items()]), cmd)
|
||||
command.append(cmd.format(script=cmd_script, args=cmd_args))
|
||||
|
||||
if logger.isEnabledFor(log.DEBUG):
|
||||
|
@ -371,8 +369,8 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
|
|||
)
|
||||
|
||||
if stdinfo:
|
||||
callbacks = ( callbacks[0], callbacks[1],
|
||||
lambda l: logger.info(l.rstrip()))
|
||||
callbacks = (callbacks[0], callbacks[1],
|
||||
lambda l: logger.info(l.rstrip()))
|
||||
|
||||
logger.debug("About to run the command '%s'" % command)
|
||||
|
||||
|
@ -384,14 +382,12 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
|
|||
# Check and return process' return code
|
||||
if returncode is None:
|
||||
if raise_on_error:
|
||||
raise MoulinetteError(
|
||||
errno.EIO, m18n.n('hook_exec_not_terminated', path=path))
|
||||
raise YunohostError('hook_exec_not_terminated', path=path)
|
||||
else:
|
||||
logger.error(m18n.n('hook_exec_not_terminated', path=path))
|
||||
return 1
|
||||
elif raise_on_error and returncode != 0:
|
||||
raise MoulinetteError(
|
||||
errno.EIO, m18n.n('hook_exec_failed', path=path))
|
||||
raise YunohostError('hook_exec_failed', path=path)
|
||||
return returncode
|
||||
|
||||
|
||||
|
|
|
@ -26,15 +26,13 @@
|
|||
|
||||
import os
|
||||
import yaml
|
||||
import errno
|
||||
import collections
|
||||
|
||||
from datetime import datetime
|
||||
from logging import FileHandler, getLogger, Formatter
|
||||
from sys import exc_info
|
||||
|
||||
from moulinette import m18n, msettings
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_file
|
||||
|
||||
|
@ -148,8 +146,7 @@ def log_display(path, number=50, share=False):
|
|||
log_path = base_path + LOG_FILE_EXT
|
||||
|
||||
if not os.path.exists(md_path) and not os.path.exists(log_path):
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('log_does_exists', log=path))
|
||||
raise YunohostError('log_does_exists', log=path)
|
||||
|
||||
infos = {}
|
||||
|
||||
|
@ -189,7 +186,7 @@ def log_display(path, number=50, share=False):
|
|||
if os.path.exists(log_path):
|
||||
logger.warning(error)
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, error)
|
||||
raise YunohostError(error)
|
||||
|
||||
# Display logs if exist
|
||||
if os.path.exists(log_path):
|
||||
|
@ -285,6 +282,7 @@ def is_unit_operation(entities=['app', 'domain', 'service', 'user'],
|
|||
|
||||
|
||||
class OperationLogger(object):
|
||||
|
||||
"""
|
||||
Instances of this class represents unit operation done on the ynh instance.
|
||||
|
||||
|
@ -316,7 +314,7 @@ class OperationLogger(object):
|
|||
"""
|
||||
|
||||
if self.started_at is None:
|
||||
self.started_at = datetime.now()
|
||||
self.started_at = datetime.utcnow()
|
||||
self.flush()
|
||||
self._register_log()
|
||||
|
||||
|
@ -408,7 +406,7 @@ class OperationLogger(object):
|
|||
return
|
||||
if error is not None and not isinstance(error, basestring):
|
||||
error = str(error)
|
||||
self.ended_at = datetime.now()
|
||||
self.ended_at = datetime.utcnow()
|
||||
self._error = error
|
||||
self._success = error is None
|
||||
if self.logger is not None:
|
||||
|
@ -425,7 +423,7 @@ class OperationLogger(object):
|
|||
else:
|
||||
if is_api:
|
||||
msg = "<strong>" + m18n.n('log_link_to_failed_log',
|
||||
name=self.name, desc=desc) + "</strong>"
|
||||
name=self.name, desc=desc) + "</strong>"
|
||||
else:
|
||||
msg = m18n.n('log_help_to_get_failed_log', name=self.name,
|
||||
desc=desc)
|
||||
|
|
|
@ -31,14 +31,13 @@ import calendar
|
|||
import subprocess
|
||||
import xmlrpclib
|
||||
import os.path
|
||||
import errno
|
||||
import os
|
||||
import dns.resolver
|
||||
import cPickle as pickle
|
||||
from datetime import datetime
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.utils.network import get_public_ip
|
||||
|
@ -83,7 +82,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False):
|
|||
result_dname = dn
|
||||
if len(devices) == 0:
|
||||
if mountpoint is not None:
|
||||
raise MoulinetteError(errno.ENODEV, m18n.n('mountpoint_unknown'))
|
||||
raise YunohostError('mountpoint_unknown')
|
||||
return result
|
||||
|
||||
# Retrieve monitoring for unit(s)
|
||||
|
@ -141,7 +140,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False):
|
|||
for dname in devices_names:
|
||||
_set(dname, 'not-available')
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown', unit=u))
|
||||
raise YunohostError('unit_unknown', unit=u)
|
||||
|
||||
if result_dname is not None:
|
||||
return result[result_dname]
|
||||
|
@ -237,7 +236,7 @@ def monitor_network(units=None, human_readable=False):
|
|||
'gateway': gateway,
|
||||
}
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown', unit=u))
|
||||
raise YunohostError('unit_unknown', unit=u)
|
||||
|
||||
if len(units) == 1:
|
||||
return result[units[0]]
|
||||
|
@ -287,9 +286,9 @@ def monitor_system(units=None, human_readable=False):
|
|||
elif u == 'infos':
|
||||
result[u] = json.loads(glances.getSystem())
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown', unit=u))
|
||||
raise YunohostError('unit_unknown', unit=u)
|
||||
|
||||
if len(units) == 1 and type(result[units[0]]) is not str:
|
||||
if len(units) == 1 and not isinstance(result[units[0]], str):
|
||||
return result[units[0]]
|
||||
return result
|
||||
|
||||
|
@ -303,7 +302,7 @@ def monitor_update_stats(period):
|
|||
|
||||
"""
|
||||
if period not in ['day', 'week', 'month']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('monitor_period_invalid'))
|
||||
raise YunohostError('monitor_period_invalid')
|
||||
|
||||
stats = _retrieve_stats(period)
|
||||
if not stats:
|
||||
|
@ -321,7 +320,7 @@ def monitor_update_stats(period):
|
|||
else:
|
||||
monitor = _monitor_all(p, 0)
|
||||
if not monitor:
|
||||
raise MoulinetteError(errno.ENODATA, m18n.n('monitor_stats_no_update'))
|
||||
raise YunohostError('monitor_stats_no_update')
|
||||
|
||||
stats['timestamp'].append(time.time())
|
||||
|
||||
|
@ -386,15 +385,13 @@ def monitor_show_stats(period, date=None):
|
|||
|
||||
"""
|
||||
if period not in ['day', 'week', 'month']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('monitor_period_invalid'))
|
||||
raise YunohostError('monitor_period_invalid')
|
||||
|
||||
result = _retrieve_stats(period, date)
|
||||
if result is False:
|
||||
raise MoulinetteError(errno.ENOENT,
|
||||
m18n.n('monitor_stats_file_not_found'))
|
||||
raise YunohostError('monitor_stats_file_not_found')
|
||||
elif result is None:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('monitor_stats_period_unavailable'))
|
||||
raise YunohostError('monitor_stats_period_unavailable')
|
||||
return result
|
||||
|
||||
|
||||
|
@ -407,7 +404,7 @@ def monitor_enable(with_stats=False):
|
|||
|
||||
"""
|
||||
from yunohost.service import (service_status, service_enable,
|
||||
service_start)
|
||||
service_start)
|
||||
|
||||
glances = service_status('glances')
|
||||
if glances['status'] != 'running':
|
||||
|
@ -417,7 +414,7 @@ def monitor_enable(with_stats=False):
|
|||
|
||||
# Install crontab
|
||||
if with_stats:
|
||||
# day: every 5 min # week: every 1 h # month: every 4 h #
|
||||
# day: every 5 min # week: every 1 h # month: every 4 h #
|
||||
rules = ('*/5 * * * * root {cmd} day >> /dev/null\n'
|
||||
'3 * * * * root {cmd} week >> /dev/null\n'
|
||||
'6 */4 * * * root {cmd} month >> /dev/null').format(
|
||||
|
@ -434,7 +431,7 @@ def monitor_disable():
|
|||
|
||||
"""
|
||||
from yunohost.service import (service_status, service_disable,
|
||||
service_stop)
|
||||
service_stop)
|
||||
|
||||
glances = service_status('glances')
|
||||
if glances['status'] != 'inactive':
|
||||
|
@ -442,7 +439,7 @@ def monitor_disable():
|
|||
if glances['loaded'] != 'disabled':
|
||||
try:
|
||||
service_disable('glances')
|
||||
except MoulinetteError as e:
|
||||
except YunohostError as e:
|
||||
logger.warning(e.strerror)
|
||||
|
||||
# Remove crontab
|
||||
|
@ -470,8 +467,8 @@ def _get_glances_api():
|
|||
from yunohost.service import service_status
|
||||
|
||||
if service_status('glances')['status'] != 'running':
|
||||
raise MoulinetteError(errno.EPERM, m18n.n('monitor_not_enabled'))
|
||||
raise MoulinetteError(errno.EIO, m18n.n('monitor_glances_con_failed'))
|
||||
raise YunohostError('monitor_not_enabled')
|
||||
raise YunohostError('monitor_glances_con_failed')
|
||||
|
||||
|
||||
def _extract_inet(string, skip_netmask=False, skip_loopback=True):
|
||||
|
|
|
@ -28,7 +28,6 @@ import time
|
|||
import yaml
|
||||
import json
|
||||
import subprocess
|
||||
import errno
|
||||
import shutil
|
||||
import hashlib
|
||||
|
||||
|
@ -36,11 +35,11 @@ from difflib import unified_diff
|
|||
from datetime import datetime
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils import log, filesystem
|
||||
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.log import is_unit_operation
|
||||
from yunohost.hook import hook_callback, hook_list
|
||||
|
||||
BASE_CONF_PATH = '/home/yunohost.conf'
|
||||
BACKUP_CONF_DIR = os.path.join(BASE_CONF_PATH, 'backup')
|
||||
|
@ -85,7 +84,7 @@ def service_add(name, status=None, log=None, runlevel=None, need_lock=False, des
|
|||
_save_services(services)
|
||||
except:
|
||||
# we'll get a logger.warning with more details in _save_services
|
||||
raise MoulinetteError(errno.EIO, m18n.n('service_add_failed', service=name))
|
||||
raise YunohostError('service_add_failed', service=name)
|
||||
|
||||
logger.success(m18n.n('service_added', service=name))
|
||||
|
||||
|
@ -103,13 +102,13 @@ def service_remove(name):
|
|||
try:
|
||||
del services[name]
|
||||
except KeyError:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=name))
|
||||
raise YunohostError('service_unknown', service=name)
|
||||
|
||||
try:
|
||||
_save_services(services)
|
||||
except:
|
||||
# we'll get a logger.warning with more details in _save_services
|
||||
raise MoulinetteError(errno.EIO, m18n.n('service_remove_failed', service=name))
|
||||
raise YunohostError('service_remove_failed', service=name)
|
||||
|
||||
logger.success(m18n.n('service_removed', service=name))
|
||||
|
||||
|
@ -130,10 +129,7 @@ def service_start(names):
|
|||
logger.success(m18n.n('service_started', service=name))
|
||||
else:
|
||||
if service_status(name)['status'] != 'running':
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('service_start_failed',
|
||||
service=name,
|
||||
logs=_get_journalctl_logs(name)))
|
||||
raise YunohostError('service_start_failed', service=name, logs=_get_journalctl_logs(name))
|
||||
logger.debug(m18n.n('service_already_started', service=name))
|
||||
|
||||
|
||||
|
@ -152,12 +148,10 @@ def service_stop(names):
|
|||
logger.success(m18n.n('service_stopped', service=name))
|
||||
else:
|
||||
if service_status(name)['status'] != 'inactive':
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('service_stop_failed',
|
||||
service=name,
|
||||
logs=_get_journalctl_logs(name)))
|
||||
raise YunohostError('service_stop_failed', service=name, logs=_get_journalctl_logs(name))
|
||||
logger.debug(m18n.n('service_already_stopped', service=name))
|
||||
|
||||
|
||||
@is_unit_operation()
|
||||
def service_enable(operation_logger, names):
|
||||
"""
|
||||
|
@ -174,10 +168,7 @@ def service_enable(operation_logger, names):
|
|||
if _run_service_command('enable', name):
|
||||
logger.success(m18n.n('service_enabled', service=name))
|
||||
else:
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('service_enable_failed',
|
||||
service=name,
|
||||
logs=_get_journalctl_logs(name)))
|
||||
raise YunohostError('service_enable_failed', service=name, logs=_get_journalctl_logs(name))
|
||||
|
||||
|
||||
def service_disable(names):
|
||||
|
@ -194,10 +185,7 @@ def service_disable(names):
|
|||
if _run_service_command('disable', name):
|
||||
logger.success(m18n.n('service_disabled', service=name))
|
||||
else:
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('service_disable_failed',
|
||||
service=name,
|
||||
logs=_get_journalctl_logs(name)))
|
||||
raise YunohostError('service_disable_failed', service=name, logs=_get_journalctl_logs(name))
|
||||
|
||||
|
||||
def service_status(names=[]):
|
||||
|
@ -220,8 +208,7 @@ def service_status(names=[]):
|
|||
|
||||
for name in names:
|
||||
if check_names and name not in services.keys():
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('service_unknown', service=name))
|
||||
raise YunohostError('service_unknown', service=name)
|
||||
|
||||
# this "service" isn't a service actually so we skip it
|
||||
#
|
||||
|
@ -248,10 +235,7 @@ def service_status(names=[]):
|
|||
'status': "unknown",
|
||||
'loaded': "unknown",
|
||||
'active': "unknown",
|
||||
'active_at': {
|
||||
"timestamp": "unknown",
|
||||
"human": "unknown",
|
||||
},
|
||||
'active_at': "unknown",
|
||||
'description': "Error: failed to get information for this service, it doesn't exists for systemd",
|
||||
'service_file_path': "unknown",
|
||||
}
|
||||
|
@ -273,13 +257,13 @@ def service_status(names=[]):
|
|||
'status': str(status.get("SubState", "unknown")),
|
||||
'loaded': "enabled" if str(status.get("LoadState", "unknown")) == "loaded" else str(status.get("LoadState", "unknown")),
|
||||
'active': str(status.get("ActiveState", "unknown")),
|
||||
'active_at': {
|
||||
"timestamp": str(status.get("ActiveEnterTimestamp", "unknown")),
|
||||
"human": datetime.fromtimestamp(status["ActiveEnterTimestamp"] / 1000000).strftime("%F %X") if "ActiveEnterTimestamp" in status else "unknown",
|
||||
},
|
||||
'description': description,
|
||||
'service_file_path': str(status.get("FragmentPath", "unknown")),
|
||||
}
|
||||
if "ActiveEnterTimestamp" in status:
|
||||
result[name]['active_at'] = datetime.utcfromtimestamp(status["ActiveEnterTimestamp"] / 1000000)
|
||||
else:
|
||||
result[name]['active_at'] = "unknown"
|
||||
|
||||
if len(names) == 1:
|
||||
return result[names[0]]
|
||||
|
@ -293,7 +277,7 @@ def _get_service_information_from_systemd(service):
|
|||
|
||||
d = dbus.SystemBus()
|
||||
|
||||
systemd = d.get_object('org.freedesktop.systemd1','/org/freedesktop/systemd1')
|
||||
systemd = d.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1')
|
||||
manager = dbus.Interface(systemd, 'org.freedesktop.systemd1.Manager')
|
||||
|
||||
try:
|
||||
|
@ -323,10 +307,10 @@ def service_log(name, number=50):
|
|||
services = _get_services()
|
||||
|
||||
if name not in services.keys():
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=name))
|
||||
raise YunohostError('service_unknown', service=name)
|
||||
|
||||
if 'log' not in services[name]:
|
||||
raise MoulinetteError(errno.EPERM, m18n.n('service_no_log', service=name))
|
||||
raise YunohostError('service_no_log', service=name)
|
||||
|
||||
log_list = services[name]['log']
|
||||
|
||||
|
@ -394,7 +378,7 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False,
|
|||
if not names:
|
||||
operation_logger.name_parameter_override = 'all'
|
||||
elif len(names) != 1:
|
||||
operation_logger.name_parameter_override = str(len(operation_logger.related_to))+'_services'
|
||||
operation_logger.name_parameter_override = str(len(operation_logger.related_to)) + '_services'
|
||||
operation_logger.start()
|
||||
|
||||
# Clean pending conf directory
|
||||
|
@ -406,7 +390,7 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False,
|
|||
shutil.rmtree(os.path.join(PENDING_CONF_DIR, name),
|
||||
ignore_errors=True)
|
||||
else:
|
||||
filesystem.mkdir(PENDING_CONF_DIR, 0755, True)
|
||||
filesystem.mkdir(PENDING_CONF_DIR, 0o755, True)
|
||||
|
||||
# Format common hooks arguments
|
||||
common_args = [1 if force else 0, 1 if dry_run else 0]
|
||||
|
@ -417,20 +401,25 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False,
|
|||
def _pre_call(name, priority, path, args):
|
||||
# create the pending conf directory for the service
|
||||
service_pending_path = os.path.join(PENDING_CONF_DIR, name)
|
||||
filesystem.mkdir(service_pending_path, 0755, True, uid='root')
|
||||
filesystem.mkdir(service_pending_path, 0o755, True, uid='root')
|
||||
|
||||
# return the arguments to pass to the script
|
||||
return pre_args + [service_pending_path, ]
|
||||
|
||||
# Don't regen SSH if not specifically specified
|
||||
if not names:
|
||||
names = hook_list('conf_regen', list_by='name',
|
||||
show_info=False)['hooks']
|
||||
names.remove('ssh')
|
||||
|
||||
pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call)
|
||||
|
||||
# Update the services name
|
||||
names = pre_result['succeed'].keys()
|
||||
|
||||
if not names:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('service_regenconf_failed',
|
||||
services=', '.join(pre_result['failed'])))
|
||||
raise YunohostError('service_regenconf_failed',
|
||||
services=', '.join(pre_result['failed']))
|
||||
|
||||
# Set the processing method
|
||||
_regen = _process_regen_conf if not dry_run else lambda *a, **k: True
|
||||
|
@ -502,8 +491,8 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False,
|
|||
# we assume that it is safe to regen it, since the file is backuped
|
||||
# anyway (by default in _regen), as long as we warn the user
|
||||
# appropriately.
|
||||
logger.info(m18n.n('service_conf_new_managed_file',
|
||||
conf=system_path, service=service))
|
||||
logger.info(m18n.n('service_conf_now_managed_by_yunohost',
|
||||
conf=system_path))
|
||||
regenerated = _regen(system_path, pending_path)
|
||||
conf_status = 'new'
|
||||
elif force:
|
||||
|
@ -606,7 +595,7 @@ def _run_service_command(action, service):
|
|||
"""
|
||||
services = _get_services()
|
||||
if service not in services.keys():
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=service))
|
||||
raise YunohostError('service_unknown', service=service)
|
||||
|
||||
possible_actions = ['start', 'stop', 'restart', 'reload', 'enable', 'disable']
|
||||
if action not in possible_actions:
|
||||
|
@ -615,7 +604,7 @@ def _run_service_command(action, service):
|
|||
cmd = 'systemctl %s %s' % (action, service)
|
||||
|
||||
need_lock = services[service].get('need_lock', False) \
|
||||
and action in ['start', 'stop', 'restart', 'reload']
|
||||
and action in ['start', 'stop', 'restart', 'reload']
|
||||
|
||||
try:
|
||||
# Launch the command
|
||||
|
@ -649,10 +638,10 @@ def _give_lock(action, service, p):
|
|||
else:
|
||||
systemctl_PID_name = "ControlPID"
|
||||
|
||||
cmd_get_son_PID ="systemctl show %s -p %s" % (service, systemctl_PID_name)
|
||||
cmd_get_son_PID = "systemctl show %s -p %s" % (service, systemctl_PID_name)
|
||||
son_PID = 0
|
||||
# As long as we did not found the PID and that the command is still running
|
||||
while son_PID == 0 and p.poll() == None:
|
||||
while son_PID == 0 and p.poll() is None:
|
||||
# Call systemctl to get the PID
|
||||
# Output of the command is e.g. ControlPID=1234
|
||||
son_PID = subprocess.check_output(cmd_get_son_PID.split()) \
|
||||
|
@ -669,11 +658,12 @@ def _give_lock(action, service, p):
|
|||
|
||||
return son_PID
|
||||
|
||||
|
||||
def _remove_lock(PID_to_remove):
|
||||
# FIXME ironically not concurrency safe because it's not atomic...
|
||||
|
||||
PIDs = filesystem.read_file(MOULINETTE_LOCK).split("\n")
|
||||
PIDs_to_keep = [ PID for PID in PIDs if int(PID) != PID_to_remove ]
|
||||
PIDs_to_keep = [PID for PID in PIDs if int(PID) != PID_to_remove]
|
||||
filesystem.write_to_file(MOULINETTE_LOCK, '\n'.join(PIDs_to_keep))
|
||||
|
||||
|
||||
|
@ -787,6 +777,7 @@ def _find_previous_log_file(file):
|
|||
|
||||
return None
|
||||
|
||||
|
||||
def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True):
|
||||
"""Compare two files and return the differences
|
||||
|
||||
|
@ -927,26 +918,26 @@ def _process_regen_conf(system_conf, new_conf=None, save=True):
|
|||
"""
|
||||
if save:
|
||||
backup_path = os.path.join(BACKUP_CONF_DIR, '{0}-{1}'.format(
|
||||
system_conf.lstrip('/'), time.strftime("%Y%m%d.%H%M%S")))
|
||||
system_conf.lstrip('/'), datetime.utcnow().strftime("%Y%m%d.%H%M%S")))
|
||||
backup_dir = os.path.dirname(backup_path)
|
||||
|
||||
if not os.path.isdir(backup_dir):
|
||||
filesystem.mkdir(backup_dir, 0755, True)
|
||||
filesystem.mkdir(backup_dir, 0o755, True)
|
||||
|
||||
shutil.copy2(system_conf, backup_path)
|
||||
logger.debug(m18n.n('service_conf_file_backed_up',
|
||||
conf=system_conf, backup=backup_path))
|
||||
conf=system_conf, backup=backup_path))
|
||||
|
||||
try:
|
||||
if not new_conf:
|
||||
os.remove(system_conf)
|
||||
logger.debug(m18n.n('service_conf_file_removed',
|
||||
conf=system_conf))
|
||||
conf=system_conf))
|
||||
else:
|
||||
system_dir = os.path.dirname(system_conf)
|
||||
|
||||
if not os.path.isdir(system_dir):
|
||||
filesystem.mkdir(system_dir, 0755, True)
|
||||
filesystem.mkdir(system_dir, 0o755, True)
|
||||
|
||||
shutil.copyfile(new_conf, system_conf)
|
||||
logger.debug(m18n.n('service_conf_file_updated',
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import os
|
||||
import json
|
||||
import errno
|
||||
|
||||
from datetime import datetime
|
||||
from collections import OrderedDict
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
logger = getActionLogger('yunohost.settings')
|
||||
|
@ -39,6 +38,7 @@ DEFAULTS = OrderedDict([
|
|||
# -1 disabled, 0 alert if listed, 1 8-letter, 2 normal, 3 strong, 4 strongest
|
||||
("security.password.admin.strength", {"type": "int", "default": 1}),
|
||||
("security.password.user.strength", {"type": "int", "default": 1}),
|
||||
("service.ssh.allow_deprecated_dsa_hostkey", {"type": "bool", "default": False}),
|
||||
])
|
||||
|
||||
|
||||
|
@ -53,8 +53,7 @@ def settings_get(key, full=False):
|
|||
settings = _get_settings()
|
||||
|
||||
if key not in settings:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_key_doesnt_exists', settings_key=key))
|
||||
raise YunohostError('global_settings_key_doesnt_exists', settings_key=key)
|
||||
|
||||
if full:
|
||||
return settings[key]
|
||||
|
@ -82,39 +81,39 @@ def settings_set(key, value):
|
|||
settings = _get_settings()
|
||||
|
||||
if key not in settings:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_key_doesnt_exists', settings_key=key))
|
||||
raise YunohostError('global_settings_key_doesnt_exists', settings_key=key)
|
||||
|
||||
key_type = settings[key]["type"]
|
||||
|
||||
if key_type == "bool":
|
||||
if not isinstance(value, bool):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_bad_type_for_setting', setting=key,
|
||||
received_type=type(value).__name__, expected_type=key_type))
|
||||
raise YunohostError('global_settings_bad_type_for_setting', setting=key,
|
||||
received_type=type(value).__name__, expected_type=key_type)
|
||||
elif key_type == "int":
|
||||
if not isinstance(value, int) or isinstance(value, bool):
|
||||
if isinstance(value, str):
|
||||
value=int(value)
|
||||
try:
|
||||
value = int(value)
|
||||
except:
|
||||
raise YunohostError('global_settings_bad_type_for_setting',
|
||||
setting=key,
|
||||
received_type=type(value).__name__,
|
||||
expected_type=key_type)
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_bad_type_for_setting', setting=key,
|
||||
received_type=type(value).__name__, expected_type=key_type))
|
||||
raise YunohostError('global_settings_bad_type_for_setting', setting=key,
|
||||
received_type=type(value).__name__, expected_type=key_type)
|
||||
elif key_type == "string":
|
||||
if not isinstance(value, basestring):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_bad_type_for_setting', setting=key,
|
||||
received_type=type(value).__name__, expected_type=key_type))
|
||||
raise YunohostError('global_settings_bad_type_for_setting', setting=key,
|
||||
received_type=type(value).__name__, expected_type=key_type)
|
||||
elif key_type == "enum":
|
||||
if value not in settings[key]["choices"]:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_bad_choice_for_enum', setting=key,
|
||||
received_type=type(value).__name__,
|
||||
expected_type=", ".join(settings[key]["choices"])))
|
||||
raise YunohostError('global_settings_bad_choice_for_enum', setting=key,
|
||||
received_type=type(value).__name__,
|
||||
expected_type=", ".join(settings[key]["choices"]))
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_unknown_type', setting=key,
|
||||
unknown_type=key_type))
|
||||
raise YunohostError('global_settings_unknown_type', setting=key,
|
||||
unknown_type=key_type)
|
||||
|
||||
settings[key]["value"] = value
|
||||
|
||||
|
@ -132,8 +131,7 @@ def settings_reset(key):
|
|||
settings = _get_settings()
|
||||
|
||||
if key not in settings:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n(
|
||||
'global_settings_key_doesnt_exists', settings_key=key))
|
||||
raise YunohostError('global_settings_key_doesnt_exists', settings_key=key)
|
||||
|
||||
settings[key]["value"] = settings[key]["default"]
|
||||
_save_settings(settings)
|
||||
|
@ -154,7 +152,7 @@ def settings_reset_all():
|
|||
# addition but we'll see if this is a common need.
|
||||
# Another solution would be to use etckeeper and integrate those
|
||||
# modification inside of it and take advantage of its git history
|
||||
old_settings_backup_path = SETTINGS_PATH_OTHER_LOCATION % datetime.now().strftime("%F_%X")
|
||||
old_settings_backup_path = SETTINGS_PATH_OTHER_LOCATION % datetime.utcnow().strftime("%F_%X")
|
||||
_save_settings(settings, location=old_settings_backup_path)
|
||||
|
||||
for value in settings.values():
|
||||
|
@ -209,8 +207,7 @@ def _get_settings():
|
|||
setting_key=key))
|
||||
unknown_settings[key] = value
|
||||
except Exception as e:
|
||||
raise MoulinetteError(errno.EIO, m18n.n('global_settings_cant_open_settings', reason=e),
|
||||
exc_info=1)
|
||||
raise YunohostError('global_settings_cant_open_settings', reason=e)
|
||||
|
||||
if unknown_settings:
|
||||
try:
|
||||
|
@ -231,14 +228,10 @@ def _save_settings(settings, location=SETTINGS_PATH):
|
|||
try:
|
||||
result = json.dumps(settings_without_description, indent=4)
|
||||
except Exception as e:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('global_settings_cant_serialize_settings', reason=e),
|
||||
exc_info=1)
|
||||
raise YunohostError('global_settings_cant_serialize_settings', reason=e)
|
||||
|
||||
try:
|
||||
with open(location, "w") as settings_fd:
|
||||
settings_fd.write(result)
|
||||
except Exception as e:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
m18n.n('global_settings_cant_write_settings', reason=e),
|
||||
exc_info=1)
|
||||
raise YunohostError('global_settings_cant_write_settings', reason=e)
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
import re
|
||||
import os
|
||||
import errno
|
||||
import pwd
|
||||
import subprocess
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, mkdir
|
||||
|
||||
SSHD_CONFIG_PATH = "/etc/ssh/sshd_config"
|
||||
|
@ -23,7 +21,7 @@ def user_ssh_allow(auth, username):
|
|||
# TODO it would be good to support different kind of shells
|
||||
|
||||
if not _get_user_for_ssh(auth, username):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username))
|
||||
raise YunohostError('user_unknown', user=username)
|
||||
|
||||
auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'})
|
||||
|
||||
|
@ -42,7 +40,7 @@ def user_ssh_disallow(auth, username):
|
|||
# TODO it would be good to support different kind of shells
|
||||
|
||||
if not _get_user_for_ssh(auth, username):
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username))
|
||||
raise YunohostError('user_unknown', user=username)
|
||||
|
||||
auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'})
|
||||
|
||||
|
@ -99,7 +97,7 @@ def user_ssh_add_key(auth, username, key, comment):
|
|||
# create empty file to set good permissions
|
||||
write_to_file(authorized_keys_file, "")
|
||||
chown(authorized_keys_file, uid=user["uid"][0])
|
||||
chmod(authorized_keys_file, 0600)
|
||||
chmod(authorized_keys_file, 0o600)
|
||||
|
||||
authorized_keys_content = read_file(authorized_keys_file)
|
||||
|
||||
|
|
|
@ -7,12 +7,14 @@ sys.path.append("..")
|
|||
def pytest_addoption(parser):
|
||||
parser.addoption("--yunodebug", action="store_true", default=False)
|
||||
|
||||
###############################################################################
|
||||
# Tweak translator to raise exceptions if string keys are not defined #
|
||||
###############################################################################
|
||||
#
|
||||
# Tweak translator to raise exceptions if string keys are not defined #
|
||||
#
|
||||
|
||||
|
||||
old_translate = moulinette.core.Translator.translate
|
||||
|
||||
|
||||
def new_translate(self, key, *args, **kwargs):
|
||||
|
||||
if key not in self._translations[self.default_locale].keys():
|
||||
|
@ -21,14 +23,15 @@ def new_translate(self, key, *args, **kwargs):
|
|||
return old_translate(self, key, *args, **kwargs)
|
||||
moulinette.core.Translator.translate = new_translate
|
||||
|
||||
|
||||
def new_m18nn(self, key, *args, **kwargs):
|
||||
return self._namespaces[self._current_namespace].translate(key, *args, **kwargs)
|
||||
|
||||
moulinette.core.Moulinette18n.n = new_m18nn
|
||||
|
||||
###############################################################################
|
||||
# Init the moulinette to have the cli loggers stuff #
|
||||
###############################################################################
|
||||
#
|
||||
# Init the moulinette to have the cli loggers stuff #
|
||||
#
|
||||
|
||||
|
||||
def pytest_cmdline_main(config):
|
||||
|
|
|
@ -5,7 +5,7 @@ import requests_mock
|
|||
import glob
|
||||
import time
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
from yunohost.app import app_fetchlist, app_removelist, app_listlists, _using_legacy_appslist_system, _migrate_appslist_system, _register_new_appslist
|
||||
|
||||
|
@ -17,7 +17,7 @@ APPSLISTS_JSON = '/etc/yunohost/appslists.json'
|
|||
def setup_function(function):
|
||||
|
||||
# Clear all appslist
|
||||
files = glob.glob(REPO_PATH+"/*")
|
||||
files = glob.glob(REPO_PATH + "/*")
|
||||
for f in files:
|
||||
os.remove(f)
|
||||
|
||||
|
@ -42,9 +42,9 @@ def cron_job_is_there():
|
|||
return r == 0
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Test listing of appslists and registering of appslists #
|
||||
###############################################################################
|
||||
#
|
||||
# Test listing of appslists and registering of appslists #
|
||||
#
|
||||
|
||||
|
||||
def test_appslist_list_empty():
|
||||
|
@ -79,7 +79,7 @@ def test_appslist_list_register_conflict_name():
|
|||
"""
|
||||
|
||||
_register_new_appslist("https://lol.com/appslist.json", "dummy")
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
_register_new_appslist("https://lol.com/appslist2.json", "dummy")
|
||||
|
||||
appslist_dict = app_listlists()
|
||||
|
@ -94,7 +94,7 @@ def test_appslist_list_register_conflict_url():
|
|||
"""
|
||||
|
||||
_register_new_appslist("https://lol.com/appslist.json", "dummy")
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
_register_new_appslist("https://lol.com/appslist.json", "plopette")
|
||||
|
||||
appslist_dict = app_listlists()
|
||||
|
@ -103,9 +103,9 @@ def test_appslist_list_register_conflict_url():
|
|||
assert "plopette" not in appslist_dict.keys()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Test fetching of appslists #
|
||||
###############################################################################
|
||||
#
|
||||
# Test fetching of appslists #
|
||||
#
|
||||
|
||||
|
||||
def test_appslist_fetch():
|
||||
|
@ -161,7 +161,7 @@ def test_appslist_fetch_unknownlist():
|
|||
|
||||
assert app_listlists() == {}
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
app_fetchlist(name="swag")
|
||||
|
||||
|
||||
|
@ -170,7 +170,7 @@ def test_appslist_fetch_url_but_no_name():
|
|||
Do a fetchlist with url given, but no name given
|
||||
"""
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
app_fetchlist(url=URL_OFFICIAL_APP_LIST)
|
||||
|
||||
|
||||
|
@ -244,9 +244,9 @@ def test_appslist_fetch_timeout():
|
|||
app_fetchlist()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Test remove of appslist #
|
||||
###############################################################################
|
||||
#
|
||||
# Test remove of appslist #
|
||||
#
|
||||
|
||||
|
||||
def test_appslist_remove():
|
||||
|
@ -270,13 +270,13 @@ def test_appslist_remove_unknown():
|
|||
Attempt to remove an unknown list
|
||||
"""
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
app_removelist("dummy")
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Test migration from legacy appslist system #
|
||||
###############################################################################
|
||||
#
|
||||
# Test migration from legacy appslist system #
|
||||
#
|
||||
|
||||
|
||||
def add_legacy_cron(name, url):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from moulinette.core import MoulinetteError, init_authenticator
|
||||
|
||||
from moulinette.core import init_authenticator
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.app import app_install, app_remove
|
||||
from yunohost.domain import _get_maindomain, domain_url_available, _normalize_domain_path
|
||||
|
||||
|
@ -22,6 +22,7 @@ def setup_function(function):
|
|||
except:
|
||||
pass
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
try:
|
||||
|
@ -43,25 +44,25 @@ def test_urlavailable():
|
|||
assert domain_url_available(auth, maindomain, "/macnuggets")
|
||||
|
||||
# We don't know the domain yolo.swag
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
assert domain_url_available(auth, "yolo.swag", "/macnuggets")
|
||||
|
||||
|
||||
def test_registerurl():
|
||||
|
||||
app_install(auth, "./tests/apps/register_url_app_ynh",
|
||||
args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"))
|
||||
args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"))
|
||||
|
||||
assert not domain_url_available(auth, maindomain, "/urlregisterapp")
|
||||
|
||||
# Try installing at same location
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
app_install(auth, "./tests/apps/register_url_app_ynh",
|
||||
args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"))
|
||||
args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"))
|
||||
|
||||
|
||||
def test_registerurl_baddomain():
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
app_install(auth, "./tests/apps/register_url_app_ynh",
|
||||
args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp"))
|
||||
args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp"))
|
||||
|
|
|
@ -12,18 +12,22 @@ from yunohost.app import app_install, app_remove, app_ssowatconf
|
|||
from yunohost.app import _is_installed
|
||||
from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete
|
||||
from yunohost.domain import _get_maindomain
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
# Get main domain
|
||||
maindomain = _get_maindomain()
|
||||
maindomain = ""
|
||||
|
||||
# Instantiate LDAP Authenticator
|
||||
AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous')
|
||||
AUTH_PARAMETERS = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'}
|
||||
auth = None
|
||||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
global maindomain
|
||||
maindomain = _get_maindomain()
|
||||
|
||||
print ""
|
||||
|
||||
global auth
|
||||
|
@ -84,9 +88,9 @@ def teardown_function(function):
|
|||
shutil.rmtree("/opt/test_backup_output_directory")
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Helpers #
|
||||
###############################################################################
|
||||
#
|
||||
# Helpers #
|
||||
#
|
||||
|
||||
def app_is_installed(app):
|
||||
|
||||
|
@ -108,6 +112,7 @@ def backup_test_dependencies_are_met():
|
|||
|
||||
return True
|
||||
|
||||
|
||||
def tmp_backup_directory_is_empty():
|
||||
|
||||
if not os.path.exists("/home/yunohost.backup/tmp/"):
|
||||
|
@ -115,6 +120,7 @@ def tmp_backup_directory_is_empty():
|
|||
else:
|
||||
return len(os.listdir('/home/yunohost.backup/tmp/')) == 0
|
||||
|
||||
|
||||
def clean_tmp_backup_directory():
|
||||
|
||||
if tmp_backup_directory_is_empty():
|
||||
|
@ -122,10 +128,10 @@ def clean_tmp_backup_directory():
|
|||
|
||||
mount_lines = subprocess.check_output("mount").split("\n")
|
||||
|
||||
points_to_umount = [ line.split(" ")[2]
|
||||
for line in mount_lines
|
||||
if len(line) >= 3
|
||||
and line.split(" ")[2].startswith("/home/yunohost.backup/tmp") ]
|
||||
points_to_umount = [line.split(" ")[2]
|
||||
for line in mount_lines
|
||||
if len(line) >= 3
|
||||
and line.split(" ")[2].startswith("/home/yunohost.backup/tmp")]
|
||||
|
||||
for point in reversed(points_to_umount):
|
||||
os.system("umount %s" % point)
|
||||
|
@ -135,6 +141,7 @@ def clean_tmp_backup_directory():
|
|||
|
||||
shutil.rmtree("/home/yunohost.backup/tmp/")
|
||||
|
||||
|
||||
def reset_ssowat_conf():
|
||||
|
||||
# Make sure we have a ssowat
|
||||
|
@ -188,14 +195,15 @@ def add_archive_system_from_2p4():
|
|||
os.system("cp ./tests/apps/backup_system_from_2p4/backup.tar.gz \
|
||||
/home/yunohost.backup/archives/backup_system_from_2p4.tar.gz")
|
||||
|
||||
###############################################################################
|
||||
# System backup #
|
||||
###############################################################################
|
||||
#
|
||||
# System backup #
|
||||
#
|
||||
|
||||
|
||||
def test_backup_only_ldap():
|
||||
|
||||
# Create the backup
|
||||
backup_create(ignore_system=False, ignore_apps=True, system=["conf_ldap"])
|
||||
backup_create(system=["conf_ldap"], apps=None)
|
||||
|
||||
archives = backup_list()["archives"]
|
||||
assert len(archives) == 1
|
||||
|
@ -211,20 +219,21 @@ def test_backup_system_part_that_does_not_exists(mocker):
|
|||
mocker.spy(m18n, "n")
|
||||
|
||||
# Create the backup
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_create(ignore_system=False, ignore_apps=True, system=["yolol"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_create(system=["yolol"], apps=None)
|
||||
|
||||
m18n.n.assert_any_call('backup_hook_unknown', hook="yolol")
|
||||
m18n.n.assert_any_call('backup_nothings_done')
|
||||
|
||||
###############################################################################
|
||||
# System backup and restore #
|
||||
###############################################################################
|
||||
#
|
||||
# System backup and restore #
|
||||
#
|
||||
|
||||
|
||||
def test_backup_and_restore_all_sys():
|
||||
|
||||
# Create the backup
|
||||
backup_create(ignore_system=False, ignore_apps=True)
|
||||
backup_create(system=[], apps=None)
|
||||
|
||||
archives = backup_list()["archives"]
|
||||
assert len(archives) == 1
|
||||
|
@ -241,40 +250,41 @@ def test_backup_and_restore_all_sys():
|
|||
|
||||
# Restore the backup
|
||||
backup_restore(auth, name=archives[0], force=True,
|
||||
ignore_system=False, ignore_apps=True)
|
||||
system=[], apps=None)
|
||||
|
||||
# Check ssowat conf is back
|
||||
assert os.path.exists("/etc/ssowat/conf.json")
|
||||
|
||||
|
||||
###############################################################################
|
||||
# System restore from 2.4 #
|
||||
###############################################################################
|
||||
#
|
||||
# System restore from 2.4 #
|
||||
#
|
||||
|
||||
@pytest.mark.with_system_archive_from_2p4
|
||||
def test_restore_system_from_Ynh2p4(monkeypatch, mocker):
|
||||
|
||||
# Backup current system
|
||||
backup_create(ignore_system=False, ignore_apps=True)
|
||||
backup_create(system=[], apps=None)
|
||||
archives = backup_list()["archives"]
|
||||
assert len(archives) == 2
|
||||
|
||||
# Restore system archive from 2.4
|
||||
try:
|
||||
backup_restore(auth, name=backup_list()["archives"][1],
|
||||
ignore_system=False,
|
||||
ignore_apps=True,
|
||||
force=True)
|
||||
system=[],
|
||||
apps=None,
|
||||
force=True)
|
||||
finally:
|
||||
# Restore system as it was
|
||||
backup_restore(auth, name=backup_list()["archives"][0],
|
||||
ignore_system=False,
|
||||
ignore_apps=True,
|
||||
force=True)
|
||||
system=[],
|
||||
apps=None,
|
||||
force=True)
|
||||
|
||||
#
|
||||
# App backup #
|
||||
#
|
||||
|
||||
###############################################################################
|
||||
# App backup #
|
||||
###############################################################################
|
||||
|
||||
@pytest.mark.with_backup_recommended_app_installed
|
||||
def test_backup_script_failure_handling(monkeypatch, mocker):
|
||||
|
@ -292,11 +302,12 @@ def test_backup_script_failure_handling(monkeypatch, mocker):
|
|||
monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec)
|
||||
mocker.spy(m18n, "n")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_create(system=None, apps=["backup_recommended_app"])
|
||||
|
||||
m18n.n.assert_any_call('backup_app_failed', app='backup_recommended_app')
|
||||
|
||||
|
||||
@pytest.mark.with_backup_recommended_app_installed
|
||||
def test_backup_not_enough_free_space(monkeypatch, mocker):
|
||||
|
||||
|
@ -312,8 +323,8 @@ def test_backup_not_enough_free_space(monkeypatch, mocker):
|
|||
|
||||
mocker.spy(m18n, "n")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_create(system=None, apps=["backup_recommended_app"])
|
||||
|
||||
m18n.n.assert_any_call('not_enough_disk_space', path=ANY)
|
||||
|
||||
|
@ -324,8 +335,8 @@ def test_backup_app_not_installed(mocker):
|
|||
|
||||
mocker.spy(m18n, "n")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_create(ignore_system=True, ignore_apps=False, apps=["wordpress"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_create(system=None, apps=["wordpress"])
|
||||
|
||||
m18n.n.assert_any_call("unbackup_app", app="wordpress")
|
||||
m18n.n.assert_any_call('backup_nothings_done')
|
||||
|
@ -340,8 +351,8 @@ def test_backup_app_with_no_backup_script(mocker):
|
|||
|
||||
mocker.spy(m18n, "n")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_create(system=None, apps=["backup_recommended_app"])
|
||||
|
||||
m18n.n.assert_any_call("backup_with_no_backup_script_for_app", app="backup_recommended_app")
|
||||
m18n.n.assert_any_call('backup_nothings_done')
|
||||
|
@ -359,7 +370,7 @@ def test_backup_app_with_no_restore_script(mocker):
|
|||
# Backuping an app with no restore script will only display a warning to the
|
||||
# user...
|
||||
|
||||
backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"])
|
||||
backup_create(system=None, apps=["backup_recommended_app"])
|
||||
|
||||
m18n.n.assert_any_call("backup_with_no_restore_script_for_app", app="backup_recommended_app")
|
||||
|
||||
|
@ -368,7 +379,7 @@ def test_backup_app_with_no_restore_script(mocker):
|
|||
def test_backup_with_different_output_directory():
|
||||
|
||||
# Create the backup
|
||||
backup_create(ignore_system=False, ignore_apps=True, system=["conf_ssh"],
|
||||
backup_create(system=["conf_ssh"], apps=None,
|
||||
output_directory="/opt/test_backup_output_directory",
|
||||
name="backup")
|
||||
|
||||
|
@ -382,10 +393,11 @@ def test_backup_with_different_output_directory():
|
|||
assert len(archives_info["system"].keys()) == 1
|
||||
assert "conf_ssh" in archives_info["system"].keys()
|
||||
|
||||
|
||||
@pytest.mark.clean_opt_dir
|
||||
def test_backup_with_no_compress():
|
||||
# Create the backup
|
||||
backup_create(ignore_system=False, ignore_apps=True, system=["conf_nginx"],
|
||||
backup_create(system=["conf_nginx"], apps=None,
|
||||
output_directory="/opt/test_backup_output_directory",
|
||||
no_compress=True,
|
||||
name="backup")
|
||||
|
@ -393,17 +405,15 @@ def test_backup_with_no_compress():
|
|||
assert os.path.exists("/opt/test_backup_output_directory/info.json")
|
||||
|
||||
|
||||
###############################################################################
|
||||
# App restore #
|
||||
###############################################################################
|
||||
#
|
||||
# App restore #
|
||||
#
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
def test_restore_app_wordpress_from_Ynh2p4():
|
||||
|
||||
backup_restore(auth, name=backup_list()["archives"][0],
|
||||
ignore_system=True,
|
||||
ignore_apps=False,
|
||||
apps=["wordpress"])
|
||||
backup_restore(auth, system=None, name=backup_list()["archives"][0],
|
||||
apps=["wordpress"])
|
||||
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
|
@ -419,11 +429,9 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker):
|
|||
|
||||
assert not _is_installed("wordpress")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_restore(auth, name=backup_list()["archives"][0],
|
||||
ignore_system=True,
|
||||
ignore_apps=False,
|
||||
apps=["wordpress"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_restore(auth, system=None, name=backup_list()["archives"][0],
|
||||
apps=["wordpress"])
|
||||
|
||||
m18n.n.assert_any_call('restore_app_failed', app='wordpress')
|
||||
m18n.n.assert_any_call('restore_nothings_done')
|
||||
|
@ -442,16 +450,14 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker):
|
|||
|
||||
assert not _is_installed("wordpress")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_restore(auth, name=backup_list()["archives"][0],
|
||||
ignore_system=True,
|
||||
ignore_apps=False,
|
||||
apps=["wordpress"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_restore(auth, system=None, name=backup_list()["archives"][0],
|
||||
apps=["wordpress"])
|
||||
|
||||
m18n.n.assert_any_call('restore_not_enough_disk_space',
|
||||
free_space=0,
|
||||
margin=ANY,
|
||||
needed_space=ANY)
|
||||
free_space=0,
|
||||
margin=ANY,
|
||||
needed_space=ANY)
|
||||
assert not _is_installed("wordpress")
|
||||
|
||||
|
||||
|
@ -463,11 +469,9 @@ def test_restore_app_not_in_backup(mocker):
|
|||
|
||||
mocker.spy(m18n, "n")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_restore(auth, name=backup_list()["archives"][0],
|
||||
ignore_system=True,
|
||||
ignore_apps=False,
|
||||
apps=["yoloswag"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_restore(auth, system=None, name=backup_list()["archives"][0],
|
||||
apps=["yoloswag"])
|
||||
|
||||
m18n.n.assert_any_call('backup_archive_app_not_found', app="yoloswag")
|
||||
assert not _is_installed("wordpress")
|
||||
|
@ -479,19 +483,15 @@ def test_restore_app_already_installed(mocker):
|
|||
|
||||
assert not _is_installed("wordpress")
|
||||
|
||||
backup_restore(auth, name=backup_list()["archives"][0],
|
||||
ignore_system=True,
|
||||
ignore_apps=False,
|
||||
apps=["wordpress"])
|
||||
backup_restore(auth, system=None, name=backup_list()["archives"][0],
|
||||
apps=["wordpress"])
|
||||
|
||||
assert _is_installed("wordpress")
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_restore(auth, name=backup_list()["archives"][0],
|
||||
ignore_system=True,
|
||||
ignore_apps=False,
|
||||
apps=["wordpress"])
|
||||
with pytest.raises(YunohostError):
|
||||
backup_restore(auth, system=None, name=backup_list()["archives"][0],
|
||||
apps=["wordpress"])
|
||||
|
||||
m18n.n.assert_any_call('restore_already_installed_app', app="wordpress")
|
||||
m18n.n.assert_any_call('restore_nothings_done')
|
||||
|
@ -520,7 +520,7 @@ def test_backup_and_restore_with_ynh_restore():
|
|||
def _test_backup_and_restore_app(app):
|
||||
|
||||
# Create a backup of this app
|
||||
backup_create(ignore_system=True, ignore_apps=False, apps=[app])
|
||||
backup_create(system=None, apps=[app])
|
||||
|
||||
archives = backup_list()["archives"]
|
||||
assert len(archives) == 1
|
||||
|
@ -535,14 +535,15 @@ def _test_backup_and_restore_app(app):
|
|||
assert not app_is_installed(app)
|
||||
|
||||
# Restore the app
|
||||
backup_restore(auth, name=archives[0], ignore_system=True,
|
||||
ignore_apps=False, apps=[app])
|
||||
backup_restore(auth, system=None, name=archives[0],
|
||||
apps=[app])
|
||||
|
||||
assert app_is_installed(app)
|
||||
|
||||
###############################################################################
|
||||
# Some edge cases #
|
||||
###############################################################################
|
||||
#
|
||||
# Some edge cases #
|
||||
#
|
||||
|
||||
|
||||
def test_restore_archive_with_no_json(mocker):
|
||||
|
||||
|
@ -553,9 +554,8 @@ def test_restore_archive_with_no_json(mocker):
|
|||
assert "badbackup" in backup_list()["archives"]
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
with pytest.raises(MoulinetteError):
|
||||
backup_restore(auth, name="badbackup", force=True,
|
||||
ignore_system=False, ignore_apps=False)
|
||||
with pytest.raises(YunohostError):
|
||||
backup_restore(auth, name="badbackup", force=True)
|
||||
m18n.n.assert_any_call('backup_invalid_archive')
|
||||
|
||||
|
||||
|
@ -567,7 +567,7 @@ def test_backup_binds_are_readonly(monkeypatch):
|
|||
|
||||
confssh = os.path.join(self.work_dir, "conf/ssh")
|
||||
output = subprocess.check_output("touch %s/test 2>&1 || true" % confssh,
|
||||
shell=True)
|
||||
shell=True, env={'LANG': 'en_US.UTF-8'})
|
||||
|
||||
assert "Read-only file system" in output
|
||||
|
||||
|
@ -577,7 +577,7 @@ def test_backup_binds_are_readonly(monkeypatch):
|
|||
self.clean()
|
||||
|
||||
monkeypatch.setattr("yunohost.backup.BackupMethod.mount_and_backup",
|
||||
custom_mount_and_backup)
|
||||
custom_mount_and_backup)
|
||||
|
||||
# Create the backup
|
||||
backup_create(ignore_system=False, ignore_apps=True)
|
||||
backup_create(system=[])
|
||||
|
|
|
@ -6,7 +6,7 @@ from moulinette.core import init_authenticator
|
|||
from yunohost.app import app_install, app_change_url, app_remove, app_map
|
||||
from yunohost.domain import _get_maindomain
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
# Instantiate LDAP Authenticator
|
||||
AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous')
|
||||
|
@ -38,7 +38,7 @@ def check_changeurl_app(path):
|
|||
|
||||
assert appmap[maindomain][path + "/"]["id"] == "change_url_app"
|
||||
|
||||
r = requests.get("https://%s%s/" % (maindomain, path), verify=False)
|
||||
r = requests.get("https://127.0.0.1%s/" % path, headers={"domain": maindomain}, verify=False)
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
|
@ -53,9 +53,10 @@ def test_appchangeurl():
|
|||
|
||||
check_changeurl_app("/newchangeurl")
|
||||
|
||||
|
||||
def test_appchangeurl_sameurl():
|
||||
install_changeurl_app("/changeurl")
|
||||
check_changeurl_app("/changeurl")
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
app_change_url(auth, "change_url_app", maindomain, "changeurl")
|
||||
|
|
|
@ -2,7 +2,7 @@ import os
|
|||
import json
|
||||
import pytest
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
from yunohost.settings import settings_get, settings_list, _get_settings, \
|
||||
settings_set, settings_reset, settings_reset_all, \
|
||||
|
@ -18,7 +18,8 @@ def teardown_function(function):
|
|||
|
||||
|
||||
def test_settings_get_bool():
|
||||
assert settings_get("example.bool") == True
|
||||
assert settings_get("example.bool")
|
||||
|
||||
|
||||
def test_settings_get_full_bool():
|
||||
assert settings_get("example.bool", True) == {"type": "bool", "value": True, "default": True, "description": "Example boolean option"}
|
||||
|
@ -27,6 +28,7 @@ def test_settings_get_full_bool():
|
|||
def test_settings_get_int():
|
||||
assert settings_get("example.int") == 42
|
||||
|
||||
|
||||
def test_settings_get_full_int():
|
||||
assert settings_get("example.int", True) == {"type": "int", "value": 42, "default": 42, "description": "Example int option"}
|
||||
|
||||
|
@ -34,6 +36,7 @@ def test_settings_get_full_int():
|
|||
def test_settings_get_string():
|
||||
assert settings_get("example.string") == "yolo swag"
|
||||
|
||||
|
||||
def test_settings_get_full_string():
|
||||
assert settings_get("example.string", True) == {"type": "string", "value": "yolo swag", "default": "yolo swag", "description": "Example string option"}
|
||||
|
||||
|
@ -41,12 +44,13 @@ def test_settings_get_full_string():
|
|||
def test_settings_get_enum():
|
||||
assert settings_get("example.enum") == "a"
|
||||
|
||||
|
||||
def test_settings_get_full_enum():
|
||||
assert settings_get("example.enum", True) == {"type": "enum", "value": "a", "default": "a", "description": "Example enum option", "choices": ["a", "b", "c"]}
|
||||
|
||||
|
||||
def test_settings_get_doesnt_exists():
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_get("doesnt.exists")
|
||||
|
||||
|
||||
|
@ -70,39 +74,39 @@ def test_settings_set_enum():
|
|||
|
||||
|
||||
def test_settings_set_doesexit():
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("doesnt.exist", True)
|
||||
|
||||
|
||||
def test_settings_set_bad_type_bool():
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.bool", 42)
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.bool", "pouet")
|
||||
|
||||
|
||||
def test_settings_set_bad_type_int():
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.int", True)
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.int", "pouet")
|
||||
|
||||
|
||||
def test_settings_set_bad_type_string():
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.string", True)
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.string", 42)
|
||||
|
||||
|
||||
def test_settings_set_bad_value_enum():
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.enum", True)
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.enum", "e")
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.enum", 42)
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.enum", "pouet")
|
||||
|
||||
|
||||
|
@ -119,7 +123,7 @@ def test_reset():
|
|||
|
||||
|
||||
def test_settings_reset_doesexit():
|
||||
with pytest.raises(MoulinetteError):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_reset("doesnt.exist")
|
||||
|
||||
|
||||
|
@ -152,7 +156,6 @@ def test_reset_all_backup():
|
|||
assert settings_after_modification == json.load(open(old_settings_backup_path, "r"))
|
||||
|
||||
|
||||
|
||||
def test_unknown_keys():
|
||||
unknown_settings_path = SETTINGS_PATH_OTHER_LOCATION % "unknown"
|
||||
unknown_setting = {
|
||||
|
|
|
@ -27,8 +27,6 @@ import re
|
|||
import os
|
||||
import yaml
|
||||
import json
|
||||
import errno
|
||||
import logging
|
||||
import subprocess
|
||||
import pwd
|
||||
import socket
|
||||
|
@ -40,7 +38,8 @@ import apt
|
|||
import apt.progress
|
||||
|
||||
from moulinette import msettings, msignals, m18n
|
||||
from moulinette.core import MoulinetteError, init_authenticator
|
||||
from moulinette.core import init_authenticator
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.process import check_output
|
||||
from moulinette.utils.filesystem import read_json, write_to_json
|
||||
|
@ -112,7 +111,7 @@ def tools_ldapinit():
|
|||
pwd.getpwnam("admin")
|
||||
except KeyError:
|
||||
logger.error(m18n.n('ldap_init_failed_to_create_admin'))
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('installation_failed'))
|
||||
raise YunohostError('installation_failed')
|
||||
|
||||
logger.success(m18n.n('ldap_initialized'))
|
||||
return auth
|
||||
|
@ -136,11 +135,10 @@ def tools_adminpw(auth, new_password, check_strength=True):
|
|||
new_hash = _hash_user_password(new_password)
|
||||
|
||||
try:
|
||||
auth.update("cn=admin", { "userPassword": new_hash, })
|
||||
auth.update("cn=admin", {"userPassword": new_hash, })
|
||||
except:
|
||||
logger.exception('unable to change admin password')
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('admin_password_change_failed'))
|
||||
raise YunohostError('admin_password_change_failed')
|
||||
else:
|
||||
# Write as root password
|
||||
try:
|
||||
|
@ -152,9 +150,11 @@ def tools_adminpw(auth, new_password, check_strength=True):
|
|||
with open('/etc/shadow', 'w') as after_file:
|
||||
after_file.write(before.replace("root:" + hash_root,
|
||||
"root:" + new_hash.replace('{CRYPT}', '')))
|
||||
except IOError as e:
|
||||
except IOError:
|
||||
logger.warning(m18n.n('root_password_desynchronized'))
|
||||
return
|
||||
|
||||
logger.info(m18n.n("root_password_replaced_by_admin_password"))
|
||||
logger.success(m18n.n('admin_password_changed'))
|
||||
|
||||
|
||||
|
@ -174,7 +174,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None):
|
|||
|
||||
# Check domain exists
|
||||
if new_domain not in domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
|
||||
raise YunohostError('domain_unknown')
|
||||
|
||||
operation_logger.related_to.append(('domain', new_domain))
|
||||
operation_logger.start()
|
||||
|
@ -197,7 +197,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None):
|
|||
_set_maindomain(new_domain)
|
||||
except Exception as e:
|
||||
logger.warning("%s" % e, exc_info=1)
|
||||
raise MoulinetteError(errno.EPERM, m18n.n('maindomain_change_failed'))
|
||||
raise YunohostError('maindomain_change_failed')
|
||||
|
||||
_set_hostname(new_domain)
|
||||
|
||||
|
@ -206,7 +206,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None):
|
|||
|
||||
# Regen configurations
|
||||
try:
|
||||
with open('/etc/yunohost/installed', 'r') as f:
|
||||
with open('/etc/yunohost/installed', 'r'):
|
||||
service_regen_conf()
|
||||
except IOError:
|
||||
pass
|
||||
|
@ -246,7 +246,7 @@ def _set_hostname(hostname, pretty_hostname=None):
|
|||
if p.returncode != 0:
|
||||
logger.warning(command)
|
||||
logger.warning(out)
|
||||
raise MoulinetteError(errno.EIO, m18n.n('domain_hostname_failed'))
|
||||
raise YunohostError('domain_hostname_failed')
|
||||
else:
|
||||
logger.debug(out)
|
||||
|
||||
|
@ -264,7 +264,7 @@ def _is_inside_container():
|
|||
stderr=subprocess.STDOUT)
|
||||
|
||||
out, _ = p.communicate()
|
||||
container = ['lxc','lxd','docker']
|
||||
container = ['lxc', 'lxd', 'docker']
|
||||
return out.split()[0] in container
|
||||
|
||||
|
||||
|
@ -287,8 +287,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
|
||||
# Do some checks at first
|
||||
if os.path.isfile('/etc/yunohost/installed'):
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('yunohost_already_installed'))
|
||||
raise YunohostError('yunohost_already_installed')
|
||||
|
||||
# Check password
|
||||
if not force_password:
|
||||
|
@ -317,15 +316,12 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
dyndns = True
|
||||
# If not, abort the postinstall
|
||||
else:
|
||||
raise MoulinetteError(errno.EEXIST,
|
||||
m18n.n('dyndns_unavailable',
|
||||
domain=domain))
|
||||
raise YunohostError('dyndns_unavailable', domain=domain)
|
||||
else:
|
||||
dyndns = False
|
||||
else:
|
||||
dyndns = False
|
||||
|
||||
|
||||
operation_logger.start()
|
||||
logger.info(m18n.n('yunohost_installing'))
|
||||
|
||||
|
@ -362,8 +358,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
with open('/etc/ssowat/conf.json.persistent') as json_conf:
|
||||
ssowat_conf = json.loads(str(json_conf.read()))
|
||||
except ValueError as e:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('ssowat_persistent_conf_read_error', error=str(e)))
|
||||
raise YunohostError('ssowat_persistent_conf_read_error', error=str(e))
|
||||
except IOError:
|
||||
ssowat_conf = {}
|
||||
|
||||
|
@ -376,8 +371,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
|
||||
json.dump(ssowat_conf, f, sort_keys=True, indent=4)
|
||||
except IOError as e:
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('ssowat_persistent_conf_write_error', error=str(e)))
|
||||
raise YunohostError('ssowat_persistent_conf_write_error', error=str(e))
|
||||
|
||||
os.system('chmod 644 /etc/ssowat/conf.json.persistent')
|
||||
|
||||
|
@ -404,8 +398,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
|
||||
if p.returncode != 0:
|
||||
logger.warning(out)
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('yunohost_ca_creation_failed'))
|
||||
raise YunohostError('yunohost_ca_creation_failed')
|
||||
else:
|
||||
logger.debug(out)
|
||||
|
||||
|
@ -432,7 +425,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
_install_appslist_fetch_cron()
|
||||
|
||||
# Init migrations (skip them, no need to run them on a fresh system)
|
||||
tools_migrations_migrate(skip=True, auto=True)
|
||||
_skip_all_migrations()
|
||||
|
||||
os.system('touch /etc/yunohost/installed')
|
||||
|
||||
|
@ -441,6 +434,24 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
service_start("yunohost-firewall")
|
||||
|
||||
service_regen_conf(force=True)
|
||||
|
||||
# Restore original ssh conf, as chosen by the
|
||||
# admin during the initial install
|
||||
#
|
||||
# c.f. the install script and in particular
|
||||
# https://github.com/YunoHost/install_script/pull/50
|
||||
# The user can now choose during the install to keep
|
||||
# the initial, existing sshd configuration
|
||||
# instead of YunoHost's recommended conf
|
||||
#
|
||||
original_sshd_conf = '/etc/ssh/sshd_config.before_yunohost'
|
||||
if os.path.exists(original_sshd_conf):
|
||||
os.rename(original_sshd_conf, '/etc/ssh/sshd_config')
|
||||
else:
|
||||
# We need to explicitly ask the regen conf to regen ssh
|
||||
# (by default, i.e. first argument = None, it won't because it's too touchy)
|
||||
service_regen_conf(names=["ssh"], force=True)
|
||||
|
||||
logger.success(m18n.n('yunohost_configured'))
|
||||
|
||||
logger.warning(m18n.n('recommend_to_add_first_user'))
|
||||
|
@ -463,7 +474,7 @@ def tools_update(ignore_apps=False, ignore_packages=False):
|
|||
# Update APT cache
|
||||
logger.debug(m18n.n('updating_apt_cache'))
|
||||
if not cache.update():
|
||||
raise MoulinetteError(errno.EPERM, m18n.n('update_cache_failed'))
|
||||
raise YunohostError('update_cache_failed')
|
||||
|
||||
cache.open(None)
|
||||
cache.upgrade(True)
|
||||
|
@ -482,7 +493,7 @@ def tools_update(ignore_apps=False, ignore_packages=False):
|
|||
if not ignore_apps:
|
||||
try:
|
||||
app_fetchlist()
|
||||
except MoulinetteError:
|
||||
except YunohostError:
|
||||
# FIXME : silent exception !?
|
||||
pass
|
||||
|
||||
|
@ -526,7 +537,7 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal
|
|||
# If API call
|
||||
if is_api:
|
||||
critical_packages = ("moulinette", "yunohost",
|
||||
"yunohost-admin", "ssowat", "python")
|
||||
"yunohost-admin", "ssowat", "python")
|
||||
critical_upgrades = set()
|
||||
|
||||
for pkg in cache.get_changes():
|
||||
|
@ -562,7 +573,6 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal
|
|||
else:
|
||||
logger.info(m18n.n('packages_no_upgrade'))
|
||||
|
||||
|
||||
if not ignore_apps:
|
||||
try:
|
||||
app_upgrade(auth)
|
||||
|
@ -613,7 +623,7 @@ def tools_diagnosis(auth, private=False):
|
|||
diagnosis['system'] = OrderedDict()
|
||||
try:
|
||||
disks = monitor_disk(units=['filesystem'], human_readable=True)
|
||||
except (MoulinetteError, Fault) as e:
|
||||
except (YunohostError, Fault) as e:
|
||||
logger.warning(m18n.n('diagnosis_monitor_disk_error', error=format(e)), exc_info=1)
|
||||
else:
|
||||
diagnosis['system']['disks'] = {}
|
||||
|
@ -629,7 +639,7 @@ def tools_diagnosis(auth, private=False):
|
|||
|
||||
try:
|
||||
system = monitor_system(units=['cpu', 'memory'], human_readable=True)
|
||||
except MoulinetteError as e:
|
||||
except YunohostError as e:
|
||||
logger.warning(m18n.n('diagnosis_monitor_system_error', error=format(e)), exc_info=1)
|
||||
else:
|
||||
diagnosis['system']['memory'] = {
|
||||
|
@ -655,7 +665,7 @@ def tools_diagnosis(auth, private=False):
|
|||
# YNH Applications
|
||||
try:
|
||||
applications = app_list()['apps']
|
||||
except MoulinetteError as e:
|
||||
except YunohostError as e:
|
||||
diagnosis['applications'] = m18n.n('diagnosis_no_apps')
|
||||
else:
|
||||
diagnosis['applications'] = {}
|
||||
|
@ -706,7 +716,7 @@ def _check_if_vulnerable_to_meltdown():
|
|||
try:
|
||||
call = subprocess.Popen("bash %s --batch json --variant 3" %
|
||||
SCRIPT_PATH, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
output, _ = call.communicate()
|
||||
|
@ -787,7 +797,7 @@ def tools_migrations_list(pending=False, done=False):
|
|||
|
||||
# Check for option conflict
|
||||
if pending and done:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n("migrations_list_conflict_pending_done"))
|
||||
raise YunohostError("migrations_list_conflict_pending_done")
|
||||
|
||||
# Get all migrations
|
||||
migrations = _get_migrations_list()
|
||||
|
@ -802,12 +812,12 @@ def tools_migrations_list(pending=False, done=False):
|
|||
migrations = [m for m in migrations if m.number > last_migration]
|
||||
|
||||
# Reduce to dictionnaries
|
||||
migrations = [{ "id": migration.id,
|
||||
"number": migration.number,
|
||||
"name": migration.name,
|
||||
"mode": migration.mode,
|
||||
"description": migration.description,
|
||||
"disclaimer": migration.disclaimer } for migration in migrations ]
|
||||
migrations = [{"id": migration.id,
|
||||
"number": migration.number,
|
||||
"name": migration.name,
|
||||
"mode": migration.mode,
|
||||
"description": migration.description,
|
||||
"disclaimer": migration.disclaimer} for migration in migrations]
|
||||
|
||||
return {"migrations": migrations}
|
||||
|
||||
|
@ -844,7 +854,7 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai
|
|||
|
||||
# validate input, target must be "0" or a valid number
|
||||
elif target != 0 and target not in all_migration_numbers:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('migrations_bad_value_for_target', ", ".join(map(str, all_migration_numbers))))
|
||||
raise YunohostError('migrations_bad_value_for_target', ", ".join(map(str, all_migration_numbers)))
|
||||
|
||||
logger.debug(m18n.n('migrations_current_target', target))
|
||||
|
||||
|
@ -872,38 +882,41 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai
|
|||
else: # can't happen, this case is handle before
|
||||
raise Exception()
|
||||
|
||||
# If we are migrating in "automatic mode" (i.e. from debian
|
||||
# configure during an upgrade of the package) but we are asked to run
|
||||
# migrations is to be ran manually by the user
|
||||
manual_migrations = [m for m in migrations if m.mode == "manual"]
|
||||
if not skip and auto and manual_migrations:
|
||||
for m in manual_migrations:
|
||||
logger.warn(m18n.n('migrations_to_be_ran_manually',
|
||||
number=m.number,
|
||||
name=m.name))
|
||||
return
|
||||
|
||||
# If some migrations have disclaimers, require the --accept-disclaimer
|
||||
# option
|
||||
migrations_with_disclaimer = [m for m in migrations if m.disclaimer]
|
||||
if not skip and not accept_disclaimer and migrations_with_disclaimer:
|
||||
for m in migrations_with_disclaimer:
|
||||
logger.warn(m18n.n('migrations_need_to_accept_disclaimer',
|
||||
number=m.number,
|
||||
name=m.name,
|
||||
disclaimer=m.disclaimer))
|
||||
return
|
||||
|
||||
# effectively run selected migrations
|
||||
for migration in migrations:
|
||||
|
||||
if not skip:
|
||||
# If we are migrating in "automatic mode" (i.e. from debian configure
|
||||
# during an upgrade of the package) but we are asked to run migrations
|
||||
# to be ran manually by the user, stop there and ask the user to
|
||||
# run the migration manually.
|
||||
if auto and migration.mode == "manual":
|
||||
logger.warn(m18n.n('migrations_to_be_ran_manually',
|
||||
number=migration.number,
|
||||
name=migration.name))
|
||||
break
|
||||
|
||||
# If some migrations have disclaimers,
|
||||
if migration.disclaimer:
|
||||
# require the --accept-disclaimer option. Otherwise, stop everything
|
||||
# here and display the disclaimer
|
||||
if not accept_disclaimer:
|
||||
logger.warn(m18n.n('migrations_need_to_accept_disclaimer',
|
||||
number=migration.number,
|
||||
name=migration.name,
|
||||
disclaimer=migration.disclaimer))
|
||||
break
|
||||
# --accept-disclaimer will only work for the first migration
|
||||
else:
|
||||
accept_disclaimer = False
|
||||
|
||||
# Start register change on system
|
||||
operation_logger= OperationLogger('tools_migrations_migrate_' + mode)
|
||||
operation_logger = OperationLogger('tools_migrations_migrate_' + mode)
|
||||
operation_logger.start()
|
||||
|
||||
if not skip:
|
||||
|
||||
logger.warn(m18n.n('migrations_show_currently_running_migration',
|
||||
logger.info(m18n.n('migrations_show_currently_running_migration',
|
||||
number=migration.number, name=migration.name))
|
||||
|
||||
try:
|
||||
|
@ -918,12 +931,15 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai
|
|||
# migration failed, let's stop here but still update state because
|
||||
# we managed to run the previous ones
|
||||
msg = m18n.n('migrations_migration_has_failed',
|
||||
exception=e,
|
||||
number=migration.number,
|
||||
name=migration.name)
|
||||
exception=e,
|
||||
number=migration.number,
|
||||
name=migration.name)
|
||||
logger.error(msg, exc_info=1)
|
||||
operation_logger.error(msg)
|
||||
break
|
||||
else:
|
||||
logger.success(m18n.n('migrations_success',
|
||||
number=migration.number, name=migration.name))
|
||||
|
||||
else: # if skip
|
||||
logger.warn(m18n.n('migrations_skip_migration',
|
||||
|
@ -938,6 +954,10 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai
|
|||
|
||||
operation_logger.success()
|
||||
|
||||
# Skip migrations one at a time
|
||||
if skip:
|
||||
break
|
||||
|
||||
# special case where we want to go back from the start
|
||||
if target == 0:
|
||||
state["last_run_migration"] = None
|
||||
|
@ -987,7 +1007,7 @@ def _get_migrations_list():
|
|||
migrations = []
|
||||
|
||||
try:
|
||||
import data_migrations
|
||||
from . import data_migrations
|
||||
except ImportError:
|
||||
# not data migrations present, return empty list
|
||||
return migrations
|
||||
|
@ -1010,7 +1030,7 @@ def _get_migration_by_name(migration_name):
|
|||
"""
|
||||
|
||||
try:
|
||||
import data_migrations
|
||||
from . import data_migrations
|
||||
except ImportError:
|
||||
raise AssertionError("Unable to find migration with name %s" % migration_name)
|
||||
|
||||
|
@ -1029,7 +1049,7 @@ def _load_migration(migration_file):
|
|||
number, name = migration_id.split("_", 1)
|
||||
|
||||
logger.debug(m18n.n('migrations_loading_migration',
|
||||
number=number, name=name))
|
||||
number=number, name=name))
|
||||
|
||||
try:
|
||||
# this is python builtin method to import a module using a name, we
|
||||
|
@ -1041,8 +1061,28 @@ def _load_migration(migration_file):
|
|||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('migrations_error_failed_to_load_migration',
|
||||
number=number, name=name))
|
||||
raise YunohostError('migrations_error_failed_to_load_migration',
|
||||
number=number, name=name)
|
||||
|
||||
|
||||
def _skip_all_migrations():
|
||||
"""
|
||||
Skip all pending migrations.
|
||||
This is meant to be used during postinstall to
|
||||
initialize the migration system.
|
||||
"""
|
||||
state = tools_migrations_state()
|
||||
|
||||
# load all migrations
|
||||
migrations = _get_migrations_list()
|
||||
migrations = sorted(migrations, key=lambda x: x.number)
|
||||
last_migration = migrations[-1]
|
||||
|
||||
state["last_run_migration"] = {
|
||||
"number": last_migration.number,
|
||||
"name": last_migration.name
|
||||
}
|
||||
write_to_json(MIGRATIONS_STATE_PATH, state)
|
||||
|
||||
|
||||
class Migration(object):
|
||||
|
@ -1074,4 +1114,3 @@ class Migration(object):
|
|||
@property
|
||||
def description(self):
|
||||
return m18n.n("migration_description_%s" % self.id)
|
||||
|
||||
|
|
|
@ -27,20 +27,20 @@ import os
|
|||
import re
|
||||
import pwd
|
||||
import json
|
||||
import errno
|
||||
import crypt
|
||||
import random
|
||||
import string
|
||||
import subprocess
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from yunohost.service import service_status
|
||||
from yunohost.log import is_unit_operation
|
||||
|
||||
logger = getActionLogger('yunohost.user')
|
||||
|
||||
|
||||
def user_list(auth, fields=None):
|
||||
"""
|
||||
List users
|
||||
|
@ -71,8 +71,7 @@ def user_list(auth, fields=None):
|
|||
if attr in keys:
|
||||
attrs.append(attr)
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('field_invalid', attr))
|
||||
raise YunohostError('field_invalid', attr)
|
||||
else:
|
||||
attrs = ['uid', 'cn', 'mail', 'mailuserquota', 'loginShell']
|
||||
|
||||
|
@ -100,7 +99,7 @@ def user_list(auth, fields=None):
|
|||
|
||||
@is_unit_operation([('username', 'user')])
|
||||
def user_create(operation_logger, auth, username, firstname, lastname, mail, password,
|
||||
mailbox_quota="0"):
|
||||
mailbox_quota="0"):
|
||||
"""
|
||||
Create user
|
||||
|
||||
|
@ -130,7 +129,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas
|
|||
# Validate uniqueness of username in system users
|
||||
all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
|
||||
if username in all_existing_usernames:
|
||||
raise MoulinetteError(errno.EEXIST, m18n.n('system_username_exists'))
|
||||
raise YunohostError('system_username_exists')
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
aliases = [
|
||||
|
@ -141,13 +140,11 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas
|
|||
]
|
||||
|
||||
if mail in aliases:
|
||||
raise MoulinetteError(errno.EEXIST,m18n.n('mail_unavailable'))
|
||||
raise YunohostError('mail_unavailable')
|
||||
|
||||
# Check that the mail domain exists
|
||||
if mail.split("@")[1] not in domain_list(auth)['domains']:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('mail_domain_unknown',
|
||||
domain=mail.split("@")[1]))
|
||||
raise YunohostError('mail_domain_unknown', domain=mail.split("@")[1])
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -188,8 +185,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas
|
|||
with open('/etc/ssowat/conf.json.persistent') as json_conf:
|
||||
ssowat_conf = json.loads(str(json_conf.read()))
|
||||
except ValueError as e:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('ssowat_persistent_conf_read_error', error=e.strerror))
|
||||
raise YunohostError('ssowat_persistent_conf_read_error', error=e.strerror)
|
||||
except IOError:
|
||||
ssowat_conf = {}
|
||||
|
||||
|
@ -199,8 +195,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas
|
|||
with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
|
||||
json.dump(ssowat_conf, f, sort_keys=True, indent=4)
|
||||
except IOError as e:
|
||||
raise MoulinetteError(errno.EPERM,
|
||||
m18n.n('ssowat_persistent_conf_write_error', error=e.strerror))
|
||||
raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror)
|
||||
|
||||
if auth.add('uid=%s,ou=users' % username, attr_dict):
|
||||
# Invalidate passwd to take user creation into account
|
||||
|
@ -226,7 +221,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas
|
|||
|
||||
return {'fullname': fullname, 'username': username, 'mail': mail}
|
||||
|
||||
raise MoulinetteError(169, m18n.n('user_creation_failed'))
|
||||
raise YunohostError('user_creation_failed')
|
||||
|
||||
|
||||
@is_unit_operation([('username', 'user')])
|
||||
|
@ -258,7 +253,7 @@ def user_delete(operation_logger, auth, username, purge=False):
|
|||
subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
|
||||
subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)])
|
||||
else:
|
||||
raise MoulinetteError(169, m18n.n('user_deletion_failed'))
|
||||
raise YunohostError('user_deletion_failed')
|
||||
|
||||
app_ssowatconf(auth)
|
||||
|
||||
|
@ -269,8 +264,8 @@ def user_delete(operation_logger, auth, username, purge=False):
|
|||
|
||||
@is_unit_operation([('username', 'user')], exclude=['auth', 'change_password'])
|
||||
def user_update(operation_logger, auth, username, firstname=None, lastname=None, mail=None,
|
||||
change_password=None, add_mailforward=None, remove_mailforward=None,
|
||||
add_mailalias=None, remove_mailalias=None, mailbox_quota=None):
|
||||
change_password=None, add_mailforward=None, remove_mailforward=None,
|
||||
add_mailalias=None, remove_mailalias=None, mailbox_quota=None):
|
||||
"""
|
||||
Update user informations
|
||||
|
||||
|
@ -286,7 +281,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None,
|
|||
remove_mailalias -- Mail aliases to remove
|
||||
|
||||
"""
|
||||
from yunohost.domain import domain_list
|
||||
from yunohost.domain import domain_list, _get_maindomain
|
||||
from yunohost.app import app_ssowatconf
|
||||
from yunohost.utils.password import assert_password_is_strong_enough
|
||||
|
||||
|
@ -297,7 +292,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None,
|
|||
# Populate user informations
|
||||
result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch)
|
||||
if not result:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username))
|
||||
raise YunohostError('user_unknown', user=username)
|
||||
user = result[0]
|
||||
|
||||
# Get modifications from arguments
|
||||
|
@ -328,11 +323,9 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None,
|
|||
]
|
||||
auth.validate_uniqueness({'mail': mail})
|
||||
if mail[mail.find('@') + 1:] not in domains:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('mail_domain_unknown',
|
||||
domain=mail[mail.find('@') + 1:]))
|
||||
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
|
||||
if mail in aliases:
|
||||
raise MoulinetteError(errno.EEXIST,m18n.n('mail_unavailable'))
|
||||
raise YunohostError('mail_unavailable')
|
||||
|
||||
del user['mail'][0]
|
||||
new_attr_dict['mail'] = [mail] + user['mail']
|
||||
|
@ -343,9 +336,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None,
|
|||
for mail in add_mailalias:
|
||||
auth.validate_uniqueness({'mail': mail})
|
||||
if mail[mail.find('@') + 1:] not in domains:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('mail_domain_unknown',
|
||||
domain=mail[mail.find('@') + 1:]))
|
||||
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
|
||||
user['mail'].append(mail)
|
||||
new_attr_dict['mail'] = user['mail']
|
||||
|
||||
|
@ -356,8 +347,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None,
|
|||
if len(user['mail']) > 1 and mail in user['mail'][1:]:
|
||||
user['mail'].remove(mail)
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('mail_alias_remove_failed', mail=mail))
|
||||
raise YunohostError('mail_alias_remove_failed', mail=mail)
|
||||
new_attr_dict['mail'] = user['mail']
|
||||
|
||||
if add_mailforward:
|
||||
|
@ -376,8 +366,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None,
|
|||
if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]:
|
||||
user['maildrop'].remove(mail)
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL,
|
||||
m18n.n('mail_forward_remove_failed', mail=mail))
|
||||
raise YunohostError('mail_forward_remove_failed', mail=mail)
|
||||
new_attr_dict['maildrop'] = user['maildrop']
|
||||
|
||||
if mailbox_quota is not None:
|
||||
|
@ -390,7 +379,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None,
|
|||
app_ssowatconf(auth)
|
||||
return user_info(auth, username)
|
||||
else:
|
||||
raise MoulinetteError(169, m18n.n('user_update_failed'))
|
||||
raise YunohostError('user_update_failed')
|
||||
|
||||
|
||||
def user_info(auth, username):
|
||||
|
@ -415,7 +404,7 @@ def user_info(auth, username):
|
|||
if result:
|
||||
user = result[0]
|
||||
else:
|
||||
raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username))
|
||||
raise YunohostError('user_unknown', user=username)
|
||||
|
||||
result_dict = {
|
||||
'username': user['uid'][0],
|
||||
|
@ -471,7 +460,7 @@ def user_info(auth, username):
|
|||
if result:
|
||||
return result_dict
|
||||
else:
|
||||
raise MoulinetteError(167, m18n.n('user_info_failed'))
|
||||
raise YunohostError('user_info_failed')
|
||||
|
||||
#
|
||||
# SSH subcategory
|
||||
|
@ -479,18 +468,23 @@ def user_info(auth, username):
|
|||
#
|
||||
import yunohost.ssh
|
||||
|
||||
|
||||
def user_ssh_allow(auth, username):
|
||||
return yunohost.ssh.user_ssh_allow(auth, username)
|
||||
|
||||
|
||||
def user_ssh_disallow(auth, username):
|
||||
return yunohost.ssh.user_ssh_disallow(auth, username)
|
||||
|
||||
|
||||
def user_ssh_list_keys(auth, username):
|
||||
return yunohost.ssh.user_ssh_list_keys(auth, username)
|
||||
|
||||
|
||||
def user_ssh_add_key(auth, username, key, comment):
|
||||
return yunohost.ssh.user_ssh_add_key(auth, username, key, comment)
|
||||
|
||||
|
||||
def user_ssh_remove_key(auth, username, key):
|
||||
return yunohost.ssh.user_ssh_remove_key(auth, username, key)
|
||||
|
||||
|
@ -498,6 +492,7 @@ def user_ssh_remove_key(auth, username, key):
|
|||
# End SSH subcategory
|
||||
#
|
||||
|
||||
|
||||
def _convertSize(num, suffix=''):
|
||||
for unit in ['K', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
||||
if abs(num) < 1024.0:
|
||||
|
|
40
src/yunohost/utils/error.py
Normal file
40
src/yunohost/utils/error.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" License
|
||||
|
||||
Copyright (C) 2018 YUNOHOST.ORG
|
||||
|
||||
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
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, see http://www.gnu.org/licenses
|
||||
|
||||
"""
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from moulinette import m18n
|
||||
|
||||
|
||||
class YunohostError(MoulinetteError):
|
||||
|
||||
"""
|
||||
Yunohost base exception
|
||||
|
||||
The (only?) main difference with MoulinetteError being that keys
|
||||
are translated via m18n.n (namespace) instead of m18n.g (global?)
|
||||
"""
|
||||
|
||||
def __init__(self, key, __raw_msg__=False, *args, **kwargs):
|
||||
if __raw_msg__:
|
||||
msg = key
|
||||
else:
|
||||
msg = m18n.n(key, *args, **kwargs)
|
||||
super(YunohostError, self).__init__(msg, __raw_msg__=True)
|
|
@ -20,10 +20,12 @@
|
|||
"""
|
||||
import os
|
||||
|
||||
|
||||
def free_space_in_directory(dirpath):
|
||||
stat = os.statvfs(dirpath)
|
||||
return stat.f_frsize * stat.f_bavail
|
||||
|
||||
|
||||
def space_used_by_directory(dirpath):
|
||||
stat = os.statvfs(dirpath)
|
||||
return stat.f_frsize * stat.f_blocks
|
||||
|
|
|
@ -71,7 +71,7 @@ def get_gateway():
|
|||
return addr.popitem()[1] if len(addr) == 1 else None
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
|
||||
|
||||
def _extract_inet(string, skip_netmask=False, skip_loopback=True):
|
||||
|
|
|
@ -33,6 +33,7 @@ logger = logging.getLogger('yunohost.utils.packages')
|
|||
# Exceptions -----------------------------------------------------------------
|
||||
|
||||
class PackageException(Exception):
|
||||
|
||||
"""Base exception related to a package
|
||||
|
||||
Represent an exception related to the package named `pkgname`. If no
|
||||
|
@ -50,16 +51,19 @@ class PackageException(Exception):
|
|||
|
||||
|
||||
class UnknownPackage(PackageException):
|
||||
|
||||
"""The package is not found in the cache."""
|
||||
message_key = 'package_unknown'
|
||||
|
||||
|
||||
class UninstalledPackage(PackageException):
|
||||
|
||||
"""The package is not installed."""
|
||||
message_key = 'package_not_installed'
|
||||
|
||||
|
||||
class InvalidSpecifier(ValueError):
|
||||
|
||||
"""An invalid specifier was found."""
|
||||
|
||||
|
||||
|
@ -68,6 +72,7 @@ class InvalidSpecifier(ValueError):
|
|||
# See: https://github.com/pypa/packaging
|
||||
|
||||
class Specifier(object):
|
||||
|
||||
"""Unique package version specifier
|
||||
|
||||
Restrict a package version according to the `spec`. It must be a string
|
||||
|
@ -257,6 +262,7 @@ class Specifier(object):
|
|||
|
||||
|
||||
class SpecifierSet(object):
|
||||
|
||||
"""A set of package version specifiers
|
||||
|
||||
Combine several Specifier separated by a comma. It allows to restrict
|
||||
|
|
|
@ -38,9 +38,11 @@ STRENGTH_LEVELS = [
|
|||
(12, 1, 1, 1, 1),
|
||||
]
|
||||
|
||||
|
||||
def assert_password_is_strong_enough(profile, password):
|
||||
PasswordValidator(profile).validate(password)
|
||||
|
||||
|
||||
class PasswordValidator(object):
|
||||
|
||||
def __init__(self, profile):
|
||||
|
@ -81,17 +83,15 @@ class PasswordValidator(object):
|
|||
# on top (at least not the moulinette ones)
|
||||
# because the moulinette needs to be correctly initialized
|
||||
# as well as modules available in python's path.
|
||||
import errno
|
||||
import logging
|
||||
from moulinette import m18n
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
logger = logging.getLogger('yunohost.utils.password')
|
||||
|
||||
status, msg = self.validation_summary(password)
|
||||
if status == "error":
|
||||
raise MoulinetteError(1, m18n.n(msg))
|
||||
raise YunohostError(msg)
|
||||
|
||||
def validation_summary(self, password):
|
||||
"""
|
||||
|
@ -159,7 +159,7 @@ class PasswordValidator(object):
|
|||
# and the strength of the password (e.g. [11, 2, 7, 2, 0])
|
||||
# and compare the values 1-by-1.
|
||||
# If one False is found, the password does not satisfy the level
|
||||
if False in [s>=c for s, c in zip(strength, level_criterias)]:
|
||||
if False in [s >= c for s, c in zip(strength, level_criterias)]:
|
||||
break
|
||||
# Otherwise, the strength of the password is at least of the current level.
|
||||
strength_level = level + 1
|
||||
|
@ -188,7 +188,7 @@ if __name__ == '__main__':
|
|||
if len(sys.argv) < 2:
|
||||
import getpass
|
||||
pwd = getpass.getpass("")
|
||||
#print("usage: password.py PASSWORD")
|
||||
# print("usage: password.py PASSWORD")
|
||||
else:
|
||||
pwd = sys.argv[1]
|
||||
status, msg = PasswordValidator('user').validation_summary(pwd)
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import requests
|
||||
import json
|
||||
import errno
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
|
||||
def yunopaste(data):
|
||||
|
||||
|
@ -13,17 +13,14 @@ def yunopaste(data):
|
|||
try:
|
||||
r = requests.post("%s/documents" % paste_server, data=data, timeout=30)
|
||||
except Exception as e:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
"Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e))
|
||||
raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e), raw_msg=True)
|
||||
|
||||
if r.status_code != 200:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
"Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text))
|
||||
raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text), raw_msg=True)
|
||||
|
||||
try:
|
||||
url = json.loads(r.text)["key"]
|
||||
except:
|
||||
raise MoulinetteError(errno.EIO,
|
||||
"Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text)
|
||||
raise YunohostError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text, raw_msg=True)
|
||||
|
||||
return "%s/raw/%s" % (paste_server, url)
|
||||
|
|
Loading…
Add table
Reference in a new issue