lists functional tests passing

This commit is contained in:
Marc 2014-10-15 12:47:28 +00:00
parent 4c7c5b5505
commit 3e246f9fe0
17 changed files with 290 additions and 86 deletions

View File

@ -147,3 +147,7 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* POST only fields (account, username, name) etc
* for list virtual_domains cleaning up we need to know the old domain name when a list changes its address domain, but this is not possible with the current design.
* update_fields=[] doesn't trigger post save!

View File

@ -11,11 +11,20 @@ class SetPasswordApiMixin(object):
obj = self.get_object()
data = request.DATA
if isinstance(data, basestring):
data = {'password': data}
data = {
'password': data
}
serializer = SetPasswordSerializer(data=data)
if serializer.is_valid():
obj.set_password(serializer.data['password'])
obj.save(update_fields=['password'])
return Response({'status': 'password changed'})
try:
obj.save(update_fields=['password'])
except ValueError:
# Some services don't store the password on the database
# update_fields=[] doesn't trigger post save!
obj.save()
return Response({
'status': 'password changed'
})
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

View File

@ -74,6 +74,7 @@ class MySQLUserBackend(ServiceController):
}
# TODO https://docs.djangoproject.com/en/1.7/ref/signals/#m2m-changed
class MySQLPermissionBackend(ServiceController):
model = 'databases.UserDatabaseRelation'
verbose_name = "MySQL permission"

View File

@ -70,6 +70,9 @@ class DatabaseTestMixin(object):
self.validate_login_error(dbname, username, password)
self.validate_create_table(dbname, username, new_password)
# TODO test add user
# TODO remove user
# TODO remove all users
class MySQLBackendMixin(object):
db_type = 'mysql'

View File

@ -3,7 +3,7 @@ from django.conf.urls import patterns
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.utils import admin_link
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
@ -11,7 +11,7 @@ from .forms import ListCreationForm, ListChangeForm
from .models import List
class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'address_name', 'address_domain_link', 'account_link')
add_fieldsets = (
(None, {
@ -38,7 +38,7 @@ class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
}),
(_("Admin"), {
'classes': ('wide',),
'fields': ('admin_email', 'password',),
'fields': ('password',),
}),
)
readonly_fields = ('account_link',)

View File

