Random fixes

This commit is contained in:
Marc Aymerich 2015-03-18 21:51:12 +00:00
parent e5e0d3aa96
commit 7711988a63
16 changed files with 86 additions and 59 deletions

View File

@ -206,6 +206,12 @@ Php binaries should have this format: /usr/bin/php5.2-cgi
* better validate options and directives (url locations, filesystem paths, etc..) * better validate options and directives (url locations, filesystem paths, etc..)
* filter php deprecated options out based on version * filter php deprecated options out based on version
* order virtualhost locations /hola / including directive
* make sure that you understand the risks * make sure that you understand the risks
* full support for deactivation of services/accounts
* Display admin.is_active (disabled account/order by)
* show details data on webapp changelist

View File

@ -79,13 +79,7 @@ class Account(auth.AbstractBaseUser):
# Trigger save() on related objects that depend on this account # Trigger save() on related objects that depend on this account
for rel in self._meta.get_all_related_objects(): for rel in self._meta.get_all_related_objects():
source = getattr(rel, 'related_model', rel.model) source = getattr(rel, 'related_model', rel.model)
if not source in services: if source in services and hasattr(source, 'active'):
continue
try:
source._meta.get_field_by_name('is_active')
except models.FieldDoesNotExist:
continue
else:
for obj in getattr(self, rel.get_accessor_name()).all(): for obj in getattr(self, rel.get_accessor_name()).all():
obj.save(update_fields=[]) obj.save(update_fields=[])

View File

