From bc51b23d97aa166d9eabe69a6da053b7d1f2c14e Mon Sep 17 00:00:00 2001 From: Marc Aymerich Date: Mon, 27 Apr 2015 14:54:17 +0000 Subject: [PATCH] Improved settings validation --- TODO.md | 1 + orchestra/contrib/accounts/settings.py | 57 ++++++----- orchestra/contrib/bills/settings.py | 71 +++++++++---- orchestra/contrib/databases/settings.py | 15 ++- orchestra/contrib/domains/settings.py | 64 ++++++++---- orchestra/contrib/domains/validators.py | 8 +- orchestra/contrib/issues/settings.py | 8 +- orchestra/contrib/lists/settings.py | 33 +++++-- orchestra/contrib/mailboxes/settings.py | 92 +++++++++++------ orchestra/contrib/miscellaneous/admin.py | 4 +- orchestra/contrib/miscellaneous/settings.py | 8 +- orchestra/contrib/orchestration/settings.py | 38 ++++--- orchestra/contrib/orchestration/tasks.py | 5 +- orchestra/contrib/orders/settings.py | 16 +-- orchestra/contrib/payments/settings.py | 23 +++-- orchestra/contrib/saas/settings.py | 32 +++--- orchestra/contrib/services/settings.py | 31 +++--- orchestra/contrib/settings/admin.py | 17 ++-- orchestra/contrib/settings/forms.py | 1 + orchestra/contrib/settings/parser.py | 6 +- .../templates/admin/settings/reload.html | 54 ++++++++++ orchestra/contrib/systemusers/settings.py | 18 +++- orchestra/contrib/vps/settings.py | 16 ++- .../contrib/webapps/backends/__init__.py | 2 +- orchestra/contrib/webapps/settings.py | 63 ++++++++---- orchestra/contrib/websites/models.py | 2 + orchestra/contrib/websites/settings.py | 31 ++++-- orchestra/management/commands/setupnginx.py | 99 ++++++++++--------- orchestra/settings.py | 11 ++- orchestra/templates/admin/orchestra/menu.html | 2 +- 30 files changed, 567 insertions(+), 261 deletions(-) create mode 100644 orchestra/contrib/settings/templates/admin/settings/reload.html diff --git a/TODO.md b/TODO.md index 16c850ae..9344216f 100644 --- a/TODO.md +++ b/TODO.md @@ -283,6 +283,7 @@ https://code.djangoproject.com/ticket/24576 # bill confirmation: show total # Amend lines??? +# orders currency setting # 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/settings.py b/orchestra/contrib/accounts/settings.py index 644c4259..f6ac48f4 100644 --- a/orchestra/contrib/accounts/settings.py +++ b/orchestra/contrib/accounts/settings.py @@ -4,7 +4,8 @@ from django.utils.translation import ugettext_lazy as _ from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting -ACCOUNTS_TYPES = Setting('ACCOUNTS_TYPES', ( +ACCOUNTS_TYPES = Setting('ACCOUNTS_TYPES', + ( ('INDIVIDUAL', _("Individual")), ('ASSOCIATION', _("Association")), ('CUSTOMER', _("Customer")), @@ -17,45 +18,55 @@ ACCOUNTS_TYPES = Setting('ACCOUNTS_TYPES', ( ) -ACCOUNTS_DEFAULT_TYPE = Setting('ACCOUNTS_DEFAULT_TYPE', 'INDIVIDUAL', choices=ACCOUNTS_TYPES) +ACCOUNTS_DEFAULT_TYPE = Setting('ACCOUNTS_DEFAULT_TYPE', + 'INDIVIDUAL', choices=ACCOUNTS_TYPES) -ACCOUNTS_LANGUAGES = Setting('ACCOUNTS_LANGUAGES', ( +ACCOUNTS_LANGUAGES = Setting('ACCOUNTS_LANGUAGES', + ( ('EN', _('English')), ), validators=[Setting.validate_choices] ) -ACCOUNTS_DEFAULT_LANGUAGE = Setting('ACCOUNTS_DEFAULT_LANGUAGE', 'EN', choices=ACCOUNTS_LANGUAGES) +ACCOUNTS_DEFAULT_LANGUAGE = Setting('ACCOUNTS_DEFAULT_LANGUAGE', + 'EN', + choices=ACCOUNTS_LANGUAGES +) -ACCOUNTS_SYSTEMUSER_MODEL = Setting('ACCOUNTS_SYSTEMUSER_MODEL', 'systemusers.SystemUser', +ACCOUNTS_SYSTEMUSER_MODEL = Setting('ACCOUNTS_SYSTEMUSER_MODEL', + 'systemusers.SystemUser', validators=[Setting.validate_model_label], ) -ACCOUNTS_MAIN_PK = Setting('ACCOUNTS_MAIN_PK', 1) +ACCOUNTS_MAIN_PK = Setting('ACCOUNTS_MAIN_PK', + 1 +) -ACCOUNTS_CREATE_RELATED = Setting('ACCOUNTS_CREATE_RELATED', ( - # , , , - ('mailboxes.Mailbox', - 'name', - { - 'name': 'account.username', - 'password': 'account.password', - }, - _("Designates whether to creates a related mailbox with the same name and password or not."), +ACCOUNTS_CREATE_RELATED = Setting('ACCOUNTS_CREATE_RELATED', + ( + # , , , + ('mailboxes.Mailbox', + 'name', + { + 'name': 'account.username', + 'password': 'account.password', + }, + _("Designates whether to creates a related mailbox with the same name and password or not."), + ), + ('domains.Domain', + 'name', + { + 'name': '"%s.{}" % account.username.replace("_", "-")'.format(ORCHESTRA_BASE_DOMAIN), + }, + _("Designates whether to creates a related subdomain <username>.{} or not.".format(ORCHESTRA_BASE_DOMAIN)), + ), ), - ('domains.Domain', - 'name', - { - 'name': '"%s.{}" % account.username.replace("_", "-")'.format(ORCHESTRA_BASE_DOMAIN), - }, - _("Designates whether to creates a related subdomain <username>.{} or not.".format(ORCHESTRA_BASE_DOMAIN)), - ), -)) +) ACCOUNTS_SERVICE_REPORT_TEMPLATE = Setting('ACCOUNTS_SERVICE_REPORT_TEMPLATE', diff --git a/orchestra/contrib/bills/settings.py b/orchestra/contrib/bills/settings.py index 4b2e4739..4bcb560d 100644 --- a/orchestra/contrib/bills/settings.py +++ b/orchestra/contrib/bills/settings.py @@ -4,46 +4,76 @@ from django_countries import data from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting -BILLS_NUMBER_LENGTH = Setting('BILLS_NUMBER_LENGTH', 4) +BILLS_NUMBER_LENGTH = Setting('BILLS_NUMBER_LENGTH', + 4 +) -BILLS_INVOICE_NUMBER_PREFIX = Setting('BILLS_INVOICE_NUMBER_PREFIX', 'I') +BILLS_INVOICE_NUMBER_PREFIX = Setting('BILLS_INVOICE_NUMBER_PREFIX', + 'I' +) -BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX', 'A') +BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX', + 'A' +) -BILLS_FEE_NUMBER_PREFIX = Setting('BILLS_FEE_NUMBER_PREFIX', 'F') +BILLS_FEE_NUMBER_PREFIX = Setting('BILLS_FEE_NUMBER_PREFIX', + 'F' +) -BILLS_AMENDMENT_FEE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_FEE_NUMBER_PREFIX', 'B') +BILLS_AMENDMENT_FEE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_FEE_NUMBER_PREFIX', + 'B' +) -BILLS_PROFORMA_NUMBER_PREFIX = Setting('BILLS_PROFORMA_NUMBER_PREFIX', 'P') +BILLS_PROFORMA_NUMBER_PREFIX = Setting('BILLS_PROFORMA_NUMBER_PREFIX', + 'P' +) -BILLS_DEFAULT_TEMPLATE = Setting('BILLS_DEFAULT_TEMPLATE', 'bills/microspective.html') +BILLS_DEFAULT_TEMPLATE = Setting('BILLS_DEFAULT_TEMPLATE', + 'bills/microspective.html' +) -BILLS_FEE_TEMPLATE = Setting('BILLS_FEE_TEMPLATE', 'bills/microspective-fee.html') +BILLS_FEE_TEMPLATE = Setting('BILLS_FEE_TEMPLATE', + 'bills/microspective-fee.html' +) -BILLS_PROFORMA_TEMPLATE = Setting('BILLS_PROFORMA_TEMPLATE', 'bills/microspective-proforma.html') +BILLS_PROFORMA_TEMPLATE = Setting('BILLS_PROFORMA_TEMPLATE', + 'bills/microspective-proforma.html' +) -BILLS_CURRENCY = Setting('BILLS_CURRENCY', 'euro') +BILLS_CURRENCY = Setting('BILLS_CURRENCY', + 'euro' +) -BILLS_SELLER_PHONE = Setting('BILLS_SELLER_PHONE', '111-112-11-222') +BILLS_SELLER_PHONE = Setting('BILLS_SELLER_PHONE', + '111-112-11-222' +) -BILLS_SELLER_EMAIL = Setting('BILLS_SELLER_EMAIL', 'sales@{}'.format(ORCHESTRA_BASE_DOMAIN)) +BILLS_SELLER_EMAIL = Setting('BILLS_SELLER_EMAIL', + 'sales@{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", +) -BILLS_SELLER_WEBSITE = Setting('BILLS_SELLER_WEBSITE', 'www.{}'.format(ORCHESTRA_BASE_DOMAIN)) +BILLS_SELLER_WEBSITE = Setting('BILLS_SELLER_WEBSITE', + 'www.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", +) -BILLS_SELLER_BANK_ACCOUNT = Setting('BILLS_SELLER_BANK_ACCOUNT', '0000 0000 00 00000000 (Orchestra Bank)') +BILLS_SELLER_BANK_ACCOUNT = Setting('BILLS_SELLER_BANK_ACCOUNT', + '0000 0000 00 00000000 (Orchestra Bank)' +) BILLS_EMAIL_NOTIFICATION_TEMPLATE = Setting('BILLS_EMAIL_NOTIFICATION_TEMPLATE', @@ -51,19 +81,24 @@ BILLS_EMAIL_NOTIFICATION_TEMPLATE = Setting('BILLS_EMAIL_NOTIFICATION_TEMPLATE', ) -BILLS_ORDER_MODEL = Setting('BILLS_ORDER_MODEL', 'orders.Order', +BILLS_ORDER_MODEL = Setting('BILLS_ORDER_MODEL', + 'orders.Order', validators=[Setting.validate_model_label] ) -BILLS_CONTACT_DEFAULT_CITY = Setting('BILLS_CONTACT_DEFAULT_CITY', 'Barcelona') +BILLS_CONTACT_DEFAULT_CITY = Setting('BILLS_CONTACT_DEFAULT_CITY', + 'Barcelona' +) -BILLS_CONTACT_COUNTRIES = Setting('BILLS_CONTACT_COUNTRIES', tuple((k,v) for k,v in data.COUNTRIES.items()), +BILLS_CONTACT_COUNTRIES = Setting('BILLS_CONTACT_COUNTRIES', + tuple((k,v) for k,v in data.COUNTRIES.items()), serializable=False ) -BILLS_CONTACT_DEFAULT_COUNTRY = Setting('BILLS_CONTACT_DEFAULT_COUNTRY', 'ES', +BILLS_CONTACT_DEFAULT_COUNTRY = Setting('BILLS_CONTACT_DEFAULT_COUNTRY', + 'ES', choices=BILLS_CONTACT_COUNTRIES ) diff --git a/orchestra/contrib/databases/settings.py b/orchestra/contrib/databases/settings.py index 4797680d..7975e115 100644 --- a/orchestra/contrib/databases/settings.py +++ b/orchestra/contrib/databases/settings.py @@ -1,7 +1,10 @@ +from orchestra.core.validators import validate_hostname + from orchestra.settings import Setting -DATABASES_TYPE_CHOICES = Setting('DATABASES_TYPE_CHOICES', ( +DATABASES_TYPE_CHOICES = Setting('DATABASES_TYPE_CHOICES', + ( ('mysql', 'MySQL'), ('postgres', 'PostgreSQL'), ), @@ -9,7 +12,13 @@ DATABASES_TYPE_CHOICES = Setting('DATABASES_TYPE_CHOICES', ( ) -DATABASES_DEFAULT_TYPE = Setting('DATABASES_DEFAULT_TYPE', 'mysql', choices=DATABASES_TYPE_CHOICES) +DATABASES_DEFAULT_TYPE = Setting('DATABASES_DEFAULT_TYPE', + 'mysql', + choices=DATABASES_TYPE_CHOICES, +) -DATABASES_DEFAULT_HOST = Setting('DATABASES_DEFAULT_HOST', 'localhost') +DATABASES_DEFAULT_HOST = Setting('DATABASES_DEFAULT_HOST', + 'localhost', + validators=[validate_hostname], +) diff --git a/orchestra/contrib/domains/settings.py b/orchestra/contrib/domains/settings.py index 6712d415..5ebe6d0d 100644 --- a/orchestra/contrib/domains/settings.py +++ b/orchestra/contrib/domains/settings.py @@ -1,39 +1,49 @@ -from orchestra.core.validators import validate_ipv4_address, validate_ipv6_address +from orchestra.core.validators import validate_ipv4_address, validate_ipv6_address, validate_ip_address from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting +from .validators import validate_zone_interval, validate_mx_record, validate_domain_name + DOMAINS_DEFAULT_NAME_SERVER = Setting('DOMAINS_DEFAULT_NAME_SERVER', - 'ns.{}'.format(ORCHESTRA_BASE_DOMAIN) + 'ns.{}'.format(ORCHESTRA_BASE_DOMAIN), + validators=[validate_domain_name], + help_text="Uses ORCHESTRA_BASE_DOMAIN by default." ) DOMAINS_DEFAULT_HOSTMASTER = Setting('DOMAINS_DEFAULT_HOSTMASTER', - 'hostmaster@{}'.format(ORCHESTRA_BASE_DOMAIN) + 'hostmaster@{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default." ) DOMAINS_DEFAULT_TTL = Setting('DOMAINS_DEFAULT_TTL', - '1h' + '1h', + validators=[validate_zone_interval], ) DOMAINS_DEFAULT_REFRESH = Setting('DOMAINS_DEFAULT_REFRESH', - '1d' + '1d', + validators=[validate_zone_interval], ) DOMAINS_DEFAULT_RETRY = Setting('DOMAINS_DEFAULT_RETRY', - '2h' + '2h', + validators=[validate_zone_interval], ) DOMAINS_DEFAULT_EXPIRATION = Setting('DOMAINS_DEFAULT_EXPIRATION', - '4w' + '4w', + validators=[validate_zone_interval], ) DOMAINS_DEFAULT_MIN_CACHING_TIME = Setting('DOMAINS_DEFAULT_MIN_CACHING_TIME', - '1h' + '1h', + validators=[validate_zone_interval], ) @@ -43,17 +53,17 @@ DOMAINS_ZONE_PATH = Setting('DOMAINS_ZONE_PATH', DOMAINS_MASTERS_PATH = Setting('DOMAINS_MASTERS_PATH', - '/etc/bind/named.conf.local' + '/etc/bind/named.conf.local', ) DOMAINS_SLAVES_PATH = Setting('DOMAINS_SLAVES_PATH', - '/etc/bind/named.conf.local' + '/etc/bind/named.conf.local', ) DOMAINS_CHECKZONE_BIN_PATH = Setting('DOMAINS_CHECKZONE_BIN_PATH', - '/usr/sbin/named-checkzone -i local -k fail -n fail' + '/usr/sbin/named-checkzone -i local -k fail -n fail', ) @@ -63,7 +73,8 @@ DOMAINS_ZONE_VALIDATION_TMP_DIR = Setting('DOMAINS_ZONE_VALIDATION_TMP_DIR', ) -DOMAINS_DEFAULT_A = Setting('DOMAINS_DEFAULT_A', '10.0.3.13', +DOMAINS_DEFAULT_A = Setting('DOMAINS_DEFAULT_A', + '10.0.3.13', validators=[validate_ipv4_address] ) @@ -73,19 +84,28 @@ DOMAINS_DEFAULT_AAAA = Setting('DOMAINS_DEFAULT_AAAA', '', ) -DOMAINS_DEFAULT_MX = Setting('DOMAINS_DEFAULT_MX', ( - '10 mail.{}.'.format(ORCHESTRA_BASE_DOMAIN), - '10 mail2.{}.'.format(ORCHESTRA_BASE_DOMAIN), -)) +DOMAINS_DEFAULT_MX = Setting('DOMAINS_DEFAULT_MX', + default=( + '10 mail.{}.'.format(ORCHESTRA_BASE_DOMAIN), + '10 mail2.{}.'.format(ORCHESTRA_BASE_DOMAIN), + ), + validators=[lambda mxs: map(validate_mx_record, mxs)], + help_text="Uses ORCHESTRA_BASE_DOMAIN by default." +) -DOMAINS_DEFAULT_NS = Setting('DOMAINS_DEFAULT_NS', ( - 'ns1.{}.'.format(ORCHESTRA_BASE_DOMAIN), - 'ns2.{}.'.format(ORCHESTRA_BASE_DOMAIN), -)) +DOMAINS_DEFAULT_NS = Setting('DOMAINS_DEFAULT_NS', + default=( + 'ns1.{}.'.format(ORCHESTRA_BASE_DOMAIN), + 'ns2.{}.'.format(ORCHESTRA_BASE_DOMAIN), + ), + validators=[lambda nss: map(validate_domain_name, nss)], + help_text="Uses ORCHESTRA_BASE_DOMAIN by default." +) -DOMAINS_FORBIDDEN = Setting('DOMAINS_FORBIDDEN', '', +DOMAINS_FORBIDDEN = Setting('DOMAINS_FORBIDDEN', + '', help_text=( "This setting prevents users from providing random domain names, i.e. google.com
" "You can generate a 5K forbidden domains list from Alexa's top 1M:
" @@ -98,11 +118,13 @@ DOMAINS_FORBIDDEN = Setting('DOMAINS_FORBIDDEN', '', DOMAINS_MASTERS = Setting('DOMAINS_MASTERS', (), + validators=[lambda masters: map(validate_ip_address, masters)], help_text="Additional master server ip addresses other than autodiscovered by router.get_servers()." ) DOMAINS_SLAVES = Setting('DOMAINS_SLAVES', (), + validators=[lambda slaves: map(validate_ip_address, slaves)], help_text="Additional slave server ip addresses other than autodiscovered by router.get_servers()." ) diff --git a/orchestra/contrib/domains/validators.py b/orchestra/contrib/domains/validators.py index 6f3686c1..7147c9c1 100644 --- a/orchestra/contrib/domains/validators.py +++ b/orchestra/contrib/domains/validators.py @@ -8,14 +8,14 @@ from orchestra.core.validators import validate_hostname from orchestra.utils import paths from orchestra.utils.sys import run -from . import settings +from .. import domains def validate_allowed_domain(value): context = { 'site_dir': paths.get_site_dir() } - fname = settings.DOMAINS_FORBIDDEN + fname = domains.settings.DOMAINS_FORBIDDEN if fname: fname = fname % context with open(fname, 'r') as forbidden: @@ -108,8 +108,8 @@ def validate_soa_record(value): def validate_zone(zone): """ Ultimate zone file validation using named-checkzone """ zone_name = zone.split()[0][:-1] - zone_path = os.path.join(settings.DOMAINS_ZONE_VALIDATION_TMP_DIR, zone_name) - checkzone = settings.DOMAINS_CHECKZONE_BIN_PATH + zone_path = os.path.join(domains.settings.DOMAINS_ZONE_VALIDATION_TMP_DIR, zone_name) + checkzone = domains.settings.DOMAINS_CHECKZONE_BIN_PATH try: with open(zone_path, 'wb') as f: f.write(zone.encode('ascii')) diff --git a/orchestra/contrib/issues/settings.py b/orchestra/contrib/issues/settings.py index 6c2f55c4..2b92f2a7 100644 --- a/orchestra/contrib/issues/settings.py +++ b/orchestra/contrib/issues/settings.py @@ -1,7 +1,11 @@ from orchestra.settings import Setting -ISSUES_SUPPORT_EMAILS = Setting('ISSUES_SUPPORT_EMAILS', ()) +ISSUES_SUPPORT_EMAILS = Setting('ISSUES_SUPPORT_EMAILS', + () +) -ISSUES_NOTIFY_SUPERUSERS = Setting('ISSUES_NOTIFY_SUPERUSERS', True) +ISSUES_NOTIFY_SUPERUSERS = Setting('ISSUES_NOTIFY_SUPERUSERS', + True +) diff --git a/orchestra/contrib/lists/settings.py b/orchestra/contrib/lists/settings.py index 99119141..725d2d3b 100644 --- a/orchestra/contrib/lists/settings.py +++ b/orchestra/contrib/lists/settings.py @@ -1,26 +1,39 @@ from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting -LISTS_DOMAIN_MODEL = Setting('LISTS_DOMAIN_MODEL', 'domains.Domain', +LISTS_DOMAIN_MODEL = Setting('LISTS_DOMAIN_MODEL', + 'domains.Domain', validators=[Setting.validate_model_label] ) -LISTS_DEFAULT_DOMAIN = Setting('LISTS_DEFAULT_DOMAIN', 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN)) - - -LISTS_LIST_URL = Setting('LISTS_LIST_URL', - 'https://lists.{}/mailman/listinfo/%(name)s'.format(ORCHESTRA_BASE_DOMAIN) +LISTS_DEFAULT_DOMAIN = Setting('LISTS_DEFAULT_DOMAIN', + 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default." ) -LISTS_MAILMAN_POST_LOG_PATH = Setting('LISTS_MAILMAN_POST_LOG_PATH', '/var/log/mailman/post') +LISTS_LIST_URL = Setting('LISTS_LIST_URL', + 'https://lists.{}/mailman/listinfo/%(name)s'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default." +) -LISTS_MAILMAN_ROOT_DIR = Setting('LISTS_MAILMAN_ROOT_DIR', '/var/lib/mailman') +LISTS_MAILMAN_POST_LOG_PATH = Setting('LISTS_MAILMAN_POST_LOG_PATH', + '/var/log/mailman/post' +) -LISTS_VIRTUAL_ALIAS_PATH = Setting('LISTS_VIRTUAL_ALIAS_PATH', '/etc/postfix/mailman_virtual_aliases') +LISTS_MAILMAN_ROOT_DIR = Setting('LISTS_MAILMAN_ROOT_DIR', + '/var/lib/mailman' +) -LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH', '/etc/postfix/mailman_virtual_domains') +LISTS_VIRTUAL_ALIAS_PATH = Setting('LISTS_VIRTUAL_ALIAS_PATH', + '/etc/postfix/mailman_virtual_aliases' +) + + +LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH', + '/etc/postfix/mailman_virtual_domains' +) diff --git a/orchestra/contrib/mailboxes/settings.py b/orchestra/contrib/mailboxes/settings.py index 348cd8e2..bc9a4392 100644 --- a/orchestra/contrib/mailboxes/settings.py +++ b/orchestra/contrib/mailboxes/settings.py @@ -7,12 +7,20 @@ from orchestra.core.validators import validate_name from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting +_names = ('name', 'username') +_backend_names = _names + ('group', 'home') + + MAILBOXES_DOMAIN_MODEL = Setting('MAILBOXES_DOMAIN_MODEL', 'domains.Domain', validators=[Setting.validate_model_label] ) -MAILBOXES_HOME = Setting('MAILBOXES_HOME', '/home/%(name)s/') +MAILBOXES_HOME = Setting('MAILBOXES_HOME', + '/home/%(name)s/', + help_text="Available fromat names: %s" % ', '.join(_names), + validators=[Setting.string_format_validator(_names)], +) MAILBOXES_SIEVE_PATH = Setting('MAILBOXES_SIEVE_PATH', @@ -20,10 +28,13 @@ MAILBOXES_SIEVE_PATH = Setting('MAILBOXES_SIEVE_PATH', ) -MAILBOXES_SIEVETEST_PATH = Setting('MAILBOXES_SIEVETEST_PATH', '/dev/shm') +MAILBOXES_SIEVETEST_PATH = Setting('MAILBOXES_SIEVETEST_PATH', + '/dev/shm' +) -MAILBOXES_SIEVETEST_BIN_PATH = Setting('MAILBOXES_SIEVETEST_BIN_PATH', '%(orchestra_root)s/bin/sieve-test', +MAILBOXES_SIEVETEST_BIN_PATH = Setting('MAILBOXES_SIEVETEST_BIN_PATH', + '%(orchestra_root)s/bin/sieve-test', validators=[Setting.string_format_validator(('orchestra_root',))] ) @@ -43,47 +54,66 @@ MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('MAILBOXES_VIRTUAL_ALIAS_DOMAINS_ ) -MAILBOXES_LOCAL_DOMAIN = Setting('MAILBOXES_LOCAL_DOMAIN', ORCHESTRA_BASE_DOMAIN, - validators=[validate_name] +MAILBOXES_LOCAL_DOMAIN = Setting('MAILBOXES_LOCAL_DOMAIN', + ORCHESTRA_BASE_DOMAIN, + validators=[validate_name], + help_text="Defaults to ORCHESTRA_BASE_DOMAIN." ) -MAILBOXES_PASSWD_PATH = Setting('MAILBOXES_PASSWD_PATH', '/etc/dovecot/passwd') +MAILBOXES_PASSWD_PATH = Setting('MAILBOXES_PASSWD_PATH', + '/etc/dovecot/passwd' +) -MAILBOXES_MAILBOX_FILTERINGS = Setting('MAILBOXES_MAILBOX_FILTERINGS', { - # value: (verbose_name, filter) - 'DISABLE': (_("Disable"), ''), - 'REJECT': (_("Reject spam"), textwrap.dedent(""" - require ["fileinto","regex","envelope","vacation","reject","relational","comparator-i;ascii-numeric"]; - if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-Score" "5" { - discard; - stop; - }""")), - 'REDIRECT': (_("Archive spam"), textwrap.dedent(""" - require ["fileinto","regex","envelope","vacation","reject","relational","comparator-i;ascii-numeric"]; - if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-Score" "5" { - fileinto "Spam"; - stop; - }""")), - 'CUSTOM': (_("Custom filtering"), lambda mailbox: mailbox.custom_filtering), -}) +MAILBOXES_MAILBOX_FILTERINGS = Setting('MAILBOXES_MAILBOX_FILTERINGS', + { + # value: (verbose_name, filter) + 'DISABLE': (_("Disable"), ''), + 'REJECT': (_("Reject spam"), textwrap.dedent(""" + require ["fileinto","regex","envelope","vacation","reject","relational","comparator-i;ascii-numeric"]; + if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-Score" "5" { + discard; + stop; + }""")), + 'REDIRECT': (_("Archive spam"), textwrap.dedent(""" + require ["fileinto","regex","envelope","vacation","reject","relational","comparator-i;ascii-numeric"]; + if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-Score" "5" { + fileinto "Spam"; + stop; + }""")), + 'CUSTOM': (_("Custom filtering"), lambda mailbox: mailbox.custom_filtering), + } +) -MAILBOXES_MAILBOX_DEFAULT_FILTERING = Setting('MAILBOXES_MAILBOX_DEFAULT_FILTERING', 'REDIRECT', +MAILBOXES_MAILBOX_DEFAULT_FILTERING = Setting('MAILBOXES_MAILBOX_DEFAULT_FILTERING', + 'REDIRECT', choices=tuple((k, v[0]) for k,v in MAILBOXES_MAILBOX_FILTERINGS.items()) ) -MAILBOXES_MAILDIRSIZE_PATH = Setting('MAILBOXES_MAILDIRSIZE_PATH', '%(home)s/Maildir/maildirsize') - - -MAILBOXES_LOCAL_ADDRESS_DOMAIN = Setting('MAILBOXES_LOCAL_ADDRESS_DOMAIN', ORCHESTRA_BASE_DOMAIN, - validators=[validate_name] +MAILBOXES_MAILDIRSIZE_PATH = Setting('MAILBOXES_MAILDIRSIZE_PATH', + '%(home)s/Maildir/maildirsize', + help_text="Available fromat names: %s" % ', '.join(_backend_names), + validators=[Setting.string_format_validator(_backend_names)], ) -MAILBOXES_MAIL_LOG_PATH = Setting('MAILBOXES_MAIL_LOG_PATH', '/var/log/mail.log') +MAILBOXES_LOCAL_ADDRESS_DOMAIN = Setting('MAILBOXES_LOCAL_ADDRESS_DOMAIN', + ORCHESTRA_BASE_DOMAIN, + validators=[validate_name], + help_text="Defaults to ORCHESTRA_BASE_DOMAIN." +) -MAILBOXES_MOVE_ON_DELETE_PATH = Setting('MAILBOXES_MOVE_ON_DELETE_PATH', '') +MAILBOXES_MAIL_LOG_PATH = Setting('MAILBOXES_MAIL_LOG_PATH', + '/var/log/mail.log' +) + + +MAILBOXES_MOVE_ON_DELETE_PATH = Setting('MAILBOXES_MOVE_ON_DELETE_PATH', + '', + help_text="Available fromat names: %s" % ', '.join(_backend_names), + validators=[Setting.string_format_validator(_backend_names)], +) diff --git a/orchestra/contrib/miscellaneous/admin.py b/orchestra/contrib/miscellaneous/admin.py index e616f0f9..2d3f4d6b 100644 --- a/orchestra/contrib/miscellaneous/admin.py +++ b/orchestra/contrib/miscellaneous/admin.py @@ -10,6 +10,7 @@ from orchestra.admin.utils import admin_link from orchestra.contrib.accounts.admin import AccountAdminMixin from orchestra.plugins import PluginModelAdapter from orchestra.plugins.admin import SelectPluginAdminMixin +from orchestra.utils.python import import_class from . import settings from .models import MiscService, Miscellaneous @@ -92,7 +93,8 @@ class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelA service = self.get_service(obj) def clean_identifier(self, service=service): identifier = self.cleaned_data['identifier'] - validator = settings.MISCELLANEOUS_IDENTIFIER_VALIDATORS.get(service.name, None) + validator_path = settings.MISCELLANEOUS_IDENTIFIER_VALIDATORS.get(service.name, None) + validator = import_class(validator_path) if validator: validator(identifier) return identifier diff --git a/orchestra/contrib/miscellaneous/settings.py b/orchestra/contrib/miscellaneous/settings.py index 7e34c968..77b448ba 100644 --- a/orchestra/contrib/miscellaneous/settings.py +++ b/orchestra/contrib/miscellaneous/settings.py @@ -1,6 +1,8 @@ from orchestra.settings import Setting -MISCELLANEOUS_IDENTIFIER_VALIDATORS = Setting('MISCELLANEOUS_IDENTIFIER_VALIDATORS', { - # : -}) +MISCELLANEOUS_IDENTIFIER_VALIDATORS = Setting('MISCELLANEOUS_IDENTIFIER_VALIDATORS', + { + # : + } +) diff --git a/orchestra/contrib/orchestration/settings.py b/orchestra/contrib/orchestration/settings.py index df5a482e..aee38ed4 100644 --- a/orchestra/contrib/orchestration/settings.py +++ b/orchestra/contrib/orchestration/settings.py @@ -1,35 +1,43 @@ -from datetime import timedelta from os import path from orchestra.settings import Setting -ORCHESTRATION_OS_CHOICES = Setting('ORCHESTRATION_OS_CHOICES', ( +ORCHESTRATION_OS_CHOICES = Setting('ORCHESTRATION_OS_CHOICES', + ( ('LINUX', "Linux"), ), validators=[Setting.validate_choices] ) -ORCHESTRATION_DEFAULT_OS = Setting('ORCHESTRATION_DEFAULT_OS', 'LINUX', - choices=ORCHESTRATION_OS_CHOICES) +ORCHESTRATION_DEFAULT_OS = Setting('ORCHESTRATION_DEFAULT_OS', + 'LINUX', + choices=ORCHESTRATION_OS_CHOICES +) -ORCHESTRATION_SSH_KEY_PATH = Setting('ORCHESTRATION_SSH_KEY_PATH', - path.join(path.expanduser('~'), '.ssh/id_rsa')) +ORCHESTRATION_SSH_KEY_PATH = Setting('ORCHESTRATION_SSH_KEY_PATH', + path.join(path.expanduser('~'), '.ssh/id_rsa') +) -ORCHESTRATION_ROUTER = Setting('ORCHESTRATION_ROUTER', 'orchestra.contrib.orchestration.models.Route', +ORCHESTRATION_ROUTER = Setting('ORCHESTRATION_ROUTER', + 'orchestra.contrib.orchestration.models.Route', validators=[Setting.validate_import_class] ) -ORCHESTRATION_TEMP_SCRIPT_DIR = Setting('ORCHESTRATION_TEMP_SCRIPT_DIR', '/dev/shm') - - -ORCHESTRATION_DISABLE_EXECUTION = Setting('ORCHESTRATION_DISABLE_EXECUTION', False) - - -ORCHESTRATION_BACKEND_CLEANUP_DELTA = Setting('ORCHESTRATION_BACKEND_CLEANUP_DELTA', - timedelta(days=15) +ORCHESTRATION_TEMP_SCRIPT_DIR = Setting('ORCHESTRATION_TEMP_SCRIPT_DIR', + '/dev/shm' +) + + +ORCHESTRATION_DISABLE_EXECUTION = Setting('ORCHESTRATION_DISABLE_EXECUTION', + False +) + + +ORCHESTRATION_BACKEND_CLEANUP_DAYS = Setting('ORCHESTRATION_BACKEND_CLEANUP_DAYS', + 15 ) diff --git a/orchestra/contrib/orchestration/tasks.py b/orchestra/contrib/orchestration/tasks.py index e3bd6599..92b9bd3f 100644 --- a/orchestra/contrib/orchestration/tasks.py +++ b/orchestra/contrib/orchestration/tasks.py @@ -1,3 +1,5 @@ +from datetime import timedelta + from celery.task.schedules import crontab from celery.decorators import periodic_task from django.utils import timezone @@ -7,5 +9,6 @@ from .models import BackendLog @periodic_task(run_every=crontab(hour=7, minute=30, day_of_week=1)) def backend_logs_cleanup(run_every=run_every): - epoch = timezone.now()-settings.ORCHESTRATION_BACKEND_CLEANUP_DELTA + days = settings.ORCHESTRATION_BACKEND_CLEANUP_DAYS + epoch = timezone.now()-timedelta(days=days) BackendLog.objects.filter(created_at__lt=epoch).delete() diff --git a/orchestra/contrib/orders/settings.py b/orchestra/contrib/orders/settings.py index fad39cab..0d171638 100644 --- a/orchestra/contrib/orders/settings.py +++ b/orchestra/contrib/orders/settings.py @@ -1,19 +1,22 @@ from orchestra.settings import Setting -ORDERS_BILLING_BACKEND = Setting('ORDERS_BILLING_BACKEND', 'orchestra.contrib.orders.billing.BillsBackend', +ORDERS_BILLING_BACKEND = Setting('ORDERS_BILLING_BACKEND', + 'orchestra.contrib.orders.billing.BillsBackend', validators=[Setting.validate_import_class], help_text="Pluggable backend for bill generation.", ) -ORDERS_SERVICE_MODEL = Setting('ORDERS_SERVICE_MODEL', 'services.Service', +ORDERS_SERVICE_MODEL = Setting('ORDERS_SERVICE_MODEL', + 'services.Service', validators=[Setting.validate_model_label], - help_text="Pluggable service class." + help_text="Pluggable service class.", ) -ORDERS_EXCLUDED_APPS = Setting('ORDERS_EXCLUDED_APPS', ( +ORDERS_EXCLUDED_APPS = Setting('ORDERS_EXCLUDED_APPS', + ( 'orders', 'admin', 'contenttypes', @@ -28,7 +31,8 @@ ORDERS_EXCLUDED_APPS = Setting('ORDERS_EXCLUDED_APPS', ( ) -ORDERS_METRIC_ERROR = Setting('ORDERS_METRIC_ERROR', 0.01, +ORDERS_METRIC_ERROR = Setting('ORDERS_METRIC_ERROR', + 0.01, help_text=("Only account for significative changes.
" - "metric_storage new value: lastvalue*(1+threshold) > currentvalue or lastvalue*threshold < currentvalue.") + "metric_storage new value: lastvalue*(1+threshold) > currentvalue or lastvalue*threshold < currentvalue."), ) diff --git a/orchestra/contrib/payments/settings.py b/orchestra/contrib/payments/settings.py index c046c503..215de3e6 100644 --- a/orchestra/contrib/payments/settings.py +++ b/orchestra/contrib/payments/settings.py @@ -3,22 +3,33 @@ from orchestra.settings import Setting from .. import payments -PAYMENT_CURRENCY = Setting('PAYMENT_CURRENCY', 'Eur') +PAYMENT_CURRENCY = Setting('PAYMENT_CURRENCY', + 'Eur' +) -PAYMENTS_DD_CREDITOR_NAME = Setting('PAYMENTS_DD_CREDITOR_NAME', 'Orchestra') +PAYMENTS_DD_CREDITOR_NAME = Setting('PAYMENTS_DD_CREDITOR_NAME', + 'Orchestra' +) -PAYMENTS_DD_CREDITOR_IBAN = Setting('PAYMENTS_DD_CREDITOR_IBAN', 'IE98BOFI90393912121212') +PAYMENTS_DD_CREDITOR_IBAN = Setting('PAYMENTS_DD_CREDITOR_IBAN', + 'IE98BOFI90393912121212' +) -PAYMENTS_DD_CREDITOR_BIC = Setting('PAYMENTS_DD_CREDITOR_BIC', 'BOFIIE2D') +PAYMENTS_DD_CREDITOR_BIC = Setting('PAYMENTS_DD_CREDITOR_BIC', + 'BOFIIE2D' +) -PAYMENTS_DD_CREDITOR_AT02_ID = Setting('PAYMENTS_DD_CREDITOR_AT02_ID', 'InvalidAT02ID') +PAYMENTS_DD_CREDITOR_AT02_ID = Setting('PAYMENTS_DD_CREDITOR_AT02_ID', + 'InvalidAT02ID' +) -PAYMENTS_ENABLED_METHODS = Setting('PAYMENTS_ENABLED_METHODS', ( +PAYMENTS_ENABLED_METHODS = Setting('PAYMENTS_ENABLED_METHODS', + ( 'orchestra.contrib.payments.methods.sepadirectdebit.SEPADirectDebit', 'orchestra.contrib.payments.methods.creditcard.CreditCard', ), diff --git a/orchestra/contrib/saas/settings.py b/orchestra/contrib/saas/settings.py index 0ca72eaf..64f39bff 100644 --- a/orchestra/contrib/saas/settings.py +++ b/orchestra/contrib/saas/settings.py @@ -3,7 +3,8 @@ from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting from .. import saas -SAAS_ENABLED_SERVICES = Setting('SAAS_ENABLED_SERVICES', ( +SAAS_ENABLED_SERVICES = Setting('SAAS_ENABLED_SERVICES', + ( 'orchestra.contrib.saas.services.moodle.MoodleService', 'orchestra.contrib.saas.services.bscw.BSCWService', 'orchestra.contrib.saas.services.gitlab.GitLabService', @@ -19,11 +20,14 @@ SAAS_ENABLED_SERVICES = Setting('SAAS_ENABLED_SERVICES', ( ) -SAAS_WORDPRESS_ADMIN_PASSWORD = Setting('SAAS_WORDPRESSMU_ADMIN_PASSWORD', 'secret') +SAAS_WORDPRESS_ADMIN_PASSWORD = Setting('SAAS_WORDPRESSMU_ADMIN_PASSWORD', + 'secret' +) SAAS_WORDPRESS_BASE_URL = Setting('SAAS_WORDPRESS_BASE_URL', - 'https://blogs.{}/'.format(ORCHESTRA_BASE_DOMAIN) + 'https://blogs.{}/'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", ) @@ -38,22 +42,25 @@ SAAS_DOKUWIKI_FARM_PATH = Setting('WEBSITES_DOKUWIKI_FARM_PATH', SAAS_DRUPAL_SITES_PATH = Setting('WEBSITES_DRUPAL_SITES_PATH', - '/home/httpd/htdocs/drupal-mu/sites/%(site_name)s' + '/home/httpd/htdocs/drupal-mu/sites/%(site_name)s', + ) SAAS_PHPLIST_DB_NAME = Setting('SAAS_PHPLIST_DB_NAME', - 'phplist_mu' + 'phplist_mu', ) SAAS_PHPLIST_BASE_DOMAIN = Setting('SAAS_PHPLIST_BASE_DOMAIN', - 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN) + 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", ) SAAS_SEAFILE_DOMAIN = Setting('SAAS_SEAFILE_DOMAIN', - 'seafile.{}'.format(ORCHESTRA_BASE_DOMAIN) + 'seafile.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", ) @@ -63,24 +70,27 @@ SAAS_SEAFILE_DEFAULT_QUOTA = Setting('SAAS_SEAFILE_DEFAULT_QUOTA', SAAS_BSCW_DOMAIN = Setting('SAAS_BSCW_DOMAIN', - 'bscw.{}'.format(ORCHESTRA_BASE_DOMAIN) + 'bscw.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", ) SAAS_BSCW_DEFAULT_QUOTA = Setting('SAAS_BSCW_DEFAULT_QUOTA', - 50 + 50, ) + SAAS_BSCW_BSADMIN_PATH = Setting('SAAS_BSCW_BSADMIN_PATH', '/home/httpd/bscw/bin/bsadmin', ) SAAS_GITLAB_ROOT_PASSWORD = Setting('SAAS_GITLAB_ROOT_PASSWORD', - 'secret' + 'secret', ) SAAS_GITLAB_DOMAIN = Setting('SAAS_GITLAB_DOMAIN', - 'gitlab.{}'.format(ORCHESTRA_BASE_DOMAIN) + 'gitlab.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", ) diff --git a/orchestra/contrib/services/settings.py b/orchestra/contrib/services/settings.py index e6aa6bca..e97fabb0 100644 --- a/orchestra/contrib/services/settings.py +++ b/orchestra/contrib/services/settings.py @@ -3,7 +3,8 @@ from django.utils.translation import ugettext_lazy as _ from orchestra.settings import Setting -SERVICES_SERVICE_TAXES = Setting('SERVICES_SERVICE_TAXES', ( +SERVICES_SERVICE_TAXES = Setting('SERVICES_SERVICE_TAXES', + ( (0, _("Duty free")), (21, "21%"), ), @@ -11,31 +12,39 @@ SERVICES_SERVICE_TAXES = Setting('SERVICES_SERVICE_TAXES', ( ) -SERVICES_SERVICE_DEFAULT_TAX = Setting('SERVICES_SERVICE_DEFAULT_TAX', 0, +SERVICES_SERVICE_DEFAULT_TAX = Setting('SERVICES_SERVICE_DEFAULT_TAX', + 0, choices=SERVICES_SERVICE_TAXES ) -SERVICES_SERVICE_ANUAL_BILLING_MONTH = Setting('SERVICES_SERVICE_ANUAL_BILLING_MONTH', 1, +SERVICES_SERVICE_ANUAL_BILLING_MONTH = Setting('SERVICES_SERVICE_ANUAL_BILLING_MONTH', + 1, choices=tuple((n, n) for n in range(1, 13)) ) -SERVICES_ORDER_MODEL = Setting('SERVICES_ORDER_MODEL', 'orders.Order', +SERVICES_ORDER_MODEL = Setting('SERVICES_ORDER_MODEL', + 'orders.Order', validators=[Setting.validate_model_label] ) -SERVICES_RATE_CLASS = Setting('SERVICES_RATE_CLASS', 'orchestra.contrib.plans.models.Rate', +SERVICES_RATE_CLASS = Setting('SERVICES_RATE_CLASS', + 'orchestra.contrib.plans.models.Rate', validators=[Setting.validate_import_class] ) -SERVICES_DEFAULT_IGNORE_PERIOD = Setting('SERVICES_DEFAULT_IGNORE_PERIOD', 'TEN_DAYS') +SERVICES_DEFAULT_IGNORE_PERIOD = Setting('SERVICES_DEFAULT_IGNORE_PERIOD', + 'TEN_DAYS' +) -SERVICES_IGNORE_ACCOUNT_TYPE = Setting('SERVICES_IGNORE_ACCOUNT_TYPE', ( - 'superuser', - 'STAFF', - 'FRIEND', -)) +SERVICES_IGNORE_ACCOUNT_TYPE = Setting('SERVICES_IGNORE_ACCOUNT_TYPE', + ( + 'superuser', + 'STAFF', + 'FRIEND', + ), +) diff --git a/orchestra/contrib/settings/admin.py b/orchestra/contrib/settings/admin.py index 9fd3edbf..7b6b9f4c 100644 --- a/orchestra/contrib/settings/admin.py +++ b/orchestra/contrib/settings/admin.py @@ -2,7 +2,7 @@ from functools import partial from django.contrib import admin, messages from django.db import models - +from django.shortcuts import render_to_response from django.views import generic from django.utils.translation import ngettext, ugettext_lazy as _ @@ -15,6 +15,7 @@ from .forms import SettingFormSet class SettingView(generic.edit.FormView): template_name = 'admin/settings/change_form.html' + reload_template_name = 'admin/settings/reload.html' form_class = SettingFormSet success_url = '.' @@ -71,13 +72,15 @@ class SettingView(generic.edit.FormView): # Save changes parser.save(changes) - n = len(changes) - messages.success(self.request, ngettext( - _("One change successfully applied, the orchestra is going to be restarted..."), - _("%s changes successfully applied, the orchestra is going to be restarted...") % n, - n) - ) sys.run('{ sleep 2 && touch %s/wsgi.py; } &' % paths.get_project_dir(), async=True) + n = len(changes) + context = { + 'message': ngettext( + _("One change successfully applied, orchestra is being restarted."), + _("%s changes successfully applied, orchestra is being restarted.") % n, + n), + } + return render_to_response(self.reload_template_name, context) else: messages.success(self.request, _("No changes have been detected.")) return super(SettingView, self).form_valid(form) diff --git a/orchestra/contrib/settings/forms.py b/orchestra/contrib/settings/forms.py index 1d4fd195..0ab4ddc5 100644 --- a/orchestra/contrib/settings/forms.py +++ b/orchestra/contrib/settings/forms.py @@ -33,6 +33,7 @@ class SettingForm(ReadOnlyFormMixin, forms.Form): FORMFIELD_FOR_SETTING_TYPE = { bool: partial(forms.BooleanField, required=False), int: forms.IntegerField, + float: forms.FloatField, tuple: TEXTAREA, list: TEXTAREA, dict: TEXTAREA, diff --git a/orchestra/contrib/settings/parser.py b/orchestra/contrib/settings/parser.py index 4231bd1a..0f5e6620 100644 --- a/orchestra/contrib/settings/parser.py +++ b/orchestra/contrib/settings/parser.py @@ -79,7 +79,7 @@ def serialize(obj, init=True): return nested _obj.append(nested) _obj = type(obj)(_obj) - elif isinstance(obj, (str, bool, int)): + elif isinstance(obj, (str, bool, int, float)): _obj = obj else: _obj = NotSupported() @@ -131,8 +131,8 @@ def apply(changes, settings_file=get_settings_file()): if num == lastend: content.extend(comments) inside = False - - # insert new variables + + # insert new variables at the end of file for name, value in changes.items(): content.append(_format_setting(name, value)) return '\n'.join(content) diff --git a/orchestra/contrib/settings/templates/admin/settings/reload.html b/orchestra/contrib/settings/templates/admin/settings/reload.html new file mode 100644 index 00000000..751a4be1 --- /dev/null +++ b/orchestra/contrib/settings/templates/admin/settings/reload.html @@ -0,0 +1,54 @@ +{% load staticfiles %} + + + + + + + + + +
+
notice: {{ message }}
Refreshing in 5.
+
+ + diff --git a/orchestra/contrib/systemusers/settings.py b/orchestra/contrib/systemusers/settings.py index c5e47b28..8563131f 100644 --- a/orchestra/contrib/systemusers/settings.py +++ b/orchestra/contrib/systemusers/settings.py @@ -3,7 +3,12 @@ from django.utils.translation import ugettext_lazy as _ from orchestra.settings import Setting -SYSTEMUSERS_SHELLS = Setting('SYSTEMUSERS_SHELLS', ( +_names = ('user', 'username') +_backend_names = _names + ('group', 'shell', 'mainuser', 'home', 'base_home') + + +SYSTEMUSERS_SHELLS = Setting('SYSTEMUSERS_SHELLS', + ( ('/dev/null', _("No shell, FTP only")), ('/bin/rssh', _("No shell, SFTP/RSYNC only")), ('/bin/bash', "/bin/bash"), @@ -13,7 +18,8 @@ SYSTEMUSERS_SHELLS = Setting('SYSTEMUSERS_SHELLS', ( ) -SYSTEMUSERS_DEFAULT_SHELL = Setting('SYSTEMUSERS_DEFAULT_SHELL', '/dev/null', +SYSTEMUSERS_DEFAULT_SHELL = Setting('SYSTEMUSERS_DEFAULT_SHELL', + '/dev/null', choices=SYSTEMUSERS_SHELLS ) @@ -27,7 +33,9 @@ SYSTEMUSERS_DISABLED_SHELLS = Setting('SYSTEMUSERS_DISABLED_SHELLS', SYSTEMUSERS_HOME = Setting('SYSTEMUSERS_HOME', - '/home/%(user)s' + '/home/%(user)s', + help_text="Available fromat names: %s" % ', '.join(_names), + validators=[Setting.string_format_validator(_names)], ) @@ -46,5 +54,7 @@ SYSTEMUSERS_DEFAULT_GROUP_MEMBERS = Setting('SYSTEMUSERS_DEFAULT_GROUP_MEMBERS', SYSTEMUSERS_MOVE_ON_DELETE_PATH = Setting('SYSTEMUSERS_MOVE_ON_DELETE_PATH', - '' + '', + help_text="Available fromat names: %s" % ', '.join(_backend_names), + validators=[Setting.string_format_validator(_backend_names)], ) diff --git a/orchestra/contrib/vps/settings.py b/orchestra/contrib/vps/settings.py index ad12ed7b..1f3e1745 100644 --- a/orchestra/contrib/vps/settings.py +++ b/orchestra/contrib/vps/settings.py @@ -1,21 +1,29 @@ from orchestra.settings import Setting -VPS_TYPES = Setting('VPS_TYPES', ( +VPS_TYPES = Setting('VPS_TYPES', + ( ('openvz', 'OpenVZ container'), ), validators=[Setting.validate_choices] ) -VPS_DEFAULT_TYPE = Setting('VPS_DEFAULT_TYPE', 'openvz', choices=VPS_TYPES) +VPS_DEFAULT_TYPE = Setting('VPS_DEFAULT_TYPE', + 'openvz', + choices=VPS_TYPES +) -VPS_TEMPLATES = Setting('VPS_TEMPLATES', ( +VPS_TEMPLATES = Setting('VPS_TEMPLATES', + ( ('debian7', 'Debian 7 - Wheezy'), ), validators=[Setting.validate_choices] ) -VPS_DEFAULT_TEMPLATE = Setting('VPS_DEFAULT_TEMPLATE', 'debian7', choices=VPS_TEMPLATES) +VPS_DEFAULT_TEMPLATE = Setting('VPS_DEFAULT_TEMPLATE', + 'debian7', + choices=VPS_TEMPLATES +) diff --git a/orchestra/contrib/webapps/backends/__init__.py b/orchestra/contrib/webapps/backends/__init__.py index eff5939d..1026c784 100644 --- a/orchestra/contrib/webapps/backends/__init__.py +++ b/orchestra/contrib/webapps/backends/__init__.py @@ -45,7 +45,7 @@ class WebAppServiceMixin(object): 'user': webapp.get_username(), 'group': webapp.get_groupname(), 'app_name': webapp.name, - 'type': webapp.type, + 'app_type': webapp.type, 'app_path': webapp.get_path(), 'banner': self.get_banner(), 'under_construction_path': settings.WEBAPPS_UNDER_CONSTRUCTION_PATH, diff --git a/orchestra/contrib/webapps/settings.py b/orchestra/contrib/webapps/settings.py index 26717126..92fbe0a7 100644 --- a/orchestra/contrib/webapps/settings.py +++ b/orchestra/contrib/webapps/settings.py @@ -3,14 +3,23 @@ from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting from .. import webapps +_names = ('home', 'user', 'group', 'app_type', 'app_name', 'app_type', 'app_id') +_php_names = _names + ('php_version', 'php_version_number',) +_python_names = _names + ('python_version', 'python_version_number',) + + WEBAPPS_BASE_DIR = Setting('WEBAPPS_BASE_DIR', - '%(home)s/webapps/%(app_name)s' + '%(home)s/webapps/%(app_name)s', + help_text="Available fromat names: %s" % ', '.join(_names), + validators=[Setting.string_format_validator(_names)], ) WEBAPPS_FPM_LISTEN = Setting('WEBAPPS_FPM_LISTEN', - # '127.0.0.1:9%(app_id)03d - '/opt/php/5.4/socks/%(user)s-%(app_name)s.sock' + '/opt/php/5.4/socks/%(user)s-%(app_name)s.sock', + help_text=("TCP socket example: 127.0.0.1:9%(app_id)03d
" + "Available fromat names: {}").format(', '.join(_php_names)), + validators=[Setting.string_format_validator(_php_names)], ) @@ -20,20 +29,25 @@ WEBAPPS_FPM_DEFAULT_MAX_CHILDREN = Setting('WEBAPPS_FPM_DEFAULT_MAX_CHILDREN', WEBAPPS_PHPFPM_POOL_PATH = Setting('WEBAPPS_PHPFPM_POOL_PATH', - '/etc/php5/fpm/pool.d/%(user)s-%(app_name)s.conf' + '/etc/php5/fpm/pool.d/%(user)s-%(app_name)s.conf', + help_text="Available fromat names: %s" % ', '.join(_php_names), + validators=[Setting.string_format_validator(_php_names)], ) WEBAPPS_FCGID_WRAPPER_PATH = Setting('WEBAPPS_FCGID_WRAPPER_PATH', '/home/httpd/fcgi-bin.d/%(user)s/%(app_name)s-wrapper', + validators=[Setting.string_format_validator(_php_names)], help_text=("Inside SuExec Document root.
" - "Make sure all account wrappers are in the same DIR.") + "Make sure all account wrappers are in the same DIR.
" + "Available fromat names: %s") % ', '.join(_php_names), ) WEBAPPS_FCGID_CMD_OPTIONS_PATH = Setting('WEBAPPS_FCGID_CMD_OPTIONS_PATH', '/etc/apache2/fcgid-conf/%(user)s-%(app_name)s.conf', - help_text="Loaded by Apache." + validators=[Setting.string_format_validator(_php_names)], + help_text="Loaded by Apache. Available fromat names: %s" % ', '.join(_php_names), ) @@ -43,7 +57,9 @@ WEBAPPS_PHP_MAX_REQUESTS = Setting('WEBAPPS_PHP_MAX_REQUESTS', ) -WEBAPPS_PHP_ERROR_LOG_PATH = Setting('WEBAPPS_PHP_ERROR_LOG_PATH', '') +WEBAPPS_PHP_ERROR_LOG_PATH = Setting('WEBAPPS_PHP_ERROR_LOG_PATH', + '' +) WEBAPPS_MERGE_PHP_WEBAPPS = Setting('WEBAPPS_MERGE_PHP_WEBAPPS', @@ -78,27 +94,35 @@ WEBAPPS_PHP_VERSIONS = Setting('WEBAPPS_PHP_VERSIONS', ( ) -WEBAPPS_DEFAULT_PHP_VERSION = Setting('WEBAPPS_DEFAULT_PHP_VERSION', '5.4-cgi', +WEBAPPS_DEFAULT_PHP_VERSION = Setting('WEBAPPS_DEFAULT_PHP_VERSION', + '5.4-cgi', choices=WEBAPPS_PHP_VERSIONS ) -WEBAPPS_PHP_CGI_BINARY_PATH = Setting('WEBAPPS_PHP_CGI_BINARY_PATH', '/usr/bin/php%(php_version_number)s-cgi', - help_text="Path of the cgi binary used by fcgid." +WEBAPPS_PHP_CGI_BINARY_PATH = Setting('WEBAPPS_PHP_CGI_BINARY_PATH', + '/usr/bin/php%(php_version_number)s-cgi', + help_text="Path of the cgi binary used by fcgid. Available fromat names: %s" % ', '.join(_php_names), + validators=[Setting.string_format_validator(_php_names)], ) -WEBAPPS_PHP_CGI_RC_DIR = Setting('WEBAPPS_PHP_CGI_RC_DIR', '/etc/php%(php_version_number)s/cgi/', - help_text="Path to php.ini." +WEBAPPS_PHP_CGI_RC_DIR = Setting('WEBAPPS_PHP_CGI_RC_DIR', + '/etc/php%(php_version_number)s/cgi/', + help_text="Path to php.ini. Available fromat names: %s" % ', '.join(_php_names), + validators=[Setting.string_format_validator(_php_names)], ) WEBAPPS_PHP_CGI_INI_SCAN_DIR = Setting('WEBAPPS_PHP_CGI_INI_SCAN_DIR', - '/etc/php%(php_version_number)s/cgi/conf.d' + '/etc/php%(php_version_number)s/cgi/conf.d', + help_text="Available fromat names: %s" % ', '.join(_php_names), + validators=[Setting.string_format_validator(_php_names)], ) -WEBAPPS_PYTHON_VERSIONS = Setting('WEBAPPS_PYTHON_VERSIONS', ( +WEBAPPS_PYTHON_VERSIONS = Setting('WEBAPPS_PYTHON_VERSIONS', + ( ('3.4-uwsgi', 'Python 3.4 uWSGI'), ('2.7-uwsgi', 'Python 2.7 uWSGI'), ), @@ -106,13 +130,17 @@ WEBAPPS_PYTHON_VERSIONS = Setting('WEBAPPS_PYTHON_VERSIONS', ( ) -WEBAPPS_DEFAULT_PYTHON_VERSION = Setting('WEBAPPS_DEFAULT_PYTHON_VERSION', '3.4-uwsgi', +WEBAPPS_DEFAULT_PYTHON_VERSION = Setting('WEBAPPS_DEFAULT_PYTHON_VERSION', + '3.4-uwsgi', choices=WEBAPPS_PYTHON_VERSIONS + ) WEBAPPS_UWSGI_SOCKET = Setting('WEBAPPS_UWSGI_SOCKET', - '/var/run/uwsgi/app/%(app_name)s/socket' + '/var/run/uwsgi/app/%(app_name)s/socket', + help_text="Available fromat names: %s" % ', '.join(_python_names), + validators=[Setting.string_format_validator(_python_names)], ) @@ -218,7 +246,8 @@ WEBAPPS_ENABLED_OPTIONS = Setting('WEBAPPS_ENABLED_OPTIONS', ( WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = Setting('WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST', - 'mysql.{}'.format(ORCHESTRA_BASE_DOMAIN) + 'mysql.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", ) diff --git a/orchestra/contrib/websites/models.py b/orchestra/contrib/websites/models.py index 6ffa6bd7..3ea12672 100644 --- a/orchestra/contrib/websites/models.py +++ b/orchestra/contrib/websites/models.py @@ -94,11 +94,13 @@ class Website(models.Model): def get_www_access_log_path(self): context = self.get_settings_context() + context['unique_name'] = self.unique_name path = settings.WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH % context return os.path.normpath(path) def get_www_error_log_path(self): context = self.get_settings_context() + context['unique_name'] = self.unique_name path = settings.WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH % context return os.path.normpath(path) diff --git a/orchestra/contrib/websites/settings.py b/orchestra/contrib/websites/settings.py index b1c6817d..597e8626 100644 --- a/orchestra/contrib/websites/settings.py +++ b/orchestra/contrib/websites/settings.py @@ -5,8 +5,14 @@ from orchestra.settings import Setting from .. import websites +_names = ('id', 'pk', 'home', 'user', 'group', 'site_name', 'protocol') +_log_names = _names + ('unique_name',) + + WEBSITES_UNIQUE_NAME_FORMAT = Setting('WEBSITES_UNIQUE_NAME_FORMAT', - default='%(user)s-%(site_name)s' + default='%(user)s-%(site_name)s', + help_text="Available fromat names: %s" % ', '.join(_names), + validators=[Setting.string_format_validator(_names)], ) @@ -69,12 +75,16 @@ WEBSITES_WEBALIZER_PATH = Setting('WEBSITES_WEBALIZER_PATH', WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH = Setting('WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH', - '/var/log/apache2/virtual/%(unique_name)s.log' + '/var/log/apache2/virtual/%(unique_name)s.log', + help_text="Available fromat names: %s" % ', '.join(_log_names), + validators=[Setting.string_format_validator(_log_names)], ) WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH = Setting('WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH', - '' + '', + help_text="Available fromat names: %s" % ', '.join(_log_names), + validators=[Setting.string_format_validator(_log_names)], ) @@ -93,11 +103,13 @@ WEBSITES_TRAFFIC_IGNORE_HOSTS = Setting('WEBSITES_TRAFFIC_IGNORE_HOSTS', # '') -WEBSITES_SAAS_DIRECTIVES = Setting('WEBSITES_SAAS_DIRECTIVES', { - 'wordpress-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock', '/home/httpd/wordpress-mu/'), - 'drupal-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock','/home/httpd/drupal-mu/'), - 'dokuwiki-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock','/home/httpd/moodle-mu/'), -}) +WEBSITES_SAAS_DIRECTIVES = Setting('WEBSITES_SAAS_DIRECTIVES', + { + 'wordpress-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock', '/home/httpd/wordpress-mu/'), + 'drupal-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock','/home/httpd/drupal-mu/'), + 'dokuwiki-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock','/home/httpd/moodle-mu/'), + }, +) WEBSITES_DEFAULT_SSL_CERT = Setting('WEBSITES_DEFAULT_SSL_CERT', @@ -112,7 +124,8 @@ WEBSITES_DEFAULT_SSL_CA = Setting('WEBSITES_DEFAULT_SSL_CA', '' ) -WEBSITES_VHOST_EXTRA_DIRECTIVES = Setting('WEBSITES_VHOST_EXTRA_DIRECTIVES', (), +WEBSITES_VHOST_EXTRA_DIRECTIVES = Setting('WEBSITES_VHOST_EXTRA_DIRECTIVES', + (), help_text=( "(, ),
" "i.e. ('/cgi-bin/', 'ScriptAlias /cgi-bin/ %(home)s/cgi-bin/')" diff --git a/orchestra/management/commands/setupnginx.py b/orchestra/management/commands/setupnginx.py index 879c3cad..0c2b4876 100644 --- a/orchestra/management/commands/setupnginx.py +++ b/orchestra/management/commands/setupnginx.py @@ -1,10 +1,11 @@ +import textwrap from optparse import make_option from os.path import expanduser from django.conf import settings from django.core.management.base import BaseCommand -from orchestra.utils.paths import get_project_dir, get_site_dir, get_project_name +from orchestra.utils import paths from orchestra.utils.sys import run, check_root, get_default_celeryd_username @@ -32,44 +33,49 @@ class Command(BaseCommand): interactive = options.get('interactive') context = { - 'project_name': get_project_name(), - 'project_dir': get_project_dir(), - 'site_dir': get_site_dir(), + 'project_name': paths.get_project_name(), + 'project_dir': paths.get_project_dir(), + 'site_dir': paths.get_site_dir(), 'static_root': settings.STATIC_ROOT, 'user': options.get('user'), 'group': options.get('group') or options.get('user'), 'home': expanduser("~%s" % options.get('user')), 'processes': int(options.get('processes')),} - nginx_conf = ( - 'server {\n' - ' listen 80;\n' - ' listen [::]:80 ipv6only=on;\n' - ' rewrite ^/$ /admin/;\n' - ' client_max_body_size 500m;\n' - ' location / {\n' - ' uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket;\n' - ' include uwsgi_params;\n' - ' }\n' - ' location /static {\n' - ' alias %(static_root)s;\n' - ' expires 30d;\n' - ' }\n' - '}\n') % context + nginx_conf = textwrap.dedent("""\ + server { + listen 80; + listen [::]:80 ipv6only=on; + rewrite ^/$ /admin/; + client_max_body_size 500m; + location / { + uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket; + include uwsgi_params; + } + location /static { + alias %(static_root)s; + expires 30d; + } + } + """ + ) % context - uwsgi_conf = ( - '[uwsgi]\n' - 'plugins = python\n' - 'chdir = %(site_dir)s\n' - 'module = %(project_name)s.wsgi\n' - 'master = true\n' - 'processes = %(processes)d\n' - 'chmod-socket = 664\n' - 'stats = /run/uwsgi/%%(deb-confnamespace)/%%(deb-confname)/statsocket\n' - 'vacuum = true\n' - 'uid = %(user)s\n' - 'gid = %(group)s\n' - 'env = HOME=%(home)s\n') % context + uwsgi_conf = textwrap.dedent("""\ + [uwsgi] + plugins = python + chdir = %(site_dir)s + module = %(project_name)s.wsgi + master = true + processes = %(processes)d + chmod-socket = 664 + stats = /run/uwsgi/%%(deb-confnamespace)/%%(deb-confname)/statsocket + vacuum = true + uid = %(user)s + gid = %(group)s + env = HOME=%(home)s + touch-reload = %(project_dir)s/wsgi.py + """ + ) % context nginx = { 'file': '/etc/nginx/conf.d/%(project_name)s.conf' % context, @@ -108,20 +114,21 @@ class Command(BaseCommand): current = "\$local_fs \$remote_fs \$network \$syslog" run('sed -i "s/ %s$/ %s \$named/g" /etc/init.d/nginx' % (current, current)) - rotate = ( - '/var/log/nginx/*.log {\n' - ' daily\n' - ' missingok\n' - ' rotate 30\n' - ' compress\n' - ' delaycompress\n' - ' notifempty\n' - ' create 640 root adm\n' - ' sharedscripts\n' - ' postrotate\n' - ' [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`\n' - ' endscript\n' - '}\n') + rotate = textwrap.dedent("""\ + /var/log/nginx/*.log { + daily + missingok + rotate 30 + compress + delaycompress + notifempty + create 640 root adm + sharedscripts + postrotate + [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid` + endscript + }""" + ) run("echo '%s' > /etc/logrotate.d/nginx" % rotate) # Allow nginx to write to uwsgi socket diff --git a/orchestra/settings.py b/orchestra/settings.py index cd6ebea7..85e1db05 100644 --- a/orchestra/settings.py +++ b/orchestra/settings.py @@ -4,6 +4,7 @@ from collections import OrderedDict from django.conf import settings from django.core.exceptions import ValidationError, AppRegistryNotReady +from django.core.validators import validate_email from django.db.models import get_model from django.utils.functional import Promise from django.utils.translation import ugettext_lazy as _ @@ -127,7 +128,8 @@ ORCHESTRA_BASE_DOMAIN = Setting('ORCHESTRA_BASE_DOMAIN', ORCHESTRA_SITE_URL = Setting('ORCHESTRA_SITE_URL', 'https://orchestra.%s' % ORCHESTRA_BASE_DOMAIN, help_text=_("Domain name used when it will not be possible to infere the domain from a request." - "For example in periodic tasks.") + "For example in periodic tasks.
" + "Uses ORCHESTRA_BASE_DOMAIN by default.") ) @@ -137,7 +139,8 @@ ORCHESTRA_SITE_NAME = Setting('ORCHESTRA_SITE_NAME', ORCHESTRA_SITE_VERBOSE_NAME = Setting('ORCHESTRA_SITE_VERBOSE_NAME', - _("%s Hosting Management" % ORCHESTRA_SITE_NAME.capitalize()), + "%s Hosting Management" % ORCHESTRA_SITE_NAME.capitalize(), + help_text="Uses ORCHESTRA_SITE_NAME by default." ) @@ -180,7 +183,9 @@ ORCHESTRA_API_ROOT_VIEW = Setting('ORCHESTRA_API_ROOT_VIEW', ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = Setting('ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL', - 'support@{}'.format(ORCHESTRA_BASE_DOMAIN) + 'support@{}'.format(ORCHESTRA_BASE_DOMAIN), + validators=[validate_email], + help_text="Uses ORCHESTRA_BASE_DOMAIN by default." ) diff --git a/orchestra/templates/admin/orchestra/menu.html b/orchestra/templates/admin/orchestra/menu.html index e1e63f52..d6c1ad59 100644 --- a/orchestra/templates/admin/orchestra/menu.html +++ b/orchestra/templates/admin/orchestra/menu.html @@ -46,7 +46,7 @@ {% endif %}