diff --git a/TODO.md b/TODO.md
index b39e84ac..27d2c50b 100644
--- a/TODO.md
+++ b/TODO.md
@@ -266,7 +266,6 @@ https://code.djangoproject.com/ticket/24576
* MultiCHoiceField proper serialization
-# Apache restart fails: detect if appache running, and execute start
* UNIFY PHP FPM settings name
# virtualhost name: name-account?
* add a delay to changes on the webserver apache to no overwelm it with backend executions?
@@ -278,6 +277,7 @@ https://code.djangoproject.com/ticket/24576
* rename resource.monitors to resource.backends ?
* abstract model classes that enabling overriding, and ORCHESTRA_DATABASE_MODEL settings + orchestra.get_database_model() instead of explicitly importing from orchestra.contrib.databases.models import Database.. (Admin and REST API are fucked then?)
+# billing order list filter detect metrics that are greater from those of billing_date
# Ignore superusers & co on billing: list filter doesn't work nor ignore detection
# bill.totals make it 100% computed?
* joomla: wget https://github.com/joomla/joomla-cms/releases/download/3.4.1/Joomla_3.4.1-Stable-Full_Package.tar.gz -O - | tar xvfz -
@@ -285,7 +285,5 @@ https://code.djangoproject.com/ticket/24576
# replace multichoicefield and jsonfield by ArrayField, HStoreField
# Amend lines???
-# Add icon on select contact view
-
# Determine the difference between data serializer used for validation and used for the rest API!
# Make PluginApiView that fills metadata and other stuff like modeladmin plugin support
diff --git a/orchestra/contrib/accounts/admin.py b/orchestra/contrib/accounts/admin.py
index f6952a79..8f96b9e4 100644
--- a/orchestra/contrib/accounts/admin.py
+++ b/orchestra/contrib/accounts/admin.py
@@ -122,12 +122,13 @@ class AccountListAdmin(AccountAdmin):
# TODO get query string from request.META['QUERY_STRING'] to preserve filters
context = {
'url': '../?account=' + str(instance.pk),
- 'name': instance.username
+ 'name': instance.username,
+ 'plus': '+',
}
- return '%(name)s' % context
+ return _('%(plus)s Add to %(name)s') % context
select_account.short_description = _("account")
select_account.allow_tags = True
- select_account.order_admin_field = 'username'
+ select_account.admin_order_field = 'username'
def changelist_view(self, request, extra_context=None):
original_app_label = request.META['PATH_INFO'].split('/')[-5]
diff --git a/orchestra/contrib/databases/admin.py b/orchestra/contrib/databases/admin.py
index df02e8f0..6d90ede1 100644
--- a/orchestra/contrib/databases/admin.py
+++ b/orchestra/contrib/databases/admin.py
@@ -14,7 +14,7 @@ from .models import Database, DatabaseUser
class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'type', 'display_users', 'account_link')
list_filter = ('type',)
- search_fields = ['name']
+ search_fields = ('name', 'account__username')
change_readonly_fields = ('name', 'type')
extra = 1
fieldsets = (
@@ -71,7 +71,7 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin):
list_display = ('username', 'type', 'display_databases', 'account_link')
list_filter = ('type',)
- search_fields = ['username']
+ search_fields = ('username', 'account__username')
form = DatabaseUserChangeForm
add_form = DatabaseUserCreationForm
change_readonly_fields = ('username', 'type')
diff --git a/orchestra/contrib/databases/backends.py b/orchestra/contrib/databases/backends.py
index d076f15e..8c216b61 100644
--- a/orchestra/contrib/databases/backends.py
+++ b/orchestra/contrib/databases/backends.py
@@ -10,14 +10,14 @@ from . import settings
class MySQLBackend(ServiceController):
"""
- Simple backend for creating MySQL databases using `CREATE DATABASE` statement.
- DATABASES_DEFAULT_HOST = %s
+ Simple backend for creating MySQL databases using CREATE DATABASE statement.
"""
- format_docstring = (settings.DATABASES_DEFAULT_HOST,)
-
verbose_name = "MySQL database"
model = 'databases.Database'
default_route_match = "database.type == 'mysql'"
+ doc_settings = (settings,
+ ('DATABASES_DEFAULT_HOST',)
+ )
def save(self, database):
if database.type != database.MYSQL:
@@ -61,13 +61,14 @@ class MySQLBackend(ServiceController):
class MySQLUserBackend(ServiceController):
"""
- Simple backend for creating MySQL users using `CREATE USER` statement.
- DATABASES_DEFAULT_HOST = %s
- """ % settings.DATABASES_DEFAULT_HOST
-
+ Simple backend for creating MySQL users using CREATE USER statement.
+ """
verbose_name = "MySQL user"
model = 'databases.DatabaseUser'
default_route_match = "databaseuser.type == 'mysql'"
+ doc_settings = (settings,
+ ('DATABASES_DEFAULT_HOST',)
+ )
def save(self, user):
if user.type != user.MYSQL:
@@ -105,7 +106,7 @@ class MySQLUserBackend(ServiceController):
class MysqlDisk(ServiceMonitor):
"""
- du -bs
+ du -bs <database_path>
Implements triggers for resource limit exceeded and recovery, disabling insert and create privileges.
"""
model = 'databases.Database'
diff --git a/orchestra/contrib/domains/admin.py b/orchestra/contrib/domains/admin.py
index bb910d17..bd0fbb4e 100644
--- a/orchestra/contrib/domains/admin.py
+++ b/orchestra/contrib/domains/admin.py
@@ -67,7 +67,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
inlines = [RecordInline, DomainInline]
list_filter = [TopDomainListFilter]
change_readonly_fields = ('name',)
- search_fields = ['name',]
+ search_fields = ('name', 'account__username')
add_form = BatchDomainCreationAdminForm
change_view_actions = [view_zone]
diff --git a/orchestra/contrib/domains/backends.py b/orchestra/contrib/domains/backends.py
index 35e4848d..e46aacb2 100644
--- a/orchestra/contrib/domains/backends.py
+++ b/orchestra/contrib/domains/backends.py
@@ -14,14 +14,8 @@ class Bind9MasterDomainBackend(ServiceController):
"""
Bind9 zone and config generation.
It auto-discovers slave Bind9 servers based on your routing configuration or you can use DOMAINS_SLAVES to explicitly configure the slaves.
- DOMAINS_SLAVES = %s
- DOMAINS_MASTERS_PATH = '%s'
"""
-
- format_docstring = (
- str(settings.DOMAINS_SLAVES),
- settings.DOMAINS_MASTERS_PATH,
- )
+ CONF_PATH = settings.DOMAINS_MASTERS_PATH
verbose_name = _("Bind9 master domain")
model = 'domains.Domain'
@@ -30,7 +24,9 @@ class Bind9MasterDomainBackend(ServiceController):
('domains.Domain', 'origin'),
)
ignore_fields = ['serial']
- CONF_PATH = settings.DOMAINS_MASTERS_PATH
+ doc_settings = (settings,
+ ('DOMAINS_SLAVES', 'DOMAINS_MASTERS_PATH')
+ )
@classmethod
def is_main(cls, obj):
@@ -136,15 +132,16 @@ class Bind9SlaveDomainBackend(Bind9MasterDomainBackend):
"""
Generate the configuartion for slave servers
It auto-discover the master server based on your routing configuration or you can use DOMAINS_MASTERS to explicitly configure the master.
- DOMAINS_MASTERS = %s
- """ % str(settings.DOMAINS_MASTERS)
+ """
+ CONF_PATH = settings.DOMAINS_SLAVES_PATH
verbose_name = _("Bind9 slave domain")
related_models = (
('domains.Domain', 'origin'),
)
- CONF_PATH = settings.DOMAINS_SLAVES_PATH
-
+ doc_settings = (settings,
+ ('DOMAINS_MASTERS', 'DOMAINS_SLAVES_PATH')
+ )
def save(self, domain):
context = self.get_context(domain)
self.update_conf(context)
diff --git a/orchestra/contrib/issues/admin.py b/orchestra/contrib/issues/admin.py
index cc8bc631..4a4f097b 100644
--- a/orchestra/contrib/issues/admin.py
+++ b/orchestra/contrib/issues/admin.py
@@ -42,8 +42,8 @@ class MessageReadOnlyInline(admin.TabularInline):
model = Message
extra = 0
can_delete = False
- fields = ['content_html']
- readonly_fields = ['content_html']
+ fields = ('content_html',)
+ readonly_fields = ('content_html',)
class Media:
css = {
@@ -79,7 +79,7 @@ class MessageInline(admin.TabularInline):
max_num = 1
form = MessageInlineForm
can_delete = False
- fields = ['content']
+ fields = ('content',)
def get_formset(self, request, obj=None, **kwargs):
""" hook request.user on the inline form """
@@ -93,14 +93,14 @@ class MessageInline(admin.TabularInline):
class TicketInline(admin.TabularInline):
- fields = [
+ fields = (
'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
'colored_priority', 'created', 'updated'
- ]
- readonly_fields = [
+ )
+ readonly_fields = (
'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
'colored_priority', 'created', 'updated'
- ]
+ )
model = Ticket
extra = 0
max_num = 0
@@ -119,35 +119,35 @@ class TicketInline(admin.TabularInline):
class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin):
- list_display = [
+ list_display = (
'unbold_id', 'bold_subject', 'display_creator', 'display_owner',
'display_queue', 'display_priority', 'display_state', 'updated'
- ]
+ )
list_display_links = ('unbold_id', 'bold_subject')
- list_filter = [
+ list_filter = (
MyTicketsListFilter, 'queue__name', 'priority', TicketStateListFilter,
- ]
+ )
default_changelist_filters = (
('my_tickets', lambda r: 'True' if not r.user.is_superuser else 'False'),
('state', 'OPEN')
)
date_hierarchy = 'created_at'
- search_fields = [
+ search_fields = (
'id', 'subject', 'creator__username', 'creator__email', 'queue__name',
'owner__username'
- ]
- actions = [
+ )
+ actions = (
mark_as_unread, mark_as_read, 'delete_selected', reject_tickets,
resolve_tickets, close_tickets, take_tickets
- ]
- sudo_actions = ['delete_selected']
- change_view_actions = [
+ )
+ sudo_actions = ('delete_selected',)
+ change_view_actions = (
resolve_tickets, close_tickets, reject_tickets, take_tickets
- ]
+ )
# change_form_template = "admin/orchestra/change_form.html"
form = TicketForm
- add_inlines = []
- inlines = [ MessageReadOnlyInline, MessageInline ]
+ add_inlines = ()
+ inlines = (MessageReadOnlyInline, MessageInline)
readonly_fields = (
'display_summary', 'display_queue', 'display_owner', 'display_state',
'display_priority'
@@ -286,10 +286,10 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin):
class QueueAdmin(admin.ModelAdmin):
- list_display = ['name', 'default', 'num_tickets']
- actions = [set_default_queue]
- inlines = [TicketInline]
- ordering = ['name']
+ list_display = ('name', 'default', 'num_tickets')
+ actions = (set_default_queue,)
+ inlines = (TicketInline,)
+ ordering = ('name',)
class Media:
css = {
diff --git a/orchestra/contrib/lists/backends.py b/orchestra/contrib/lists/backends.py
index 1ffa5dc0..a506ab3d 100644
--- a/orchestra/contrib/lists/backends.py
+++ b/orchestra/contrib/lists/backends.py
@@ -11,18 +11,8 @@ from .models import List
class MailmanBackend(ServiceController):
"""
- Mailman backend based on `newlist`, it handles custom domains.
- LISTS_VIRTUAL_ALIAS_PATH = '%s'
- LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = '%s'
- LISTS_DEFAULT_DOMAIN = '%s'
- LISTS_MAILMAN_ROOT_DIR = '%s'
+ Mailman 2 backend based on newlist, it handles custom domains.
"""
- format_docstring = (
- settings.LISTS_VIRTUAL_ALIAS_PATH,
- settings.LISTS_VIRTUAL_ALIAS_DOMAINS_PATH,
- settings.LISTS_DEFAULT_DOMAIN,
- settings.LISTS_MAILMAN_ROOT_DIR,
- )
verbose_name = "Mailman"
model = 'lists.List'
addresses = [
@@ -37,6 +27,12 @@ class MailmanBackend(ServiceController):
'-subscribe',
'-unsubscribe'
]
+ doc_settings = (settings, (
+ 'LISTS_VIRTUAL_ALIAS_PATH',
+ 'LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
+ 'LISTS_DEFAULT_DOMAIN',
+ 'LISTS_MAILMAN_ROOT_DIR'
+ ))
def include_virtual_alias_domain(self, context):
if context['address_domain']:
@@ -165,16 +161,15 @@ class MailmanBackend(ServiceController):
class MailmanTraffic(ServiceMonitor):
"""
- Parses mailman log file looking for email size and multiples it by `list_members` count.
- LISTS_MAILMAN_POST_LOG_PATH = '%s'
+ Parses mailman log file looking for email size and multiples it by list_members count.
"""
- format_docstring = (
- settings.LISTS_MAILMAN_POST_LOG_PATH,
- )
model = 'lists.List'
resource = ServiceMonitor.TRAFFIC
verbose_name = _("Mailman traffic")
script_executable = '/usr/bin/python'
+ doc_settings = (settings,
+ ('LISTS_MAILMAN_POST_LOG_PATH',)
+ )
def prepare(self):
postlog = settings.LISTS_MAILMAN_POST_LOG_PATH
@@ -272,7 +267,7 @@ class MailmanTraffic(ServiceMonitor):
class MailmanSubscribers(ServiceMonitor):
"""
- Monitors number of list subscribers via `list_members`
+ Monitors number of list subscribers via list_members
"""
model = 'lists.List'
verbose_name = _("Mailman subscribers")
diff --git a/orchestra/contrib/mailboxes/admin.py b/orchestra/contrib/mailboxes/admin.py
index 58457646..af0e111d 100644
--- a/orchestra/contrib/mailboxes/admin.py
+++ b/orchestra/contrib/mailboxes/admin.py
@@ -3,6 +3,8 @@ from urllib.parse import parse_qs
from django import forms
from django.contrib import admin
+from django.db.models import F, Value as V
+from django.db.models.functions import Concat
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
@@ -104,13 +106,15 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = (
- 'email', 'account_link', 'domain_link', 'display_mailboxes', 'display_forward',
+ 'display_email', 'account_link', 'domain_link', 'display_mailboxes', 'display_forward',
)
list_filter = (HasMailboxListFilter, HasForwardListFilter)
fields = ('account_link', 'email_link', 'mailboxes', 'forward')
add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
inlines = [AutoresponseInline]
- search_fields = ('name', 'domain__name', 'forward', 'mailboxes__name', 'account__username')
+ search_fields = (
+ 'name', 'domain__name', 'forward', 'mailboxes__name', 'account__username', 'computed_email'
+ )
readonly_fields = ('account_link', 'domain_link', 'email_link')
filter_by_account_fields = ('domain', 'mailboxes')
filter_horizontal = ['mailboxes']
@@ -119,6 +123,11 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
domain_link = admin_link('domain', order='domain__name')
+ def display_email(self, address):
+ return address.computed_email
+ display_email.short_description = _("Email")
+ display_email.admin_order_field = 'computed_email'
+
def email_link(self, address):
link = self.domain_link(address)
return "%s@%s" % (address.name, link)
@@ -153,6 +162,10 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
fields = list(fields)
fields.remove('mailboxes')
return fields
+
+ def get_queryset(self, request):
+ qs = super(AddressAdmin, self).get_queryset(request)
+ return qs.annotate(computed_email=Concat(F('name'), V('@'), F('domain__name')))
admin.site.register(Mailbox, MailboxAdmin)
diff --git a/orchestra/contrib/mailboxes/backends.py b/orchestra/contrib/mailboxes/backends.py
index bdf61131..fe4963b8 100644
--- a/orchestra/contrib/mailboxes/backends.py
+++ b/orchestra/contrib/mailboxes/backends.py
@@ -185,21 +185,15 @@ class DovecotPostfixPasswdVirtualUserBackend(ServiceController):
class PostfixAddressBackend(ServiceController):
"""
Addresses based on Postfix virtual alias domains.
- MAILBOXES_LOCAL_DOMAIN = '%s'
- MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = '%s'
- MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH = '%s'
"""
- format_docstring = (
- settings.MAILBOXES_LOCAL_DOMAIN,
- settings.MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH,
- settings.MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH,
- )
verbose_name = _("Postfix address")
model = 'mailboxes.Address'
related_models = (
('mailboxes.Mailbox', 'addresses'),
)
-
+ doc_settings = (settings,
+ ('MAILBOXES_LOCAL_DOMAIN', 'MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH', 'MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH',)
+ )
def include_virtual_alias_domain(self, context):
if context['domain'] != context['local_domain']:
self.append(textwrap.dedent("""
@@ -296,15 +290,13 @@ class DovecotMaildirDisk(ServiceMonitor):
"""
Maildir disk usage based on Dovecot maildirsize file
http://wiki2.dovecot.org/Quota/Maildir
-
- MAILBOXES_MAILDIRSIZE_PATH = '%s'
"""
- format_docstring = (
- settings.MAILBOXES_MAILDIRSIZE_PATH,
- )
model = 'mailboxes.Mailbox'
resource = ServiceMonitor.DISK
verbose_name = _("Dovecot Maildir size")
+ doc_settings = (settings,
+ ('MAILBOXES_MAILDIRSIZE_PATH',)
+ )
def prepare(self):
super(DovecotMaildirDisk, self).prepare()
@@ -331,15 +323,14 @@ class PostfixMailscannerTraffic(ServiceMonitor):
"""
A high-performance log parser.
Reads the mail.log file only once, for all users.
- MAILBOXES_MAIL_LOG_PATH = '%s'
"""
- format_docstring = (
- settings.MAILBOXES_MAIL_LOG_PATH,
- )
model = 'mailboxes.Mailbox'
resource = ServiceMonitor.TRAFFIC
verbose_name = _("Postfix-Mailscanner traffic")
script_executable = '/usr/bin/python'
+ doc_settings = (settings,
+ ('MAILBOXES_MAIL_LOG_PATH',)
+ )
def prepare(self):
mail_log = settings.MAILBOXES_MAIL_LOG_PATH
diff --git a/orchestra/contrib/miscellaneous/admin.py b/orchestra/contrib/miscellaneous/admin.py
index 93c1af92..e616f0f9 100644
--- a/orchestra/contrib/miscellaneous/admin.py
+++ b/orchestra/contrib/miscellaneous/admin.py
@@ -58,7 +58,7 @@ class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelA
)
list_filter = ('service__name', 'is_active')
list_select_related = ('service', 'account')
- search_fields = ('identifier', 'description')
+ search_fields = ('identifier', 'description', 'account__username')
plugin_field = 'service'
plugin = MiscServicePlugin
diff --git a/orchestra/contrib/orchestration/backends.py b/orchestra/contrib/orchestration/backends.py
index e55d7097..13d9de6f 100644
--- a/orchestra/contrib/orchestration/backends.py
+++ b/orchestra/contrib/orchestration/backends.py
@@ -45,7 +45,7 @@ class ServiceBackend(plugins.Plugin, metaclass=ServiceMount):
default_route_match = 'True'
# Force the backend manager to block in multiple backend executions executing them synchronously
block = False
- format_docstring = ()
+ doc_settings = None
def __str__(self):
return type(self).__name__
diff --git a/orchestra/contrib/orchestration/helpers.py b/orchestra/contrib/orchestration/helpers.py
index 0c55d224..98c941c5 100644
--- a/orchestra/contrib/orchestration/helpers.py
+++ b/orchestra/contrib/orchestration/helpers.py
@@ -11,30 +11,36 @@ from django.utils.translation import ungettext, ugettext_lazy as _
def get_backends_help_text(backends):
help_texts = {}
for backend in backends:
+ help_text = backend.__doc__ or ''
context = {
'model': backend.model,
'related_models': str(backend.related_models),
'script_executable': backend.script_executable,
- 'script_method': str(backend.script_method),
- 'function_method': str(backend.script_method),
- 'actions': ', '.join(backend.actions),
+ 'script_method': '.'.join((backend.script_method.__module__, backend.script_method.__name__)),
+ 'function_method': '.'.join((backend.function_method.__module__, backend.function_method.__name__)),
+ 'actions': str(backend.actions),
}
- help_text = textwrap.dedent("""
- - Model: '%(model)s'
- - Related models: %(related_models)s
- - Script executable: %(script_executable)s
- - Script method: %(script_method)s
- - Function method: %(function_method)s
- - Actions: %(actions)s
"""
+ help_text += textwrap.dedent("""
+ - Model: '%(model)s'
+ - Related models: %(related_models)s
+ - Script executable: %(script_executable)s
+ - Script method: %(script_method)s
+ - Function method: %(function_method)s
+ - Actions: %(actions)s
+ """
) % context
- docstring = backend.__doc__
- if docstring:
- try:
- docstring = (docstring % backend.format_docstring).strip().splitlines()
- except TypeError as e:
- raise TypeError(str(backend) + str(e))
- help_text += '
' + '
'.join(docstring)
- help_texts[backend.get_name()] = help_text
+ help_text = help_text.lstrip().splitlines()
+ help_settings = ['']
+ if backend.doc_settings:
+ module, names = backend.doc_settings
+ for name in names:
+ value = getattr(module, name)
+ if isinstance(value, str):
+ help_settings.append("%s = '%s'" % (name, value))
+ else:
+ help_settings.append("%s = %s" % (name, str(value)))
+ help_text += help_settings
+ help_texts[backend.get_name()] = '
'.join(help_text)
return help_texts
diff --git a/orchestra/contrib/saas/backends/bscw.py b/orchestra/contrib/saas/backends/bscw.py
index 742daac9..ab21e0e6 100644
--- a/orchestra/contrib/saas/backends/bscw.py
+++ b/orchestra/contrib/saas/backends/bscw.py
@@ -12,6 +12,9 @@ class BSCWBackend(ServiceController):
model = 'saas.SaaS'
default_route_match = "saas.service == 'bscw'"
actions = ('save', 'delete', 'validate_creation')
+ doc_settings = (settings,
+ ('SAAS_BSCW_BSADMIN_PATH',)
+ )
def validate_creation(self, saas):
context = self.get_context(saas)
diff --git a/orchestra/contrib/saas/backends/dokuwikimu.py b/orchestra/contrib/saas/backends/dokuwikimu.py
index ce19043b..245e73c4 100644
--- a/orchestra/contrib/saas/backends/dokuwikimu.py
+++ b/orchestra/contrib/saas/backends/dokuwikimu.py
@@ -8,8 +8,14 @@ from .. import settings
class DokuWikiMuBackend(ServiceController):
+ """
+ Creates a DokuWiki site on a DokuWiki multisite installation.
+ """
verbose_name = _("DokuWiki multisite")
model = 'webapps.WebApp'
+ doc_settings = (settings,
+ ('SAAS_DOKUWIKI_TEMPLATE_PATH', 'SAAS_DOKUWIKI_FARM_PATH')
+ )
def save(self, webapp):
context = self.get_context(webapp)
@@ -25,7 +31,7 @@ class DokuWikiMuBackend(ServiceController):
def get_context(self, webapp):
context = super(DokuWikiMuBackend, self).get_context(webapp)
context.update({
- 'template': settings.WEBAPPS_DOKUWIKIMU_TEMPLATE_PATH,
- 'app_path': os.path.join(settings.WEBAPPS_DOKUWIKIMU_FARM_PATH, webapp.name)
+ 'template': settings.SAAS_DOKUWIKI_TEMPLATE_PATH,
+ 'app_path': os.path.join(settings.SAAS_DOKUWIKI_FARM_PATH, webapp.name)
})
return replace(context, "'", '"')
diff --git a/orchestra/contrib/saas/backends/drupalmu.py b/orchestra/contrib/saas/backends/drupalmu.py
index 5cd8f16b..574f8c94 100644
--- a/orchestra/contrib/saas/backends/drupalmu.py
+++ b/orchestra/contrib/saas/backends/drupalmu.py
@@ -9,9 +9,15 @@ from .. import settings
class DrupalMuBackend(ServiceController):
+ """
+ Creates a Drupal site on a Drupal multisite installation
+ """
verbose_name = _("Drupal multisite")
model = 'webapps.WebApp'
-
+ doc_settings = (settings,
+ ('SAAS_DRUPAL_SITES_PATH',)
+ )
+
def save(self, webapp):
context = self.get_context(webapp)
self.append(textwrap.dedent("""\
@@ -32,6 +38,6 @@ class DrupalMuBackend(ServiceController):
def get_context(self, webapp):
context = super(DrupalMuBackend, self).get_context(webapp)
- context['drupal_path'] = settings.WEBAPPS_DRUPAL_SITES_PATH % context
+ context['drupal_path'] = settings.SAAS_DRUPAL_SITES_PATH % context
context['drupal_settings'] = os.path.join(context['drupal_path'], 'settings.php')
return replace(context, "'", '"')
diff --git a/orchestra/contrib/saas/backends/gitlab.py b/orchestra/contrib/saas/backends/gitlab.py
index 3f39d287..55c13cd5 100644
--- a/orchestra/contrib/saas/backends/gitlab.py
+++ b/orchestra/contrib/saas/backends/gitlab.py
@@ -14,6 +14,9 @@ class GitLabSaaSBackend(ServiceController):
default_route_match = "saas.service == 'gitlab'"
block = True
actions = ('save', 'delete', 'validate_creation')
+ doc_settings = (settings,
+ ('SAAS_GITLAB_DOMAIN', 'SAAS_GITLAB_ROOT_PASSWORD'),
+ )
def get_base_url(self):
return 'https://%s/api/v3' % settings.SAAS_GITLAB_DOMAIN
diff --git a/orchestra/contrib/saas/backends/phplist.py b/orchestra/contrib/saas/backends/phplist.py
index 1b028f5e..e7ed0aa0 100644
--- a/orchestra/contrib/saas/backends/phplist.py
+++ b/orchestra/contrib/saas/backends/phplist.py
@@ -7,6 +7,14 @@ from orchestra.contrib.orchestration import ServiceController
class PhpListSaaSBackend(ServiceController):
+ """
+ Creates a new phplist instance on a phpList multisite installation.
+ The site is created by means of creating a new database per phpList site, but all sites share the same code.
+
+ // config/config.php
+ $site = array_shift((explode(".",$_SERVER['HTTP_HOST'])));
+ $database_name = "phplist_mu_{$site}";
+ """
verbose_name = _("phpList SaaS")
model = 'saas.SaaS'
default_route_match = "saas.service == 'phplist'"
diff --git a/orchestra/contrib/saas/backends/wordpressmu.py b/orchestra/contrib/saas/backends/wordpressmu.py
index e8501a21..e2fdf7d5 100644
--- a/orchestra/contrib/saas/backends/wordpressmu.py
+++ b/orchestra/contrib/saas/backends/wordpressmu.py
@@ -9,16 +9,22 @@ from .. import settings
class WordpressMuBackend(ServiceController):
+ """
+ Creates a wordpress site on a WordPress MultiSite installation.
+ """
verbose_name = _("Wordpress multisite")
model = 'webapps.WebApp'
default_route_match = "webapp.type == 'wordpress-mu'"
+ doc_settings = (settings,
+ ('SAAS_WORDPRESS_ADMIN_PASSWORD', 'SAAS_WORDPRESS_BASE_URL')
+ )
def login(self, session):
base_url = self.get_base_url()
login_url = base_url + '/wp-login.php'
login_data = {
'log': 'admin',
- 'pwd': settings.WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD,
+ 'pwd': settings.SAAS_WORDPRESS_ADMIN_PASSWORD,
'redirect_to': '/wp-admin/'
}
response = session.post(login_url, data=login_data)
@@ -26,7 +32,7 @@ class WordpressMuBackend(ServiceController):
raise IOError("Failure login to remote application")
def get_base_url(self):
- base_url = settings.WEBAPPS_WORDPRESSMU_BASE_URL
+ base_url = settings.SAAS_WORDPRESS_BASE_URL
return base_url.rstrip('/')
def validate_response(self, response):
diff --git a/orchestra/contrib/systemusers/backends.py b/orchestra/contrib/systemusers/backends.py
index 82e6dce8..6214c6fc 100644
--- a/orchestra/contrib/systemusers/backends.py
+++ b/orchestra/contrib/systemusers/backends.py
@@ -11,18 +11,14 @@ from . import settings
class UNIXUserBackend(ServiceController):
"""
- Basic UNIX system user/group support based on `useradd`, `usermod`, `userdel` and `groupdel`.
- SYSTEMUSERS_DEFAULT_GROUP_MEMBERS = '%s'
- SYSTEMUSERS_MOVE_ON_DELETE_PATH = '%s'
+ Basic UNIX system user/group support based on useradd, usermod, userdel and groupdel.
"""
- format_docstring = (
- settings.SYSTEMUSERS_DEFAULT_GROUP_MEMBERS,
- settings.SYSTEMUSERS_MOVE_ON_DELETE_PATH,
- )
-
verbose_name = _("UNIX user")
model = 'systemusers.SystemUser'
actions = ('save', 'delete', 'grant_permission')
+ doc_settings = (settings,
+ ('SYSTEMUSERS_DEFAULT_GROUP_MEMBERS', 'SYSTEMUSERS_MOVE_ON_DELETE_PATH')
+ )
def save(self, user):
context = self.get_context(user)
@@ -95,7 +91,7 @@ class UNIXUserBackend(ServiceController):
class UNIXUserDisk(ServiceMonitor):
"""
- `du -bs `
+ du -bs <home>
"""
model = 'systemusers.SystemUser'
resource = ServiceMonitor.DISK
@@ -123,17 +119,15 @@ class UNIXUserDisk(ServiceMonitor):
class Exim4Traffic(ServiceMonitor):
"""
- Exim4 mainlog parser for mails sent on the webserver by system users (e.g. via PHP mail())
- SYSTEMUSERS_MAIL_LOG_PATH = '%s'
+ Exim4 mainlog parser for mails sent on the webserver by system users (e.g. via PHP mail())
"""
- format_docstring = (
- settings.SYSTEMUSERS_MAIL_LOG_PATH,
- )
-
model = 'systemusers.SystemUser'
resource = ServiceMonitor.TRAFFIC
verbose_name = _("Exim4 traffic")
script_executable = '/usr/bin/python'
+ doc_settings = (settings,
+ ('SYSTEMUSERS_MAIL_LOG_PATH',)
+ )
def prepare(self):
mainlog = settings.SYSTEMUSERS_MAIL_LOG_PATH
@@ -211,15 +205,14 @@ class Exim4Traffic(ServiceMonitor):
class VsFTPdTraffic(ServiceMonitor):
"""
vsFTPd log parser.
- SYSTEMUSERS_FTP_LOG_PATH = '%s'
"""
- format_docstring = (
- settings.SYSTEMUSERS_FTP_LOG_PATH,
- )
model = 'systemusers.SystemUser'
resource = ServiceMonitor.TRAFFIC
verbose_name = _('VsFTPd traffic')
script_executable = '/usr/bin/python'
+ doc_settings = (settings,
+ ('SYSTEMUSERS_FTP_LOG_PATH',)
+ )
def prepare(self):
vsftplog = settings.SYSTEMUSERS_FTP_LOG_PATH
diff --git a/orchestra/contrib/webapps/backends/__init__.py b/orchestra/contrib/webapps/backends/__init__.py
index 30796a3d..9b9fa1ae 100644
--- a/orchestra/contrib/webapps/backends/__init__.py
+++ b/orchestra/contrib/webapps/backends/__init__.py
@@ -12,6 +12,9 @@ class WebAppServiceMixin(object):
('webapps.WebAppOption', 'webapp'),
)
directive = None
+ doc_settings = (settings,
+ ('WEBAPPS_UNDER_CONSTRUCTION_PATH', 'WEBAPPS_MOVE_ON_DELETE_PATH',)
+ )
def create_webapp_dir(self, context):
self.append(textwrap.dedent("""\
@@ -45,7 +48,7 @@ class WebAppServiceMixin(object):
'type': webapp.type,
'app_path': webapp.get_path(),
'banner': self.get_banner(),
- 'under_construction_path': settings.settings.WEBAPPS_UNDER_CONSTRUCTION_PATH,
+ 'under_construction_path': settings.WEBAPPS_UNDER_CONSTRUCTION_PATH,
'is_mounted': webapp.content_set.exists(),
}
context['deleted_app_path'] = settings.WEBAPPS_MOVE_ON_DELETE_PATH % context
diff --git a/orchestra/contrib/webapps/backends/php.py b/orchestra/contrib/webapps/backends/php.py
index 24e15839..3cad18b4 100644
--- a/orchestra/contrib/webapps/backends/php.py
+++ b/orchestra/contrib/webapps/backends/php.py
@@ -14,29 +14,21 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
"""
PHP support for apache-mod-fcgid and php-fpm.
It handles switching between these two PHP process management systemes.
- WEBAPPS_MERGE_PHP_WEBAPPS = %s
- WEBAPPS_FPM_DEFAULT_MAX_CHILDREN = %s
- WEBAPPS_PHP_CGI_BINARY_PATH = '%s'
- WEBAPPS_PHP_CGI_RC_DIR = '%s'
- WEBAPPS_PHP_CGI_INI_SCAN_DIR = '%s'
- WEBAPPS_FCGID_CMD_OPTIONS_PATH = '%s'
- WEBAPPS_PHPFPM_POOL_PATH = '%s'
- WEBAPPS_PHP_MAX_REQUESTS = %s
"""
- format_docstring = (
- settings.WEBAPPS_MERGE_PHP_WEBAPPS,
- settings.WEBAPPS_FPM_DEFAULT_MAX_CHILDREN,
- settings.WEBAPPS_PHP_CGI_BINARY_PATH,
- settings.WEBAPPS_PHP_CGI_RC_DIR,
- settings.WEBAPPS_PHP_CGI_INI_SCAN_DIR,
- settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH,
- settings.WEBAPPS_PHPFPM_POOL_PATH,
- settings.WEBAPPS_PHP_MAX_REQUESTS,
- )
+ MERGE = settings.WEBAPPS_MERGE_PHP_WEBAPPS
verbose_name = _("PHP FPM/FCGID")
default_route_match = "webapp.type.endswith('php')"
- MERGE = settings.WEBAPPS_MERGE_PHP_WEBAPPS
+ doc_settings = (settings, (
+ 'WEBAPPS_MERGE_PHP_WEBAPPS',
+ 'WEBAPPS_FPM_DEFAULT_MAX_CHILDREN',
+ 'WEBAPPS_PHP_CGI_BINARY_PATH',
+ 'WEBAPPS_PHP_CGI_RC_DIR',
+ 'WEBAPPS_PHP_CGI_INI_SCAN_DIR',
+ 'WEBAPPS_FCGID_CMD_OPTIONS_PATH',
+ 'WEBAPPS_PHPFPM_POOL_PATH',
+ 'WEBAPPS_PHP_MAX_REQUESTS',
+ ))
def save(self, webapp):
context = self.get_context(webapp)
diff --git a/orchestra/contrib/webapps/backends/python.py b/orchestra/contrib/webapps/backends/python.py
index 9c384c75..a0ee69d3 100644
--- a/orchestra/contrib/webapps/backends/python.py
+++ b/orchestra/contrib/webapps/backends/python.py
@@ -12,22 +12,16 @@ from .. import settings
class uWSGIPythonBackend(WebAppServiceMixin, ServiceController):
"""
- Emperor mode
- http://uwsgi-docs.readthedocs.org/en/latest/Emperor.html
- WEBAPPS_UWSGI_BASE_DIR = '%s'
- WEBAPPS_PYTHON_MAX_REQUESTS = %s
- WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS = %s
- WEBAPPS_PYTHON_DEFAULT_TIMEOUT = %s
+ Emperor mode
"""
- format_docstring = (
- settings.WEBAPPS_UWSGI_BASE_DIR,
- settings.WEBAPPS_PYTHON_MAX_REQUESTS,
- settings.WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS,
- settings.WEBAPPS_PYTHON_DEFAULT_TIMEOUT,
- )
-
verbose_name = _("Python uWSGI")
default_route_match = "webapp.type.endswith('python')"
+ doc_settings = (settings, (
+ 'WEBAPPS_UWSGI_BASE_DIR',
+ 'WEBAPPS_PYTHON_MAX_REQUESTS',
+ 'WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS',
+ 'WEBAPPS_PYTHON_DEFAULT_TIMEOUT',
+ ))
def save(self, webapp):
context = self.get_context(webapp)
diff --git a/orchestra/contrib/webapps/backends/symboliclink.py b/orchestra/contrib/webapps/backends/symboliclink.py
index add8eab1..a5675333 100644
--- a/orchestra/contrib/webapps/backends/symboliclink.py
+++ b/orchestra/contrib/webapps/backends/symboliclink.py
@@ -11,7 +11,6 @@ class SymbolicLinkBackend(PHPBackend, ServiceController):
"""
Same as PHPBackend but allows you to have the webapps on a directory diferent than the webapps dir.
"""
- format_docstring = ()
verbose_name = _("Symbolic link webapp")
model = 'webapps.WebApp'
default_route_match = "webapp.type == 'symbolic-link'"
diff --git a/orchestra/contrib/webapps/backends/wordpress.py b/orchestra/contrib/webapps/backends/wordpress.py
index 082ce091..e8fc6ec4 100644
--- a/orchestra/contrib/webapps/backends/wordpress.py
+++ b/orchestra/contrib/webapps/backends/wordpress.py
@@ -14,15 +14,14 @@ class WordPressBackend(WebAppServiceMixin, ServiceController):
"""
Installs the latest version of WordPress available on www.wordpress.org
It fully configures the wp-config.php (keys included) and sets up the database with initial admin password.
- WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = '%s'
"""
- format_docstring = (
- settings.WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST,
- )
verbose_name = _("Wordpress")
model = 'webapps.WebApp'
default_route_match = "webapp.type == 'wordpress-php'"
script_executable = '/usr/bin/php'
+ doc_settings = (settings,
+ ('WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST',)
+ )
def prepare(self):
self.append(textwrap.dedent("""\
diff --git a/orchestra/contrib/webapps/options.py b/orchestra/contrib/webapps/options.py
index ee19a337..74b18da0 100644
--- a/orchestra/contrib/webapps/options.py
+++ b/orchestra/contrib/webapps/options.py
@@ -308,7 +308,7 @@ class PHPSuhosinExecutorIncludeWhitelist(PHPAppOption):
class PHPUploadMaxFileSize(PHPAppOption):
name = 'upload_max_filesize'
- verbose_name = _("Upload max filezise")
+ verbose_name = _("Upload max filesize")
help_text = _("Value between 0M and 999M.")
regex = r'^[0-9]{1,3}M$'
diff --git a/orchestra/contrib/websites/backends/apache.py b/orchestra/contrib/websites/backends/apache.py
index cb988946..d3464a5d 100644
--- a/orchestra/contrib/websites/backends/apache.py
+++ b/orchestra/contrib/websites/backends/apache.py
@@ -13,6 +13,11 @@ from ..utils import normurlpath
class Apache2Backend(ServiceController):
+ """
+ Apache 2.4 backend with support for the following directives:
+ static, location, fpm, fcgid, uwsgi, \
+ ssl, security, redirects, proxies, saas
+ """
HTTP_PORT = 80
HTTPS_PORT = 443
@@ -22,6 +27,15 @@ class Apache2Backend(ServiceController):
('webapps.WebApp', 'website_set'),
)
verbose_name = _("Apache 2")
+ doc_settings = (settings, (
+ 'WEBSITES_VHOST_EXTRA_DIRECTIVES',
+ 'WEBSITES_DEFAULT_SSL_CERT',
+ 'WEBSITES_DEFAULT_SSL_KEY',
+ 'WEBSITES_DEFAULT_SSL_CA',
+ 'WEBSITES_BASE_APACHE_CONF',
+ 'WEBSITES_DEFAULT_IPS',
+ 'WEBSITES_SAAS_DIRECTIVES',
+ ))
def render_virtual_host(self, site, context, ssl=False):
context['port'] = self.HTTPS_PORT if ssl else self.HTTP_PORT
@@ -362,12 +376,14 @@ class Apache2Backend(ServiceController):
class Apache2Traffic(ServiceMonitor):
"""
Parses apache logs,
- looking for the size of each request on the last word of the log line
+ looking for the size of each request on the last word of the log line.
"""
model = 'websites.Website'
resource = ServiceMonitor.TRAFFIC
verbose_name = _("Apache 2 Traffic")
-
+ doc_settings = (settings,
+ ('WEBSITES_TRAFFIC_IGNORE_HOSTS',)
+ )
def prepare(self):
super(Apache2Traffic, self).prepare()
ignore_hosts = '\\|'.join(settings.WEBSITES_TRAFFIC_IGNORE_HOSTS)
diff --git a/orchestra/contrib/websites/backends/webalizer.py b/orchestra/contrib/websites/backends/webalizer.py
index 3947c94c..5e508aea 100644
--- a/orchestra/contrib/websites/backends/webalizer.py
+++ b/orchestra/contrib/websites/backends/webalizer.py
@@ -15,6 +15,9 @@ class WebalizerBackend(ServiceController):
verbose_name = _("Webalizer Content")
model = 'websites.Content'
default_route_match = "content.webapp.type == 'webalizer'"
+ doc_settings = (settings,
+ ('WEBSITES_WEBALIZER_PATH',)
+ )
def save(self, content):
context = self.get_context(content)