@ -46,7 +46,7 @@ class MailmanBackend(ServiceController):
self.append('sed -i "/^%(address_domain)s\s*$/d" %(virtual_alias_domains)s' % context) self.append('sed -i "/^%(address_domain)s\s*$/d" %(virtual_alias_domains)s' % context)
def get_virtual_aliases(self, context): def get_virtual_aliases(self, context):
aliases = [] aliases = ['# %(banner)s' % context]
for address in self.addresses: for address in self.addresses:
context['address'] = address context['address'] = address
aliases.append("%(address_name)s%(address)s@%(domain)s\t%(name)s%(address)s" % context) aliases.append("%(address_name)s%(address)s@%(domain)s\t%(name)s%(address)s" % context)
@ -65,15 +65,13 @@ class MailmanBackend(ServiceController):
# Preserve indentation # Preserve indentation
self.append(textwrap.dedent("""\ self.append(textwrap.dedent("""\
if [[ ! $(grep '\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then if [[ ! $(grep '\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
echo '# %(banner)s\n%(aliases)s echo '%(aliases)s' >> %(virtual_alias)s
' >> %(virtual_alias)s
UPDATED_VIRTUAL_ALIAS=1 UPDATED_VIRTUAL_ALIAS=1
else else
if [[ ! $(grep '^\s*%(address_name)s@%(address_domain)s\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then if [[ ! $(grep '^\s*%(address_name)s@%(address_domain)s\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
sed -i -e '/^.*\s%(name)s\(%(address_regex)s\)\s*$/d' \\ sed -i -e '/^.*\s%(name)s\(%(address_regex)s\)\s*$/d' \\
-e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s -e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s
echo '# %(banner)s\n%(aliases)s echo '%(aliases)s' >> %(virtual_alias)s
' >> %(virtual_alias)s
UPDATED_VIRTUAL_ALIAS=1 UPDATED_VIRTUAL_ALIAS=1
fi fi
fi""") % context fi""") % context
@ -99,15 +97,19 @@ class MailmanBackend(ServiceController):
'%(mailman_root)s/bin/change_pw --listname="%(name)s" --password="%(password)s"' % context '%(mailman_root)s/bin/change_pw --listname="%(name)s" --password="%(password)s"' % context
) )
self.include_virtual_alias_domain(context) self.include_virtual_alias_domain(context)
if mail_list.active:
self.append('chmod 775 %(mailman_root)s/lists/%(name)s' % context)
else:
self.append('chmod 000 %(mailman_root)s/lists/%(name)s' % context)
def delete(self, mail_list): def delete(self, mail_list):
context = self.get_context(mail_list) context = self.get_context(mail_list)
self.exclude_virtual_alias_domain(context) self.exclude_virtual_alias_domain(context)
self.append(textwrap.dedent("""\ self.append(textwrap.dedent("""
sed -i -e '/^.*\s%(name)s\(%(address_regex)s\)\s*$/d' \\ sed -i -e '/^.*\s%(name)s\(%(address_regex)s\)\s*$/d' \\
-e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s""") % context -e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s""") % context
) )
self.append(textwrap.dedent("""\ self.append(textwrap.dedent("""
# Non-existent list archives produce exit code 1 # Non-existent list archives produce exit code 1
exit_code=0 exit_code=0
rmlist -a %(name)s || exit_code=$? rmlist -a %(name)s || exit_code=$?
@ -119,9 +121,12 @@ class MailmanBackend(ServiceController):
def commit(self): def commit(self):
context = self.get_context_files() context = self.get_context_files()
self.append(textwrap.dedent(""" self.append(textwrap.dedent("""
[[ $UPDATED_VIRTUAL_ALIAS == 1 ]] && { postmap %(virtual_alias)s; } if [[ $UPDATED_VIRTUAL_ALIAS == 1 ]]; then
[[ $UPDATED_VIRTUAL_ALIAS_DOMAINS == 1 ]] && { /etc/init.d/postfix reload; } postmap %(virtual_alias)s
""") % context fi
if [[ $UPDATED_VIRTUAL_ALIAS_DOMAINS == 1 ]]; then
/etc/init.d/postfix reload
fi""") % context
) )
def get_context_files(self): def get_context_files(self):

View File

@ -1,4 +1,5 @@
from django.db import models from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.core import services from orchestra.core import services
@ -20,7 +21,10 @@ class List(models.Model):
help_text=_("Administration email address")) help_text=_("Administration email address"))
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
related_name='lists') related_name='lists')
# TODO also admin
# TODO is_active = models.BooleanField(_("active"), default=True,
# help_text=_("Designates whether this account should be treated as active. "
# "Unselect this instead of deleting accounts."))
password = None password = None
class Meta: class Meta:
@ -35,6 +39,10 @@ class List(models.Model):
return "%s@%s" % (self.address_name, self.address_domain) return "%s@%s" % (self.address_name, self.address_domain)
return '' return ''
@cached_property
def active(self):
return self.is_active and self.account.is_active
def get_address_name(self): def get_address_name(self):
return self.address_name or self.name return self.address_name or self.name

View File

@ -21,16 +21,13 @@ def compute_resource_usage(data):
slot = (data.created_at-ini).total_seconds() slot = (data.created_at-ini).total_seconds()
result += data.value * slot/total result += data.value * slot/total
ini = data.created_at ini = data.created_at
elif resource.period == resource.MONTHLY_SUM: elif resource.period in (resource.MONTHLY_SUM, resource.LAST):
# FIXME Aggregation of 0s returns None! django bug? # FIXME Aggregation of 0s returns None! django bug?
# value = dataset.aggregate(models.Sum('value'))['value__sum'] # value = dataset.aggregate(models.Sum('value'))['value__sum']
values = dataset.values_list('value', flat=True) values = dataset.values_list('value', flat=True)
if values: if values:
has_result = True has_result = True
result += sum(values) result += sum(values)
elif resource.period == resource.LAST:
result += dataset.value
has_result = True
else: else:
raise NotImplementedError("%s support not implemented" % data.period) raise NotImplementedError("%s support not implemented" % data.period)
return float(result)/resource.get_scale() if has_result else None return float(result)/resource.get_scale() if has_result else None

View File

@ -239,8 +239,11 @@ class ResourceData(models.Model):
) )
) )
elif resource.period == resource.LAST: elif resource.period == resource.LAST:
# Get last monitoring data per object_id
try: try:
datasets.append(dataset.latest()) datasets.append(
dataset.order_by('object_id', '-id').distinct('object_id')
)
except MonitorData.DoesNotExist: except MonitorData.DoesNotExist:
continue continue
else: else:

View File

@ -30,7 +30,7 @@ def monitor(resource_id, ids=None, async=True):
op = Operation.create(backend, obj, Operation.MONITOR) op = Operation.create(backend, obj, Operation.MONITOR)
operations.append(op) operations.append(op)
monitorings.append(op) monitorings.append(op)
# TODO async=TRue only when running with celery # TODO async=True only when running with celery
Operation.execute(monitorings, async=async) Operation.execute(monitorings, async=async)
kwargs = {'id__in': ids} if ids else {} kwargs = {'id__in': ids} if ids else {}

View File

