Improved db indexes

This commit is contained in:
Marc Aymerich 2016-02-19 10:11:28 +00:00
parent 4301d76011
commit ebd5ff03ce
23 changed files with 276 additions and 34 deletions

View file

@ -461,6 +461,6 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# TODO save serialized versions ob backendoperation.instance in order to allow backend reexecution of deleted objects # TODO save serialized versions ob backendoperation.instance in order to allow backend reexecution of deleted objects
# INDEXES for most used queries: account FK

View file

@ -112,7 +112,7 @@ def admin_link(*args, **kwargs):
return '---' return '---'
display = kwargs.get('display') display = kwargs.get('display')
if display: if display:
display = getattr(obj, display, None) display = getattr(obj, display, display)
else: else:
display = obj display = obj
try: try:
@ -123,7 +123,8 @@ def admin_link(*args, **kwargs):
extra = '' extra = ''
if kwargs['popup']: if kwargs['popup']:
extra = 'onclick="return showAddAnotherPopup(this);"' extra = 'onclick="return showAddAnotherPopup(this);"'
return mark_safe('<a href="%s" %s>%s</a>' % (url, extra, display)) title = "Change %s" % obj._meta.verbose_name
return mark_safe('<a href="%s" title="%s" %s>%s</a>' % (url, title, extra, display))
@admin_field @admin_field

View file

@ -16,7 +16,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import SendEmail from orchestra.admin.actions import SendEmail
from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, change_url from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query
from orchestra.core import services, accounts from orchestra.core import services, accounts
from orchestra.forms import UserChangeForm from orchestra.forms import UserChangeForm
@ -189,8 +189,7 @@ class AccountAdminMixin(object):
def account_link(self, instance): def account_link(self, instance):
account = instance.account if instance.pk else self.account account = instance.account if instance.pk else self.account
url = change_url(account) return admin_link()(account)
return '<a href="%s">%s</a>' % (url, account)
account_link.short_description = _("account") account_link.short_description = _("account")
account_link.allow_tags = True account_link.allow_tags = True
account_link.admin_order_field = 'account__username' account_link.admin_order_field = 'account__username'

View file

@ -85,7 +85,9 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
for website in websites: for website in websites:
site_link = get_on_site_link(website.get_absolute_url()) site_link = get_on_site_link(website.get_absolute_url())
admin_url = change_url(website) admin_url = change_url(website)
link = '<a href="%s">%s %s</a>' % (admin_url, website.name, site_link) title = _("Edit website")
link = '<a href="%s" title="%s">%s %s</a>' % (
admin_url, title, website.name, site_link)
links.append(link) links.append(link)
return '<br>'.join(links) return '<br>'.join(links)
site_link = get_on_site_link('http://%s' % domain.name) site_link = get_on_site_link('http://%s' % domain.name)
@ -127,7 +129,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
structured_name=Concat('top__name', 'name') structured_name=Concat('top__name', 'name')
).order_by('-structured_id', 'structured_name') ).order_by('-structured_id', 'structured_name')
if apps.isinstalled('orchestra.contrib.websites'): if apps.isinstalled('orchestra.contrib.websites'):
qs = qs.prefetch_related('websites') qs = qs.prefetch_related('websites__domains')
return qs return qs
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import orchestra.contrib.domains.validators
class Migration(migrations.Migration):
dependencies = [
('domains', '0004_auto_20150720_1121'),
]
operations = [
migrations.AlterField(
model_name='domain',
name='name',
field=models.CharField(max_length=256, validators=[orchestra.contrib.domains.validators.validate_domain_name, orchestra.contrib.domains.validators.validate_allowed_domain], db_index=True, verbose_name='name', unique=True, help_text='Domain or subdomain name.'),
),
migrations.AlterField(
model_name='domain',
name='top',
field=models.ForeignKey(editable=False, verbose_name='top domain', related_name='subdomain_set', to='domains.Domain', null=True),
),
migrations.AlterField(
model_name='record',
name='type',
field=models.CharField(max_length=32, verbose_name='type', choices=[('MX', 'MX'), ('NS', 'NS'), ('CNAME', 'CNAME'), ('A', 'A (IPv4 address)'), ('AAAA', 'AAAA (IPv6 address)'), ('SRV', 'SRV'), ('TXT', 'TXT'), ('SPF', 'SPF'), ('SOA', 'SOA')]),
),
]

