diff --git a/TODO.md b/TODO.md
index 25804c9a..ab595ef6 100644
--- a/TODO.md
+++ b/TODO.md
@@ -199,4 +199,11 @@ Php binaries should have this format: /usr/bin/php5.2-cgi
* Orchestra global search box on the header, based https://github.com/django/django/blob/master/django/contrib/admin/options.py#L866 and iterating over all registered services and inspectin its admin.search_fields
-* contain error on plugin missing key (plugin dissabled)
+* contain error on plugin missing key (plugin dissabled): NOP, fail hard is better than silently
+
+* contact.alternative_phone on a phone.tooltip, email:to
+
+
+* better validate options and directives (url locations, filesystem paths, etc..)
+* filter php deprecated options out based on version
+* Todo get php_version for fcgid wrapper
diff --git a/orchestra/apps/mailboxes/backends.py b/orchestra/apps/mailboxes/backends.py
index 483c4c93..fb752539 100644
--- a/orchestra/apps/mailboxes/backends.py
+++ b/orchestra/apps/mailboxes/backends.py
@@ -56,13 +56,13 @@ class MailSystemUserBackend(ServiceController):
def delete(self, mailbox):
context = self.get_context(mailbox)
+ self.append('mv %(home)s %(home)s.deleted' % context)
self.append(textwrap.dedent("""
{ sleep 2 && killall -u %(user)s -s KILL; } &
killall -u %(user)s || true
userdel %(user)s || true
groupdel %(user)s || true""") % context
)
- self.append('mv %(home)s %(home)s.deleted' % context)
def get_context(self, mailbox):
context = {
diff --git a/orchestra/apps/orchestration/admin.py b/orchestra/apps/orchestration/admin.py
index a74277f5..8c83a0b4 100644
--- a/orchestra/apps/orchestration/admin.py
+++ b/orchestra/apps/orchestration/admin.py
@@ -25,8 +25,7 @@ STATE_COLORS = {
class RouteAdmin(admin.ModelAdmin):
list_display = [
- 'id', 'backend', 'host', 'match', 'display_model', 'display_actions',
- 'is_active'
+ 'backend', 'host', 'match', 'display_model', 'display_actions', 'is_active'
]
list_editable = ['host', 'match', 'is_active']
list_filter = ['host', 'is_active', 'backend']
@@ -65,7 +64,7 @@ class RouteAdmin(admin.ModelAdmin):
""" Include dynamic help text for existing objects """
form = super(RouteAdmin, self).get_form(request, obj=obj, **kwargs)
if obj:
- form.base_fields['backend'].help_text = self.BACKEND_HELP_TEXT[obj.backend]
+ form.base_fields['backend'].help_text = self.BACKEND_HELP_TEXT.get(obj.backend, '')
return form
diff --git a/orchestra/apps/orchestration/manager.py b/orchestra/apps/orchestration/manager.py
index d0b220dd..69ca0f4e 100644
--- a/orchestra/apps/orchestration/manager.py
+++ b/orchestra/apps/orchestration/manager.py
@@ -10,6 +10,7 @@ from orchestra.utils.python import import_class
from . import settings
from .helpers import send_report
from .models import BackendLog
+from .signals import pre_action, post_action
logger = logging.getLogger(__name__)
@@ -65,8 +66,17 @@ def execute(operations, async=False):
else:
scripts[key][1].append(operation)
# Get and call backend action method
- method = getattr(scripts[key][0], operation.action)
+ backend = scripts[key][0]
+ method = getattr(backend, operation.action)
+ kwargs = {
+ 'sender': backend.__class__,
+ 'backend': backend,
+ 'instance': operation.instance,
+ 'action': operation.action,
+ }
+ pre_action.send(**kwargs)
method(operation.instance)
+ post_action.send(**kwargs)
# Execute scripts on each server
threads = []
executions = []
diff --git a/orchestra/apps/orchestration/middlewares.py b/orchestra/apps/orchestration/middlewares.py
index 16ee19ca..c65561ea 100644
--- a/orchestra/apps/orchestration/middlewares.py
+++ b/orchestra/apps/orchestration/middlewares.py
@@ -7,9 +7,9 @@ from django.http.response import HttpResponseServerError
from orchestra.utils.python import OrderedSet
-from .manager import router
from .backends import ServiceBackend
from .helpers import message_user
+from .manager import router
from .models import BackendLog
from .models import BackendOperation as Operation
@@ -103,7 +103,7 @@ class OperationsMiddleware(object):
pass
else:
update_fields = kwargs.get('update_fields', None)
- if update_fields:
+ if update_fields is not None:
# "update_fileds=[]" is a convention for explicitly executing backend
# i.e. account.disable()
if update_fields != []:
diff --git a/orchestra/apps/orchestration/signals.py b/orchestra/apps/orchestration/signals.py
new file mode 100644
index 00000000..6f8dc6e3
--- /dev/null
+++ b/orchestra/apps/orchestration/signals.py
@@ -0,0 +1,6 @@
+import django.dispatch
+
+
+pre_action = django.dispatch.Signal(providing_args=['backend', 'instance', 'action'])
+
+post_action = django.dispatch.Signal(providing_args=['backend', 'instance', 'action'])
diff --git a/orchestra/apps/webapps/admin.py b/orchestra/apps/webapps/admin.py
index a472427d..4b8aa003 100644
--- a/orchestra/apps/webapps/admin.py
+++ b/orchestra/apps/webapps/admin.py
@@ -53,6 +53,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
inlines = [WebAppOptionInline]
readonly_fields = ('account_link',)
change_readonly_fields = ('name', 'type')
+ search_fuelds = ('name', 'account__username')
list_prefetch_related = ('content_set__website',)
plugin = AppType
plugin_field = 'type'
diff --git a/orchestra/apps/webapps/backends/php.py b/orchestra/apps/webapps/backends/php.py
new file mode 100644
index 00000000..6388cba1
--- /dev/null
+++ b/orchestra/apps/webapps/backends/php.py
@@ -0,0 +1,177 @@
+import os
+import textwrap
+
+from django.template import Template, Context
+from django.utils.translation import ugettext_lazy as _
+
+from orchestra.apps.orchestration import ServiceController
+
+from . import WebAppServiceMixin
+from .. import settings
+
+
+class PHPBackend(WebAppServiceMixin, ServiceController):
+ verbose_name = _("PHP FPM/FCGID")
+ default_route_match = "webapp.type == 'php'"
+
+ def save(self, webapp):
+ context = self.get_context(webapp)
+ if webapp.type_instance.is_fpm:
+ self.save_fpm(webapp, context)
+ self.delete_fcgid(webapp, context)
+ elif webapp.type_instance.is_fcgid:
+ self.save_fcgid(webapp, context)
+ self.delete_fpm(webapp, context)
+
+ def save_fpm(self, webapp, context):
+ self.create_webapp_dir(context)
+ self.set_under_construction(context)
+ self.append(textwrap.dedent("""\
+ {
+ echo -e '%(fpm_config)s' | diff -N -I'^\s*;;' %(fpm_path)s -
+ } || {
+ echo -e '%(fpm_config)s' > %(fpm_path)s
+ UPDATEDFPM=1
+ }""") % context
+ )
+
+ def save_fcgid(self, webapp, context):
+ self.create_webapp_dir(context)
+ self.set_under_construction(context)
+ self.append("mkdir -p %(wrapper_dir)s" % context)
+ self.append(textwrap.dedent("""\
+ {
+ echo -e '%(wrapper)s' | diff -N -I'^\s*#' %(wrapper_path)s -
+ } || {
+ echo -e '%(wrapper)s' > %(wrapper_path)s; UPDATED_APACHE=1
+ }""") % context
+ )
+ self.append("chmod +x %(wrapper_path)s" % context)
+ self.append("chown -R %(user)s:%(group)s %(wrapper_dir)s" % context)
+ if context['cmd_options']:
+ self.append(textwrap.dedent("""
+ {
+ echo -e '%(cmd_options)s' | diff -N -I'^\s*#' %(cmd_options_path)s -
+ } || {
+ echo -e '%(cmd_options)s' > %(cmd_options_path)s; UPDATED_APACHE=1
+ }""" ) % context
+ )
+ else:
+ self.append("rm -f %(cmd_options_path)s" % context)
+
+ def delete(self, webapp):
+ context = self.get_context(webapp)
+ if webapp.type_instance.is_fpm:
+ self.delete_fpm(webapp, context)
+ elif webapp.type_instance.is_fcgid:
+ self.delete_fcgid(webapp, context)
+ self.delete_webapp_dir(context)
+
+ def delete_fpm(self, webapp, context):
+ self.append("rm -f %(fpm_path)s" % context)
+
+ def delete_fcgid(self, webapp, context):
+ self.append("rm -f %(wrapper_path)s" % context)
+ self.append("rm -f %(cmd_options_path)s" % context)
+
+ def commit(self):
+ if self.content:
+ self.append(textwrap.dedent("""
+ if [[ $UPDATEDFPM == 1 ]]; then
+ service php5-fpm reload
+ service php5-fpm start
+ fi""")
+ )
+ self.append(textwrap.dedent("""\
+ if [[ $UPDATED_APACHE == 1 ]]; then
+ service apache2 reload
+ fi""")
+ )
+
+ def get_fpm_config(self, webapp, context):
+ context.update({
+ 'init_vars': webapp.type_instance.get_php_init_vars(),
+ 'max_children': webapp.get_options().get('processes', False),
+ 'request_terminate_timeout': webapp.get_options().get('timeout', False),
+ })
+ context['fpm_listen'] = webapp.type_instance.FPM_LISTEN % context
+ fpm_config = Template(textwrap.dedent("""\
+ ;; {{ banner }}
+ [{{ user }}]
+ user = {{ user }}
+ group = {{ group }}
+
+ listen = {{ fpm_listen | safe }}
+ listen.owner = {{ user }}
+ listen.group = {{ group }}
+ pm = ondemand
+ {% if max_children %}pm.max_children = {{ max_children }}{% endif %}
+ {% if request_terminate_timeout %}request_terminate_timeout = {{ request_terminate_timeout }}{% endif %}
+ {% for name, value in init_vars.iteritems %}
+ php_admin_value[{{ name | safe }}] = {{ value | safe }}{% endfor %}
+ """
+ ))
+ return fpm_config.render(Context(context))
+
+ def get_fcgid_wrapper(self, webapp, context):
+ opt = webapp.type_instance
+ # Format PHP init vars
+ init_vars = opt.get_php_init_vars()
+ if init_vars:
+ init_vars = [ '-d %s="%s"' % (k,v) for k,v in init_vars.iteritems() ]
+ init_vars = ', '.join(init_vars)
+ context.update({
+ 'php_binary': os.path.normpath(settings.WEBAPPS_PHP_CGI_BINARY_PATH % context),
+ 'php_rc': os.path.normpath(settings.WEBAPPS_PHP_CGI_RC_DIR % context),
+ 'php_ini_scan': os.path.normpath(settings.WEBAPPS_PHP_CGI_INI_SCAN_DIR % context),
+ 'php_init_vars': init_vars,
+ })
+ return textwrap.dedent("""\
+ #!/bin/sh
+ # %(banner)s
+ export PHPRC=%(php_rc)s
+ export PHP_INI_SCAN_DIR=%(php_ini_scan)s
+ exec %(php_binary)s %(php_init_vars)s""") % context
+
+ def get_fcgid_cmd_options(self, webapp, context):
+ maps = {
+ 'MaxProcesses': webapp.get_options().get('processes', None),
+ 'IOTimeout': webapp.get_options().get('timeout', None),
+ }
+ cmd_options = []
+ for directive, value in maps.iteritems():
+ if value:
+ cmd_options.append("%s %s" % (directive, value))
+ if cmd_options:
+ head = '# %(banner)s\nFcgidCmdOptions %(wrapper_path)s' % context
+ cmd_options.insert(0, head)
+ return ' \\\n '.join(cmd_options)
+
+ def update_fcgid_context(self, webapp, context):
+ wrapper_path = webapp.type_instance.FCGID_WRAPPER_PATH % context
+ context.update({
+ 'wrapper': self.get_fcgid_wrapper(webapp, context),
+ 'wrapper_path': wrapper_path,
+ 'wrapper_dir': os.path.dirname(wrapper_path),
+ })
+ context.update({
+ 'cmd_options': self.get_fcgid_cmd_options(webapp, context),
+ 'cmd_options_path': settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH % context,
+ })
+
+ def update_fpm_context(self, webapp, context):
+ context.update({
+ 'fpm_config': self.get_fpm_config(webapp, context),
+ 'fpm_path': settings.WEBAPPS_PHPFPM_POOL_PATH % context,
+ })
+ return context
+
+ def get_context(self, webapp):
+ context = super(PHPBackend, self).get_context(webapp)
+ context.update({
+ 'php_version': webapp.type_instance.get_php_version(),
+ 'php_version_number': webapp.type_instance.get_php_version_number(),
+ })
+ self.update_fcgid_context(webapp, context)
+ self.update_fpm_context(webapp, context)
+ return context
diff --git a/orchestra/apps/webapps/backends/phpfcgid.py b/orchestra/apps/webapps/backends/phpfcgid.py
deleted file mode 100644
index d8103b84..00000000
--- a/orchestra/apps/webapps/backends/phpfcgid.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import os
-import textwrap
-
-from django.utils.translation import ugettext_lazy as _
-
-from orchestra.apps.orchestration import ServiceController
-
-from . import WebAppServiceMixin
-from .. import settings
-
-
-class PHPFcgidBackend(WebAppServiceMixin, ServiceController):
- """ Per-webapp fcgid application """
- verbose_name = _("PHP-Fcgid")
- directive = 'fcgid'
- default_route_match = "webapp.type_class.php_execution == 'fcgid'"
-
- def save(self, webapp):
- context = self.get_context(webapp)
- self.create_webapp_dir(context)
- self.set_under_construction(context)
- self.append("mkdir -p %(wrapper_dir)s" % context)
- self.append(textwrap.dedent("""\
- {
- echo -e '%(wrapper)s' | diff -N -I'^\s*#' %(wrapper_path)s -
- } || {
- echo -e '%(wrapper)s' > %(wrapper_path)s; UPDATED_APACHE=1
- }""") % context
- )
- self.append("chmod +x %(wrapper_path)s" % context)
- self.append("chown -R %(user)s:%(group)s %(wrapper_dir)s" % context)
- if context['cmd_options']:
- self.append(textwrap.dedent("""
- {
- echo -e '%(cmd_options)s' | diff -N -I'^\s*#' %(cmd_options_path)s -
- } || {
- echo -e '%(cmd_options)s' > %(cmd_options_path)s; UPDATED_APACHE=1
- }""" ) % context
- )
- else:
- self.append("rm -f %(cmd_options_path)s" % context)
-
- def delete(self, webapp):
- context = self.get_context(webapp)
- self.append("rm -f '%(wrapper_path)s'" % context)
- self.append("rm -f '%(cmd_options_path)s'" % context)
- self.delete_webapp_dir(context)
-
- def commit(self):
- self.append('if [[ $UPDATED_APACHE == 1 ]]; then service apache2 reload; fi')
-
- def get_fcgid_wrapper(self, webapp, context):
- opt = webapp.type_instance
- # Format PHP init vars
- init_vars = opt.get_php_init_vars()
- if init_vars:
- init_vars = [ '-d %s="%s"' % (k,v) for k,v in init_vars.iteritems() ]
- init_vars = ', '.join(init_vars)
-
- context.update({
- 'php_binary': opt.get_php_binary_path(),
- 'php_rc': opt.get_php_rc_path(),
- 'php_init_vars': init_vars,
- })
- return textwrap.dedent("""\
- #!/bin/sh
- # %(banner)s
- export PHPRC=%(php_rc)s
- exec %(php_binary)s %(php_init_vars)s""") % context
-
- def get_fcgid_cmd_options(self, webapp, context):
- maps = {
- 'MaxProcesses': webapp.get_options().get('processes', None),
- 'IOTimeout': webapp.get_options().get('timeout', None),
- }
- cmd_options = []
- for directive, value in maps.iteritems():
- if value:
- cmd_options.append("%s %s" % (directive, value))
- if cmd_options:
- head = '# %(banner)s\nFcgidCmdOptions %(wrapper_path)s' % context
- cmd_options.insert(0, head)
- return ' \\\n '.join(cmd_options)
-
- def get_context(self, webapp):
- context = super(PHPFcgidBackend, self).get_context(webapp)
- wrapper_path = settings.WEBAPPS_FCGID_WRAPPER_PATH % context
- context.update({
- 'wrapper': self.get_fcgid_wrapper(webapp, context),
- 'wrapper_path': wrapper_path,
- 'wrapper_dir': os.path.dirname(wrapper_path),
- })
- context.update({
- 'cmd_options': self.get_fcgid_cmd_options(webapp, context),
- 'cmd_options_path': settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH % context,
- })
- return context
diff --git a/orchestra/apps/webapps/backends/phpfpm.py b/orchestra/apps/webapps/backends/phpfpm.py
deleted file mode 100644
index 77384459..00000000
--- a/orchestra/apps/webapps/backends/phpfpm.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import os
-import textwrap
-
-from django.template import Template, Context
-from django.utils.translation import ugettext_lazy as _
-
-from orchestra.apps.orchestration import ServiceController
-
-from . import WebAppServiceMixin
-from .. import settings
-
-
-class PHPFPMBackend(WebAppServiceMixin, ServiceController):
- """ Per-webapp php application """
- verbose_name = _("PHP-FPM")
- default_route_match = "webapp.type_class.php_execution == 'fpm'"
-
- def save(self, webapp):
- context = self.get_context(webapp)
- self.create_webapp_dir(context)
- self.set_under_construction(context)
- self.append(textwrap.dedent("""\
- {
- echo -e '%(fpm_config)s' | diff -N -I'^\s*;;' %(fpm_path)s -
- } || {
- echo -e '%(fpm_config)s' > %(fpm_path)s
- UPDATEDFPM=1
- }""") % context
- )
-
- def delete(self, webapp):
- context = self.get_context(webapp)
- self.append("rm '%(fpm_path)s'" % context)
- self.delete_webapp_dir(context)
-
- def commit(self):
- if not self.cmds:
- return
- super(PHPFPMBackend, self).commit()
- self.append(textwrap.dedent("""
- if [[ $UPDATEDFPM == 1 ]]; then
- service php5-fpm reload
- service php5-fpm start
- fi"""))
-
- def get_fpm_config(self, webapp, context):
- context.update({
- 'init_vars': webapp.type_instance.get_php_init_vars(),
- 'fpm_port': webapp.get_fpm_port(),
- 'max_children': webapp.get_options().get('processes', False),
- 'request_terminate_timeout': webapp.get_options().get('timeout', False),
- })
- context['fpm_listen'] = settings.WEBAPPS_FPM_LISTEN % context
- fpm_config = Template(textwrap.dedent("""\
- ;; {{ banner }}
- [{{ user }}]
- user = {{ user }}
- group = {{ group }}
-
- listen = {{ fpm_listen | safe }}
- listen.owner = {{ user }}
- listen.group = {{ group }}
- pm = ondemand
- {% if max_children %}pm.max_children = {{ max_children }}{% endif %}
- {% if request_terminate_timeout %}request_terminate_timeout = {{ request_terminate_timeout }}{% endif %}
- {% for name, value in init_vars.iteritems %}
- php_admin_value[{{ name | safe }}] = {{ value | safe }}{% endfor %}
- """
- ))
- return fpm_config.render(Context(context))
-
- def get_context(self, webapp):
- context = super(PHPFPMBackend, self).get_context(webapp)
- context.update({
- 'fpm_config': self.get_fpm_config(webapp, context),
- 'fpm_path': settings.WEBAPPS_PHPFPM_POOL_PATH % context,
- })
- return context
diff --git a/orchestra/apps/webapps/settings.py b/orchestra/apps/webapps/settings.py
index 022d0753..ae9b844f 100644
--- a/orchestra/apps/webapps/settings.py
+++ b/orchestra/apps/webapps/settings.py
@@ -27,50 +27,48 @@ WEBAPPS_FCGID_CMD_OPTIONS_PATH = getattr(settings, 'WEBAPPS_FCGID_CMD_OPTIONS_PA
WEBAPPS_PHP_ERROR_LOG_PATH = getattr(settings, 'WEBAPPS_PHP_ERROR_LOG_PATH',
'')
+
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', (
- 'orchestra.apps.webapps.types.php.PHPFPMApp',
- 'orchestra.apps.webapps.types.php.PHPFCGIDApp',
+ 'orchestra.apps.webapps.types.php.PHPApp',
'orchestra.apps.webapps.types.misc.StaticApp',
'orchestra.apps.webapps.types.misc.WebalizerApp',
'orchestra.apps.webapps.types.saas.WordPressMuApp',
'orchestra.apps.webapps.types.saas.DokuWikiMuApp',
'orchestra.apps.webapps.types.saas.DrupalMuApp',
'orchestra.apps.webapps.types.misc.SymbolicLinkApp',
- 'orchestra.apps.webapps.types.wordpress.WordPressFPMApp',
- 'orchestra.apps.webapps.types.wordpress.WordPressFCGIDApp',
+ 'orchestra.apps.webapps.types.wordpress.WordPressApp',
))
-
-WEBAPPS_PHP_FCGID_VERSIONS = getattr(settings, 'WEBAPPS_PHP_FCGID_VERSIONS', (
- ('5.4', '5.4'),
- ('5.3', '5.3'),
- ('5.2', '5.2'),
- ('4', '4'),
+WEBAPPS_PHP_VERSIONS = getattr(settings, 'WEBAPPS_PHP_VERSIONS', (
+ # Execution modle choose by ending with -fpm or -cgi
+ ('php-5.4-fpm', 'PHP 5.4 FPM'),
+ ('php-5.4-cgi', 'PHP 5.4 FCGID'),
+ ('php-5.3-cgi', 'PHP 5.3 FCGID'),
+ ('php-5.2-cgi', 'PHP 5.2 FCGID'),
+ ('php-4-cgi', 'PHP 4 FCGID'),
))
-WEBAPPS_PHP_FCGID_DEFAULT_VERSION = getattr(settings, 'WEBAPPS_PHP_FCGID_DEFAULT_VERSION',
- '5.4')
+WEBAPPS_DEFAULT_PHP_VERSION = getattr(settings, 'WEBAPPS_DEFAULT_PHP_VERSION',
+ '5.4-cgi')
WEBAPPS_PHP_CGI_BINARY_PATH = getattr(settings, 'WEBAPPS_PHP_CGI_BINARY_PATH',
# Path of the cgi binary used by fcgid
- '/usr/bin/php%(php_version)s-cgi')
+ '/usr/bin/php%(php_version_number)s-cgi')
-WEBAPPS_PHP_CGI_RC_PATH = getattr(settings, 'WEBAPPS_PHP_CGI_RC_PATH',
+
+WEBAPPS_PHP_CGI_RC_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_RC_DIR',
# Path to php.ini
- '/etc/php%(php_version)s/cgi/')
+ '/etc/php%(php_version_number)s/cgi/')
-WEBAPPS_PHP_FPM_VERSIONS = getattr(settings, 'WEBAPPS_PHP_FPM_VERSIONS', (
- ('5.4', '5.4'),
-))
+WEBAPPS_PHP_CGI_INI_SCAN_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_INI_SCAN_DIR',
+ # Path to php.ini
+ '/etc/php%(php_version_number)s/cgi/conf.d')
-WEBAPPS_PHP_FPM_DEFAULT_VERSION = getattr(settings, 'WEBAPPS_PHP_DEFAULT_VERSION',
- '5.4')
-
WEBAPPS_UNDER_CONSTRUCTION_PATH = getattr(settings, 'WEBAPPS_UNDER_CONSTRUCTION_PATH',
# Server-side path where a under construction stock page is
diff --git a/orchestra/apps/webapps/types/__init__.py b/orchestra/apps/webapps/types/__init__.py
index 4fa2a773..17fe6aa9 100644
--- a/orchestra/apps/webapps/types/__init__.py
+++ b/orchestra/apps/webapps/types/__init__.py
@@ -20,7 +20,6 @@ class AppType(plugins.Plugin):
unique_name = False
option_groups = (AppOption.FILESYSTEM, AppOption.PROCESS, AppOption.PHP)
# TODO generic name like 'execution' ?
- php_execution = None
@classmethod
@cached
diff --git a/orchestra/apps/webapps/types/misc.py b/orchestra/apps/webapps/types/misc.py
index 7a6d3d1a..82829e3f 100644
--- a/orchestra/apps/webapps/types/misc.py
+++ b/orchestra/apps/webapps/types/misc.py
@@ -9,7 +9,7 @@ from orchestra.plugins.forms import PluginDataForm
from ..options import AppOption
from . import AppType
-from .php import PHPAppType
+from .php import PHPApp
class StaticApp(AppType):
@@ -48,7 +48,7 @@ class SymbolicLinkSerializer(serializers.Serializer):
path = serializers.CharField(label=_("Path"))
-class SymbolicLinkApp(PHPAppType):
+class SymbolicLinkApp(PHPApp):
name = 'symbolic-link'
verbose_name = "Symbolic link"
form = SymbolicLinkForm
diff --git a/orchestra/apps/webapps/types/php.py b/orchestra/apps/webapps/types/php.py
index b8098544..dc0cdc12 100644
--- a/orchestra/apps/webapps/types/php.py
+++ b/orchestra/apps/webapps/types/php.py
@@ -1,4 +1,5 @@
import os
+import re
from django import forms
from django.utils.translation import ugettext_lazy as _
@@ -12,12 +13,46 @@ from .. import settings
from . import AppType
-class PHPAppType(AppType):
- FPM = 'fpm'
- FCGID = 'fcgid'
+help_message = _("Version of PHP used to execute this webapp.
"
+ "Changing the PHP version may result in application malfunction, "
+ "make sure that everything continue to work as expected.")
+
+
+class PHPAppForm(PluginDataForm):
+ php_version = forms.ChoiceField(label=_("PHP version"),
+ choices=settings.WEBAPPS_PHP_VERSIONS,
+ initial=settings.WEBAPPS_DEFAULT_PHP_VERSION,
+ help_text=help_message)
+
+
+class PHPAppSerializer(serializers.Serializer):
+ php_version = serializers.ChoiceField(label=_("PHP version"),
+ choices=settings.WEBAPPS_PHP_VERSIONS,
+ default=settings.WEBAPPS_DEFAULT_PHP_VERSION,
+ help_text=help_message)
+
+
+class PHPApp(AppType):
+ name = 'php'
+ verbose_name = "PHP"
+ help_text = _("This creates a PHP application under ~/webapps/<app_name>
")
+ form = PHPAppForm
+ serializer = PHPAppSerializer
+ icon = 'orchestra/icons/apps/PHP.png'
- php_version = 5.4
- fpm_listen = settings.WEBAPPS_FPM_LISTEN
+ DEFAULT_PHP_VERSION = settings.WEBAPPS_DEFAULT_PHP_VERSION
+ PHP_DISABLED_FUNCTIONS = settings.WEBAPPS_PHP_DISABLED_FUNCTIONS
+ PHP_ERROR_LOG_PATH = settings.WEBAPPS_PHP_ERROR_LOG_PATH
+ FPM_LISTEN = settings.WEBAPPS_FPM_LISTEN
+ FCGID_WRAPPER_PATH = settings.WEBAPPS_FCGID_WRAPPER_PATH
+
+ @property
+ def is_fpm(self):
+ return self.get_php_version().endswith('-fpm')
+
+ @property
+ def is_fcgid(self):
+ return self.get_php_version().endswith('-cgi')
def get_context(self):
""" context used to format settings """
@@ -46,95 +81,37 @@ class PHPAppType(AppType):
enabled_functions += enabled_functions.get().value.split(',')
if enabled_functions:
disabled_functions = []
- for function in settings.WEBAPPS_PHP_DISABLED_FUNCTIONS:
+ for function in self.PHP_DISABLED_FUNCTIONS:
if function not in enabled_functions:
disabled_functions.append(function)
init_vars['dissabled_functions'] = ','.join(disabled_functions)
- if settings.WEBAPPS_PHP_ERROR_LOG_PATH and 'error_log' not in init_vars:
+ if self.PHP_ERROR_LOG_PATH and 'error_log' not in init_vars:
context = self.get_context()
- error_log_path = os.path.normpath(settings.WEBAPPS_PHP_ERROR_LOG_PATH % context)
+ error_log_path = os.path.normpath(self.PHP_ERROR_LOG_PATH % context)
init_vars['error_log'] = error_log_path
return init_vars
-
-
-help_message = _("Version of PHP used to execute this webapp.
"
- "Changing the PHP version may result in application malfunction, "
- "make sure that everything continue to work as expected.")
-
-
-class PHPFPMAppForm(PluginDataForm):
- php_version = forms.ChoiceField(label=_("PHP version"),
- choices=settings.WEBAPPS_PHP_FPM_VERSIONS,
- initial=settings.WEBAPPS_PHP_FPM_DEFAULT_VERSION,
- help_text=help_message)
-
-
-class PHPFPMAppSerializer(serializers.Serializer):
- php_version = serializers.ChoiceField(label=_("PHP version"),
- choices=settings.WEBAPPS_PHP_FPM_VERSIONS,
- default=settings.WEBAPPS_PHP_FPM_DEFAULT_VERSION,
- help_text=help_message)
-
-
-class PHPFPMApp(PHPAppType):
- name = 'php-fpm'
- php_execution = PHPAppType.FPM
- verbose_name = "PHP FPM"
- help_text = _("This creates a PHP application under ~/webapps/<app_name>
"
- "PHP-FPM will be used to execute PHP files.")
- icon = 'orchestra/icons/apps/PHPFPM.png'
- form = PHPFPMAppForm
- serializer = PHPFPMAppSerializer
def get_directive(self):
context = self.get_directive_context()
- socket_type = 'unix'
- if ':' in self.fpm_listen:
- socket_type = 'tcp'
- socket = self.fpm_listen % context
- return ('fpm', socket_type, socket, self.instance.get_path())
-
-
-class PHPFCGIDAppForm(PluginDataForm):
- php_version = forms.ChoiceField(label=_("PHP version"),
- choices=settings.WEBAPPS_PHP_FCGID_VERSIONS,
- initial=settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION,
- help_text=help_message)
-
-
-class PHPFCGIDAppSerializer(serializers.Serializer):
- php_version = serializers.ChoiceField(label=_("PHP version"),
- choices=settings.WEBAPPS_PHP_FCGID_VERSIONS,
- default=settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION,
- help_text=help_message)
-
-
-class PHPFCGIDApp(PHPAppType):
- name = 'php-fcgid'
- php_execution = PHPAppType.FCGID
- verbose_name = "PHP FCGID"
- help_text = _("This creates a PHP application under ~/webapps/<app_name>
"
- "Apache-mod-fcgid will be used to execute PHP files.")
- icon = 'orchestra/icons/apps/PHPFCGI.png'
- form = PHPFCGIDAppForm
- serializer = PHPFCGIDAppSerializer
+ if self.is_fpm:
+ socket_type = 'unix'
+ if ':' in self.FPM_LISTEN:
+ socket_type = 'tcp'
+ socket = self.FPM_LISTEN % context
+ return ('fpm', socket_type, socket, self.instance.get_path())
+ elif self.is_fcgid:
+ wrapper_path = os.path.normpath(self.FCGID_WRAPPER_PATH % context)
+ return ('fcgid', self.instance.get_path(), wrapper_path)
+ else:
+ raise ValueError("Unknown directive for php version '%s'" % php_version)
- def get_directive(self):
- context = self.get_directive_context()
- wrapper_path = os.path.normpath(settings.WEBAPPS_FCGID_PATH % context)
- return ('fcgid', self.instance.get_path(), wrapper_path)
+ def get_php_version(self):
+ default_version = self.DEFAULT_PHP_VERSION
+ return self.instance.data.get('php_version', default_version)
- def get_php_binary_path(self):
- default_version = settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION
- context = {
- 'php_version': self.instance.data.get('php_version', default_version)
- }
- return os.path.normpath(settings.WEBAPPS_PHP_CGI_BINARY_PATH % context)
-
- def get_php_rc_path(self):
- default_version = settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION
- context = {
- 'php_version': self.instance.data.get('php_version', default_version)
- }
- return os.path.normpath(settings.WEBAPPS_PHP_CGI_RC_PATH % context)
-
+ def get_php_version_number(self):
+ php_version = self.get_php_version()
+ number = re.findall(r'[0-9]+\.?[0-9]+', php_version)
+ if len(number) > 1:
+ raise ValueError("Multiple version number matches for '%'" % php_version)
+ return number[0]
diff --git a/orchestra/apps/webapps/types/wordpress.py b/orchestra/apps/webapps/types/wordpress.py
index b835a3a3..5278a5bf 100644
--- a/orchestra/apps/webapps/types/wordpress.py
+++ b/orchestra/apps/webapps/types/wordpress.py
@@ -9,11 +9,10 @@ from orchestra.utils.python import random_ascii
from .. import settings
-from .php import (PHPAppType, PHPFCGIDApp, PHPFPMApp, PHPFCGIDAppForm, PHPFCGIDAppSerializer,
- PHPFPMAppForm, PHPFPMAppSerializer)
+from .php import PHPApp, PHPAppForm, PHPAppSerializer
-class WordPressAbstractAppForm(PluginDataForm):
+class WordPressAppForm(PHPAppForm):
db_name = forms.CharField(label=_("Database name"),
help_text=_("Database used for this webapp."))
db_user = forms.CharField(label=_("Database user"),)
@@ -21,16 +20,20 @@ class WordPressAbstractAppForm(PluginDataForm):
help_text=_("Initial database password."))
-class WordPressAbstractAppSerializer(serializers.Serializer):
+class WordPressAppSerializer(PHPAppSerializer):
db_name = serializers.CharField(label=_("Database name"), required=False)
db_user = serializers.CharField(label=_("Database user"), required=False)
db_pass = serializers.CharField(label=_("Database user password"), required=False)
-class WordPressAbstractApp(object):
- icon = 'orchestra/icons/apps/WordPress.png'
+class WordPressApp(PHPApp):
+ name = 'wordpress'
+ verbose_name = "WordPress"
+ serializer = WordPressAppSerializer
+ change_form = WordPressAppForm
change_readonly_fileds = ('db_name', 'db_user', 'db_pass',)
help_text = _("Visit http://<domain.lan>/wp-admin/install.php to finish the installation.")
+ icon = 'orchestra/icons/apps/WordPress.png'
def get_db_name(self):
db_name = 'wp_%s_%s' % (self.instance.name, self.instance.account)
@@ -46,7 +49,7 @@ class WordPressAbstractApp(object):
return random_ascii(10)
def validate(self):
- super(WordPressAbstractApp, self).validate()
+ super(WordPressApp, self).validate()
create = not self.instance.pk
if create:
db = Database(name=self.get_db_name(), account=self.instance.account)
@@ -79,7 +82,7 @@ class WordPressAbstractApp(object):
else:
# Trigger related backends
for related in self.get_related():
- related.save()
+ related.save(updated_fields=[])
def delete(self):
for related in self.get_related():
@@ -101,23 +104,3 @@ class WordPressAbstractApp(object):
else:
related.append(db)
return related
-
-
-class WordPressFPMApp(WordPressAbstractApp, PHPFPMApp):
- name = 'wordpress-fpm'
- php_execution = PHPAppType.FPM
- verbose_name = "WordPress (FPM)"
- serializer = type('WordPressFPMSerializer',
- (WordPressAbstractAppSerializer, PHPFPMAppSerializer), {})
- change_form = type('WordPressFPMForm',
- (WordPressAbstractAppForm, PHPFPMAppForm), {})
-
-
-class WordPressFCGIDApp(WordPressAbstractApp, PHPFCGIDApp):
- name = 'wordpress-fcgid'
- php_execution = PHPAppType.FCGID
- verbose_name = "WordPress (FCGID)"
- serializer = type('WordPressFCGIDSerializer',
- (WordPressAbstractAppSerializer, PHPFCGIDAppSerializer), {})
- change_form = type('WordPressFCGIDForm',
- (WordPressAbstractAppForm, PHPFCGIDAppForm), {})
diff --git a/orchestra/apps/websites/backends/apache.py b/orchestra/apps/websites/backends/apache.py
index f117e02f..dea37cda 100644
--- a/orchestra/apps/websites/backends/apache.py
+++ b/orchestra/apps/websites/backends/apache.py
@@ -164,11 +164,11 @@ class Apache2Backend(ServiceController):
for rules in directives.get('sec_rule_remove', []):
for rule in rules.value.split():
config += "SecRuleRemoveById %i\n" % int(rule)
- for modsecurity in directives.get('sec_rule_off', []):
+ for modsecurity in directives.get('sec_engine', []):
config += textwrap.dedent("""\
SecRuleEngine off
-
+
""") % modsecurity
return config
diff --git a/orchestra/apps/websites/backends/webalizer.py b/orchestra/apps/websites/backends/webalizer.py
index 440fe898..335fb790 100644
--- a/orchestra/apps/websites/backends/webalizer.py
+++ b/orchestra/apps/websites/backends/webalizer.py
@@ -11,6 +11,7 @@ from .. import settings
class WebalizerBackend(ServiceController):
verbose_name = _("Webalizer Content")
model = 'websites.Content'
+ default_route_match = "content.webapp.type == 'webalizer'"
def save(self, content):
context = self.get_context(content)
diff --git a/orchestra/apps/websites/directives.py b/orchestra/apps/websites/directives.py
index 7187b1b0..2a80d1c5 100644
--- a/orchestra/apps/websites/directives.py
+++ b/orchestra/apps/websites/directives.py
@@ -149,5 +149,5 @@ class SecEngine(SiteDirective):
name = 'sec_engine'
verbose_name = _("Modsecurity engine")
help_text = _("URL location for disabling modsecurity engine.")
- regex = r'^[^ ]+$'
+ regex = r'^/[^ ]*$'
group = SiteDirective.SEC
diff --git a/orchestra/static/orchestra/icons/apps/PHP.png b/orchestra/static/orchestra/icons/apps/PHP.png
new file mode 100644
index 00000000..d3bcfb71
Binary files /dev/null and b/orchestra/static/orchestra/icons/apps/PHP.png differ
diff --git a/orchestra/static/orchestra/icons/apps/PHP.svg b/orchestra/static/orchestra/icons/apps/PHP.svg
index ffa40a38..c4b54a8f 100644
--- a/orchestra/static/orchestra/icons/apps/PHP.svg
+++ b/orchestra/static/orchestra/icons/apps/PHP.svg
@@ -1,63 +1,203 @@
+
+
\ No newline at end of file
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/orchestra/utils/humanize.py b/orchestra/utils/humanize.py
index 4fa44170..da34d075 100644
--- a/orchestra/utils/humanize.py
+++ b/orchestra/utils/humanize.py
@@ -64,7 +64,7 @@ def naturaldatetime(date, include_seconds=False):
if days == 0:
if hours == 0:
if minutes > 0:
- minutes += float(seconds)/60
+ minutes = float(seconds)/60
return ungettext(
_('{minutes:.1f} minute{ago}'),
_('{minutes:.1f} minutes{ago}'), minutes
@@ -77,7 +77,7 @@ def naturaldatetime(date, include_seconds=False):
).format(seconds=seconds, ago=ago)
return _('just now')
else:
- hours += float(minutes)/60
+ hours = float(minutes)/60
return ungettext(
_('{hours:.1f} hour{ago}'),
_('{hours:.1f} hours{ago}'), hours