From 5dc3549896e3a66d9492fd52ab3657267487a03a Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sat, 24 Sep 2016 21:57:28 +0200 Subject: [PATCH] Improve documentation This commit adds a lot of documentation including block diagramms for message flows. --- docs/code/osusers.rst | 6 + docs/code/userdbs.rst | 6 + docs/conf.py | 7 +- docs/index.rst | 27 +- docs/task_flows.rst | 35 +++ gnuviechadmin/fileservertasks/tasks.py | 333 ++++++++++++++++++++++--- gnuviechadmin/ldaptasks/tasks.py | 81 ++++-- gnuviechadmin/osusers/admin.py | 28 +++ gnuviechadmin/osusers/signals.py | 275 ++++++++++++++++++-- gnuviechadmin/userdbs/signals.py | 141 +++++++++++ requirements/local.txt | 1 + 11 files changed, 853 insertions(+), 87 deletions(-) create mode 100644 docs/task_flows.rst diff --git a/docs/code/osusers.rst b/docs/code/osusers.rst index 3353239..29d62e3 100644 --- a/docs/code/osusers.rst +++ b/docs/code/osusers.rst @@ -31,6 +31,12 @@ .. automodule:: osusers.models :members: +:py:mod:`signals ` +----------------------------------- + +.. automodule:: osusers.signals + :members: + :py:mod:`urls ` ----------------------------- diff --git a/docs/code/userdbs.rst b/docs/code/userdbs.rst index c7af8a1..d5b0da2 100644 --- a/docs/code/userdbs.rst +++ b/docs/code/userdbs.rst @@ -30,6 +30,12 @@ .. automodule:: userdbs.models :members: +:py:mod:`signals ` +----------------------------------- + +.. automodule:: userdbs.signals + :members: + :py:mod:`templatetags ` --------------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index 9fa7d82..11a3fa5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,12 +33,17 @@ django.setup() # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx'] +extensions = [ + 'releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx', + 'sphinxcontrib.blockdiag'] # configuration for releases extension releases_issue_uri = 'https://dev.gnuviech-server.de/gva/ticket/%s' releases_release_uri = 'https://dev.gnuviech-server.de/gva/browser/?rev=%s' +# configuration for blockdiag extension +blockdiag_fontpath = '/usr/share/fonts/truetype/dejavu/' + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/index.rst b/docs/index.rst index c40633d..e5ae1d7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,6 +9,20 @@ Welcome to gnuviechadmin's documentation! .. include:: ../README.rst +Contents +-------- + +.. toctree:: + :maxdepth: 3 + + install + deploy + tests + code + ideas + task_flows + changelog + License ------- @@ -20,19 +34,6 @@ later version. .. include:: ../COPYING :literal: -Contents --------- - -.. toctree:: - :maxdepth: 2 - - install - deploy - tests - code - ideas - changelog - Indices and tables ------------------ diff --git a/docs/task_flows.rst b/docs/task_flows.rst new file mode 100644 index 0000000..c75e878 --- /dev/null +++ b/docs/task_flows.rst @@ -0,0 +1,35 @@ +********** +Task Flows +********** + +gva uses Celery tasks to trigger actions on several servers, this chapter lists +the code parts that start tasks. See the code documentation for details on the +information flow. + +:py:mod:`osusers.admin` +======================= + + * :py:meth:`osusers.admin.SshPublicKeyAdmin.perform_delete_selected` + + +:py:mod:`osusers.signals` +========================= + + * :py:func:`osusers.signals.handle_group_created` + * :py:func:`osusers.signals.handle_group_deleted` + * :py:func:`osusers.signals.handle_ssh_keys_changed` + * :py:func:`osusers.signals.handle_user_added_to_group` + * :py:func:`osusers.signals.handle_user_created` + * :py:func:`osusers.signals.handle_user_deleted` + * :py:func:`osusers.signals.handle_user_password_set` + * :py:func:`osusers.signals.handle_user_removed_from_group` + + +:py:mod:`userdbs.signals` +========================= + + * :py:func:`userdbs.signals.handle_dbuser_created` + * :py:func:`userdbs.signals.handle_dbuser_deleted` + * :py:func:`userdbs.signals.handle_dbuser_deleted` + * :py:func:`userdbs.signals.handle_dbuser_password_set` + * :py:func:`userdbs.signals.handle_userdb_created` diff --git a/gnuviechadmin/fileservertasks/tasks.py b/gnuviechadmin/fileservertasks/tasks.py index 75ebad0..2de6d84 100644 --- a/gnuviechadmin/fileservertasks/tasks.py +++ b/gnuviechadmin/fileservertasks/tasks.py @@ -14,10 +14,38 @@ def setup_file_sftp_userdir(username, *args, **kwargs): This task creates the home directory for an SFTP user if it does not exist yet. - :param str username: the user name + :param str username: the username :raises Exception: if the SFTP directory of the user cannot be created - :return: the created directory name - :rtype: str + :return: a dictionary with the key :py:const:`username` set to the + username value and a new key :py:const:`sftp_directory` set to the + path of the created SFTP directory + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use :py:func:`fileservertasks.tasks.setup_file_sftp_userdir_chained` + at other positions in the task chain. + + """ + + +@shared_task +def setup_file_sftp_userdir_chained(previous_result, *args, **kwargs): + """ + This task creates the home directory for an SFTP user if it does not exist + yet. + + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` key + :raises Exception: if the SFTP directory of the user cannot be created + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`sftp_directory` key set to the path of the created SFTP + directory + :rtype: dict """ @@ -26,12 +54,40 @@ def setup_file_sftp_userdir(username, *args, **kwargs): def delete_file_sftp_userdir(username, *args, **kwargs): """ This task recursively deletes the home directory of an SFTP user if it - does not exist yet. + exists. - :param str username: the user name + :param str username: the username :raises Exception: if the SFTP directory of the user cannot be removed - :return: the removed directory name - :rtype: str + :return: a dictionary with the key :py:const:`username` set to the username + value and the new key :py:const:`sftp_directory` set to the path of the + deleted SFTP directory + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use :py:func:`fileservertasks.tasks.delete_file_sftp_userdir_chained` + at other positions in the task chain. + + """ + + +@shared_task +def delete_file_sftp_userdir_chained(previous_result, *args, **kwargs): + """ + This task recursively deletes the home directory of an SFTP user if it + exists. + + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` key + :raises Exception: if the SFTP directory of the user cannot be removed + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`sftp_directory` key set to the path of the removed SFTP + directory + :rtype: dict """ @@ -42,11 +98,40 @@ def setup_file_mail_userdir(username, *args, **kwargs): This task creates the mail base directory for a user if it does not exist yet. - :param str username: the user name + :param str username: the username :raises Exception: if the mail base directory for the user cannot be created - :return: the created directory name - :rtype: str + :return: a dictionary with the key :py:const:`username` set to the + username value and a new key :py:const:`mail_directory` set to the path + of the created mail directory + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use :py:func:`fileservertasks.tasks.setup_file_mail_userdir_chained` + at other positions in the task chain. + + """ + + +@shared_task +def setup_file_mail_userdir_chained(previous_result, *args, **kwargs): + """ + This task creates the mail base directory for a user if it does not exist + yet. + + :param dict previous_result: a dictionary containing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` key + :raises Exception: if the mail base directory for the user cannot be + created + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`mail_directory` key set to the path of the created mail + directory + :rtype: dict """ @@ -57,79 +142,255 @@ def delete_file_mail_userdir(username, *args, **kwargs): This task recursively deletes the mail base directory for a user if it does not exist yet. - :param str username: the user name - :raises Exception: if the mail base directory of the user cannot be removed - :return: the removed directory name + :param str username: the username + :raises Exception: if the mail base directory of the user cannot be deleted + :return: a dictionary with the key :py:const:`username` set to the + username value and a new key :py:const:`mail_directory` set to the path + of the deleted mail directory + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use :py:func:`fileservertasks.tasks.delete_file_mail_userdir_chained` + at other positions in the task chain. + + """ + + +@shared_task +def delete_file_mail_userdir_chained(previous_result, *args, **kwargs): + """ + This task recursively deletes the mail base directory for a user if it + does not exist yet. + + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` key + :raises Exception: if the mail base directory of the user cannot be deleted + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`mail_directory` key set to the path of the deleted mail + directory :rtype: str """ @shared_task -def create_file_mailbox(username, mailboxname): +def create_file_mailbox(username, mailboxname, *args, **kwargs): """ This task creates a new mailbox directory for the given user and mailbox name. - :param str username: the user name + :param str username: the username :param str mailboxname: the mailbox name :raises Exception: if the mailbox directory cannot be created - :return: the created mailbox directory name - :rtype: str + :return: a dictionary with the keys :py:const:`username` and + :py:const:`mailboxname` set to the values of username and mailboxname + and a new key :py:const:`mailbox_directory` set to the path of the + created mailbox directory + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use :py:func:`fileservertasks.tasks.create_file_mailbox_chained` at + other positions in the task chain. """ @shared_task -def delete_file_mailbox(username, mailboxname): +def create_file_mailbox_chained(previous_result, *args, **kwargs): + """ + This task creates a new mailbox directory for the given user and mailbox + name. + + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` and a :py:const:`mailboxname` key + :raises Exception: if the mailbox directory cannot be created + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`mailbox_directory` key set to the path of the created + mailbox directory + :rtype: dict + + """ + + +@shared_task +def delete_file_mailbox(username, mailboxname, *args, **kwargs): """ This task deletes the given mailbox of the given user. - :param str username: the user name + :param str username: the username :param str mailboxname: the mailbox name :raises Exception: if the mailbox directory cannot be deleted - :return: the deleted mailbox directory name - :rtype: str + :return: a dictionary with the keys :py:const:`username` and + :py:const:`mailboxname` set to the values of username and mailboxname + and a new key :py:const:`mailbox_directory` set to the path of the + deleted mailbox directory + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use :py:func:`fileservertasks.tasks.delete_file_mailbox_chained` for + other positions in the task chain. """ @shared_task -def create_file_website_hierarchy(username, sitename): +def delete_file_mailbox_chained(previous_result, *args, **kwargs): + """ + This task deletes the given mailbox of the given user. + + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` and a :py:const:`mailboxname` key + :raises Exception: if the mailbox directory cannot be deleted + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`mailbox_directory` key set to the path of the deleted + mailbox directory + :rtype: dict + + """ + + +@shared_task +def create_file_website_hierarchy(username, sitename, *args, **kwargs): """ This task creates the directory hierarchy for a website. - :param str username: the user name - :param str sitename: name of the website - :return: the directory name - :rtype: str + :param str username: the username + :param str sitename: the sitename + :raises Exception: if the website directory hierarchy directory cannot be + created + :return: a dictionary with the keys :py:const:`username` and + :py:const:`sitename` set to the values of username and sitename and a + new key :py:const:`website_directory` set to the path of the created + website directory + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use + :py:func:`fileservertasks.tasks.create_file_website_hierarchy_chained` + at other positions in the task chain """ @shared_task -def delete_file_website_hierarchy(username, sitename): +def create_file_website_hierarchy_chained(previous_result, *args, **kwargs): + """ + This task creates the directory hierarchy for a website. + + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` and a :py:const:`sitename` key + :raises Exception: if the website directory hierarchy directory cannot be + created + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`website_directory` key set to the path of the created + website directory + :rtype: dict + + """ + + +@shared_task +def delete_file_website_hierarchy(username, sitename, *args, **kwargs): + """ + This task deletes a website hierarchy recursively. + + :param str username: a username + :param str sitename: a site name + :return: a dictionary with the keys :py:const:`username` and + :py:const:`sitename` set to their original values and a new key + :py:const:`website_directory` set to the path of the deleted website + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use + :py:func:`fileservertasks.tasks.delete_file_website_hierarchy_chained` + at other positions in the task chain + + """ + + +@shared_task +def delete_file_website_hierarchy_chained(previous_result, *args, **kwargs): """ This task deletes the website hierarchy recursively. - :param str username: the user name - :param str sitename: name of the website - :return: the directory name - :rtype: str + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` and a :py:const:`sitename` key + :raises Exception: if the website directory hierarchy directory cannot be + deleted + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`website_directory` set to the path of the deleted website + directory + :rtype: dict """ @shared_task -def set_file_ssh_authorized_keys(username, ssh_keys): +def set_file_ssh_authorized_keys(username, ssh_keys, *args, **kwargs): + """ + This task set the authorized keys for ssh logins. + + :param str username: a username + :param list ssh_keys: a list of ssh keys + :raises Exception: if the update of the creation or update of ssh + authorized_keys failed + :return: a dictionary with the keys :py:const:`username` and + :py:const:`ssh_keys` set to their original values and a new key + :py:const:`ssh_authorized_keys` set to the path of the SSH + authorized_keys file + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use + :py:func:`fileservertasks.tasks.set_file_ssh_authorized_keys_chained` + at other positions in the task chain + + """ + + +@shared_task +def set_file_ssh_authorized_keys_chained(previous_result, *args, **kwargs): """ This task sets the authorized keys for ssh logins. - :param str username: the user name - :param list ssh_key: an ssh_key + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` and a :py:const:`ssh_keys` 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 + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`ssh_authorized_keys` set to the path of the SSH + authorized_keys file + :rtype: dict """ diff --git a/gnuviechadmin/ldaptasks/tasks.py b/gnuviechadmin/ldaptasks/tasks.py index 72acd8d..f47437c 100644 --- a/gnuviechadmin/ldaptasks/tasks.py +++ b/gnuviechadmin/ldaptasks/tasks.py @@ -9,7 +9,7 @@ from celery import shared_task @shared_task -def create_ldap_group(groupname, gid, descr): +def create_ldap_group(groupname, gid, description): """ This task creates an :py:class:`LDAP group ` if it does not exist yet. @@ -19,9 +19,12 @@ def create_ldap_group(groupname, gid, descr): :param str groupname: the group name :param int gid: the group id - :param str descr: description text for the group - :return: the distinguished name of the group - :rtype: str + :param str description: description text for the group + :return: dictionary containing groupname, gid, description and + :py:const:`group_dn` set to the distinguished name of the newly created + or existing LDAP group + :rtype: dict + """ @@ -45,8 +48,10 @@ def create_ldap_user(username, uid, gid, gecos, homedir, shell, password): is passed the password is not touched :raises celery.exceptions.Reject: if the specified primary group does not exist - :return: the distinguished name of the user - :rtype: str + :return: dictionary containing username, uid, gid, gecos, homedir, shell, + password and :py:const:`user_dn` set to the distinguished name of the + newly created or existing LDAP user + :rtype: dict """ @@ -59,8 +64,10 @@ def set_ldap_user_password(username, password): :param str username: the user name :param str password: teh clear text password - :return: :py:const:`True` if the password has been set, :py:const:`False` - if the user does not exist. + :return: dictionary containing the username and a flag + :py:const:`password_set` that is set to :py:const:`True` if the + password has been set, :py:const:`False` if the user does not exist. + :rtype: dict """ @@ -76,8 +83,10 @@ def add_ldap_user_to_group(username, groupname): :param str groupname: the group name :raises celery.exceptions.Retry: if the user does not exist yet, :py:func:`create_ldap_user` should be called before - :return: True if the user has been added to the group otherwise False - :rtype: boolean + :return: dictionary containing the username, groupname and a flag + :py:const`added` that is as a :py:const:`True` if the user has been + added to the group otherwise to :py:const:`False` + :rtype: dict """ @@ -89,20 +98,48 @@ def remove_ldap_user_from_group(username, groupname): :param str username: the user name :param str groupname: the group name - :return: True if the user has been removed, False otherwise - :rtype: boolean + :return: dictionary containing the input parameters and a flag + :py:const:`removed` that is set to :py:const:`True` if the user has + been removed, False otherwise + :rtype: dict """ @shared_task -def delete_ldap_user(username): +def delete_ldap_user(username, *args, **kwargs): """ This task deletes the given user. :param str username: the user name - :return: True if the user has been deleted, False otherwise - :rtype: boolean + :return: dictionary containing the username and a flag :py:const:`deleted` + that is set to :py:const:`True` if the user has been deleted and is set + to :py:const:`False` otherwise + :rtype: dict + + .. note:: + + This variant can only be used at the beginning of a Celery task chain + or as a standalone task. + + Use :py:func:`ldaptasks.tasks.delete_ldap_user_chained` at other + positions in the task chain + + """ + + +@shared_task +def delete_ldap_user_chained(previous_result, *args, **kwargs): + """ + This task deletes the given user. + + :param dict previous_result: a dictionary describing the result of the + previous step in the Celery task chain. This dictionary must contain a + :py:const:`username` key + :return: a copy of the :py:obj:`previous_result` dictionary with a new + :py:const:`deleted` key set to :py:const:`True` if the user has been + deleted and set to :py:const:`False` otherwise + :rtype: dict """ @@ -113,8 +150,10 @@ def delete_ldap_group_if_empty(groupname): This task deletes the given group if it is empty. :param str groupname: the group name - :return: True if the user has been deleted, False otherwise - :rtype: boolean + :return: dictionary that contains the groupname and a flag + :py:const:`deleted` that is set to :py:const:`True` if the group has + been deleted and is set to :py:const:`False` otherwise + :rtype: dict """ @@ -122,10 +161,12 @@ def delete_ldap_group_if_empty(groupname): @shared_task def delete_ldap_group(groupname): """ - This taks deletes the given group. + This task deletes the given group. :param str groupname: the group name - :return: True if the user has been deleted, False otherwise - :rtype: boolean + :return: dictionary that contains the groupname and a flag + :py:const:`deleted` that is set to :py:const:`True` if the group has + been deleted and is set to :py:const:`False` otherwise + :rtype: dict """ diff --git a/gnuviechadmin/osusers/admin.py b/gnuviechadmin/osusers/admin.py index addb70b..3b4fb2b 100644 --- a/gnuviechadmin/osusers/admin.py +++ b/gnuviechadmin/osusers/admin.py @@ -1,6 +1,10 @@ """ This module contains the Django admin classes of the :py:mod:`osusers` app. +The module starts Celery_ tasks. + +.. _Celery: http://www.celeryproject.org/ + """ from django import forms from django.utils.translation import ugettext_lazy as _ @@ -341,6 +345,30 @@ class SshPublicKeyAdmin(admin.ModelAdmin): :param request: the current HTTP request :param queryset: Django ORM queryset representing the selected ssh keys + This method starts a Celery_ task to update the list of authorized keys + for each affected user. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + + A [ label = "", shape = beginpoint, + description = "this method" + ]; + B [ label = "set file ssh authorized_keys", + description = ":py:func:`set_file_ssh_authorized_keys() + ` + called with username and a list of keys, returning the path + of the ssh authorized_keys file", + color = "LightGreen", + stacked + ]; + } + """ users = set([ item['user'] for item in diff --git a/gnuviechadmin/osusers/signals.py b/gnuviechadmin/osusers/signals.py index a74442b..e062cb9 100644 --- a/gnuviechadmin/osusers/signals.py +++ b/gnuviechadmin/osusers/signals.py @@ -1,6 +1,10 @@ """ This module contains the signal handlers of the :py:mod:`osusers` app. +The module starts Celery_ tasks. + +.. _Celery: http://www.celeryproject.org/ + """ from __future__ import absolute_import, unicode_literals @@ -14,17 +18,17 @@ from django.dispatch import receiver from fileservertasks.tasks import ( delete_file_mail_userdir, - delete_file_sftp_userdir, + delete_file_sftp_userdir_chained, set_file_ssh_authorized_keys, - setup_file_mail_userdir, - setup_file_sftp_userdir, + setup_file_mail_userdir_chained, + setup_file_sftp_userdir_chained, ) from ldaptasks.tasks import ( add_ldap_user_to_group, create_ldap_group, create_ldap_user, delete_ldap_group, - delete_ldap_user, + delete_ldap_user_chained, remove_ldap_user_from_group, set_ldap_user_password, ) @@ -44,6 +48,33 @@ _LOGGER = logging.getLogger(__name__) @receiver(password_set, sender=User) def handle_user_password_set(sender, instance, password, **kwargs): + """ + Handles password changes on :py:class:`User ` + instances. + + :param sender: sender of the signal + :param instance: User instance + :param str password: the new password + + This signal handler starts a Celery_ task. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "set ldap user password", color = "Wheat", + description = ":py:func:`set_ldap_user_password() + ` called with + username and password, returning :py:const:`True` if the + password has been set" ]; + } + """ taskresult = TaskResult.objects.create_task_result( 'handle_user_password_set', set_ldap_user_password.s(instance.username, password)) @@ -52,15 +83,35 @@ def handle_user_password_set(sender, instance, password, **kwargs): taskresult.task_id) -# @receiver(post_save) -# def handle_post_save(sender, **kwargs): -# _LOGGER.debug( -# 'handling post_save signal for %s with args %s', -# sender, kwargs) - - @receiver(post_save, sender=Group) def handle_group_created(sender, instance, created, **kwargs): + """ + Handles post creation actions on :py:class:`Group ` + instances. + + :param sender: sender of the signal + :param instance: Group instance + :param bool created: whether the instance has just been created + + This signal handler starts a Celery_ task. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "create ldap group", color = "Wheat", + description = ":py:func:`create_ldap_group() + ` called with groupname, + gid and description, returning group DN" ]; + } + + """ if created: taskresult = TaskResult.objects.create_task_result( 'handle_group_created', @@ -75,13 +126,52 @@ def handle_group_created(sender, instance, created, **kwargs): @receiver(post_save, sender=User) def handle_user_created(sender, instance, created, **kwargs): + """ + Handles post creation actions on :py:class:`User ` + instances. + + :param sender: sender of the signal + :param instance: User instance + :param bool created: whether the instance has just bean created + + This signal handler starts a chain of Celery_ tasks. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B -> C -> D; + B -> C [folded]; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "create ldap user", color = "Wheat", + description = ":py:func:`create_ldap_user() + ` called with username, uid, + gid, gecos, homeidr, shell, :py:const:`None`, returning + username" ]; + C [ label = "setup file sftp userdir", color = "LightGreen", + description = ":py:func:`setup_file_sftp_userdir_chained() + ` + called with the result of create ldap user task, returning a + dictionary containing username and sftp_directory"]; + D [ label = "setup file mail userdir", color = "LightGreen", + description = ":py:func:`setup_file_mail_userdir_chained() + ` called + with result of setup file sftp userdir task, returning + dictionary containing username, sftp_directory and + mail_directory" ]; + } + + """ if created: chain = create_ldap_user.s( instance.username, instance.uid, instance.group.gid, instance.gecos, instance.homedir, instance.shell, None - ) | setup_file_sftp_userdir.s( - instance.username - ) | setup_file_mail_userdir.s(instance.username) + ) | setup_file_sftp_userdir_chained.s() | ( + setup_file_mail_userdir_chained.s()) taskresult = TaskResult.objects.create_task_result( 'handle_user_created', chain) _LOGGER.info( @@ -93,6 +183,34 @@ def handle_user_created(sender, instance, created, **kwargs): @receiver(post_save, sender=AdditionalGroup) def handle_user_added_to_group(sender, instance, created, **kwargs): + """ + Handles post creation actions on :py:class:`AdditionalGroup + ` instances. + + :param sender: sender of the signal + :param instance: AdditionalGroup instance + :param bool created: whether the instance has just bean created + + This signal handler starts a Celery_ task. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "add ldap user to group", color = "Wheat", + description = ":py:func:`add_ldap_user_to_group() + ` called with username + and groupname, returning :py:const:`True` if the user has been + added to the group" ]; + } + + """ if created: taskresult = TaskResult.objects.create_task_result( 'handle_user_added_to_group', @@ -106,6 +224,33 @@ def handle_user_added_to_group(sender, instance, created, **kwargs): @receiver(post_save, sender=SshPublicKey) @receiver(post_delete, sender=SshPublicKey) def handle_ssh_keys_changed(sender, instance, **kwargs): + """ + Handles changes to :py:class:`SshPublicKey ` + instances related to a user. + + :param sender: sender of the signal + :param instance: SshPublicKey instance + + This signal handler starts a Celery_ task. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "set file ssh authorized_keys", color = "LightGreen", + description = ":py:func:`set_file_ssh_authorized_keys() + ` called + with username and the corresponding list of keys, returning the + path of the ssh_authorized_keys_file" ]; + } + + """ sig = set_file_ssh_authorized_keys.s( instance.user.username, [ str(key) for key in @@ -126,6 +271,33 @@ def handle_ssh_keys_changed(sender, instance, **kwargs): @receiver(post_delete, sender=Group) def handle_group_deleted(sender, instance, **kwargs): + """ + Handles cleanup actions to be done after deletion of a :py:class:`Group + ` instance. + + :param sender: sender of the signal + :param instance: Group instance + + This signal handler starts a Celery_ task. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + + A [ label = "", shape = beginpoint, + description = "this signal handler" + ]; + B [ label = "delete ldap group", color = "Wheat", + description = ":py:func:`delete_ldap_group() + ` called with groupname, + returning :py:const:`True` if the group has been deleted" ]; + } + + """ taskresult = TaskResult.objects.create_task_result( 'handle_group_deleted', delete_ldap_group.s(instance.groupname)) @@ -136,11 +308,49 @@ def handle_group_deleted(sender, instance, **kwargs): @receiver(post_delete, sender=User) def handle_user_deleted(sender, instance, **kwargs): + """ + Handles cleanup actions to be done after deletion of a :py:class:`User + ` instance. + + :param sender: sender of the signal + :param instance: User instance + + This signal handler starts a chain of Celery_ tasks. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B -> C -> D; + B -> C [folded]; + + A [ label = "", shape = beginpoint, + description = "this signal handler" + ]; + B [ label = "delete file mail userdir", color = "LightGreen", + description = ":py:func:`delete_file_mail_userdir() + ` called with + username, returning a dictionary containing the username and + the deleted mail_directory" ]; + C [ label = "delete file sftp userdir", color = "LightGreen", + description = ":py:func:`delete_file_sftp_userdir_chained() + ` + called with the result of delete mail userdir, returning + dictionary containing username, deleted mail_directory and + deleted sftp_directory" ]; + D [ label = "delete ldap user", color = "Wheat", + description = ":py:func:`delete_ldap_user_chained() + ` called with the + result of delete file sftp userdir and adding the deleted user + DN to the result" ]; + } + + """ chain = delete_file_mail_userdir.s( instance.username - ) | delete_file_sftp_userdir.s( - instance.username - ) | delete_ldap_user.s(instance.username) + ) | delete_file_sftp_userdir_chained.s() | delete_ldap_user_chained.s() _LOGGER.debug('chain signature %s', chain) taskresult = TaskResult.objects.create_task_result( 'handle_user_deleted', chain) @@ -151,6 +361,37 @@ def handle_user_deleted(sender, instance, **kwargs): @receiver(post_delete, sender=AdditionalGroup) def handle_user_removed_from_group(sender, instance, **kwargs): + """ + Handles cleanup actions to be done after removing a user from a group by + deleting the :py:class:`AdditionalGroup ` + instance. + + :param sender: sender of the signal + :param instance: AdditionalGroup instance + + This signal handler starts a Celery_ task. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + + A [ label = "", shape = beginpoint, + description = "this signal handler" + ]; + B [ label = "remove ldap user from group", color = "Wheat", + description = ":py:func:`remove_ldap_user_from_group() + ` called with + username and groupname, returning :py:const:`True` if the user + has been a member of the group and has been removed from the + group" + ]; + } + + """ taskresult = TaskResult.objects.create_task_result( 'handle_user_removed_from_group', remove_ldap_user_from_group.s( diff --git a/gnuviechadmin/userdbs/signals.py b/gnuviechadmin/userdbs/signals.py index fde20ac..4a5bc32 100644 --- a/gnuviechadmin/userdbs/signals.py +++ b/gnuviechadmin/userdbs/signals.py @@ -1,6 +1,10 @@ """ This module contains the signal handlers of the :py:mod:`userdbs` app. +The module starts Celery_ tasks. + +.. _Celery: http://www.celeryproject.org/ + """ from __future__ import unicode_literals @@ -29,6 +33,34 @@ def handle_dbuser_password_set(sender, instance, password, **kwargs): Signal handler triggered by password changes for :py:class:`userdbs.models.DatabaseUser` instances. + :param sender: the sender of the signal + :param instance: the Database user instance + :param str password: the new password for the database user + + This signal handler starts Celery_ tasks depending on the db_type value of + the database user. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + A -> C; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "set mysql userpassword", color = "PowderBlue", + description = ":py:func:`set_mysql_userpassword() + ` called with + database username and password" ]; + C [ label = "set pgsql userpassword", color = "DodgerBlue", + description = ":py:func:`set_pgsql_userpassword() + ` called with + database username and password" ]; + } + """ if instance.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( @@ -59,6 +91,35 @@ def handle_dbuser_created(sender, instance, created, **kwargs): Signal handler triggered after the creation of or updates to :py:class:`userdbs.models.DatabaseUser` instances. + :param sender: the sender of the signal + :param instance: the DatabaseUser instance + :param bool created: whether this signal handler is called for a newly + created instance + + This signal handler starts Celery_ tasks depending on the db_type value of + the database user. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + A -> C; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "create mysql user", color = "PowderBlue", + description = ":py:func:`create_mysql_user() + ` called with database + username and password" ]; + C [ label = "create pgsql user", color = "DodgerBlue", + description = ":py:func:`create_pgsql_user + ` called with database + username and password" ]; + } + """ if created: password = kwargs.get('password', generate_password()) @@ -95,6 +156,32 @@ def handle_dbuser_deleted(sender, instance, **kwargs): Signal handler triggered after the deletion of :py:class:`userdbs.models.DatabaseUser` instances. + :param sender: the sender of the signal + :param instance: the DatabaseUser instance + + This signal handler starts Celery_ tasks depending on the db_type value of + the database user. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + A -> C; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "delete mysql user", color = "PowderBlue", + description = ":py:func:`delete_mysql_user() + ` called with username + from instance.name" ]; + C [ label = "delete pgsql user", color = "DodgerBlue", + description = ":py:func:`delete_pgsql_user() + ` called with username + from instance.name" ]; + } """ if instance.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( @@ -126,6 +213,34 @@ def handle_userdb_created(sender, instance, created, **kwargs): Signal handler triggered after the creation of or updates to :py:class:`userdbs.models.UserDatabase` instances. + :param sender: the sender of the signal + :param instance: the UserDatabase instance + :param bool created: whether this signal handler has been called for a + newly created instance + + This signal handler starts Celery_ tasks depending on the db_type value of + the database user owning the UserDatabase instance. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + A -> C; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "create mysql database", color = "PowderBlue", + description = ":py:func:`create_mysql_database() + ` called with database + name and username" ]; + C [ label = "create pgsql database", color = "DodgerBlue", + description = ":py:func:`create_pgsql_database() + ` called with database + name and username" ]; + } """ if created: if instance.db_user.db_type == DB_TYPES.mysql: @@ -163,6 +278,32 @@ def handle_userdb_deleted(sender, instance, **kwargs): Signal handler triggered after the deletion of :py:class:`userdbs.models.UserDatabase` instances. + :param sender: the sender of the signal + :param instance: the UserDatabase instance + + This signal handler starts Celery_ tasks depending on the db_type value of + the database user owning the UserDatabase instance. + + .. blockdiag:: + :desctable: + + blockdiag { + node_width = 200; + + A -> B; + A -> C; + + A [ label = "", shape = beginpoint, + description = "this signal handler" ]; + B [ label = "delete mysql database", color = "PowderBlue", + description = ":py:func:`delete_mysql_user() + ` called with database + name and username" ]; + C [ label = "delete pgsql database", color = "DodgerBlue", + description = ":py:func:`delete_pgsql_user() + ` called with database + name" ]; + } """ if instance.db_user.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( diff --git a/requirements/local.txt b/requirements/local.txt index bf0bc9e..27428d9 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -9,3 +9,4 @@ flake8==2.5.2 mccabe==0.4.0 pep8==1.7.0 pyflakes==1.0.0 +sphinxcontrib-blockdiag==1.5.5