View file

@ -24,7 +24,7 @@ class DomainQuerySet(models.QuerySet):
class Domain(models.Model): class Domain(models.Model):
name = models.CharField(_("name"), max_length=256, unique=True, name = models.CharField(_("name"), max_length=256, unique=True, db_index=True,
help_text=_("Domain or subdomain name."), help_text=_("Domain or subdomain name."),
validators=[ validators=[
validators.validate_domain_name, validators.validate_domain_name,

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.core.validators
class Migration(migrations.Migration):
dependencies = [
('mailboxes', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='mailbox',
name='filtering',
field=models.CharField(max_length=16, choices=[('CUSTOM', 'Custom filtering'), ('DISABLE', 'Disable'), ('REDIRECT', 'Archive spam (Score&ge;8)'), ('REDIRECT5', 'Archive spam (Score&ge;5)'), ('REJECT', 'Reject spam (Score&ge;8)'), ('REJECT5', 'Reject spam (Score&ge;5)')], default='REDIRECT'),
),
migrations.AlterField(
model_name='mailbox',
name='name',
field=models.CharField(max_length=64, db_index=True, unique=True, help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid mailbox name.')], verbose_name='name'),
),
]

View file

@ -14,7 +14,7 @@ from . import validators, settings
class Mailbox(models.Model): class Mailbox(models.Model):
CUSTOM = 'CUSTOM' CUSTOM = 'CUSTOM'
name = models.CharField(_("name"), max_length=64, unique=True, name = models.CharField(_("name"), max_length=64, unique=True, db_index=True,
help_text=_("Required. %s characters or fewer. Letters, digits and ./-/_ only.") % help_text=_("Required. %s characters or fewer. Letters, digits and ./-/_ only.") %
settings.MAILBOXES_NAME_MAX_LENGTH, settings.MAILBOXES_NAME_MAX_LENGTH,
validators=[ validators=[

View file

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mailer', '0004_auto_20150805_1328'),
]
operations = [
migrations.AlterField(
model_name='message',
name='last_try',
field=models.DateTimeField(null=True, db_index=True, verbose_name='last try'),
),
migrations.AlterField(
model_name='message',
name='priority',
field=models.PositiveIntegerField(default=2, choices=[(0, 'Critical (not queued)'), (1, 'High'), (2, 'Normal'), (3, 'Low')], db_index=True, verbose_name='Priority'),
),
migrations.AlterField(
model_name='message',
name='retries',
field=models.PositiveIntegerField(default=0, db_index=True, verbose_name='retries'),
),
migrations.AlterField(
model_name='message',
name='state',
field=models.CharField(default='QUEUED', choices=[('QUEUED', 'Queued'), ('SENT', 'Sent'), ('DEFERRED', 'Deferred'), ('FAILED', 'Failed')], db_index=True, max_length=16, verbose_name='State'),
),
]

View file

@ -27,15 +27,17 @@ class Message(models.Model):
(LOW, _("Low")), (LOW, _("Low")),
) )
state = models.CharField(_("State"), max_length=16, choices=STATES, default=QUEUED) state = models.CharField(_("State"), max_length=16, choices=STATES, default=QUEUED,
priority = models.PositiveIntegerField(_("Priority"), choices=PRIORITIES, default=NORMAL) db_index=True)
priority = models.PositiveIntegerField(_("Priority"), choices=PRIORITIES, default=NORMAL,
db_index=True)
to_address = models.CharField(max_length=256) to_address = models.CharField(max_length=256)
from_address = models.CharField(max_length=256) from_address = models.CharField(max_length=256)
subject = models.TextField(_("subject")) subject = models.TextField(_("subject"))
content = models.TextField(_("content")) content = models.TextField(_("content"))
created_at = models.DateTimeField(_("created at"), auto_now_add=True) created_at = models.DateTimeField(_("created at"), auto_now_add=True)
retries = models.PositiveIntegerField(_("retries"), default=0) retries = models.PositiveIntegerField(_("retries"), default=0, db_index=True)
last_try = models.DateTimeField(_("last try"), null=True) last_try = models.DateTimeField(_("last try"), null=True, db_index=True)
def __str__(self): def __str__(self):
return '%s to %s' % (self.subject, self.to_address) return '%s to %s' % (self.subject, self.to_address)

View file

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import orchestra.core.validators
import orchestra.models.fields
class Migration(migrations.Migration):
dependencies = [
('orchestration', '0005_auto_20150709_1016'),
]
operations = [
migrations.AlterField(
model_name='route',
name='backend',
field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic'), ('Apache2Backend', '[S] Apache 2'), ('BSCWBackend', '[S] BSCW SaaS'), ('Bind9MasterDomainBackend', '[S] Bind9 master domain'), ('Bind9SlaveDomainBackend', '[S] Bind9 slave domain'), ('DokuWikiMuBackend', '[S] DokuWiki multisite'), ('DrupalMuBackend', '[S] Drupal multisite'), ('GitLabSaaSBackend', '[S] GitLab SaaS'), ('AutoresponseBackend', '[S] Mail autoresponse'), ('MailScannerSpamRuleBackend', '[S] MailScanner ruleset'), ('MailmanBackend', '[S] Mailman'), ('MailmanVirtualDomainBackend', '[S] Mailman virtdomain-only'), ('MoodleBackend', '[S] Moodle'), ('MoodleWWWRootBackend', '[S] Moodle WWWRoot (required)'), ('MoodleMuBackend', '[S] Moodle multisite'), ('MySQLBackend', '[S] MySQL database'), ('MySQLUserBackend', '[S] MySQL user'), ('PHPBackend', '[S] PHP FPM/FCGID'), ('PangeaProxmoxOVZ', '[S] PangeaProxmoxOVZ'), ('PostfixAddressBackend', '[S] Postfix address'), ('PostfixAddressVirtualDomainBackend', '[S] Postfix address virtdomain-only'), ('PostfixRecipientAccessBackend', '[S] Postfix recipient access'), ('ProxmoxOVZ', '[S] ProxmoxOVZ'), ('uWSGIPythonBackend', '[S] Python uWSGI'), ('StaticBackend', '[S] Static'), ('SymbolicLinkBackend', '[S] Symbolic link webapp'), ('SyncBind9MasterDomainBackend', '[S] Sync Bind9 master domain'), ('SyncBind9SlaveDomainBackend', '[S] Sync Bind9 slave domain'), ('UNIXUserMaildirBackend', '[S] UNIX maildir user'), ('UNIXUserBackend', '[S] UNIX user'), ('WebalizerAppBackend', '[S] Webalizer App'), ('WebalizerBackend', '[S] Webalizer Content'), ('WordPressURLBackend', '[S] WordPress URL'), ('WordPressBackend', '[S] Wordpress'), ('WordpressMuBackend', '[S] Wordpress multisite'), ('OwnCloudBackend', '[S] ownCloud SaaS'), ('PhpListSaaSBackend', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
),
migrations.AlterField(
model_name='server',
name='address',
field=orchestra.models.fields.NullableCharField(help_text='Optional IP address or domain name. If blank, name field will be used for address resolution.<br>If the IP address never changes you can set this field and save DNS requests.', verbose_name='address', validators=[orchestra.core.validators.OrValidator(orchestra.core.validators.validate_ip_address, orchestra.core.validators.validate_hostname)], blank=True, max_length=256, unique=True, null=True),
),
migrations.AlterField(
model_name='server',
name='name',
field=models.CharField(help_text='Verbose name or hostname of this server.', max_length=256, verbose_name='name', unique=True),
),
migrations.AlterIndexTogether(
name='backendoperation',
index_together=set([('content_type', 'object_id')]),
),
]

View file

@ -148,6 +148,9 @@ class BackendOperation(models.Model):
class Meta: class Meta:
verbose_name = _("Operation") verbose_name = _("Operation")
verbose_name_plural = _("Operations") verbose_name_plural = _("Operations")
index_together = (
('content_type', 'object_id'),
)
def __str__(self): def __str__(self):
return '%s.%s(%s)' % (self.backend, self.action, self.instance or self.instance_repr) return '%s.%s(%s)' % (self.backend, self.action, self.instance or self.instance_repr)

View file

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('orders', '0004_auto_20150729_0945'),
]
operations = [
migrations.AlterField(
model_name='order',
name='billed_metric',
field=models.DecimalField(decimal_places=2, null=True, verbose_name='billed metric', blank=True, max_digits=16),
),
migrations.AlterField(
model_name='order',
name='content_object_repr',
field=models.CharField(max_length=256, verbose_name='content object representation', help_text='Used for searches.', editable=False),
),
migrations.AlterIndexTogether(
name='order',
index_together=set([('content_type', 'object_id')]),
),
]

View file

@ -172,6 +172,9 @@ class Order(models.Model):
class Meta: class Meta:
get_latest_by = 'id' get_latest_by = 'id'
index_together = (
('content_type', 'object_id'),
)
def __str__(self): def __str__(self):
return str(self.service) return str(self.service)

View file

@ -134,6 +134,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
list_display = ('id', 'file_url', 'display_transactions', 'created_at') list_display = ('id', 'file_url', 'display_transactions', 'created_at')
fields = ('data', 'file_url', 'created_at') fields = ('data', 'file_url', 'created_at')
readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at') readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at')
list_prefetch_related = ('transactions',)
inlines = [TransactionInline] inlines = [TransactionInline]
change_view_actions = ( change_view_actions = (
actions.mark_process_as_executed, actions.abort, actions.commit, actions.report actions.mark_process_as_executed, actions.abort, actions.commit, actions.report
@ -150,8 +151,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
ids = [] ids = []
lines = [] lines = []
counter = 0 counter = 0
# Because of values_list this query doesn't benefit from prefetch_related for trans in process.transactions.all():
for trans in process.transactions.only('id', 'state'):
color = STATE_COLORS.get(trans.state, 'black') color = STATE_COLORS.get(trans.state, 'black')
state = trans.get_state_display() state = trans.get_state_display()
ids.append('<span style="color:%s" title="%s">%i</span>' % (color, state, trans.id)) ids.append('<span style="color:%s" title="%s">%i</span>' % (color, state, trans.id))
@ -163,7 +163,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
lines.append(','.join(ids)) lines.append(','.join(ids))
transactions = '<br'.join(lines) transactions = '<br'.join(lines)
url = reverse('admin:payments_transaction_changelist') url = reverse('admin:payments_transaction_changelist')
url += '?processes=%i' % process.id url += '?process_id=%i' % process.id
return '<a href="%s">%s</a>' % (url, transactions) return '<a href="%s">%s</a>' % (url, transactions)
display_transactions.short_description = _("Transactions") display_transactions.short_description = _("Transactions")
display_transactions.allow_tags = True display_transactions.allow_tags = True
@ -172,7 +172,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
return False return False
def get_change_view_actions(self, obj=None): def get_change_view_actions(self, obj=None):
actions = super(TransactionProcessAdmin, self).get_change_view_actions() actions = super().get_change_view_actions()
exclude = [] exclude = []
if obj: if obj:
if obj.state == TransactionProcess.EXECUTED: if obj.state == TransactionProcess.EXECUTED:
@ -186,13 +186,11 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
def delete_view(self, request, object_id, extra_context=None): def delete_view(self, request, object_id, extra_context=None):
queryset = self.model.objects.filter(id=object_id) queryset = self.model.objects.filter(id=object_id)
related_transactions = helpers.pre_delete_processes(self, request, queryset) related_transactions = helpers.pre_delete_processes(self, request, queryset)
response = super(TransactionProcessAdmin, self).delete_view( response = super().delete_view(request, object_id, extra_context)
request, object_id, extra_context)
if isinstance(response, HttpResponseRedirect): if isinstance(response, HttpResponseRedirect):
helpers.post_delete_processes(self, request, related_transactions) helpers.post_delete_processes(self, request, related_transactions)
return response return response
admin.site.register(PaymentSource, PaymentSourceAdmin) admin.site.register(PaymentSource, PaymentSourceAdmin)
admin.site.register(Transaction, TransactionAdmin) admin.site.register(Transaction, TransactionAdmin)
admin.site.register(TransactionProcess, TransactionProcessAdmin) admin.site.register(TransactionProcess, TransactionProcessAdmin)

View file

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import orchestra.models.fields
class Migration(migrations.Migration):
dependencies = [
('resources', '0009_auto_20150804_1450'),
]
operations = [
migrations.AlterField(
model_name='monitordata',
name='monitor',
field=models.CharField(db_index=True, choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic')], verbose_name='monitor', max_length=256),
),
migrations.AlterField(
model_name='monitordata',
name='object_id',
field=models.PositiveIntegerField(verbose_name='object id'),
),
migrations.AlterField(
model_name='resource',
name='disable_trigger',
field=models.BooleanField(help_text='Disables monitors exeeded and recovery triggers', verbose_name='disable trigger', default=True),
),
migrations.AlterField(
model_name='resource',
name='monitors',
field=orchestra.models.fields.MultiSelectField(help_text='Monitor backends used for monitoring this resource.', blank=True, verbose_name='monitors', max_length=256, choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic')]),
),
migrations.AlterField(
model_name='resourcedata',
name='object_id',
field=models.PositiveIntegerField(verbose_name='object id'),
),
migrations.AlterIndexTogether(
name='monitordata',
index_together=set([('content_type', 'object_id')]),
),
migrations.AlterIndexTogether(
name='resourcedata',
index_together=set([('content_type', 'object_id')]),
),
]

View file

@ -179,8 +179,8 @@ class ResourceDataQuerySet(models.QuerySet):
class ResourceData(models.Model): class ResourceData(models.Model):
""" Stores computed resource usage and allocation """ """ Stores computed resource usage and allocation """
resource = models.ForeignKey(Resource, related_name='dataset', verbose_name=_("resource")) resource = models.ForeignKey(Resource, related_name='dataset', verbose_name=_("resource"))
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"), db_index=True) content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
object_id = models.PositiveIntegerField(_("object id"), db_index=True) object_id = models.PositiveIntegerField(_("object id"))
used = models.DecimalField(_("used"), max_digits=16, decimal_places=3, null=True, used = models.DecimalField(_("used"), max_digits=16, decimal_places=3, null=True,
editable=False) editable=False)
updated_at = models.DateTimeField(_("updated"), null=True, editable=False) updated_at = models.DateTimeField(_("updated"), null=True, editable=False)
@ -194,6 +194,9 @@ class ResourceData(models.Model):
class Meta: class Meta:
unique_together = ('resource', 'content_type', 'object_id') unique_together = ('resource', 'content_type', 'object_id')
verbose_name_plural = _("resource data") verbose_name_plural = _("resource data")
index_together = (
('content_type', 'object_id'),
)
def __str__(self): def __str__(self):
return "%s: %s" % (self.resource, self.content_object) return "%s: %s" % (self.resource, self.content_object)
@ -264,8 +267,8 @@ class MonitorData(models.Model):
""" Stores monitored data """ """ Stores monitored data """
monitor = models.CharField(_("monitor"), max_length=256, db_index=True, monitor = models.CharField(_("monitor"), max_length=256, db_index=True,
choices=ServiceMonitor.get_choices()) choices=ServiceMonitor.get_choices())
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"), db_index=True) content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
object_id = models.PositiveIntegerField(_("object id"), db_index=True) object_id = models.PositiveIntegerField(_("object id"))
created_at = models.DateTimeField(_("created"), default=timezone.now, db_index=True) created_at = models.DateTimeField(_("created"), default=timezone.now, db_index=True)
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2) value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
state = models.DecimalField(_("state"), max_digits=16, decimal_places=2, null=True, state = models.DecimalField(_("state"), max_digits=16, decimal_places=2, null=True,
@ -279,6 +282,9 @@ class MonitorData(models.Model):
class Meta: class Meta:
get_latest_by = 'id' get_latest_by = 'id'
verbose_name_plural = _("monitor data") verbose_name_plural = _("monitor data")
index_together = (
('content_type', 'object_id'),
)
def __str__(self): def __str__(self):
return str(self.monitor) return str(self.monitor)

View file

@ -5,7 +5,7 @@ from django.utils.encoding import force_text
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import change_url, get_modeladmin from orchestra.admin.utils import admin_link, get_modeladmin
from orchestra.contrib.accounts.actions import list_accounts from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin from orchestra.contrib.accounts.admin import AccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect from orchestra.forms.widgets import DynamicHelpTextSelect
@ -72,9 +72,8 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
site_url = content.get_absolute_url() site_url = content.get_absolute_url()
site_link = get_on_site_link(site_url) site_link = get_on_site_link(site_url)
website = content.website website = content.website
admin_url = change_url(website) name = "%s on %s %s" % (website.name, content.path, site_link)
name = "%s on %s" % (website.name, content.path) link = admin_link(display=name)(website)
link = '<a href="%s">%s %s</a>' % (admin_url, name, site_link)
websites.append(link) websites.append(link)
if not websites: if not websites:
add_url = reverse('admin:websites_website_add') add_url = reverse('admin:websites_website_add')

View file

@ -87,7 +87,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
for content in website.content_set.all(): for content in website.content_set.all():
site_link = get_on_site_link(content.get_absolute_url()) site_link = get_on_site_link(content.get_absolute_url())
webapp = content.webapp webapp = content.webapp
detail = webapp.get_type_display() detail = _("Edit Webapp") + ' ' + webapp.get_type_display()
try: try:
detail += ' ' + webapp.type_instance.get_detail() detail += ' ' + webapp.type_instance.get_detail()
except KeyError: except KeyError:

View file

@ -55,15 +55,18 @@ class Apache2Backend(ServiceController):
return '\n'.join([conf for location, conf in extra_conf]) return '\n'.join([conf for location, conf in extra_conf])
def render_virtual_host(self, site, context, ssl=False): def render_virtual_host(self, site, context, ssl=False):
context['port'] = self.HTTPS_PORT if ssl else self.HTTP_PORT context.update({
context['vhost_set_fcgid'] = False 'port': self.HTTPS_PORT if ssl else self.HTTP_PORT,
'vhost_set_fcgid': False,
'server_alias_lines': ' \\\n '.join(context['server_alias'])
})
context['extra_conf'] = self.get_extra_conf(site, context, ssl) context['extra_conf'] = self.get_extra_conf(site, context, ssl)
return Template(textwrap.dedent("""\ 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] IncludeOptional /etc/apache2/site[s]-override/{{ site_unique_name }}.con[f]
ServerName {{ server_name }}\ ServerName {{ server_name }}\
{% if server_alias %} {% if server_alias %}
ServerAlias {{ server_alias|join:' ' }}{% endif %}\ ServerAlias {{ server_alias_lines }}{% endif %}\
{% if access_log %} {% if access_log %}
CustomLog {{ access_log }} common{% endif %}\ CustomLog {{ access_log }} common{% endif %}\
{% if error_log %} {% if error_log %}

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('websites', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='website',
name='domains',
field=models.ManyToManyField(to='domains.Domain', related_name='websites', verbose_name='domains', blank=True),
),
migrations.AlterField(
model_name='websitedirective',
name='name',
field=models.CharField(choices=[(None, '-------'), ('HTTPD', [('redirect', 'Redirection'), ('proxy', 'Proxy'), ('error-document', 'ErrorDocumentRoot')]), ('SaaS', [('wordpress-saas', 'WordPress SaaS'), ('dokuwiki-saas', 'DokuWiki SaaS'), ('drupal-saas', 'Drupdal SaaS'), ('moodle-saas', 'Moodle SaaS')]), ('ModSecurity', [('sec-rule-remove', 'SecRuleRemoveById'), ('sec-engine', 'SecRuleEngine Off')]), ('SSL', [('ssl-ca', 'SSL CA'), ('ssl-cert', 'SSL cert'), ('ssl-key', 'SSL key')])], db_index=True, verbose_name='name', max_length=128),
),
]

View file

@ -115,7 +115,7 @@ class Website(models.Model):
class WebsiteDirective(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, db_index=True,
choices=SiteDirective.get_choices()) choices=SiteDirective.get_choices())
value = models.CharField(_("value"), max_length=256) value = models.CharField(_("value"), max_length=256)

View file

@ -5,6 +5,7 @@ from ipaddress import ip_address
import phonenumbers import phonenumbers
from django.core import validators from django.core import validators
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from ..utils.python import import_class from ..utils.python import import_class
@ -37,6 +38,7 @@ def all_valid(*args):
raise ValidationError(errors) raise ValidationError(errors)
@deconstructible
class OrValidator(object): class OrValidator(object):
""" """
Run validators with an OR logic Run validators with an OR logic