Random fixes
This commit is contained in:
parent
0e850d6e2d
commit
d96afe41fa
2
TODO.md
2
TODO.md
|
@ -462,3 +462,5 @@ with open(file) as handler:
|
||||||
# Bill amend and related transaction, what to do? allow edit transaction ammount of amends when their are pending execution
|
# Bill amend and related transaction, what to do? allow edit transaction ammount of amends when their are pending execution
|
||||||
|
|
||||||
# DASHBOARD: Show owned tickets, scheduled actions, maintenance operations (diff domains)
|
# DASHBOARD: Show owned tickets, scheduled actions, maintenance operations (diff domains)
|
||||||
|
|
||||||
|
# Add confirmation step on transaction actions like process transaction
|
||||||
|
|
|
@ -26,11 +26,11 @@ from .models import (Bill, Invoice, AmendmentInvoice, Fee, AmendmentFee, ProForm
|
||||||
|
|
||||||
PAYMENT_STATE_COLORS = {
|
PAYMENT_STATE_COLORS = {
|
||||||
Bill.OPEN: 'grey',
|
Bill.OPEN: 'grey',
|
||||||
Bill.CREATED: 'darkorange',
|
Bill.CREATED: 'magenta',
|
||||||
Bill.PROCESSED: 'darkorange',
|
Bill.PROCESSED: 'darkorange',
|
||||||
Bill.AMENDED: 'blue',
|
Bill.AMENDED: 'blue',
|
||||||
Bill.PAID: 'green',
|
Bill.PAID: 'green',
|
||||||
Bill.EXECUTED: 'darkorange',
|
Bill.EXECUTED: 'olive',
|
||||||
Bill.BAD_DEBT: 'red',
|
Bill.BAD_DEBT: 'red',
|
||||||
Bill.INCOMPLETE: 'red',
|
Bill.INCOMPLETE: 'red',
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,7 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
)
|
)
|
||||||
list_filter = (
|
list_filter = (
|
||||||
BillTypeListFilter, 'is_open', 'is_sent', TotalListFilter, PaymentStateListFilter,
|
BillTypeListFilter, 'is_open', 'is_sent', TotalListFilter, PaymentStateListFilter,
|
||||||
AmendedListFilter
|
AmendedListFilter, 'account__is_active',
|
||||||
)
|
)
|
||||||
add_fields = ('account', 'type', 'amend_of', 'is_open', 'due_on', 'comments')
|
add_fields = ('account', 'type', 'amend_of', 'is_open', 'due_on', 'comments')
|
||||||
change_list_template = 'admin/bills/bill/change_list.html'
|
change_list_template = 'admin/bills/bill/change_list.html'
|
||||||
|
|
|
@ -77,12 +77,12 @@ class Bind9MasterDomainController(ServiceController):
|
||||||
)
|
)
|
||||||
if 'zone_path' in context:
|
if 'zone_path' in context:
|
||||||
context['zone_subdomains_path'] = re.sub(r'^(.*/)', r'\1*.', context['zone_path'])
|
context['zone_subdomains_path'] = re.sub(r'^(.*/)', r'\1*.', context['zone_path'])
|
||||||
self.append('rm -f %(zone_subdomains_path)s' % context)
|
self.append('rm -f -- %(zone_subdomains_path)s' % context)
|
||||||
|
|
||||||
def delete(self, domain):
|
def delete(self, domain):
|
||||||
context = self.get_context(domain)
|
context = self.get_context(domain)
|
||||||
self.append('# Delete zone file for %(name)s' % context)
|
self.append('# Delete zone file for %(name)s' % context)
|
||||||
self.append('rm -f %(zone_path)s;' % context)
|
self.append('rm -f -- %(zone_path)s;' % context)
|
||||||
self.delete_conf(context)
|
self.delete_conf(context)
|
||||||
|
|
||||||
def delete_conf(self, context):
|
def delete_conf(self, context):
|
||||||
|
|
|
@ -27,7 +27,7 @@ class LetsEncryptController(ServiceController):
|
||||||
self.append(" --webroot-path %(webroot)s \\" % context)
|
self.append(" --webroot-path %(webroot)s \\" % context)
|
||||||
self.append(" --email %(email)s \\" % context)
|
self.append(" --email %(email)s \\" % context)
|
||||||
self.append(" -d %(domains)s \\" % context)
|
self.append(" -d %(domains)s \\" % context)
|
||||||
self.cleanup.append("rm -rf %(webroot)s/.well-known" % context)
|
self.cleanup.append("rm -rf -- %(webroot)s/.well-known" % context)
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
self.append(" || exit_code=$?")
|
self.append(" || exit_code=$?")
|
||||||
|
@ -49,6 +49,6 @@ class LetsEncryptController(ServiceController):
|
||||||
return {
|
return {
|
||||||
'letsencrypt_auto': settings.LETSENCRYPT_AUTO_PATH,
|
'letsencrypt_auto': settings.LETSENCRYPT_AUTO_PATH,
|
||||||
'webroot': content.webapp.get_path(),
|
'webroot': content.webapp.get_path(),
|
||||||
'email': website.account.email,
|
'email': settings.LETSENCRYPT_EMAIL or website.account.email,
|
||||||
'domains': ' \\\n -d '.join(website.encrypt_domains),
|
'domains': ' \\\n -d '.join(website.encrypt_domains),
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,3 +9,9 @@ LETSENCRYPT_AUTO_PATH = Setting('LETSENCRYPT_AUTO_PATH',
|
||||||
LETSENCRYPT_LIVE_PATH = Setting('LETSENCRYPT_LIVE_PATH',
|
LETSENCRYPT_LIVE_PATH = Setting('LETSENCRYPT_LIVE_PATH',
|
||||||
'/etc/letsencrypt/live'
|
'/etc/letsencrypt/live'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LETSENCRYPT_EMAIL = Setting('LETSENCRYPT_EMAIL',
|
||||||
|
'',
|
||||||
|
help_text="Uses account.email by default",
|
||||||
|
)
|
||||||
|
|
|
@ -52,7 +52,7 @@ class MailmanVirtualDomainController(ServiceController):
|
||||||
|
|
||||||
def delete(self, mail_list):
|
def delete(self, mail_list):
|
||||||
context = self.get_context(mail_list)
|
context = self.get_context(mail_list)
|
||||||
self.include_virtual_alias_domain(context)
|
self.exclude_virtual_alias_domain(context)
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
context = self.get_context_files()
|
context = self.get_context_files()
|
||||||
|
|
|
@ -24,7 +24,7 @@ class SieveFilteringMixin:
|
||||||
context['box'] = box
|
context['box'] = box
|
||||||
self.append(textwrap.dedent("""
|
self.append(textwrap.dedent("""
|
||||||
# Create %(box)s mailbox
|
# Create %(box)s mailbox
|
||||||
su %(user)s --shell /bin/bash << 'EOF'
|
su - %(user)s --shell /bin/bash << 'EOF'
|
||||||
mkdir -p "%(maildir)s/.%(box)s"
|
mkdir -p "%(maildir)s/.%(box)s"
|
||||||
EOF
|
EOF
|
||||||
if ! grep '%(box)s' %(maildir)s/subscriptions > /dev/null; then
|
if ! grep '%(box)s' %(maildir)s/subscriptions > /dev/null; then
|
||||||
|
@ -39,7 +39,7 @@ class SieveFilteringMixin:
|
||||||
context['filtering'] = ('# %(banner)s\n' + content) % context
|
context['filtering'] = ('# %(banner)s\n' + content) % context
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
# Create and compile orchestra sieve filtering
|
# Create and compile orchestra sieve filtering
|
||||||
su %(user)s --shell /bin/bash << 'EOF'
|
su - %(user)s --shell /bin/bash << 'EOF'
|
||||||
mkdir -p $(dirname "%(filtering_path)s")
|
mkdir -p $(dirname "%(filtering_path)s")
|
||||||
cat << ' EOF' > %(filtering_path)s
|
cat << ' EOF' > %(filtering_path)s
|
||||||
%(filtering)s
|
%(filtering)s
|
||||||
|
@ -97,7 +97,7 @@ class UNIXUserMaildirController(SieveFilteringMixin, ServiceController):
|
||||||
#unit_to_bytes(mailbox.resources.disk.unit)
|
#unit_to_bytes(mailbox.resources.disk.unit)
|
||||||
self.append(textwrap.dedent("""
|
self.append(textwrap.dedent("""
|
||||||
# Set Maildir quota for %(user)s
|
# Set Maildir quota for %(user)s
|
||||||
su %(user)s --shell /bin/bash << 'EOF'
|
su - %(user)s --shell /bin/bash << 'EOF'
|
||||||
mkdir -p %(maildir)s
|
mkdir -p %(maildir)s
|
||||||
EOF
|
EOF
|
||||||
if [ ! -f %(maildir)s/maildirsize ]; then
|
if [ ! -f %(maildir)s/maildirsize ]; then
|
||||||
|
@ -121,7 +121,7 @@ class UNIXUserMaildirController(SieveFilteringMixin, ServiceController):
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.append("rm -fr %(base_home)s" % context)
|
self.append("rm -fr -- %(base_home)s" % context)
|
||||||
self.append(textwrap.dedent("""
|
self.append(textwrap.dedent("""
|
||||||
nohup bash -c '{ sleep 2 && killall -u %(user)s -s KILL; }' &> /dev/null &
|
nohup bash -c '{ sleep 2 && killall -u %(user)s -s KILL; }' &> /dev/null &
|
||||||
killall -u %(user)s || true
|
killall -u %(user)s || true
|
||||||
|
@ -195,7 +195,7 @@ class UNIXUserMaildirController(SieveFilteringMixin, ServiceController):
|
||||||
# if context['deleted_home']:
|
# if context['deleted_home']:
|
||||||
# self.append("mv %(home)s %(deleted_home)s || exit_code=$?" % context)
|
# self.append("mv %(home)s %(deleted_home)s || exit_code=$?" % context)
|
||||||
# else:
|
# else:
|
||||||
# self.append("rm -fr %(home)s" % context)
|
# self.append("rm -fr -- %(home)s" % context)
|
||||||
#
|
#
|
||||||
# def get_extra_fields(self, mailbox, context):
|
# def get_extra_fields(self, mailbox, context):
|
||||||
# context['quota'] = self.get_quota(mailbox)
|
# context['quota'] = self.get_quota(mailbox)
|
||||||
|
|
|
@ -45,9 +45,9 @@ def keep_log(execute, log, operations):
|
||||||
if not log.is_success:
|
if not log.is_success:
|
||||||
send_report(execute, args, log)
|
send_report(execute, args, log)
|
||||||
stdout = log.stdout.strip()
|
stdout = log.stdout.strip()
|
||||||
stdout and logger.debug('STDOUT %s', stdout)
|
stdout and logger.debug('STDOUT %s', stdout.encode('ascii', errors='replace').decode())
|
||||||
stderr = log.stderr.strip()
|
stderr = log.stderr.strip()
|
||||||
stderr and logger.debug('STDERR %s', stderr)
|
stderr and logger.debug('STDERR %s', stderr.encode('ascii', errors='replace').decode())
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@ -197,6 +197,7 @@ def collect(instance, action, **kwargs):
|
||||||
# Only schedule operations if the router has execution routes
|
# Only schedule operations if the router has execution routes
|
||||||
routes = router.objects.get_for_operation(operation, cache=route_cache)
|
routes = router.objects.get_for_operation(operation, cache=route_cache)
|
||||||
if routes:
|
if routes:
|
||||||
|
logger.debug("Operation %s collected for execution" % operation)
|
||||||
operation.routes = routes
|
operation.routes = routes
|
||||||
if iaction != Operation.DELETE:
|
if iaction != Operation.DELETE:
|
||||||
# usually we expect to be using last object state,
|
# usually we expect to be using last object state,
|
||||||
|
|
|
@ -97,7 +97,7 @@ class OperationsMiddleware(object):
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
""" Processes pending backend operations """
|
""" Processes pending backend operations """
|
||||||
if not isinstance(response, HttpResponseServerError):
|
if response.status_code != 500:
|
||||||
operations = self.get_pending_operations()
|
operations = self.get_pending_operations()
|
||||||
if operations:
|
if operations:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -285,7 +285,7 @@ class MetricStorageQuerySet(models.QuerySet):
|
||||||
last.save()
|
last.save()
|
||||||
else:
|
else:
|
||||||
error = decimal.Decimal(str(settings.ORDERS_METRIC_ERROR))
|
error = decimal.Decimal(str(settings.ORDERS_METRIC_ERROR))
|
||||||
if value > last.value+error or value < last.value-error:
|
if (value > last.value+error or value < last.value-error) or (value == 0 and last.value > 0):
|
||||||
self.create(order=order, value=value, updated_on=now)
|
self.create(order=order, value=value, updated_on=now)
|
||||||
else:
|
else:
|
||||||
last.updated_on = now
|
last.updated_on = now
|
||||||
|
|
|
@ -26,6 +26,8 @@ ORDERS_EXCLUDED_APPS = Setting('ORDERS_EXCLUDED_APPS',
|
||||||
'orchestration',
|
'orchestration',
|
||||||
'bills',
|
'bills',
|
||||||
'services',
|
'services',
|
||||||
|
'mailer',
|
||||||
|
'issues',
|
||||||
),
|
),
|
||||||
help_text="Prevent inspecting these apps for service accounting."
|
help_text="Prevent inspecting these apps for service accounting."
|
||||||
)
|
)
|
||||||
|
|
|
@ -77,7 +77,7 @@ Notice that two optional forms can be provided `form` and `change_form`. When no
|
||||||
A backend class is required to interface with the web application and perform `save()` and `delete()` operations on it.
|
A backend class is required to interface with the web application and perform `save()` and `delete()` operations on it.
|
||||||
|
|
||||||
- The more reliable way of interfacing with the application is by means of a CLI (e.g. [Moodle](backends/moodle.py)), but not all CMS come with this tool.
|
- The more reliable way of interfacing with the application is by means of a CLI (e.g. [Moodle](backends/moodle.py)), but not all CMS come with this tool.
|
||||||
- The second preferable way is using some sort of API, possibly HTTP-based (e.g. [gitLab](backends/gitlab.py)). This is less reliable because additional moving parts are used underneath the interface; a busy web server can timeout our requests.
|
- The second preferable way is using some sort of networked API, possibly HTTP-based (e.g. [gitLab](backends/gitlab.py)). This is less reliable because additional moving parts are used underneath the interface; a busy web server can timeout our requests.
|
||||||
- The least preferred way is interfacing with an HTTP-HTML interface designed for human consumption, really painful to implement but sometimes is the only way (e.g. [WordPress](backends/wordpressmu.py)).
|
- The least preferred way is interfacing with an HTTP-HTML interface designed for human consumption, really painful to implement but sometimes is the only way (e.g. [WordPress](backends/wordpressmu.py)).
|
||||||
|
|
||||||
Some applications do not support multi-tenancy by default, but we can hack the configuration file of such apps and generate *table prefix* or *database name* based on some property of the URL. Example of this services are [moodle](backends/moodle.py) and [phplist](backends/phplist.py) respectively.
|
Some applications do not support multi-tenancy by default, but we can hack the configuration file of such apps and generate *table prefix* or *database name* based on some property of the URL. Example of this services are [moodle](backends/moodle.py) and [phplist](backends/phplist.py) respectively.
|
||||||
|
|
|
@ -88,7 +88,7 @@ class MoodleMuController(ServiceController):
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
# Configuring Moodle crontabs
|
# Configuring Moodle crontabs
|
||||||
if ! crontab -u %(user)s -l | grep 'Moodle:"%(site_name)s"' > /dev/null; then
|
if ! crontab -u %(user)s -l | grep 'Moodle:"%(site_name)s"' > /dev/null; then
|
||||||
cat << EOF | su %(user)s --shell /bin/bash -c 'crontab'
|
cat << EOF | su - %(user)s --shell /bin/bash -c 'crontab'
|
||||||
$(crontab -u %(user)s -l)
|
$(crontab -u %(user)s -l)
|
||||||
|
|
||||||
# %(banner)s - Moodle:"%(site_name)s"
|
# %(banner)s - Moodle:"%(site_name)s"
|
||||||
|
@ -139,7 +139,7 @@ class MoodleMuController(ServiceController):
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
crontab -u %(user)s -l \\
|
crontab -u %(user)s -l \\
|
||||||
| grep -v 'Moodle:"%(site_name)s"\\|%(crontab_regex)s' \\
|
| grep -v 'Moodle:"%(site_name)s"\\|%(crontab_regex)s' \\
|
||||||
| su %(user)s --shell /bin/bash -c 'crontab'
|
| su - %(user)s --shell /bin/bash -c 'crontab'
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
self.delete_site_map(context)
|
self.delete_site_map(context)
|
||||||
|
|
|
@ -88,7 +88,7 @@ class PhpListSaaSController(ServiceController):
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
# Configuring phpList crontabs
|
# Configuring phpList crontabs
|
||||||
if ! crontab -u %(user)s -l | grep 'phpList:"%(site_name)s"' > /dev/null; then
|
if ! crontab -u %(user)s -l | grep 'phpList:"%(site_name)s"' > /dev/null; then
|
||||||
cat << EOF | su %(user)s --shell /bin/bash -c 'crontab'
|
cat << EOF | su - %(user)s --shell /bin/bash -c 'crontab'
|
||||||
$(crontab -u %(user)s -l)
|
$(crontab -u %(user)s -l)
|
||||||
|
|
||||||
# %(banner)s - phpList:"%(site_name)s"
|
# %(banner)s - phpList:"%(site_name)s"
|
||||||
|
@ -105,7 +105,7 @@ class PhpListSaaSController(ServiceController):
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
crontab -u %(user)s -l \\
|
crontab -u %(user)s -l \\
|
||||||
| grep -v 'phpList:"%(site_name)s"\\|%(crontab_regex)s' \\
|
| grep -v 'phpList:"%(site_name)s"\\|%(crontab_regex)s' \\
|
||||||
| su %(user)s --shell /bin/bash -c 'crontab'
|
| su - %(user)s --shell /bin/bash -c 'crontab'
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ def create_link(modeladmin, request, queryset):
|
||||||
messages.error(request, "Users from the same account should be selected.")
|
messages.error(request, "Users from the same account should be selected.")
|
||||||
return
|
return
|
||||||
user = queryset[0]
|
user = queryset[0]
|
||||||
form = LinkForm(user)
|
form = LinkForm(user, queryset=queryset)
|
||||||
action_value = 'create_link'
|
action_value = 'create_link'
|
||||||
if request.POST.get('post') == 'generic_confirmation':
|
if request.POST.get('post') == 'generic_confirmation':
|
||||||
form = LinkForm(user, request.POST, queryset=queryset)
|
form = LinkForm(user, request.POST, queryset=queryset)
|
||||||
|
|
|
@ -115,7 +115,7 @@ class UNIXUserController(ServiceController):
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.append("rm -fr '%(base_home)s'" % context)
|
self.append("rm -fr -- '%(base_home)s'" % context)
|
||||||
|
|
||||||
def grant_permissions(self, user, context):
|
def grant_permissions(self, user, context):
|
||||||
context['perms'] = user.set_perm_perms
|
context['perms'] = user.set_perm_perms
|
||||||
|
@ -206,7 +206,7 @@ class UNIXUserController(ServiceController):
|
||||||
})
|
})
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
# Create link
|
# Create link
|
||||||
su %(user)s --shell /bin/bash << 'EOF' || exit_code=1
|
su - %(user)s --shell /bin/bash << 'EOF' || exit_code=1
|
||||||
if [[ ! -e '%(link_name)s' ]]; then
|
if [[ ! -e '%(link_name)s' ]]; then
|
||||||
ln -s '%(link_target)s' '%(link_name)s'
|
ln -s '%(link_target)s' '%(link_name)s'
|
||||||
else
|
else
|
||||||
|
|
|
@ -96,8 +96,7 @@ class LinkForm(forms.Form):
|
||||||
widget=forms.TextInput(attrs={'size':'70'}),
|
widget=forms.TextInput(attrs={'size':'70'}),
|
||||||
help_text=_("Relative path to chosen directory."))
|
help_text=_("Relative path to chosen directory."))
|
||||||
link_name = forms.CharField(label=_("Link name"), required=False, initial='',
|
link_name = forms.CharField(label=_("Link name"), required=False, initial='',
|
||||||
widget=forms.TextInput(attrs={'size':'70'}),
|
widget=forms.TextInput(attrs={'size':'70'}))
|
||||||
help_text=_("If left blank or relative path: link will be created in each user home."))
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.instance = args[0]
|
self.instance = args[0]
|
||||||
|
@ -110,6 +109,12 @@ class LinkForm(forms.Form):
|
||||||
self.fields['base_home'].choices = (
|
self.fields['base_home'].choices = (
|
||||||
(user.get_base_home(), user.get_base_home()) for user in related_users
|
(user.get_base_home(), user.get_base_home()) for user in related_users
|
||||||
)
|
)
|
||||||
|
if len(self.queryset) == 1:
|
||||||
|
user = self.instance
|
||||||
|
help_text = _("If left blank or relative path: the link will be created in %s home.") % user
|
||||||
|
else:
|
||||||
|
help_text = _("If left blank or relative path: the link will be created in each user home.")
|
||||||
|
self.fields['link_name'].help_text = help_text
|
||||||
|
|
||||||
def clean_home_extension(self):
|
def clean_home_extension(self):
|
||||||
home_extension = self.cleaned_data['home_extension']
|
home_extension = self.cleaned_data['home_extension']
|
||||||
|
|
|
@ -70,7 +70,7 @@ class MoodleController(WebAppServiceMixin, ServiceController):
|
||||||
# Run install moodle cli command on the background, because it takes so long...
|
# Run install moodle cli command on the background, because it takes so long...
|
||||||
stdout=$(mktemp)
|
stdout=$(mktemp)
|
||||||
stderr=$(mktemp)
|
stderr=$(mktemp)
|
||||||
nohup su %(user)s --shell /bin/bash << 'EOF' > $stdout 2> $stderr &
|
nohup su - %(user)s --shell /bin/bash << 'EOF' > $stdout 2> $stderr &
|
||||||
php %(app_path)s/admin/cli/install_database.php \\
|
php %(app_path)s/admin/cli/install_database.php \\
|
||||||
--fullname="%(site_name)s" \\
|
--fullname="%(site_name)s" \\
|
||||||
--shortname="%(site_name)s" \\
|
--shortname="%(site_name)s" \\
|
||||||
|
|
Loading…
Reference in New Issue