Random fixes

This commit is contained in:
Marc Aymerich 2016-06-17 10:00:04 +00:00
parent 0e850d6e2d
commit d96afe41fa
18 changed files with 44 additions and 28 deletions

View File

@ -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

View File

@ -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'

View File

@ -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):

View File

@ -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),
} }

View File

@ -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",
)

View File

@ -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()

View File

@ -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)

View File

@ -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,

View File

@ -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:

View File

@ -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

View File

@ -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."
) )

View File

@ -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.

View File

@ -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)

View File

@ -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
) )

View File

@ -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)

View File

@ -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

View File

@ -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']

View File

@ -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" \\