@ -24,7 +24,8 @@ class WebApp(models.Model):
choices=AppType.get_plugin_choices()) choices=AppType.get_plugin_choices())
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
related_name='webapps') related_name='webapps')
data = JSONField(_("data"), help_text=_("Extra information dependent of each service.")) data = JSONField(_("data"), blank=True,
help_text=_("Extra information dependent of each service."))
class Meta: class Meta:
unique_together = ('name', 'account') unique_together = ('name', 'account')
@ -122,4 +123,7 @@ def type_save(sender, *args, **kwargs):
@receiver(pre_delete, sender=WebApp, dispatch_uid='webapps.type.delete') @receiver(pre_delete, sender=WebApp, dispatch_uid='webapps.type.delete')
def type_delete(sender, *args, **kwargs): def type_delete(sender, *args, **kwargs):
instance = kwargs['instance'] instance = kwargs['instance']
try:
instance.type_instance.delete() instance.type_instance.delete()
except KeyError:
pass

View File

@ -336,8 +336,8 @@ class PHPUploadMaxFileSize(AppOption):
group = AppOption.PHP group = AppOption.PHP
class PHPPostMaxSize(AppOption): class PHPZendExtension(AppOption):
name = 'post_max_size' name = 'zend_extension'
verbose_name = _("zend_extension") verbose_name = _("Zend extension")
regex = r'^[^ ]+$' regex = r'^[^ ]+$'
group = AppOption.PHP group = AppOption.PHP

View File

@ -142,7 +142,7 @@ WEBAPPS_ENABLED_OPTIONS = getattr(settings, 'WEBAPPS_ENABLED_OPTIONS', (
'orchestra.apps.webapps.options.PHPSuhosinSimulation', 'orchestra.apps.webapps.options.PHPSuhosinSimulation',
'orchestra.apps.webapps.options.PHPSuhosinExecutorIncludeWhitelist', 'orchestra.apps.webapps.options.PHPSuhosinExecutorIncludeWhitelist',
'orchestra.apps.webapps.options.PHPUploadMaxFileSize', 'orchestra.apps.webapps.options.PHPUploadMaxFileSize',
'orchestra.apps.webapps.options.PHPPostMaxSize', 'orchestra.apps.webapps.options.PHPZendExtension',
)) ))

View File