@ -1,3 +1,4 @@
import re
import textwrap
from django.utils import timezone
@ -12,8 +13,23 @@ from .models import List
class MailmanBackend(ServiceController):
verbose_name = "Mailman"
model = 'lists.List'
addresses = [
'',
'-admin',
'-bounces',
'-confirm',
'-join',
'-leave',
'-owner',
'-request',
'-subscribe',
'-unsubscribe'
]
def include_virtual_alias_domain(self, context):
# TODO for list virtual_domains cleaning up we need to know the old domain name when a list changes its address
# domain, but this is not possible with the current design.
# sync the whole file everytime?
if context['address_domain']:
self.append(textwrap.dedent("""
[[ $(grep "^\s*%(address_domain)s\s*$" %(virtual_alias_domains)s) ]] || {
@ -29,42 +45,62 @@ class MailmanBackend(ServiceController):
def get_virtual_aliases(self, context):
aliases = []
addresses = [
'',
'-admin',
'-bounces',
'-confirm',
'-join',
'-leave',
'-owner',
'-request',
'-subscribe',
'-unsubscribe'
]
for address in addresses:
for address in self.addresses:
context['address'] = address
aliases.append("%(address_name)s%(address)s@%(domain)s\t%(name)s%(address)s" % context)
return '\n'.join(aliases)
def save(self, mail_list):
if not getattr(mail_list, 'password', None):
# TODO
# Create only support for now
return
context = self.get_context(mail_list)
self.append("newlist --quiet --emailhost='%(domain)s' '%(name)s' '%(admin)s' '%(password)s'" % context)
# Create list
self.append(textwrap.dedent("""\
[[ ! -e %(mailman_root)s/lists/%(name)s ]] && {
newlist --quiet --emailhost='%(domain)s' '%(name)s' '%(admin)s' '%(password)s'
}""" % context))
# Custom domain
if mail_list.address:
context['aliases'] = self.get_virtual_aliases(context)
self.append(
"if [[ ! $(grep '^\s*%(name)s\s' %(virtual_alias)s) ]]; then\n"
" echo '# %(banner)s\n%(aliases)s\n' >> %(virtual_alias)s\n"
" UPDATED_VIRTUAL_ALIAS=1\n"
"fi" % context
)
aliases = self.get_virtual_aliases(context)
# Preserve indentation
spaces = ' '*4
context['aliases'] = spaces + aliases.replace('\n', '\n'+spaces)
self.append(textwrap.dedent("""\
if [[ ! $(grep '\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
echo '# %(banner)s\n%(aliases)s
' >> %(virtual_alias)s
UPDATED_VIRTUAL_ALIAS=1
else
if [[ ! $(grep '^\s*%(address_name)s@%(address_domain)s\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
sed -i "s/^.*\s%(name)s\s*$//" %(virtual_alias)s
echo '# %(banner)s\n%(aliases)s
' >> %(virtual_alias)s
UPDATED_VIRTUAL_ALIAS=1
fi
fi""" % context
))
self.append('echo "require_explicit_destination = 0" | '
'%(mailman_root)s/bin/config_list -i /dev/stdin %(name)s' % context)
self.append(textwrap.dedent("""\
echo "host_name = '%(address_domain)s'" | \
%(mailman_root)s/bin/config_list -i /dev/stdin %(name)s""" % context))
else:
# Cleanup shit
self.append(textwrap.dedent("""\
if [[ ! $(grep '\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
sed -i "s/^.*\s%(name)s\s*$//" %(virtual_alias)s
fi""" % context
))
# Update
if context['password'] is not None:
self.append('%(mailman_root)s/bin/change_pw --listname="%(name)s" --password="%(password)s"' % context)
self.include_virtual_alias_domain(context)
def delete(self, mail_list):
pass
context = self.get_context(mail_list)
self.exclude_virtual_alias_domain(context)
for address in self.addresses:
context['address'] = address
self.append('sed -i "s/^.*\s%(name)s%(address)s\s*$//" %(virtual_alias)s' % context)
self.append("rmlist -a %(name)s" % context)
def commit(self):
context = self.get_context_files()
@ -77,7 +113,7 @@ class MailmanBackend(ServiceController):
def get_context_files(self):
return {
'virtual_alias': settings.LISTS_VIRTUAL_ALIAS_PATH,
'virtual_alias_domains': settings.MAILS_VIRTUAL_ALIAS_DOMAINS_PATH,
'virtual_alias_domains': settings.LISTS_VIRTUAL_ALIAS_DOMAINS_PATH,
}
def get_context(self, mail_list):
@ -90,6 +126,7 @@ class MailmanBackend(ServiceController):
'address_name': mail_list.address_name,
'address_domain': mail_list.address_domain,
'admin': mail_list.admin_email,
'mailman_root': settings.LISTS_MAILMAN_ROOT_PATH,
})
return context
@ -101,7 +138,7 @@ class MailmanTraffic(ServiceMonitor):
def prepare(self):
current_date = timezone.localtime(self.current_date)
current_date = current_date.strftime("%b %d %H:%M:%S")
self.append(textwrap.dedent("""
self.append(textwrap.dedent("""\
function monitor () {
OBJECT_ID=$1
LAST_DATE=$2

View File

@ -21,6 +21,8 @@ class List(models.Model):
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
related_name='lists')
password = None
class Meta:
unique_together = ('address_name', 'address_domain')

View File

@ -1,7 +1,6 @@
from django.conf import settings
LISTS_DOMAIN_MODEL = getattr(settings, 'LISTS_DOMAIN_MODEL', 'domains.Domain')
@ -12,9 +11,13 @@ LISTS_MAILMAN_POST_LOG_PATH = getattr(settings, 'LISTS_MAILMAN_POST_LOG_PATH',
'/var/log/mailman/post')
LISTS_MAILMAN_ROOT_PATH = getattr(settings, 'LISTS_MAILMAN_ROOT_PATH',
'/var/lib/mailman/')
LISTS_VIRTUAL_ALIAS_PATH = getattr(settings, 'LISTS_VIRTUAL_ALIAS_PATH',
'/etc/postfix/mailman_virtual_aliases')
MAILS_VIRTUAL_ALIAS_DOMAINS_PATH = getattr(settings, 'MAILS_VIRTUAL_ALIAS_DOMAINS_PATH',
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = getattr(settings, 'LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
'/etc/postfix/mailman_virtual_domains')

View File

@ -3,6 +3,7 @@ import os
import smtplib
import time
import textwrap
import requests
from email.mime.text import MIMEText
from django.conf import settings as djsettings
@ -11,12 +12,14 @@ from django.core.management.base import CommandError
from django.core.urlresolvers import reverse
from selenium.webdriver.support.select import Select
from orchestra.admin.utils import change_url
from orchestra.apps.accounts.models import Account
from orchestra.apps.domains.models import Domain
from orchestra.apps.orchestration.models import Server, Route
from orchestra.apps.resources.models import Resource
from orchestra.utils.system import run, sshrun
from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii, snapshot_on_error, save_response_on_error
from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii, snapshot_on_error,
save_response_on_error)
from ... import backends, settings
from ...models import List
@ -40,10 +43,30 @@ class ListMixin(object):
if not address:
address = "%s@%s" % (name, settings.LISTS_DEFAULT_DOMAIN)
subscribe_address = "{}-subscribe@{}".format(*address.split('@'))
request_address = "{}-request@{}".format(name, address.split('@')[1])
self.subscribe(subscribe_address)
time.sleep(2)
time.sleep(3)
sshrun(self.MASTER_SERVER,
'grep -v ":\|^\s\|^$\|-\|\.\|\s" /var/spool/mail/nobody | base64 -d | grep "%s"' % address, display=False)
'grep -v ":\|^\s\|^$\|-\|\.\|\s" /var/spool/mail/nobody | base64 -d | grep "%s"'
% request_address, display=False)
def validate_login(self, name, password):
url = 'http://%s/cgi-bin/mailman/admin/%s' % (settings.LISTS_DEFAULT_DOMAIN, name)
self.assertEqual(200, requests.post(url, data={'adminpw': password}).status_code)
def validate_delete(self, name):
context = {
'name': name,
'domain': Domain.objects.get().name,
'virtual_domain': settings.LISTS_VIRTUAL_ALIAS_DOMAINS_PATH,
'virtual_alias': settings.LISTS_VIRTUAL_ALIAS_PATH,
}
self.assertRaises(CommandError, sshrun, self.MASTER_SERVER,
'grep "\s%(name)s\s*" %(virtual_alias)s' % context, display=False)
self.assertRaises(CommandError, sshrun, self.MASTER_SERVER,
'grep "^\s*$(domain)s\s*$" %(virtual_domain)s' % context, display=False)
self.assertRaises(CommandError, sshrun, self.MASTER_SERVER,
'list_lists | grep -i "^\s*%(name)s\s"' % context, display=False)
def subscribe(self, subscribe_address):
msg = MIMEText('')
@ -70,19 +93,75 @@ class ListMixin(object):
admin_email = 'root@test3.orchestra.lan'
self.add(name, password, admin_email)
self.validate_add(name)
# self.addCleanup(self.delete, username)
self.validate_login(name, password)
self.addCleanup(self.delete, name)
def test_add_with_address(self):
name = '%s_list' % random_ascii(10)
password = '@!?%spppP001' % random_ascii(5)
print password
admin_email = 'root@test3.orchestra.lan'
address_name = '%s_name' % random_ascii(10)
domain_name = '%sdomain.lan' % random_ascii(10)
address_domain = Domain.objects.create(name=domain_name, account=self.account)
self.add(name, password, admin_email, address_name=address_name, address_domain=address_domain)
self.addCleanup(self.delete, name)
# Mailman doesn't support changing the address, only the domain
self.validate_add(name, address="%s@%s" % (address_name, address_domain))
def test_change_password(self):
name = '%s_list' % random_ascii(10)
password = '@!?%spppP001' % random_ascii(5)
admin_email = 'root@test3.orchestra.lan'
self.add(name, password, admin_email)
self.addCleanup(self.delete, name)
self.validate_login(name, password)
new_password = '@!?%spppP001' % random_ascii(5)
self.change_password(name, new_password)
self.validate_login(name, new_password)
def test_change_domain(self):
name = '%s_list' % random_ascii(10)
password = '@!?%spppP001' % random_ascii(5)
admin_email = 'root@test3.orchestra.lan'
address_name = '%s_name' % random_ascii(10)
domain_name = '%sdomain.lan' % random_ascii(10)
address_domain = Domain.objects.create(name=domain_name, account=self.account)
self.add(name, password, admin_email, address_name=address_name, address_domain=address_domain)
self.addCleanup(self.delete, name)
# Mailman doesn't support changing the address, only the domain
domain_name = '%sdomain.lan' % random_ascii(10)
address_domain = Domain.objects.create(name=domain_name, account=self.account)
self.update_domain(name, domain_name)
self.validate_add(name, address="%s@%s" % (address_name, address_domain))
def test_change_address_name(self):
name = '%s_list' % random_ascii(10)
password = '@!?%spppP001' % random_ascii(5)
admin_email = 'root@test3.orchestra.lan'
address_name = '%s_name' % random_ascii(10)
domain_name = '%sdomain.lan' % random_ascii(10)
address_domain = Domain.objects.create(name=domain_name, account=self.account)
self.add(name, password, admin_email, address_name=address_name, address_domain=address_domain)
# self.addCleanup(self.delete, name)
# Mailman doesn't support changing the address, only the domain
address_name = '%s_name' % random_ascii(10)
self.update_address_name(name, address_name)
self.validate_add(name, address="%s@%s" % (address_name, address_domain))
def test_delete(self):
name = '%s_list' % random_ascii(10)
password = '@!?%spppP001' % random_ascii(5)
admin_email = 'root@test3.orchestra.lan'
address_name = '%s_name' % random_ascii(10)
domain_name = '%sdomain.lan' % random_ascii(10)
address_domain = Domain.objects.create(name=domain_name, account=self.account)
self.add(name, password, admin_email, address_name=address_name, address_domain=address_domain)
# Mailman doesn't support changing the address, only the domain
self.validate_add(name, address="%s@%s" % (address_name, address_domain))
self.delete(name)
self.assertRaises(AssertionError, self.validate_login, name, password)
self.validate_delete(name)
class RESTListMixin(ListMixin):
def setUp(self):
@ -101,8 +180,23 @@ class RESTListMixin(ListMixin):
@save_response_on_error
def delete(self, name):
list = self.rest.lists.retrieve(name=name).get()
list.delete()
self.rest.lists.retrieve(name=name).delete()
@save_response_on_error
def change_password(self, name, password):
mail_list = self.rest.lists.retrieve(name=name).get()
mail_list.set_password(password)
@save_response_on_error
def update_domain(self, name, domain_name):
mail_list = self.rest.lists.retrieve(name=name).get()
domain = self.rest.domains.retrieve(name=domain_name).get()
mail_list.update(address_domain=domain.url)
@save_response_on_error
def update_address_name(self, name, address_name):
mail_list = self.rest.lists.retrieve(name=name).get()
mail_list.update(address_name=address_name)
class AdminListMixin(ListMixin):
@ -111,48 +205,76 @@ class AdminListMixin(ListMixin):
self.admin_login()
@snapshot_on_error
def add(self, name, password, admin_email):
url = self.live_server_url + reverse('admin:mails_List_add')
def add(self, name, password, admin_email, address_name=None, address_domain=None):
url = self.live_server_url + reverse('admin:lists_list_add')
self.selenium.get(url)
account_input = self.selenium.find_element_by_id('id_account')
account_select = Select(account_input)
account_select.select_by_value(str(self.account.pk))
name_field = self.selenium.find_element_by_id('id_name')
name_field.send_keys(username)
name_field.send_keys(name)
password_field = self.selenium.find_element_by_id('id_password1')
password_field.send_keys(password)
password_field = self.selenium.find_element_by_id('id_password2')
password_field.send_keys(password)
if quota is not None:
quota_id = 'id_resources-resourcedata-content_type-object_id-0-allocated'
quota_field = self.selenium.find_element_by_id(quota_id)
quota_field.clear()
quota_field.send_keys(quota)
admin_email_field = self.selenium.find_element_by_id('id_admin_email')
admin_email_field.send_keys(admin_email)
if filtering is not None:
filtering_input = self.selenium.find_element_by_id('id_filtering')
filtering_select = Select(filtering_input)
filtering_select.select_by_value("CUSTOM")
filtering_inline = self.selenium.find_element_by_id('fieldsetcollapser0')
filtering_inline.click()
time.sleep(0.5)
filtering_field = self.selenium.find_element_by_id('id_custom_filtering')
filtering_field.send_keys(filtering)
if address_name:
address_name_field = self.selenium.find_element_by_id('id_address_name')
address_name_field.send_keys(address_name)
domain = Domain.objects.get(name=address_domain)
domain_input = self.selenium.find_element_by_id('id_address_domain')
domain_select = Select(domain_input)
domain_select.select_by_value(str(domain.pk))
name_field.submit()
self.assertNotEqual(url, self.selenium.current_url)
@snapshot_on_error
def delete(self, name):
mail_list = List.objects.get(name=name)
self.admin_delete(mail_list)
@snapshot_on_error
def change_password(self, name, password):
mail_list = List.objects.get(name=name)
self.admin_change_password(mail_list, password)
@snapshot_on_error
def update_domain(self, name, domain_name):
mail_list = List.objects.get(name=name)
url = self.live_server_url + change_url(mail_list)
self.selenium.get(url)
domain = Domain.objects.get(name=domain_name)
domain_input = self.selenium.find_element_by_id('id_address_domain')
domain_select = Select(domain_input)
domain_select.select_by_value(str(domain.pk))
save = self.selenium.find_element_by_name('_save')
save.submit()
self.assertNotEqual(url, self.selenium.current_url)
@snapshot_on_error
def update_address_name(self, name, address_name):
mail_list = List.objects.get(name=name)
url = self.live_server_url + change_url(mail_list)
self.selenium.get(url)
address_name_field = self.selenium.find_element_by_id('id_address_name')
address_name_field.clear()
address_name_field.send_keys(address_name)
save = self.selenium.find_element_by_name('_save')
save.submit()
self.assertNotEqual(url, self.selenium.current_url)
class RESTListTest(RESTListMixin, BaseLiveServerTestCase):
pass
#class AdminListTest(AdminListMixin, BaseLiveServerTestCase):
# pass
class AdminListTest(AdminListMixin, BaseLiveServerTestCase):
pass

View File

@ -233,6 +233,9 @@ class MailboxMixin(object):
sshrun(self.MASTER_SERVER,
"grep '%s' %s/Maildir/.%s/new/*" % (token, home, folder), display=False)
# TODO test update shit
# TODO test autoreply
class RESTMailboxMixin(MailboxMixin):
def setUp(self):

View File

@ -24,6 +24,8 @@ def BashSSH(backend, log, server, cmds):
log.script = script
log.save(update_fields=['script'])
logger.debug('%s is going to be executed on %s' % (backend, server))
channel = None
ssh = None
try:
# Avoid "Argument list too long" on large scripts by genereting a file
# and scping it to the remote server
@ -93,8 +95,10 @@ def BashSSH(backend, log, server, cmds):
logger.debug(log.traceback)
log.save()
finally:
channel.close()
ssh.close()
if channel is not None:
channel.close()
if ssh is not None:
ssh.close()
def Python(backend, log, server, cmds):

View File

@ -1,4 +1,3 @@
import copy
from threading import local
from django.core.urlresolvers import resolve
@ -16,12 +15,12 @@ from .models import BackendOperation as Operation
@receiver(post_save, dispatch_uid='orchestration.post_save_collector')
def post_save_collector(sender, *args, **kwargs):
if sender != BackendLog:
if sender not in [BackendLog, Operation]:
OperationsMiddleware.collect(Operation.SAVE, **kwargs)
@receiver(pre_delete, dispatch_uid='orchestration.pre_delete_collector')
def pre_delete_collector(sender, *args, **kwargs):
if sender != BackendLog:
if sender not in [BackendLog, Operation]:
OperationsMiddleware.collect(Operation.DELETE, **kwargs)
@ -49,7 +48,6 @@ class OperationsMiddleware(object):
request = getattr(cls.thread_locals, 'request', None)
if request is None:
return
good_action = action
pending_operations = cls.get_pending_operations()
for backend in ServiceBackend.get_backends():
instance = None
@ -84,8 +82,6 @@ class OperationsMiddleware(object):
break
if not execute:
continue
instance = copy.copy(instance)
good = instance
operation = Operation.create(backend, instance, action)
if action != Operation.DELETE:
# usually we expect to be using last object state,

View File

@ -1,3 +1,4 @@
import copy
import socket
from django.contrib.contenttypes import generic
@ -124,6 +125,9 @@ class BackendOperation(models.Model):
def create(cls, backend, instance, action):
op = cls(backend=backend.get_name(), instance=instance, action=action)
op.backend = backend
# instance should maintain any dynamic attribute until backend execution
# deep copy is prefered over copy otherwise objects will share same atributes (queryset cache)
op.instance = copy.deepcopy(instance)
return op
@classmethod

View File

@ -10,6 +10,7 @@ from django.core.management.base import CommandError
from django.core.urlresolvers import reverse
from selenium.webdriver.support.select import Select
from orchestra.admin.utils import change_url
from orchestra.apps.accounts.models import Account
from orchestra.apps.orchestration.models import Server, Route
from orchestra.utils.system import run, sshrun
@ -268,8 +269,7 @@ class AdminSystemUserMixin(SystemUserMixin):
@snapshot_on_error
def add_group(self, username, groupname):
user = SystemUser.objects.get(username=username)
change = reverse('admin:systemusers_systemuser_change', args=(user.pk,))
url = self.live_server_url + change
url = self.live_server_url + change_url(user)
self.selenium.get(url)
groups = self.selenium.find_element_by_id('id_groups_add_all_link')
groups.click()
@ -281,8 +281,7 @@ class AdminSystemUserMixin(SystemUserMixin):
@snapshot_on_error
def save(self, username):
user = SystemUser.objects.get(username=username)
change = reverse('admin:systemusers_systemuser_change', args=(user.pk,))
url = self.live_server_url + change
url = self.live_server_url + change_url(user)
self.selenium.get(url)
save = self.selenium.find_element_by_name('_save')
save.submit()
@ -367,3 +366,10 @@ class AdminSystemUserTest(AdminSystemUserMixin, BaseLiveServerTestCase):
self.assertNotEqual(url, self.selenium.current_url)
self.assertRaises(ftplib.error_perm, self.validate_ftp, username, password)
self.selenium.get(url)
self.assertNotEqual(url, self.selenium.current_url)
# Reenable for test cleanup
self.account.is_active = True
self.account.save()
# self.admin_login()

View File

@ -79,10 +79,7 @@ class RESTWebsiteMixin(RESTWebAppMixin):
@save_response_on_error
def delete_website(self, name):
print 'hola'
pass
self.rest.websites.retrieve(name=name).delete()
# self.rest.websites.retrieve(name=name).delete()
@save_response_on_error
def add_content(self, website, webapp, path):
@ -94,6 +91,12 @@ class RESTWebsiteMixin(RESTWebAppMixin):
})
website.save()
# TODO test disable
# TODO test https (refactor ssl)
# TODO test php options
# TODO read php-version /fpm/fcgid
# TODO max_processes, timeouts, memory...
class StaticRESTWebsiteTest(RESTWebsiteMixin, StaticWebAppMixin, WebsiteMixin, BaseLiveServerTestCase):
def test_mix_webapps(self):

View File

@ -168,7 +168,8 @@ function install_requirements () {
orchestra-orm==dev \
django-debug-toolbar==1.2.1 \
django-nose==1.2 \
sqlparse"
sqlparse
requests"
fi
# Make sure locales are in place before installing postgres

View File

@ -46,3 +46,9 @@ sed -i "s/DEFAULT_EMAIL_HOST\s*=\s*.*/DEFAULT_EMAIL_HOST = 'lists.orchestra.lan'
sed -i "s/DEFAULT_URL_HOST\s*=\s*.*/DEFAULT_URL_HOST = 'lists.orchestra.lan'/" /etc/mailman/mm_cfg.py
# apache
cp /etc/mailman/apache.conf /etc/apache2/sites-available/mailman.conf
a2ensite mailman.conf
/etc/init.d/apache2 restart