Added backend docstring as helptext
This commit is contained in:
parent
1a78317028
commit
eebaee1097
3
TODO.md
3
TODO.md
|
@ -286,3 +286,6 @@ https://code.djangoproject.com/ticket/24576
|
|||
# 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
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import copy
|
||||
|
||||
from django.forms import widgets
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from rest_framework.utils import model_meta
|
||||
|
||||
from ..core.validators import validate_password
|
||||
|
||||
|
@ -10,32 +13,47 @@ class SetPasswordSerializer(serializers.Serializer):
|
|||
style={'widget': widgets.PasswordInput}, validators=[validate_password])
|
||||
|
||||
|
||||
#class HyperlinkedModelSerializerOptions(serializers.HyperlinkedModelSerializerOptions):
|
||||
# def __init__(self, meta):
|
||||
# super(HyperlinkedModelSerializerOptions, self).__init__(meta)
|
||||
# self.postonly_fields = getattr(meta, 'postonly_fields', ())
|
||||
|
||||
|
||||
class HyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
|
||||
""" support for postonly_fields, fields whose value can only be set on post """
|
||||
# _options_class = HyperlinkedModelSerializerOptions
|
||||
|
||||
def validate(self, attrs):
|
||||
""" calls model.clean() """
|
||||
attrs = super(HyperlinkedModelSerializer, self).validate(attrs)
|
||||
instance = self.Meta.model(**attrs)
|
||||
validated_data = dict(attrs)
|
||||
ModelClass = self.Meta.model
|
||||
# Remove many-to-many relationships from validated_data.
|
||||
info = model_meta.get_field_info(ModelClass)
|
||||
for field_name, relation_info in info.relations.items():
|
||||
if relation_info.to_many and (field_name in validated_data):
|
||||
validated_data.pop(field_name)
|
||||
if self.instance:
|
||||
# on update: Merge provided fields with instance field
|
||||
instance = copy.deepcopy(self.instance)
|
||||
for key, value in validated_data.items():
|
||||
setattr(instance, key, value)
|
||||
else:
|
||||
instance = ModelClass(**validated_data)
|
||||
instance.clean()
|
||||
return attrs
|
||||
|
||||
# TODO raise validationError instead of silently removing fields
|
||||
def update(self, instance, validated_data):
|
||||
""" removes postonly_fields from attrs when not posting """
|
||||
def post_only_cleanning(self, instance, validated_data):
|
||||
""" removes postonly_fields from attrs """
|
||||
model_attrs = dict(**validated_data)
|
||||
if instance is not None:
|
||||
for attr, value in validated_data.items():
|
||||
if attr in self.Meta.postonly_fields:
|
||||
model_attrs.pop(attr)
|
||||
return model_attrs
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
""" removes postonly_fields from attrs when not posting """
|
||||
model_attrs = self.post_only_cleanning(instance, validated_data)
|
||||
return super(HyperlinkedModelSerializer, self).update(instance, model_attrs)
|
||||
|
||||
def partial_update(self, instance, validated_data):
|
||||
""" removes postonly_fields from attrs when not posting """
|
||||
model_attrs = self.post_only_cleanning(instance, validated_data)
|
||||
return super(HyperlinkedModelSerializer, self).partial_update(instance, model_attrs)
|
||||
|
||||
|
||||
class SetPasswordHyperlinkedSerializer(HyperlinkedModelSerializer):
|
||||
|
|
|
@ -8,13 +8,13 @@ from .serializers import DatabaseSerializer, DatabaseUserSerializer
|
|||
|
||||
|
||||
class DatabaseViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
||||
queryset = Database.objects.all()
|
||||
queryset = Database.objects.prefetch_related('users').all()
|
||||
serializer_class = DatabaseSerializer
|
||||
filter_fields = ('name',)
|
||||
|
||||
|
||||
class DatabaseUserViewSet(LogApiMixin, AccountApiMixin, SetPasswordApiMixin, viewsets.ModelViewSet):
|
||||
queryset = DatabaseUser.objects.all()
|
||||
queryset = DatabaseUser.objects.prefetch_related('databases').all()
|
||||
serializer_class = DatabaseUserSerializer
|
||||
filter_fields = ('username',)
|
||||
|
||||
|
|
|
@ -9,6 +9,12 @@ from . import settings
|
|||
|
||||
|
||||
class MySQLBackend(ServiceController):
|
||||
"""
|
||||
Simple backend for creating MySQL databases using `CREATE DATABASE` statement.
|
||||
DATABASES_DEFAULT_HOST = %s
|
||||
"""
|
||||
format_docstring = (settings.DATABASES_DEFAULT_HOST,)
|
||||
|
||||
verbose_name = "MySQL database"
|
||||
model = 'databases.Database'
|
||||
default_route_match = "database.type == 'mysql'"
|
||||
|
@ -54,6 +60,11 @@ class MySQLBackend(ServiceController):
|
|||
|
||||
|
||||
class MySQLUserBackend(ServiceController):
|
||||
"""
|
||||
Simple backend for creating MySQL users using `CREATE USER` statement.
|
||||
DATABASES_DEFAULT_HOST = %s
|
||||
""" % settings.DATABASES_DEFAULT_HOST
|
||||
|
||||
verbose_name = "MySQL user"
|
||||
model = 'databases.DatabaseUser'
|
||||
default_route_match = "databaseuser.type == 'mysql'"
|
||||
|
@ -93,6 +104,10 @@ class MySQLUserBackend(ServiceController):
|
|||
|
||||
|
||||
class MysqlDisk(ServiceMonitor):
|
||||
"""
|
||||
du -bs <database_path>
|
||||
Implements triggers for resource limit exceeded and recovery, disabling insert and create privileges.
|
||||
"""
|
||||
model = 'databases.Database'
|
||||
verbose_name = _("MySQL disk")
|
||||
|
||||
|
|
|
@ -11,6 +11,18 @@ from . import settings
|
|||
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
verbose_name = _("Bind9 master domain")
|
||||
model = 'domains.Domain'
|
||||
related_models = (
|
||||
|
@ -121,6 +133,12 @@ class Bind9MasterDomainBackend(ServiceController):
|
|||
|
||||
|
||||
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)
|
||||
|
||||
verbose_name = _("Bind9 slave domain")
|
||||
related_models = (
|
||||
('domains.Domain', 'origin'),
|
||||
|
|
|
@ -10,6 +10,19 @@ 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'
|
||||
"""
|
||||
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 = [
|
||||
|
@ -149,75 +162,15 @@ class MailmanBackend(ServiceController):
|
|||
return replace(context, "'", '"')
|
||||
|
||||
|
||||
class MailmanTrafficBash(ServiceMonitor):
|
||||
model = 'lists.List'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _("Mailman traffic (Bash)")
|
||||
|
||||
def prepare(self):
|
||||
super(MailmanTraffic, self).prepare()
|
||||
context = {
|
||||
'mailman_log': '%s{,.1}' % settings.LISTS_MAILMAN_POST_LOG_PATH,
|
||||
'current_date': self.current_date.strftime("%Y-%m-%d %H:%M:%S %Z"),
|
||||
}
|
||||
self.append(textwrap.dedent("""\
|
||||
function monitor () {
|
||||
OBJECT_ID=$1
|
||||
# Dates convertions are done server-side because of timezone discrepancies
|
||||
INI_DATE=$(date "+%%Y%%m%%d%%H%%M%%S" -d "$2")
|
||||
END_DATE=$(date '+%%Y%%m%%d%%H%%M%%S' -d '%(current_date)s')
|
||||
LIST_NAME="$3"
|
||||
MAILMAN_LOG=%(mailman_log)s
|
||||
|
||||
SUBSCRIBERS=$(list_members ${LIST_NAME} | wc -l)
|
||||
{
|
||||
{ grep " post to ${LIST_NAME} " ${MAILMAN_LOG} || echo '\\r'; } \\
|
||||
| awk -v ini="${INI_DATE}" -v end="${END_DATE}" -v subs="${SUBSCRIBERS}" '
|
||||
BEGIN {
|
||||
sum = 0
|
||||
months["Jan"] = "01"
|
||||
months["Feb"] = "02"
|
||||
months["Mar"] = "03"
|
||||
months["Apr"] = "04"
|
||||
months["May"] = "05"
|
||||
months["Jun"] = "06"
|
||||
months["Jul"] = "07"
|
||||
months["Aug"] = "08"
|
||||
months["Sep"] = "09"
|
||||
months["Oct"] = "10"
|
||||
months["Nov"] = "11"
|
||||
months["Dec"] = "12"
|
||||
} {
|
||||
# Mar 01 08:29:02 2015
|
||||
month = months[$1]
|
||||
day = $2
|
||||
year = $4
|
||||
split($3, time, ":")
|
||||
line_date = year month day time[1] time[2] time[3]
|
||||
if ( line_date > ini && line_date < end)
|
||||
sum += substr($11, 6, length($11)-6)
|
||||
} END {
|
||||
print sum * subs
|
||||
}' || [[ $? == 1 ]] && true
|
||||
} | xargs echo ${OBJECT_ID}
|
||||
}""") % context)
|
||||
|
||||
def monitor(self, mail_list):
|
||||
context = self.get_context(mail_list)
|
||||
self.append(
|
||||
'monitor %(object_id)i "%(last_date)s" "%(list_name)s"' % context
|
||||
)
|
||||
|
||||
def get_context(self, mail_list):
|
||||
context = {
|
||||
'list_name': mail_list.name,
|
||||
'object_id': mail_list.pk,
|
||||
'last_date': self.get_last_date(mail_list.pk).strftime("%Y-%m-%d %H:%M:%S %Z"),
|
||||
}
|
||||
return replace(context, "'", '"')
|
||||
|
||||
|
||||
class MailmanTraffic(ServiceMonitor):
|
||||
"""
|
||||
Parses mailman log file looking for email size and multiples it by `list_members` count.
|
||||
LISTS_MAILMAN_POST_LOG_PATH = '%s'
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.LISTS_MAILMAN_POST_LOG_PATH,
|
||||
)
|
||||
model = 'lists.List'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _("Mailman traffic")
|
||||
|
@ -235,13 +188,13 @@ class MailmanTraffic(ServiceMonitor):
|
|||
import sys
|
||||
from datetime import datetime
|
||||
from dateutil import tz
|
||||
|
||||
|
||||
def to_local_timezone(date, tzlocal=tz.tzlocal()):
|
||||
date = datetime.strptime(date, '%Y-%m-%d %H:%M:%S %Z')
|
||||
date = date.replace(tzinfo=tz.tzutc())
|
||||
date = date.astimezone(tzlocal)
|
||||
return date
|
||||
|
||||
|
||||
postlogs = {postlogs}
|
||||
# Use local timezone
|
||||
end_date = to_local_timezone('{current_date}')
|
||||
|
@ -261,13 +214,13 @@ class MailmanTraffic(ServiceMonitor):
|
|||
'Nov': '11',
|
||||
'Dec': '12',
|
||||
}}
|
||||
|
||||
|
||||
def prepare(object_id, list_name, ini_date):
|
||||
global lists
|
||||
ini_date = to_local_timezone(ini_date)
|
||||
ini_date = int(ini_date.strftime('%Y%m%d%H%M%S'))
|
||||
lists[list_name] = [ini_date, object_id, 0]
|
||||
|
||||
|
||||
def monitor(lists, end_date, months, postlogs):
|
||||
for postlog in postlogs:
|
||||
try:
|
||||
|
@ -318,6 +271,9 @@ class MailmanTraffic(ServiceMonitor):
|
|||
|
||||
|
||||
class MailmanSubscribers(ServiceMonitor):
|
||||
"""
|
||||
Monitors number of list subscribers via `list_members`
|
||||
"""
|
||||
model = 'lists.List'
|
||||
verbose_name = _("Mailman subscribers")
|
||||
|
||||
|
|
|
@ -8,13 +8,12 @@ from .serializers import AddressSerializer, MailboxSerializer
|
|||
|
||||
|
||||
class AddressViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
||||
queryset = Address.objects.all()
|
||||
queryset = Address.objects.select_related('domain').prefetch_related('mailboxes').all()
|
||||
serializer_class = AddressSerializer
|
||||
|
||||
|
||||
|
||||
class MailboxViewSet(LogApiMixin, SetPasswordApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
||||
queryset = Mailbox.objects.all()
|
||||
queryset = Mailbox.objects.prefetch_related('addresses__domain').all()
|
||||
serializer_class = MailboxSerializer
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class UNIXUserMaildirBackend(ServiceController):
|
||||
"""
|
||||
Assumes that all system users on this servers all mail accounts.
|
||||
If you want to have system users AND mailboxes on the same server you should consider using virtual mailboxes
|
||||
"""
|
||||
verbose_name = _("UNIX maildir user")
|
||||
model = 'mailboxes.Mailbox'
|
||||
|
||||
|
@ -74,6 +78,9 @@ class UNIXUserMaildirBackend(ServiceController):
|
|||
|
||||
|
||||
class DovecotPostfixPasswdVirtualUserBackend(ServiceController):
|
||||
"""
|
||||
WARNING: This backends is not fully implemented
|
||||
"""
|
||||
verbose_name = _("Dovecot-Postfix virtualuser")
|
||||
model = 'mailboxes.Mailbox'
|
||||
# TODO related_models = ('resources__content_type') ?? needed for updating disk usage from resource.data
|
||||
|
@ -176,6 +183,17 @@ class DovecotPostfixPasswdVirtualUserBackend(ServiceController):
|
|||
|
||||
|
||||
class PostfixAddressBackend(ServiceController):
|
||||
"""
|
||||
Addresses based on Postfix virtual alias domains.
|
||||
<tt>MAILBOXES_LOCAL_DOMAIN = '%s'
|
||||
MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = '%s'
|
||||
MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH = '%s'</tt>
|
||||
"""
|
||||
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 = (
|
||||
|
@ -267,6 +285,9 @@ class PostfixAddressBackend(ServiceController):
|
|||
|
||||
|
||||
class AutoresponseBackend(ServiceController):
|
||||
"""
|
||||
WARNING: not implemented
|
||||
"""
|
||||
verbose_name = _("Mail autoresponse")
|
||||
model = 'mailboxes.Autoresponse'
|
||||
|
||||
|
@ -274,9 +295,13 @@ class AutoresponseBackend(ServiceController):
|
|||
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")
|
||||
|
@ -304,9 +329,13 @@ class DovecotMaildirDisk(ServiceMonitor):
|
|||
|
||||
class PostfixMailscannerTraffic(ServiceMonitor):
|
||||
"""
|
||||
A high-performance log parser
|
||||
Reads the mail.log file only once, for all users
|
||||
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")
|
||||
|
@ -460,8 +489,3 @@ class PostfixMailscannerTraffic(ServiceMonitor):
|
|||
'last_date': self.get_last_date(mailbox.pk).strftime("%Y-%m-%d %H:%M:%S %Z"),
|
||||
}
|
||||
return replace(context, "'", '"')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -25,10 +25,10 @@ class RelatedAddressSerializer(AccountSerializerMixin, serializers.HyperlinkedMo
|
|||
class Meta:
|
||||
model = Address
|
||||
fields = ('url', 'name', 'domain', 'forward')
|
||||
|
||||
def from_native(self, data, files=None):
|
||||
queryset = self.opts.model.objects.filter(account=self.account)
|
||||
return get_object_or_404(queryset, name=data['name'])
|
||||
#
|
||||
# def from_native(self, data, files=None):
|
||||
# queryset = self.opts.model.objects.filter(account=self.account)
|
||||
# return get_object_or_404(queryset, name=data['name'])
|
||||
|
||||
|
||||
class MailboxSerializer(AccountSerializerMixin, SetPasswordHyperlinkedSerializer):
|
||||
|
|
|
@ -25,9 +25,7 @@ class Operation():
|
|||
self.backend = backend
|
||||
# instance should maintain any dynamic attribute until backend execution
|
||||
# deep copy is prefered over copy otherwise objects will share same atributes (queryset cache)
|
||||
print('aa', getattr(instance, 'password', 'NOOOO'), id(instance))
|
||||
self.instance = copy.deepcopy(instance)
|
||||
print('aa', getattr(self.instance, 'password', 'NOOOO'), id(self.instance))
|
||||
self.action = action
|
||||
self.servers = servers
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from orchestra.admin.html import monospace_format
|
||||
from orchestra.admin.utils import admin_link, admin_date, admin_colored
|
||||
|
||||
from . import settings
|
||||
from . import settings, helpers
|
||||
from .backends import ServiceBackend
|
||||
from .models import Server, Route, BackendLog, BackendOperation
|
||||
from .widgets import RouteBackendSelect
|
||||
|
@ -31,10 +31,7 @@ class RouteAdmin(admin.ModelAdmin):
|
|||
list_filter = ('host', 'is_active', 'backend')
|
||||
ordering = ('backend',)
|
||||
|
||||
BACKEND_HELP_TEXT = {
|
||||
backend: "This backend operates over '%s'" % ServiceBackend.get_backend(backend).model
|
||||
for backend, __ in ServiceBackend.get_choices()
|
||||
}
|
||||
BACKEND_HELP_TEXT = helpers.get_backends_help_text(ServiceBackend.get_backends())
|
||||
DEFAULT_MATCH = {
|
||||
backend.get_name(): backend.default_route_match for backend in ServiceBackend.get_backends()
|
||||
}
|
||||
|
|
|
@ -45,6 +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 = ()
|
||||
|
||||
def __str__(self):
|
||||
return type(self).__name__
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import textwrap
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.mail import mail_admins
|
||||
from django.core.urlresolvers import reverse
|
||||
|
@ -6,6 +8,36 @@ from django.utils.safestring import mark_safe
|
|||
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||
|
||||
|
||||
def get_backends_help_text(backends):
|
||||
help_texts = {}
|
||||
for backend in backends:
|
||||
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),
|
||||
}
|
||||
help_text = textwrap.dedent("""
|
||||
- Model: '%(model)s'<br>
|
||||
- Related models: %(related_models)s<br>
|
||||
- Script executable: %(script_executable)s<br>
|
||||
- Script method: %(script_method)s<br>
|
||||
- Function method: %(function_method)s<br>
|
||||
- Actions: %(actions)s<br>"""
|
||||
) % 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 += '<br>' + '<br>'.join(docstring)
|
||||
help_texts[backend.get_name()] = help_text
|
||||
return help_texts
|
||||
|
||||
|
||||
def send_report(method, args, log):
|
||||
server = args[0]
|
||||
backend = method.__self__.__class__.__name__
|
||||
|
|
|
@ -32,5 +32,5 @@ ORCHESTRATION_DISABLE_EXECUTION = getattr(settings, 'ORCHESTRATION_DISABLE_EXECU
|
|||
|
||||
|
||||
ORCHESTRATION_BACKEND_CLEANUP_DELTA = getattr(settings, 'ORCHESTRATION_BACKEND_CLEANUP_DELTA',
|
||||
timedelta(days=40)
|
||||
timedelta(days=15)
|
||||
)
|
||||
|
|
|
@ -10,6 +10,16 @@ from . import settings
|
|||
|
||||
|
||||
class UNIXUserBackend(ServiceController):
|
||||
"""
|
||||
Basic UNIX system user/group support based on `useradd`, `usermod`, `userdel` and `groupdel`.
|
||||
<tt>SYSTEMUSERS_DEFAULT_GROUP_MEMBERS = '%s'
|
||||
SYSTEMUSERS_MOVE_ON_DELETE_PATH = '%s'</tt>
|
||||
"""
|
||||
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')
|
||||
|
@ -84,6 +94,9 @@ class UNIXUserBackend(ServiceController):
|
|||
|
||||
|
||||
class UNIXUserDisk(ServiceMonitor):
|
||||
"""
|
||||
`du -bs <home>`
|
||||
"""
|
||||
model = 'systemusers.SystemUser'
|
||||
resource = ServiceMonitor.DISK
|
||||
verbose_name = _('UNIX user disk')
|
||||
|
@ -109,6 +122,14 @@ 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'
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.SYSTEMUSERS_MAIL_LOG_PATH,
|
||||
)
|
||||
|
||||
model = 'systemusers.SystemUser'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _("Exim4 traffic")
|
||||
|
@ -188,6 +209,13 @@ 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')
|
||||
|
@ -279,4 +307,3 @@ class VsFTPdTraffic(ServiceMonitor):
|
|||
'username': user.username,
|
||||
}
|
||||
return replace(context, "'", '"')
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@ from orchestra.contrib.resources import ServiceMonitor
|
|||
|
||||
|
||||
class OpenVZTraffic(ServiceMonitor):
|
||||
"""
|
||||
WARNING: Not fully implemeted
|
||||
"""
|
||||
model = 'vps.VPS'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@ from orchestra.contrib.accounts.api import AccountApiMixin
|
|||
|
||||
from . import settings
|
||||
from .models import WebApp
|
||||
from .options import AppOption
|
||||
from .serializers import WebAppSerializer
|
||||
from .types import AppType
|
||||
|
||||
|
||||
class WebAppViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
||||
queryset = WebApp.objects.all()
|
||||
queryset = WebApp.objects.prefetch_related('options').all()
|
||||
serializer_class = WebAppSerializer
|
||||
filter_fields = ('name',)
|
||||
|
||||
|
@ -22,6 +24,31 @@ class WebAppViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
|||
metadata.data['settings'] = {
|
||||
name.lower(): getattr(settings, name, None) for name in names
|
||||
}
|
||||
# AppTypes
|
||||
meta = self.metadata_class()
|
||||
app_types = {}
|
||||
for app_type in AppType.get_plugins():
|
||||
if app_type.serializer:
|
||||
data = meta.get_serializer_info(app_type.serializer())
|
||||
else:
|
||||
data = {}
|
||||
options = []
|
||||
for group, option in app_type.get_options():
|
||||
options += [opt.name for opt in option]
|
||||
app_types[app_type.get_name()] = {
|
||||
'data': data,
|
||||
'options': options,
|
||||
}
|
||||
metadata.data['actions']['types'] = app_types
|
||||
# Options
|
||||
options = {}
|
||||
for option in AppOption.get_plugins():
|
||||
options[option.get_name()] = {
|
||||
'verbose_name': option.get_verbose_name(),
|
||||
'help_text': option.help_text,
|
||||
'group': option.group,
|
||||
}
|
||||
metadata.data['actions']['options'] = options
|
||||
return metadata
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,29 @@ from .. import settings
|
|||
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
verbose_name = _("PHP FPM/FCGID")
|
||||
default_route_match = "webapp.type.endswith('php')"
|
||||
MERGE = settings.WEBAPPS_MERGE_PHP_WEBAPPS
|
||||
|
|
|
@ -13,9 +13,19 @@ 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
|
||||
"""
|
||||
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')"
|
||||
|
||||
|
|
|
@ -6,6 +6,10 @@ from . import WebAppServiceMixin
|
|||
|
||||
|
||||
class StaticBackend(WebAppServiceMixin, ServiceController):
|
||||
"""
|
||||
Static web pages.
|
||||
Only creates the webapp dir and leaves the web server the decision to execute CGIs or not.
|
||||
"""
|
||||
verbose_name = _("Static")
|
||||
default_route_match = "webapp.type == 'static'"
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@ from .php import PHPBackend
|
|||
|
||||
|
||||
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'"
|
||||
|
|
|
@ -7,7 +7,9 @@ from . import WebAppServiceMixin
|
|||
|
||||
# TODO DEPRECATE
|
||||
class WebalizerAppBackend(WebAppServiceMixin, ServiceController):
|
||||
""" Needed for cleaning up webalizer main folder when webapp deleteion withou related contents """
|
||||
"""
|
||||
Needed for cleaning up webalizer main folder when webapp deleteion withou related contents
|
||||
"""
|
||||
verbose_name = _("Webalizer App")
|
||||
default_route_match = "webapp.type == 'webalizer'"
|
||||
|
||||
|
|
|
@ -11,6 +11,14 @@ from . import WebAppServiceMixin
|
|||
|
||||
# Based on https://github.com/mtomic/wordpress-install/blob/master/wpinstall.php
|
||||
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'"
|
||||
|
|
|
@ -35,7 +35,7 @@ class WebAppSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
|||
|
||||
def update(self, instance, validated_data):
|
||||
options_data = validated_data.pop('options')
|
||||
instance = super(WebAppSerializer, self).update(validated_data)
|
||||
instance = super(WebAppSerializer, self).update(instance, validated_data)
|
||||
existing = {}
|
||||
for obj in instance.options.all():
|
||||
existing[obj.name] = obj
|
||||
|
|
|
@ -114,6 +114,11 @@ WEBAPPS_UWSGI_SOCKET = getattr(settings, 'WEBAPPS_UWSGI_SOCKET',
|
|||
)
|
||||
|
||||
|
||||
WEBAPPS_UWSGI_BASE_DIR = getattr(settings, 'WEBAPPS_UWSGI_BASE_DIR',
|
||||
'/etc/uwsgi/'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PYTHON_MAX_REQUESTS = getattr(settings, 'WEBAPPS_PYTHON_MAX_REQUESTS',
|
||||
500
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ from .serializers import WebsiteSerializer
|
|||
|
||||
|
||||
class WebsiteViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
||||
queryset = Website.objects.all()
|
||||
queryset = Website.objects.prefetch_related('domains', 'content_set__webapp', 'directives').all()
|
||||
serializer_class = WebsiteSerializer
|
||||
filter_fields = ('name',)
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class Apache2Backend(ServiceController):
|
|||
extra_conf = sorted(extra_conf, key=lambda a: len(a[0]), reverse=True)
|
||||
context['extra_conf'] = '\n'.join([conf for location, conf in extra_conf])
|
||||
return Template(textwrap.dedent("""\
|
||||
<VirtualHost {% for ip in ips %}{{ ip }}:{{ port }} {% endfor %}>
|
||||
<VirtualHost{% for ip in ips %} {{ ip }}:{{ port }}{% endfor %}>
|
||||
IncludeOptional /etc/apache2/site[s]-override/{{ site_unique_name }}.con[f]
|
||||
ServerName {{ server_name }}\
|
||||
{% if server_alias %}
|
||||
|
@ -59,7 +59,7 @@ class Apache2Backend(ServiceController):
|
|||
def render_redirect_https(self, context):
|
||||
context['port'] = self.HTTP_PORT
|
||||
return Template(textwrap.dedent("""
|
||||
<VirtualHost {% for ip in ips %}{{ ip }}:{{ port }} {% endfor %}>
|
||||
<VirtualHost{% for ip in ips %} {{ ip }}:{{ port }}{% endfor %}>
|
||||
ServerName {{ server_name }}\
|
||||
{% if server_alias %}
|
||||
ServerAlias {{ server_alias|join:' ' }}{% endif %}\
|
||||
|
|
|
@ -9,6 +9,9 @@ from .. import settings
|
|||
|
||||
|
||||
class WebalizerBackend(ServiceController):
|
||||
"""
|
||||
Creates webalizer conf file for each time a webalizer webapp is mounted on a website.
|
||||
"""
|
||||
verbose_name = _("Webalizer Content")
|
||||
model = 'websites.Content'
|
||||
default_route_match = "content.webapp.type == 'webalizer'"
|
||||
|
|
|
@ -82,7 +82,7 @@ class WebsiteSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
|||
|
||||
def update(self, instance, validated_data):
|
||||
options_data = validated_data.pop('options')
|
||||
instance = super(WebsiteSerializer, self).update(validated_data)
|
||||
instance = super(WebsiteSerializer, self).update(instance, validated_data)
|
||||
existing = {}
|
||||
for obj in instance.options.all():
|
||||
existing[obj.name] = obj
|
||||
|
|
Loading…
Reference in a new issue