@ -13,11 +13,11 @@ from orchestra.forms.widgets import DynamicHelpTextSelect
from . import settings from . import settings
from .directives import SiteDirective from .directives import SiteDirective
from .forms import WebsiteAdminForm from .forms import WebsiteAdminForm
from .models import Content, Website, Directive from .models import Content, Website, WebsiteDirective
class DirectiveInline(admin.TabularInline): class WebsiteDirectiveInline(admin.TabularInline):
model = Directive model = WebsiteDirective
extra = 1 extra = 1
DIRECTIVES_HELP_TEXT = { DIRECTIVES_HELP_TEXT = {
@ -37,7 +37,7 @@ class DirectiveInline(admin.TabularInline):
kwargs['widget'] = DynamicHelpTextSelect( kwargs['widget'] = DynamicHelpTextSelect(
'this.id.replace("name", "value")', self.DIRECTIVES_HELP_TEXT 'this.id.replace("name", "value")', self.DIRECTIVES_HELP_TEXT
) )
return super(DirectiveInline, self).formfield_for_dbfield(db_field, **kwargs) return super(WebsiteDirectiveInline, self).formfield_for_dbfield(db_field, **kwargs)
class ContentInline(AccountAdminMixin, admin.TabularInline): class ContentInline(AccountAdminMixin, admin.TabularInline):
@ -61,7 +61,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'display_domains', 'display_webapps', 'account_link') list_display = ('name', 'display_domains', 'display_webapps', 'account_link')
list_filter = ('protocol', 'is_active',) list_filter = ('protocol', 'is_active',)
change_readonly_fields = ('name',) change_readonly_fields = ('name',)
inlines = [ContentInline, DirectiveInline] inlines = [ContentInline, WebsiteDirectiveInline]
filter_horizontal = ['domains'] filter_horizontal = ['domains']
fieldsets = ( fieldsets = (
(None, { (None, {

View File

@ -245,14 +245,14 @@ class Apache2Backend(ServiceController):
option = site.get_directives().get('user_group') option = site.get_directives().get('user_group')
if option: if option:
return option[0] return option[0]
return site.account.username return site.get_username()
def get_groupname(self, site): def get_groupname(self, site):
option = site.get_directives().get('user_group') option = site.get_directives().get('user_group')
if option and ' ' in option: if option and ' ' in option:
user, group = option.split() user, group = option.split()
return group return group
return site.account.username return site.get_groupname()
def get_context(self, site): def get_context(self, site):
base_apache_conf = settings.WEBSITES_BASE_APACHE_CONF base_apache_conf = settings.WEBSITES_BASE_APACHE_CONF

View File

@ -21,7 +21,9 @@ class WebalizerBackend(ServiceController):
echo 'Webstats are coming soon' > %(webalizer_path)s/index.html echo 'Webstats are coming soon' > %(webalizer_path)s/index.html
fi fi
echo '%(webalizer_conf)s' > %(webalizer_conf_path)s echo '%(webalizer_conf)s' > %(webalizer_conf_path)s
chown %(user)s:www-data %(webalizer_path)s""") % context chown %(user)s:www-data %(webalizer_path)s
chmod g+xr %(webalizer_path)s
""") % context
) )
def delete(self, content): def delete(self, content):

View File

@ -84,13 +84,13 @@ class UserGroup(SiteDirective):
group = SiteDirective.HTTPD group = SiteDirective.HTTPD
def validate(self, directive): def validate(self, directive):
super(UserGroupDirective, self).validate(directive) super(UserGroup, self).validate(directive)
options = directive.split() options = directive.value.split()
syetmusers = [options[0]] systemusers = [options[0]]
if len(options) > 1: if len(options) > 1:
systemusers.append(options[1]) systemusers.append(options[1])
# TODO not sure about this dependency maybe make it part of pangea only # TODO not sure about this dependency maybe make it part of pangea only
from orchestra.apps.users.models import SystemUser from orchestra.apps.systemusers.models import SystemUser
errors = [] errors = []
for user in systemusers: for user in systemusers:
if not SystemUser.objects.filter(username=user).exists(): if not SystemUser.objects.filter(username=user).exists():

View File

@ -46,12 +46,19 @@ class Website(models.Model):
@property @property
def unique_name(self): def unique_name(self):
return settings.WEBSITES_UNIQUE_NAME_FORMAT % { context = self.get_settings_context()
return settings.WEBSITES_UNIQUE_NAME_FORMAT % context
def get_settings_context(self):
""" format settings strings """
return {
'id': self.id, 'id': self.id,
'pk': self.pk, 'pk': self.pk,
'account': self.account.username, 'home': self.get_user().get_home(),
'user': self.get_username(),
'group': self.get_groupname(),
'site_name': self.name,
'protocol': self.protocol, 'protocol': self.protocol,
'name': self.name,
} }
def get_protocol(self): def get_protocol(self):
@ -74,27 +81,27 @@ class Website(models.Model):
if domain: if domain:
return '%s://%s' % (self.get_protocol(), domain) return '%s://%s' % (self.get_protocol(), domain)
def get_www_log_context(self): def get_user(self):
return { return self.account.main_systemuser
'home': self.account.main_systemuser.get_home(),
'account': self.account.username, def get_username(self):
'user': self.account.username, return self.get_user().username
'site_name': self.name,
'unique_name': self.unique_name def get_groupname(self):
} return self.get_username()
def get_www_access_log_path(self): def get_www_access_log_path(self):
context = self.get_www_log_context() context = self.get_settings_context()
path = settings.WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH % context path = settings.WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH % context
return os.path.normpath(path.replace('//', '/')) return os.path.normpath(path.replace('//', '/'))
def get_www_error_log_path(self): def get_www_error_log_path(self):
context = self.get_www_log_context() context = self.get_settings_context()
path = settings.WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH % context path = settings.WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH % context
return os.path.normpath(path.replace('//', '/')) return os.path.normpath(path.replace('//', '/'))
class Directive(models.Model): class WebsiteDirective(models.Model):
website = models.ForeignKey(Website, verbose_name=_("web site"), website = models.ForeignKey(Website, verbose_name=_("web site"),
related_name='directives') related_name='directives')
name = models.CharField(_("name"), max_length=128, name = models.CharField(_("name"), max_length=128,

View File

@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _
WEBSITES_UNIQUE_NAME_FORMAT = getattr(settings, 'WEBSITES_UNIQUE_NAME_FORMAT', WEBSITES_UNIQUE_NAME_FORMAT = getattr(settings, 'WEBSITES_UNIQUE_NAME_FORMAT',
'%(account)s-%(name)s') '%(user)s-%(site_name)s')
# TODO 'http', 'https', 'https-only', 'http and https' and rename to PROTOCOL # TODO 'http', 'https', 'https-only', 'http and https' and rename to PROTOCOL
@ -72,3 +72,4 @@ WEBSITES_TRAFFIC_IGNORE_HOSTS = getattr(settings, 'WEBSITES_TRAFFIC_IGNORE_HOSTS
#WEBSITES_DEFAULT_SSl_KEY = getattr(settings, 'WEBSITES_DEFAULT_SSl_KEY', #WEBSITES_DEFAULT_SSl_KEY = getattr(settings, 'WEBSITES_DEFAULT_SSl_KEY',
# '') # '')