From 4e144fb49d78378e3ecc1224acbb4f1994fbf792 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 29 Jan 2015 20:24:46 +0100 Subject: [PATCH 1/6] improved logging in fileservertasks.tasks --- docs/changelog.rst | 3 + docs/code.rst | 10 +-- gvafile/fileservertasks/tasks.py | 113 ++++++++++++++++++------------- gvafile/gvafile/exceptions.py | 12 ---- 4 files changed, 71 insertions(+), 67 deletions(-) delete mode 100644 gvafile/gvafile/exceptions.py diff --git a/docs/changelog.rst b/docs/changelog.rst index b19d7cb..528c1a4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,9 @@ Changelog ========= +* :support:`-` improved logging in fileservertasks.tasks, got rid of + GVAFileException + * :release:`0.4.0 <2015-01-26>` * :feature:`-` implement new tasks create_file_website_hierarchy and delete_file_website_hierarchy diff --git a/docs/code.rst b/docs/code.rst index 5e632c2..bf27672 100644 --- a/docs/code.rst +++ b/docs/code.rst @@ -20,12 +20,6 @@ The project module :py:mod:`gvafile` :members: -:py:mod:`exceptions ` ------------------------------------------ - -.. automodule:: gvafile.exceptions - - :py:mod:`settings ` ------------------------------------- @@ -33,8 +27,8 @@ The project module :py:mod:`gvafile` :members: -:py:mod:`fileservertasks` app -============================= +:py:mod:`fileservertasks` module +================================ .. automodule:: fileservertasks diff --git a/gvafile/fileservertasks/tasks.py b/gvafile/fileservertasks/tasks.py index 05123e2..f33f5df 100644 --- a/gvafile/fileservertasks/tasks.py +++ b/gvafile/fileservertasks/tasks.py @@ -14,10 +14,7 @@ from gvafile import settings from celery import shared_task from celery.utils.log import get_task_logger -from gvafile.exceptions import GVAFileException - - -_logger = get_task_logger(__name__) +_LOGGER = get_task_logger(__name__) SUDO_CMD = '/usr/bin/sudo' INSTALL_CMD = '/usr/bin/install' @@ -25,6 +22,21 @@ SETFACL_CMD = '/usr/bin/setfacl' RM_CMD = '/bin/rm' +def log_and_raise(exception, message, *args): + """ + Log and raise a :py:class:`subprocess.CalledProcessError`. + + :param exception: exception + :param str message: log message + :param args: arguments to fill placeholders in message + :raises Exception: raises an exception with the formatted message + + """ + logargs = list(args) + [exception.returncode, exception.output] + _LOGGER.error(message + "\nreturncode: %d\noutput:\n%s", *logargs) + raise Exception(message % args) + + def _build_sftp_directory_name(username): """ Constructs the SFTP directory name for a given username. @@ -61,11 +73,11 @@ def setup_file_sftp_userdir(username): subprocess.check_output([ SUDO_CMD, SETFACL_CMD, '-m', 'www-data:--x', sftp_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( - 'could not create SFTP directory for user %s', username) - raise GVAFileException( - "could not create SFTP directory for user %s" % username) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'cold not create SFTP directory for user %s', username) + _LOGGER.info( + 'created sftp directory %s for user %s', sftp_directory, username) return sftp_directory @@ -86,11 +98,11 @@ def delete_file_sftp_userdir(username): subprocess.check_output([ SUDO_CMD, RM_CMD, '-r', '-f', sftp_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( - 'could not remove SFTP directory for user %s', username) - raise GVAFileException( - "could not remove SFTP directory for user %s" % username) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not remove SFTP directory for user %s', username) + _LOGGER.info( + "deleted sftp directory %s of user %s", sftp_directory, username) return sftp_directory @@ -112,11 +124,11 @@ def setup_file_mail_userdir(username): subprocess.check_output([ SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, '-m', '0500', '-d', mail_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( - 'could not create mail base directory for user %s', username) - raise GVAFileException( - "could not create mail base directory for user %s" % username) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not create mail base directory for user %s', username) + _LOGGER.info( + 'created mail directory %s for user %s', mail_directory, username) return mail_directory @@ -137,11 +149,11 @@ def delete_file_mail_userdir(username): subprocess.check_output([ SUDO_CMD, RM_CMD, '-r', '-f', mail_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( - 'could not remove mail base directory of user %s', username) - raise GVAFileException( - "could not remove mail base directory of user %s" % username) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not remove mail base directory of user %s', username) + _LOGGER.info( + 'deleted mail directory %s of user %s', mail_directory, username) return mail_directory @@ -153,7 +165,7 @@ def create_file_mailbox(username, mailboxname): :param str username: the user name :param str mailboxname: the mailbox name - :raises GVAFileException: if the mailbox directory cannot be created + :raises Exception: if the mailbox directory cannot be created :return: the created mailbox directory name :rtype: str @@ -164,12 +176,13 @@ def create_file_mailbox(username, mailboxname): subprocess.check_output([ SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, '-m', '0700', '-d', mailbox_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( - 'could not create mailbox %s for user %s', mailboxname, username) - raise GVAFileException( - "could not create mailbox %s for user %s" % (mailboxname, username) - ) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not create mailbox %s for user %s', mailboxname, + username) + _LOGGER.info( + 'created mailbox directory %s for user %s', mailbox_directory, + username) return mailbox_directory @@ -180,7 +193,7 @@ def delete_file_mailbox(username, mailboxname): :param str username: the user name :param str mailboxname: the mailbox name - :raises GVAFileException: if the mailbox directory cannot be deleted + :raises Exception: if the mailbox directory cannot be deleted :return: the deleted mailbox directory name :rtype: str @@ -191,11 +204,12 @@ def delete_file_mailbox(username, mailboxname): subprocess.check_output([ SUDO_CMD, RM_CMD, '-r', '-f', mailbox_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( - 'could not remove mailbox %s of user %s', mailboxname, username) - raise GVAFileException( - "could not remove mailbox %s of user %s" % (mailboxname, username)) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not remove mailbox %s of user %s', mailboxname, + username) + _LOGGER.info( + 'deleted mailbox directory %s of user %s', mailbox_directory, username) return mailbox_directory @@ -206,6 +220,8 @@ def create_file_website_hierarchy(username, sitename): :param str username: the user name :param str sitename: name of the website + :raises Exception: if the website directory hierarchy directory cannot be + created :return: the directory name :rtype: str @@ -233,13 +249,14 @@ def create_file_website_hierarchy(username, sitename): subprocess.check_output([ SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, '-m', '0750', '-d', tmpdir], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, ("could not setup website file hierarchy for user %s's " "website %s"), username, sitename) - raise GVAFileException( - ("could not setup website file hierarchy for user %s's " - "website %s") % (username, sitename)) + _LOGGER.info( + 'created website directory %s for user %s', website_directory, + username) return website_directory @@ -250,6 +267,8 @@ def delete_file_website_hierarchy(username, sitename): :param str username: the user name :param str sitename: name of the website + :raises Exception: if the website directory hierarchy directory cannot be + deleted :return: the directory name :rtype: str @@ -260,11 +279,11 @@ def delete_file_website_hierarchy(username, sitename): subprocess.check_output([ SUDO_CMD, RM_CMD, '-r', '-f', website_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _logger.exception( + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, ("could not delete the website file hierarchy of user %s's " "website %s"), username, sitename) - raise GVAFileException( - ("could not delete the website file hierarchy of user %s's " - "website %s") % (username, sitename)) + _LOGGER.info( + 'deleted website directory %s of user %s', website_directory, username) return website_directory diff --git a/gvafile/gvafile/exceptions.py b/gvafile/gvafile/exceptions.py deleted file mode 100644 index 06f6cfb..0000000 --- a/gvafile/gvafile/exceptions.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -This module defines exceptions for gvafile. - -""" -from __future__ import unicode_literals - - -class GVAFileException(Exception): - """ - Generic Exception class for gvafile. - - """ From 307ccf430749c6bc0594d676b381730b3f64bee2 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 29 Jan 2015 22:23:11 +0100 Subject: [PATCH 2/6] add new task set_file_ssh_authorized_keys --- docs/changelog.rst | 2 + gvafile/fileservertasks/tasks.py | 63 ++++++++++++++++++++++++++++++++ gvafile/gvafile/settings.py | 2 + 3 files changed, 67 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 528c1a4..9812df4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,8 @@ Changelog ========= +* :feature:`-` add new task set_file_ssh_authorized_keys to add SSH keys for + users * :support:`-` improved logging in fileservertasks.tasks, got rid of GVAFileException diff --git a/gvafile/fileservertasks/tasks.py b/gvafile/fileservertasks/tasks.py index f33f5df..fecba37 100644 --- a/gvafile/fileservertasks/tasks.py +++ b/gvafile/fileservertasks/tasks.py @@ -8,6 +8,7 @@ from __future__ import absolute_import, unicode_literals import os import subprocess +from tempfile import mkstemp from gvafile import settings @@ -37,6 +38,19 @@ def log_and_raise(exception, message, *args): raise Exception(message % args) +def _build_authorized_keys_path(username): + """ + Constructs the file path for the authorized_keys file for a given username. + + :param str username: the user name + :return: the file name + :rtype: str + + """ + return os.path.join( + settings.GVAFILE_SFTP_AUTHKEYS_DIRECTORY, username, 'keys') + + def _build_sftp_directory_name(username): """ Constructs the SFTP directory name for a given username. @@ -287,3 +301,52 @@ def delete_file_website_hierarchy(username, sitename): _LOGGER.info( 'deleted website directory %s of user %s', website_directory, username) return website_directory + + +@shared_task +def set_file_ssh_authorized_keys(username, ssh_keys): + """ + This task sets the authorized keys for ssh logins. + + :param str username: the user name + :param list ssh_key: an ssh_key + :raises Exception: if the update of the creation or update of ssh + authorized_keys failed + :return: the name of the authorized_keys file + :rtype: str + + """ + ssh_authorized_keys_file = _build_authorized_keys_path(username) + if ssh_keys: + try: + authkeystemp, filename = mkstemp() + conffile = os.fdopen(authkeystemp, 'w') + conffile.write("\n".join(ssh_keys)) + finally: + if conffile: + conffile.close() + try: + subprocess.check_output([ + SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, + 'm', '0500', '-d', os.path.dirname(ssh_authorized_keys_file)], + stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, + '-m', '0400', filename, ssh_authorized_keys_file], + stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, RM_CMD, filename], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not write authorized_keys file for user %s', + username) + else: + try: + subprocess.check_output([ + SUDO_CMD, RM_CMD, '-rf', + os.path.dirname(ssh_authorized_keys_file)], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not remove the authorized_keys file of user %s', + username) diff --git a/gvafile/gvafile/settings.py b/gvafile/gvafile/settings.py index d3418c4..71d43f7 100644 --- a/gvafile/gvafile/settings.py +++ b/gvafile/gvafile/settings.py @@ -42,4 +42,6 @@ BROKER_URL = get_env_setting('GVAFILE_BROKER_URL') ########## GVAFILE CONFIGURATION GVAFILE_SFTP_DIRECTORY = get_env_setting('GVAFILE_SFTP_DIRECTORY') GVAFILE_MAIL_DIRECTORY = get_env_setting('GVAFILE_MAIL_DIRECTORY') +GVAFILE_SFTP_AUTHKEYS_DIRECTORY = get_env_setting( + 'GVAFILE_SFTP_AUTHKEYS_DIRECTORY') ########## END GVAFILE CONFIGURATION From 7dbb51da1a2d61e7ef137beebbb9a7e35b817c03 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 29 Jan 2015 22:39:01 +0100 Subject: [PATCH 3/6] add logging for set_file_ssh_authorized_keys --- gvafile/fileservertasks/tasks.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gvafile/fileservertasks/tasks.py b/gvafile/fileservertasks/tasks.py index fecba37..3bbbbc4 100644 --- a/gvafile/fileservertasks/tasks.py +++ b/gvafile/fileservertasks/tasks.py @@ -336,6 +336,8 @@ def set_file_ssh_authorized_keys(username, ssh_keys): stderr=subprocess.STDOUT) subprocess.check_output([ SUDO_CMD, RM_CMD, filename], stderr=subprocess.STDOUT) + _LOGGER.info( + 'set %d authorized_keys for user %s', len(ssh_keys), username) except subprocess.CalledProcessError as cpe: log_and_raise( cpe, 'could not write authorized_keys file for user %s', @@ -346,6 +348,8 @@ def set_file_ssh_authorized_keys(username, ssh_keys): SUDO_CMD, RM_CMD, '-rf', os.path.dirname(ssh_authorized_keys_file)], stderr=subprocess.STDOUT) + _LOGGER.info( + 'deleted authorized_keys of user %s', username) except subprocess.CalledProcessError as cpe: log_and_raise( cpe, 'could not remove the authorized_keys file of user %s', From 0db08db831e6edfeb69cd97274c51b9cbfc03055 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 29 Jan 2015 22:44:10 +0100 Subject: [PATCH 4/6] add return value as documented --- gvafile/fileservertasks/tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gvafile/fileservertasks/tasks.py b/gvafile/fileservertasks/tasks.py index 3bbbbc4..cdb06cc 100644 --- a/gvafile/fileservertasks/tasks.py +++ b/gvafile/fileservertasks/tasks.py @@ -354,3 +354,4 @@ def set_file_ssh_authorized_keys(username, ssh_keys): log_and_raise( cpe, 'could not remove the authorized_keys file of user %s', username) + return ssh_authorized_keys_file From 92beb28816801bd0603cc135075b66c3288184d4 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 29 Jan 2015 22:55:45 +0100 Subject: [PATCH 5/6] add missing - to install parameter --- gvafile/fileservertasks/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gvafile/fileservertasks/tasks.py b/gvafile/fileservertasks/tasks.py index cdb06cc..3f4473a 100644 --- a/gvafile/fileservertasks/tasks.py +++ b/gvafile/fileservertasks/tasks.py @@ -328,7 +328,7 @@ def set_file_ssh_authorized_keys(username, ssh_keys): try: subprocess.check_output([ SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, - 'm', '0500', '-d', os.path.dirname(ssh_authorized_keys_file)], + '-m', '0500', '-d', os.path.dirname(ssh_authorized_keys_file)], stderr=subprocess.STDOUT) subprocess.check_output([ SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, From dd866381beb1d40c9961be806227822737ba7244 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 29 Jan 2015 22:58:10 +0100 Subject: [PATCH 6/6] update docs version, add release to changelog --- docs/changelog.rst | 1 + docs/conf.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9812df4..36c3984 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,7 @@ Changelog ========= +* :release:`0.5.0 <2015-01-29>` * :feature:`-` add new task set_file_ssh_authorized_keys to add SSH keys for users * :support:`-` improved logging in fileservertasks.tasks, got rid of diff --git a/docs/conf.py b/docs/conf.py index a557fc7..a9ea99c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -59,9 +59,9 @@ copyright = u'2014, 2015 Jan Dittberner' # built documents. # # The short X.Y version. -version = '0.4' +version = '0.5' # The full version, including alpha/beta/rc tags. -release = '0.4.0' +release = '0.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages.