Improved performance of webapps and websites change lists

This commit is contained in:
Marc Aymerich 2016-02-17 12:32:30 +00:00
parent 50ff252be7
commit 4301d76011
11 changed files with 48 additions and 24 deletions

View file

@ -11,6 +11,7 @@ Orchestra is a Django-based framework for building web hosting control panels.
Motivation Motivation
---------- ----------
There are a lot of widely used open source hosting control panels, however, none of them seems apropiate when you already have an existing service infrastructure or simply you want your services to run on a particular architecture. There are a lot of widely used open source hosting control panels, however, none of them seems apropiate when you already have an existing service infrastructure or simply you want your services to run on a particular architecture.
The goal of this project is to provide the tools for easily build a fully featured control panel that is not tied to any particular service architecture. The goal of this project is to provide the tools for easily build a fully featured control panel that is not tied to any particular service architecture.
@ -33,9 +34,9 @@ Overview
Fast Deployment Setup Fast Deployment Setup
--------------------- ---------------------
This deployment is **not suitable for production** but more than enough for checking out this project. Checkout the steps for other deployments: This deployment is **not suitable for production** but more than enough for checking out this project. For other deployments checkout these links:
* [development](INSTALLDEV.md) * [Development](INSTALLDEV.md)
* [production](INSTALL.md) * [Production](INSTALL.md)
```bash ```bash
# Create and activate a Python virtualenv # Create and activate a Python virtualenv

View file

@ -50,13 +50,15 @@ class WebAppOptionInline(admin.TabularInline):
class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'display_type', 'display_detail', 'display_websites', 'account_link') list_display = (
'name', 'display_type', 'display_detail', 'display_websites', 'account_link'
)
list_filter = ('type', HasWebsiteListFilter, PHPVersionListFilter) list_filter = ('type', HasWebsiteListFilter, PHPVersionListFilter)
inlines = [WebAppOptionInline] inlines = [WebAppOptionInline]
readonly_fields = ('account_link', ) readonly_fields = ('account_link', )
change_readonly_fields = ('name', 'type', 'display_websites') change_readonly_fields = ('name', 'type', 'display_websites')
search_fields = ('name', 'account__username', 'data', 'website__domains__name') search_fields = ('name', 'account__username', 'data', 'website__domains__name')
list_prefetch_related = ('content_set__website',) list_prefetch_related = ('content_set__website', 'content_set__website__domains')
plugin = AppType plugin = AppType
plugin_field = 'type' plugin_field = 'type'
plugin_title = _("Web application type") plugin_title = _("Web application type")
@ -67,7 +69,8 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
def display_websites(self, webapp): def display_websites(self, webapp):
websites = [] websites = []
for content in webapp.content_set.all(): for content in webapp.content_set.all():
site_link = get_on_site_link(content.get_absolute_url()) site_url = content.get_absolute_url()
site_link = get_on_site_link(site_url)
website = content.website website = content.website
admin_url = change_url(website) admin_url = change_url(website)
name = "%s on %s" % (website.name, content.path) name = "%s on %s" % (website.name, content.path)

View file

@ -55,7 +55,8 @@ class WebAppServiceMixin(object):
self.append("rm -fr %(app_path)s" % context) self.append("rm -fr %(app_path)s" % context)
def get_context(self, webapp): def get_context(self, webapp):
context = { context = webapp.type_instance.get_directive_context()
context.update({
'user': webapp.get_username(), 'user': webapp.get_username(),
'group': webapp.get_groupname(), 'group': webapp.get_groupname(),
'app_name': webapp.name, 'app_name': webapp.name,
@ -64,7 +65,7 @@ class WebAppServiceMixin(object):
'banner': self.get_banner(), 'banner': self.get_banner(),
'under_construction_path': settings.WEBAPPS_UNDER_CONSTRUCTION_PATH, 'under_construction_path': settings.WEBAPPS_UNDER_CONSTRUCTION_PATH,
'is_mounted': webapp.content_set.exists(), 'is_mounted': webapp.content_set.exists(),
} })
context['deleted_app_path'] = settings.WEBAPPS_MOVE_ON_DELETE_PATH % context context['deleted_app_path'] = settings.WEBAPPS_MOVE_ON_DELETE_PATH % context
return context return context

View file

@ -167,6 +167,7 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
echo "$state" | grep -v ' RESTART$' || is_last=1 echo "$state" | grep -v ' RESTART$' || is_last=1
} }
if [[ $is_last -eq 1 ]]; then if [[ $is_last -eq 1 ]]; then
echo "Last backend to run, update: $UPDATED_APACHE, state: '$state'"
if [[ $UPDATED_APACHE -eq 1 || "$state" =~ .*RESTART$ ]]; then if [[ $UPDATED_APACHE -eq 1 || "$state" =~ .*RESTART$ ]]; then
if service apache2 status > /dev/null; then if service apache2 status > /dev/null; then
service apache2 reload service apache2 reload
@ -280,10 +281,8 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
return context return context
def get_context(self, webapp): def get_context(self, webapp):
context = super(PHPBackend, self).get_context(webapp) context = super().get_context(webapp)
context.update({ context.update({
'php_version': webapp.type_instance.get_php_version(),
'php_version_number': webapp.type_instance.get_php_version_number(),
'max_requests': settings.WEBAPPS_PHP_MAX_REQUESTS, 'max_requests': settings.WEBAPPS_PHP_MAX_REQUESTS,
}) })
self.update_fpm_context(webapp, context) self.update_fpm_context(webapp, context)

View file

@ -4,8 +4,8 @@ from orchestra.settings import ORCHESTRA_BASE_DOMAIN
from .. import webapps from .. import webapps
_names = ('home', 'user', 'group', 'app_type', 'app_name', 'app_type', 'app_id') _names = ('home', 'user', 'user_id', 'group', 'app_type', 'app_name', 'app_type', 'app_id', 'account_id')
_php_names = _names + ('php_version', 'php_version_number',) _php_names = _names + ('php_version', 'php_version_number', 'php_version_int')
_python_names = _names + ('python_version', 'python_version_number',) _python_names = _names + ('python_version', 'python_version_number',)
@ -17,8 +17,11 @@ WEBAPPS_BASE_DIR = Setting('WEBAPPS_BASE_DIR',
WEBAPPS_FPM_LISTEN = Setting('WEBAPPS_FPM_LISTEN', WEBAPPS_FPM_LISTEN = Setting('WEBAPPS_FPM_LISTEN',
'/opt/php/5.4/socks/%(user)s-%(app_name)s.sock', '127.0.0.1:5%(app_id)04d',
help_text=("TCP socket example: <tt>127.0.0.1:9%(app_id)03d</tt><br>" help_text=("TCP socket example: <tt>127.0.0.1:5%(app_id)04d</tt><br>"
"UDS example: <tt>/var/lib/php/sockets/%(user)s-%(app_name)s.sock</tt><br>"
"Merged TCP example: <tt>127.0.0.1:%(php_version_int)02d%(account_id)03d</tt></br>"
"Merged UDS example: <tt>/var/lib/php/sockets/%(user)s-%(php_version)s.sock</tt></br>"
"Available fromat names: <tt>{}</tt>").format(', '.join(_php_names)), "Available fromat names: <tt>{}</tt>").format(', '.join(_php_names)),
validators=[Setting.string_format_validator(_php_names)], validators=[Setting.string_format_validator(_php_names)],
) )

View file

@ -85,6 +85,8 @@ class AppType(plugins.Plugin, metaclass=plugins.PluginMount):
return { return {
'app_id': self.instance.id, 'app_id': self.instance.id,
'app_name': self.instance.name, 'app_name': self.instance.name,
'user': self.instance.account.username, 'user': self.instance.get_username(),
'user_id': self.instance.account.main_systemuser_id,
'home': self.instance.account.main_systemuser.get_home(), 'home': self.instance.account.main_systemuser.get_home(),
'account_id': self.instance.account_id,
} }

View file

@ -121,7 +121,6 @@ class PHPApp(AppType):
init_vars['post_max_size'] = post_max_size init_vars['post_max_size'] = post_max_size
if upload_max_filesize_value > post_max_size_value: if upload_max_filesize_value > post_max_size_value:
init_vars['post_max_size'] = upload_max_filesize init_vars['post_max_size'] = upload_max_filesize
print(init_vars)
return init_vars return init_vars
def get_directive_context(self): def get_directive_context(self):
@ -129,6 +128,7 @@ class PHPApp(AppType):
context.update({ context.update({
'php_version': self.get_php_version(), 'php_version': self.get_php_version(),
'php_version_number': self.get_php_version_number(), 'php_version_number': self.get_php_version_number(),
'php_version_int': int(self.get_php_version_number().replace('.', '')),
}) })
return context return context

View file

@ -11,6 +11,7 @@ from orchestra.admin.utils import admin_link, change_url
from orchestra.contrib.accounts.actions import list_accounts from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect from orchestra.forms.widgets import DynamicHelpTextSelect
from orchestra.utils.html import get_on_site_link
from .directives import SiteDirective from .directives import SiteDirective
from .forms import WebsiteAdminForm, WebsiteDirectiveInlineFormSet from .forms import WebsiteAdminForm, WebsiteDirectiveInlineFormSet
@ -84,10 +85,16 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
def display_webapps(self, website): def display_webapps(self, website):
webapps = [] webapps = []
for content in website.content_set.all(): for content in website.content_set.all():
site_link = get_on_site_link(content.get_absolute_url())
webapp = content.webapp webapp = content.webapp
detail = webapp.get_type_display()
try:
detail += ' ' + webapp.type_instance.get_detail()
except KeyError:
pass
url = change_url(webapp) url = change_url(webapp)
name = "%s on %s" % (webapp.name, content.path or '/') name = "%s on %s" % (webapp.name, content.path or '/')
webapps.append('<a href="%s">%s</a>' % (url, name)) webapps.append('<a href="%s" title="%s">%s %s</a>' % (url, detail, name, site_link))
return '<br>'.join(webapps) return '<br>'.join(webapps)
display_webapps.allow_tags = True display_webapps.allow_tags = True
display_webapps.short_description = _("Web apps") display_webapps.short_description = _("Web apps")

View file

@ -160,6 +160,7 @@ class Apache2Backend(ServiceController):
echo "$state" | grep -v ' RESTART$' || is_last=1 echo "$state" | grep -v ' RESTART$' || is_last=1
} }
if [[ $is_last -eq 1 ]]; then if [[ $is_last -eq 1 ]]; then
echo "Last backend to run, update: $UPDATED_APACHE, state: '$state'"
if [[ $UPDATED_APACHE -eq 1 || "$state" =~ .*RESTART$ ]]; then if [[ $UPDATED_APACHE -eq 1 || "$state" =~ .*RESTART$ ]]; then
if service apache2 status > /dev/null; then if service apache2 status > /dev/null; then
service apache2 reload service apache2 reload
@ -218,6 +219,7 @@ class Apache2Backend(ServiceController):
# UNIX socket # UNIX socket
target = 'unix:%(socket)s|fcgi://127.0.0.1%(app_path)s/' target = 'unix:%(socket)s|fcgi://127.0.0.1%(app_path)s/'
if context['location']: if context['location']:
# FIXME unix sockets do not support $1
target = 'unix:%(socket)s|fcgi://127.0.0.1%(app_path)s/$1' target = 'unix:%(socket)s|fcgi://127.0.0.1%(app_path)s/$1'
context.update({ context.update({
'app_path': os.path.normpath(app_path), 'app_path': os.path.normpath(app_path),

View file

@ -83,8 +83,11 @@ class Website(models.Model):
return directives return directives
def get_absolute_url(self): def get_absolute_url(self):
domain = self.domains.first() try:
if domain: domain = self.domains.all()[0]
except IndexError:
return
else:
return '%s://%s' % (self.get_protocol(), domain) return '%s://%s' % (self.get_protocol(), domain)
def get_user(self): def get_user(self):
@ -113,7 +116,7 @@ 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,
choices=SiteDirective.get_choices()) choices=SiteDirective.get_choices())
value = models.CharField(_("value"), max_length=256) value = models.CharField(_("value"), max_length=256)
def __str__(self): def __str__(self):
@ -157,6 +160,9 @@ class Content(models.Model):
self.path = '/' self.path = '/'
def get_absolute_url(self): def get_absolute_url(self):
domain = self.website.domains.first() try:
if domain: domain = self.website.domains.all()[0]
except IndexError:
return
else:
return '%s://%s%s' % (self.website.get_protocol(), domain, self.path) return '%s://%s%s' % (self.website.get_protocol(), domain, self.path)

View file

@ -29,7 +29,7 @@ def html_to_pdf(html, pagination=False):
def get_on_site_link(url): def get_on_site_link(url):
context = { context = {
'title': _("View on site"), 'title': _("View on site %s") % url,
'url': url, 'url': url,
'image': '<img src="%s"></img>' % static('orchestra/images/view-on-site.png'), 'image': '<img src="%s"></img>' % static('orchestra/images/view-on-site.png'),
} }