Merge pull request #6 from ribaguifi/dev/python36support
Refactor to support Python 3.6 and Django 2.0
This commit is contained in:
commit
422305a636
|
@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from fluent_dashboard import dashboard, appsettings
|
from fluent_dashboard import dashboard, appsettings
|
||||||
from fluent_dashboard.modules import CmsAppIconList
|
from fluent_dashboard.modules import CmsAppIconList
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from admin_tools.menu import items, Menu
|
from admin_tools.menu import items, Menu
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from functools import wraps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.core.urlresolvers import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
from rest_framework.reverse import reverse
|
from rest_framework.reverse import reverse
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class APIRoot(views.APIView):
|
||||||
'accountancy': {},
|
'accountancy': {},
|
||||||
'services': {},
|
'services': {},
|
||||||
}
|
}
|
||||||
if not request.user.is_anonymous():
|
if not request.user.is_anonymous:
|
||||||
list_name = '{basename}-list'
|
list_name = '{basename}-list'
|
||||||
detail_name = '{basename}-detail'
|
detail_name = '{basename}-detail'
|
||||||
for prefix, viewset, basename in self.router.registry:
|
for prefix, viewset, basename in self.router.registry:
|
||||||
|
|
|
@ -157,7 +157,7 @@ function install_requirements () {
|
||||||
PIP="${PIP} \
|
PIP="${PIP} \
|
||||||
selenium \
|
selenium \
|
||||||
xvfbwrapper \
|
xvfbwrapper \
|
||||||
freezegun \
|
freezegun==0.3.14 \
|
||||||
coverage \
|
coverage \
|
||||||
flake8 \
|
flake8 \
|
||||||
django-debug-toolbar==1.3.0 \
|
django-debug-toolbar==1.3.0 \
|
||||||
|
|
|
@ -178,7 +178,7 @@ def fire_pending_tasks(manage, db):
|
||||||
if is_due(now, minute, hour, day_of_week, day_of_month, month_of_year):
|
if is_due(now, minute, hour, day_of_week, day_of_month, month_of_year):
|
||||||
command = 'python3 -W ignore::DeprecationWarning {manage} runtask {task_id}'.format(
|
command = 'python3 -W ignore::DeprecationWarning {manage} runtask {task_id}'.format(
|
||||||
manage=manage, task_id=task_id)
|
manage=manage, task_id=task_id)
|
||||||
proc = run(command, async=True)
|
proc = run(command, run_async=True)
|
||||||
yield proc
|
yield proc
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ def fire_pending_messages(settings, db):
|
||||||
|
|
||||||
if has_pending_messages(settings, db):
|
if has_pending_messages(settings, db):
|
||||||
command = 'python3 -W ignore::DeprecationWarning {manage} sendpendingmessages'.format(manage=manage)
|
command = 'python3 -W ignore::DeprecationWarning {manage} sendpendingmessages'.format(manage=manage)
|
||||||
proc = run(command, async=True)
|
proc = run(command, run_async=True)
|
||||||
yield proc
|
yield proc
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -228,7 +228,7 @@ REST_FRAMEWORK = {
|
||||||
'rest_framework.authentication.TokenAuthentication',
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
),
|
),
|
||||||
'DEFAULT_FILTER_BACKENDS': (
|
'DEFAULT_FILTER_BACKENDS': (
|
||||||
('rest_framework.filters.DjangoFilterBackend',)
|
('django_filters.rest_framework.DjangoFilterBackend',)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,7 +228,7 @@ REST_FRAMEWORK = {
|
||||||
'rest_framework.authentication.TokenAuthentication',
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
),
|
),
|
||||||
'DEFAULT_FILTER_BACKENDS': (
|
'DEFAULT_FILTER_BACKENDS': (
|
||||||
('rest_framework.filters.DjangoFilterBackend',)
|
('django_filters.rest_framework.DjangoFilterBackend',)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.contrib import messages
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.contrib.admin.utils import NestedObjects, quote
|
from django.contrib.admin.utils import NestedObjects, quote
|
||||||
from django.contrib.auth import get_permission_codename
|
from django.contrib.auth import get_permission_codename
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.db import router
|
from django.db import router
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
@ -175,7 +175,7 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
for model, objs in collector.model_objs.items():
|
for model, objs in collector.model_objs.items():
|
||||||
count = 0
|
count = 0
|
||||||
# discount main systemuser
|
# discount main systemuser
|
||||||
if model is modeladmin.model.main_systemuser.field.rel.to:
|
if model is modeladmin.model.main_systemuser.field.model:
|
||||||
count = len(objs) - 1
|
count = len(objs) - 1
|
||||||
# Discount account
|
# Discount account
|
||||||
elif model is not modeladmin.model and model in registered_services:
|
elif model is not modeladmin.model and model in registered_services:
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.conf.urls import url
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
from django.contrib.auth import admin as auth
|
from django.contrib.auth import admin as auth
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
|
@ -47,7 +47,7 @@ def create_account_creation_form():
|
||||||
# Previous validation error
|
# Previous validation error
|
||||||
return
|
return
|
||||||
errors = {}
|
errors = {}
|
||||||
systemuser_model = Account.main_systemuser.field.rel.to
|
systemuser_model = Account.main_systemuser.field.model
|
||||||
if systemuser_model.objects.filter(username=account.username).exists():
|
if systemuser_model.objects.filter(username=account.username).exists():
|
||||||
errors['username'] = _("A system user with this name already exists.")
|
errors['username'] = _("A system user with this name already exists.")
|
||||||
for model, key, related_kwargs, __ in create_related:
|
for model, key, related_kwargs, __ in create_related:
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
|
import django.db.models.deletion
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class Migration(migrations.Migration):
|
||||||
('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', default=False, verbose_name='superuser status')),
|
('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', default=False, verbose_name='superuser status')),
|
||||||
('is_active', models.BooleanField(help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', default=True, verbose_name='active')),
|
('is_active', models.BooleanField(help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', default=True, verbose_name='active')),
|
||||||
('date_joined', models.DateTimeField(verbose_name='date joined', default=django.utils.timezone.now)),
|
('date_joined', models.DateTimeField(verbose_name='date joined', default=django.utils.timezone.now)),
|
||||||
('main_systemuser', models.ForeignKey(to='systemusers.SystemUser', editable=False, null=True, related_name='accounts_main')),
|
('main_systemuser', models.ForeignKey(to='systemusers.SystemUser', editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accounts_main')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:08
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
import orchestra.contrib.accounts.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('accounts', '0001_initial'), ('accounts', '0002_auto_20170528_2005'), ('accounts', '0003_auto_20210330_1049'), ('accounts', '0004_auto_20210422_1108')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('systemusers', '0001_initial'),
|
||||||
|
('auth', '0006_require_contenttypes_0002'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Account',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('username', models.CharField(help_text='Required. 64 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid username.', 'invalid')], verbose_name='username')),
|
||||||
|
('short_name', models.CharField(blank=True, max_length=64, verbose_name='short name')),
|
||||||
|
('full_name', models.CharField(max_length=256, verbose_name='full name')),
|
||||||
|
('email', models.EmailField(help_text='Used for password recovery', max_length=254, verbose_name='email address')),
|
||||||
|
('type', models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('COMPANY', 'Company'), ('PUBLICBODY', 'Public body'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type')),
|
||||||
|
('language', models.CharField(choices=[('EN', 'English')], default='EN', max_length=2, verbose_name='language')),
|
||||||
|
('comments', models.TextField(blank=True, max_length=256, verbose_name='comments')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('main_systemuser', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accounts_main', to='systemusers.SystemUser')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterModelManagers(
|
||||||
|
name='account',
|
||||||
|
managers=[
|
||||||
|
('objects', orchestra.contrib.accounts.models.AccountManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='language',
|
||||||
|
field=models.CharField(choices=[('CA', 'Catalan'), ('ES', 'Spanish'), ('EN', 'English')], default='CA', max_length=2, verbose_name='language'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid username.', 'invalid')], verbose_name='username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='language',
|
||||||
|
field=models.CharField(choices=[('EN', 'English')], default='EN', max_length=2, verbose_name='language'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('COMPANY', 'Company'), ('PUBLICBODY', 'Public body'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='main_systemuser',
|
||||||
|
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='accounts_main', to='systemusers.SystemUser'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-03-30 10:49
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0002_auto_20170528_2005'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='language',
|
||||||
|
field=models.CharField(choices=[('EN', 'English')], default='EN', max_length=2, verbose_name='language'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('COMPANY', 'Company'), ('PUBLICBODY', 'Public body'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -29,7 +29,7 @@ class Account(auth.AbstractBaseUser):
|
||||||
validators.RegexValidator(r'^[\w.-]+$', _("Enter a valid username."), 'invalid')
|
validators.RegexValidator(r'^[\w.-]+$', _("Enter a valid username."), 'invalid')
|
||||||
])
|
])
|
||||||
main_systemuser = models.ForeignKey(settings.ACCOUNTS_SYSTEMUSER_MODEL, null=True,
|
main_systemuser = models.ForeignKey(settings.ACCOUNTS_SYSTEMUSER_MODEL, null=True,
|
||||||
related_name='accounts_main', editable=False)
|
related_name='accounts_main', editable=False, on_delete=models.SET_NULL)
|
||||||
short_name = models.CharField(_("short name"), max_length=64, blank=True)
|
short_name = models.CharField(_("short name"), max_length=64, blank=True)
|
||||||
full_name = models.CharField(_("full name"), max_length=256)
|
full_name = models.CharField(_("full name"), max_length=256)
|
||||||
email = models.EmailField(_('email address'), help_text=_("Used for password recovery"))
|
email = models.EmailField(_('email address'), help_text=_("Used for password recovery"))
|
||||||
|
@ -52,6 +52,11 @@ class Account(auth.AbstractBaseUser):
|
||||||
USERNAME_FIELD = 'username'
|
USERNAME_FIELD = 'username'
|
||||||
REQUIRED_FIELDS = ['email']
|
REQUIRED_FIELDS = ['email']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# ignore `is_staff` kwarg because is handled with `is_superuser`
|
||||||
|
kwargs.pop('is_staff', None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from datetime import date
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.forms.models import modelformset_factory
|
from django.forms.models import modelformset_factory
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django import forms
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, Sum, Prefetch
|
from django.db.models import F, Sum, Prefetch
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib.admin import SimpleListFilter
|
from django.contrib.admin import SimpleListFilter
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
@ -21,7 +21,7 @@ def validate_contact(request, bill, error=True):
|
||||||
message = msg.format(relation=_("Related"), account=account, url=url)
|
message = msg.format(relation=_("Related"), account=account, url=url)
|
||||||
send(request, mark_safe(message))
|
send(request, mark_safe(message))
|
||||||
valid = False
|
valid = False
|
||||||
main = type(bill).account.field.rel.to.objects.get_main()
|
main = type(bill).account.field.model.objects.get_main()
|
||||||
if not hasattr(main, 'billcontact'):
|
if not hasattr(main, 'billcontact'):
|
||||||
account = force_text(main)
|
account = force_text(main)
|
||||||
url = reverse('admin:accounts_account_change', args=(main.id,))
|
url = reverse('admin:accounts_account_change', args=(main.id,))
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='bill',
|
model_name='bill',
|
||||||
name='amend_of',
|
name='amend_of',
|
||||||
field=models.ForeignKey(to='bills.Bill', blank=True, related_name='amends', verbose_name='amend of', null=True),
|
field=models.ForeignKey(to='bills.Bill', blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='amends', verbose_name='amend of', null=True),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='billcontact',
|
model_name='billcontact',
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.core.validators import ValidationError, RegexValidator
|
from django.core.validators import ValidationError, RegexValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, Sum
|
from django.db.models import F, Sum
|
||||||
|
@ -24,7 +24,7 @@ from . import settings
|
||||||
|
|
||||||
class BillContact(models.Model):
|
class BillContact(models.Model):
|
||||||
account = models.OneToOneField('accounts.Account', verbose_name=_("account"),
|
account = models.OneToOneField('accounts.Account', verbose_name=_("account"),
|
||||||
related_name='billcontact')
|
related_name='billcontact', on_delete=models.CASCADE)
|
||||||
name = models.CharField(_("name"), max_length=256, blank=True,
|
name = models.CharField(_("name"), max_length=256, blank=True,
|
||||||
help_text=_("Account full name will be used when left blank."))
|
help_text=_("Account full name will be used when left blank."))
|
||||||
address = models.TextField(_("address"))
|
address = models.TextField(_("address"))
|
||||||
|
@ -102,9 +102,9 @@ class Bill(models.Model):
|
||||||
|
|
||||||
number = models.CharField(_("number"), max_length=16, unique=True, blank=True)
|
number = models.CharField(_("number"), max_length=16, unique=True, blank=True)
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
||||||
related_name='%(class)s')
|
related_name='%(class)s', on_delete=models.CASCADE)
|
||||||
amend_of = models.ForeignKey('self', null=True, blank=True, verbose_name=_("amend of"),
|
amend_of = models.ForeignKey('self', null=True, blank=True, verbose_name=_("amend of"),
|
||||||
related_name='amends')
|
related_name='amends', on_delete=models.SET_NULL)
|
||||||
type = models.CharField(_("type"), max_length=16, choices=TYPES)
|
type = models.CharField(_("type"), max_length=16, choices=TYPES)
|
||||||
created_on = models.DateField(_("created on"), auto_now_add=True)
|
created_on = models.DateField(_("created on"), auto_now_add=True)
|
||||||
closed_on = models.DateField(_("closed on"), blank=True, null=True, db_index=True)
|
closed_on = models.DateField(_("closed on"), blank=True, null=True, db_index=True)
|
||||||
|
@ -416,7 +416,7 @@ class ProForma(Bill):
|
||||||
|
|
||||||
class BillLine(models.Model):
|
class BillLine(models.Model):
|
||||||
""" Base model for bill item representation """
|
""" Base model for bill item representation """
|
||||||
bill = models.ForeignKey(Bill, verbose_name=_("bill"), related_name='lines')
|
bill = models.ForeignKey(Bill, verbose_name=_("bill"), related_name='lines', on_delete=models.CASCADE)
|
||||||
description = models.CharField(_("description"), max_length=256)
|
description = models.CharField(_("description"), max_length=256)
|
||||||
rate = models.DecimalField(_("rate"), blank=True, null=True, max_digits=12, decimal_places=2)
|
rate = models.DecimalField(_("rate"), blank=True, null=True, max_digits=12, decimal_places=2)
|
||||||
quantity = models.DecimalField(_("quantity"), blank=True, null=True, max_digits=12,
|
quantity = models.DecimalField(_("quantity"), blank=True, null=True, max_digits=12,
|
||||||
|
@ -434,7 +434,7 @@ class BillLine(models.Model):
|
||||||
created_on = models.DateField(_("created"), auto_now_add=True)
|
created_on = models.DateField(_("created"), auto_now_add=True)
|
||||||
# Amendment
|
# Amendment
|
||||||
amended_line = models.ForeignKey('self', verbose_name=_("amended line"),
|
amended_line = models.ForeignKey('self', verbose_name=_("amended line"),
|
||||||
related_name='amendment_lines', null=True, blank=True)
|
related_name='amendment_lines', null=True, blank=True, on_delete=models.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
@ -495,7 +495,7 @@ class BillSubline(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: order info for undoing
|
# TODO: order info for undoing
|
||||||
line = models.ForeignKey(BillLine, verbose_name=_("bill line"), related_name='sublines')
|
line = models.ForeignKey(BillLine, verbose_name=_("bill line"), related_name='sublines', on_delete=models.CASCADE)
|
||||||
description = models.CharField(_("description"), max_length=256)
|
description = models.CharField(_("description"), max_length=256)
|
||||||
total = models.DecimalField(max_digits=12, decimal_places=2)
|
total = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
type = models.CharField(_("type"), max_length=16, choices=TYPES, default=OTHER)
|
type = models.CharField(_("type"), max_length=16, choices=TYPES, default=OTHER)
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -33,7 +33,7 @@ class Contact(models.Model):
|
||||||
objects = ContactQuerySet.as_manager()
|
objects = ContactQuerySet.as_manager()
|
||||||
|
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||||
related_name='contacts', null=True)
|
related_name='contacts', null=True, on_delete=models.SET_NULL)
|
||||||
short_name = models.CharField(_("short name"), max_length=128)
|
short_name = models.CharField(_("short name"), max_length=128)
|
||||||
full_name = models.CharField(_("full name"), max_length=256, blank=True)
|
full_name = models.CharField(_("full name"), max_length=256, blank=True)
|
||||||
email = models.EmailField()
|
email = models.EmailField()
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import django.db.models.deletion
|
||||||
import orchestra.core.validators
|
import orchestra.core.validators
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
|
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
|
||||||
('name', models.CharField(verbose_name='name', max_length=64, validators=[orchestra.core.validators.validate_name])),
|
('name', models.CharField(verbose_name='name', max_length=64, validators=[orchestra.core.validators.validate_name])),
|
||||||
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)),
|
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)),
|
||||||
('account', models.ForeignKey(related_name='databases', verbose_name='Account', to=settings.AUTH_USER_MODEL)),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databases', verbose_name='Account', to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
@ -29,7 +30,7 @@ class Migration(migrations.Migration):
|
||||||
('username', models.CharField(verbose_name='username', max_length=16, validators=[orchestra.core.validators.validate_name])),
|
('username', models.CharField(verbose_name='username', max_length=16, validators=[orchestra.core.validators.validate_name])),
|
||||||
('password', models.CharField(verbose_name='password', max_length=256)),
|
('password', models.CharField(verbose_name='password', max_length=256)),
|
||||||
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)),
|
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)),
|
||||||
('account', models.ForeignKey(related_name='databaseusers', verbose_name='Account', to=settings.AUTH_USER_MODEL)),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databaseusers', verbose_name='Account', to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'DB users',
|
'verbose_name_plural': 'DB users',
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:25
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import orchestra.core.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('databases', '0001_initial'), ('databases', '0002_auto_20170528_2005'), ('databases', '0003_database_comments'), ('databases', '0004_auto_20210330_1049')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Database',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=64, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
|
||||||
|
('type', models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databases', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DatabaseUser',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('username', models.CharField(max_length=16, validators=[orchestra.core.validators.validate_name], verbose_name='username')),
|
||||||
|
('password', models.CharField(max_length=256, verbose_name='password')),
|
||||||
|
('type', models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databaseusers', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'DB users',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='database',
|
||||||
|
name='users',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='databases', to='databases.DatabaseUser', verbose_name='users'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='databaseuser',
|
||||||
|
unique_together=set([('username', 'type')]),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='database',
|
||||||
|
unique_together=set([('name', 'type')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='database',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='databaseuser',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='database',
|
||||||
|
name='comments',
|
||||||
|
field=models.TextField(blank=True, default=''),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='database',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='databaseuser',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-03-30 10:49
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('databases', '0003_database_comments'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='database',
|
||||||
|
name='comments',
|
||||||
|
field=models.TextField(blank=True, default=''),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='database',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='databaseuser',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -20,8 +20,8 @@ class Database(models.Model):
|
||||||
type = models.CharField(_("type"), max_length=32,
|
type = models.CharField(_("type"), max_length=32,
|
||||||
choices=settings.DATABASES_TYPE_CHOICES,
|
choices=settings.DATABASES_TYPE_CHOICES,
|
||||||
default=settings.DATABASES_DEFAULT_TYPE)
|
default=settings.DATABASES_DEFAULT_TYPE)
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
|
||||||
related_name='databases')
|
verbose_name=_("Account"), related_name='databases')
|
||||||
comments = models.TextField(default="", blank=True)
|
comments = models.TextField(default="", blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -60,8 +60,8 @@ class DatabaseUser(models.Model):
|
||||||
type = models.CharField(_("type"), max_length=32,
|
type = models.CharField(_("type"), max_length=32,
|
||||||
choices=settings.DATABASES_TYPE_CHOICES,
|
choices=settings.DATABASES_TYPE_CHOICES,
|
||||||
default=settings.DATABASES_DEFAULT_TYPE)
|
default=settings.DATABASES_DEFAULT_TYPE)
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
|
||||||
related_name='databaseusers')
|
verbose_name=_("Account"), related_name='databaseusers')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = _("DB users")
|
verbose_name_plural = _("DB users")
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
import MySQLdb
|
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import MySQLdb
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from selenium.webdriver.support.select import Select
|
|
||||||
|
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
from orchestra.contrib.orchestration.models import Server, Route
|
from orchestra.contrib.orchestration.models import Route, Server
|
||||||
from orchestra.utils.sys import sshrun
|
from orchestra.utils.sys import sshrun
|
||||||
from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii, save_response_on_error,
|
from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii,
|
||||||
snapshot_on_error)
|
save_response_on_error, snapshot_on_error)
|
||||||
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from ... import backends, settings
|
from ... import backends, settings
|
||||||
from ...models import Database, DatabaseUser
|
from ...models import Database, DatabaseUser
|
||||||
|
|
||||||
|
TEST_REST_API = int(os.getenv('TEST_REST_API', '0'))
|
||||||
|
|
||||||
|
|
||||||
class DatabaseTestMixin(object):
|
class DatabaseTestMixin(object):
|
||||||
MASTER_SERVER = os.environ.get('ORCHESTRA_SECOND_SERVER', 'localhost')
|
MASTER_SERVER = os.environ.get('ORCHESTRA_SECOND_SERVER', 'localhost')
|
||||||
|
@ -181,6 +183,7 @@ class MySQLControllerMixin(object):
|
||||||
"""mysql mysql -e 'SELECT * FROM user WHERE user="%(username)s";'""" % context, display=False).stdout)
|
"""mysql mysql -e 'SELECT * FROM user WHERE user="%(username)s";'""" % context, display=False).stdout)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(TEST_REST_API, "REST API tests")
|
||||||
class RESTDatabaseMixin(DatabaseTestMixin):
|
class RESTDatabaseMixin(DatabaseTestMixin):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RESTDatabaseMixin, self).setUp()
|
super(RESTDatabaseMixin, self).setUp()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.functions import Concat, Coalesce
|
from django.db.models.functions import Concat, Coalesce
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
import django.db.models.deletion
|
||||||
import orchestra.contrib.domains.utils
|
import orchestra.contrib.domains.utils
|
||||||
import orchestra.contrib.domains.validators
|
import orchestra.contrib.domains.validators
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -20,8 +21,8 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
|
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
|
||||||
('name', models.CharField(unique=True, max_length=256, validators=[orchestra.contrib.domains.validators.validate_domain_name, orchestra.contrib.domains.validators.validate_allowed_domain], verbose_name='name', help_text='Domain or subdomain name.')),
|
('name', models.CharField(unique=True, max_length=256, validators=[orchestra.contrib.domains.validators.validate_domain_name, orchestra.contrib.domains.validators.validate_allowed_domain], verbose_name='name', help_text='Domain or subdomain name.')),
|
||||||
('serial', models.IntegerField(default=orchestra.contrib.domains.utils.generate_zone_serial, verbose_name='serial', help_text='Serial number')),
|
('serial', models.IntegerField(default=orchestra.contrib.domains.utils.generate_zone_serial, verbose_name='serial', help_text='Serial number')),
|
||||||
('account', models.ForeignKey(related_name='domains', help_text='Automatically selected for subdomains.', to=settings.AUTH_USER_MODEL, verbose_name='Account', blank=True)),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='domains', help_text='Automatically selected for subdomains.', to=settings.AUTH_USER_MODEL, verbose_name='Account', blank=True)),
|
||||||
('top', models.ForeignKey(null=True, to='domains.Domain', editable=False, related_name='subdomain_set')),
|
('top', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, null=True, to='domains.Domain', editable=False, related_name='subdomain_set')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
@ -31,7 +32,7 @@ class Migration(migrations.Migration):
|
||||||
('ttl', models.CharField(help_text='Record TTL, defaults to 1h', max_length=8, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='TTL', blank=True)),
|
('ttl', models.CharField(help_text='Record TTL, defaults to 1h', max_length=8, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='TTL', blank=True)),
|
||||||
('type', models.CharField(max_length=32, verbose_name='type', choices=[('MX', 'MX'), ('NS', 'NS'), ('CNAME', 'CNAME'), ('A', 'A (IPv4 address)'), ('AAAA', 'AAAA (IPv6 address)'), ('SRV', 'SRV'), ('TXT', 'TXT'), ('SOA', 'SOA')])),
|
('type', models.CharField(max_length=32, verbose_name='type', choices=[('MX', 'MX'), ('NS', 'NS'), ('CNAME', 'CNAME'), ('A', 'A (IPv4 address)'), ('AAAA', 'AAAA (IPv6 address)'), ('SRV', 'SRV'), ('TXT', 'TXT'), ('SOA', 'SOA')])),
|
||||||
('value', models.CharField(max_length=256, verbose_name='value')),
|
('value', models.CharField(max_length=256, verbose_name='value')),
|
||||||
('domain', models.ForeignKey(related_name='records', to='domains.Domain', verbose_name='domain')),
|
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='records', to='domains.Domain', verbose_name='domain')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import orchestra.contrib.domains.utils
|
||||||
|
import orchestra.contrib.domains.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('domains', '0001_initial'), ('domains', '0002_auto_20150715_1017'), ('domains', '0003_auto_20150720_1121'), ('domains', '0004_auto_20150720_1121'), ('domains', '0005_auto_20160219_1034'), ('domains', '0006_auto_20170528_2011'), ('domains', '0007_auto_20190805_1134'), ('domains', '0008_domain_dns2136_address_match_list'), ('domains', '0009_auto_20200204_1217'), ('domains', '0010_auto_20210330_1049')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Domain',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Domain or subdomain name.', max_length=256, unique=True, validators=[orchestra.contrib.domains.validators.validate_domain_name, orchestra.contrib.domains.validators.validate_allowed_domain], verbose_name='name')),
|
||||||
|
('serial', models.IntegerField(default=orchestra.contrib.domains.utils.generate_zone_serial, help_text='Serial number', verbose_name='serial')),
|
||||||
|
('account', models.ForeignKey(blank=True, help_text='Automatically selected for subdomains.', on_delete=django.db.models.deletion.CASCADE, related_name='domains', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
|
||||||
|
('top', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='subdomain_set', to='domains.Domain')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Record',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('ttl', models.CharField(blank=True, help_text='Record TTL, defaults to 1h', max_length=8, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='TTL')),
|
||||||
|
('type', models.CharField(choices=[('MX', 'MX'), ('NS', 'NS'), ('CNAME', 'CNAME'), ('A', 'A (IPv4 address)'), ('AAAA', 'AAAA (IPv6 address)'), ('SRV', 'SRV'), ('TXT', 'TXT'), ('SPF', 'SPF')], max_length=32, verbose_name='type')),
|
||||||
|
('value', models.CharField(help_text='MX, NS and CNAME records sould end with a dot.', max_length=1024, verbose_name='value')),
|
||||||
|
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='records', to='domains.Domain', verbose_name='domain')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='domain',
|
||||||
|
name='serial',
|
||||||
|
field=models.IntegerField(default=orchestra.contrib.domains.utils.generate_zone_serial, editable=False, help_text='A revision number that changes whenever this domain is updated.', verbose_name='serial'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='expire',
|
||||||
|
field=models.CharField(blank=True, help_text='The time that a secondary server will keep trying to complete a zone transfer. If this time expires prior to a successful zone transfer, the secondary server will expire its zone file. This means the secondary will stop answering queries. The default value is <tt>4w</tt>.', max_length=16, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='expire'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='min_ttl',
|
||||||
|
field=models.CharField(blank=True, help_text='The minimum time-to-live value applies to all resource records in the zone file. This value is supplied in query responses to inform other servers how long they should keep the data in cache. The default value is <tt>1h</tt>.', max_length=16, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='min TTL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='refresh',
|
||||||
|
field=models.CharField(blank=True, help_text="The time a secondary DNS server waits before querying the primary DNS server's SOA record to check for changes. When the refresh time expires, the secondary DNS server requests a copy of the current SOA record from the primary. The primary DNS server complies with this request. The secondary DNS server compares the serial number of the primary DNS server's current SOA record and the serial number in it's own SOA record. If they are different, the secondary DNS server will request a zone transfer from the primary DNS server. The default value is <tt>1d</tt>.", max_length=16, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='refresh'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='retry',
|
||||||
|
field=models.CharField(blank=True, help_text='The time a secondary server waits before retrying a failed zone transfer. Normally, the retry time is less than the refresh time. The default value is <tt>2h</tt>.', max_length=16, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='retry'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='domain',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(db_index=True, help_text='Domain or subdomain name.', max_length=256, unique=True, validators=[orchestra.contrib.domains.validators.validate_domain_name, orchestra.contrib.domains.validators.validate_allowed_domain], verbose_name='name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='domain',
|
||||||
|
name='top',
|
||||||
|
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='subdomain_set', to='domains.Domain', verbose_name='top domain'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='dns2136_address_match_list',
|
||||||
|
field=models.CharField(blank=True, default='key pangea.key;', help_text="A bind-9 'address_match_list' that will be granted permission to perform dns2136 updates. Chiefly used to enable Let's Encrypt self-service validation.", max_length=80),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
import orchestra.contrib.domains.validators
|
import orchestra.contrib.domains.validators
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='domain',
|
model_name='domain',
|
||||||
name='top',
|
name='top',
|
||||||
field=models.ForeignKey(editable=False, verbose_name='top domain', related_name='subdomain_set', to='domains.Domain', null=True),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, editable=False, verbose_name='top domain', related_name='subdomain_set', to='domains.Domain', null=True),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='record',
|
model_name='record',
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-03-30 10:49
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import orchestra.contrib.domains.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('domains', '0009_auto_20200204_1217'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='domain',
|
||||||
|
name='min_ttl',
|
||||||
|
field=models.CharField(blank=True, help_text='The minimum time-to-live value applies to all resource records in the zone file. This value is supplied in query responses to inform other servers how long they should keep the data in cache. The default value is <tt>1h</tt>.', max_length=16, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='min TTL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='record',
|
||||||
|
name='ttl',
|
||||||
|
field=models.CharField(blank=True, help_text='Record TTL, defaults to 1h', max_length=8, validators=[orchestra.contrib.domains.validators.validate_zone_interval], verbose_name='TTL'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -31,9 +31,9 @@ class Domain(models.Model):
|
||||||
validators.validate_allowed_domain
|
validators.validate_allowed_domain
|
||||||
])
|
])
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), blank=True,
|
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), blank=True,
|
||||||
related_name='domains', help_text=_("Automatically selected for subdomains."))
|
related_name='domains', on_delete=models.CASCADE, help_text=_("Automatically selected for subdomains."))
|
||||||
top = models.ForeignKey('domains.Domain', null=True, related_name='subdomain_set',
|
top = models.ForeignKey('domains.Domain', null=True, related_name='subdomain_set',
|
||||||
editable=False, verbose_name=_("top domain"))
|
editable=False, verbose_name=_("top domain"), on_delete=models.CASCADE)
|
||||||
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial, editable=False,
|
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial, editable=False,
|
||||||
help_text=_("A revision number that changes whenever this domain is updated."))
|
help_text=_("A revision number that changes whenever this domain is updated."))
|
||||||
refresh = models.CharField(_("refresh"), max_length=16, blank=True,
|
refresh = models.CharField(_("refresh"), max_length=16, blank=True,
|
||||||
|
@ -318,7 +318,7 @@ class Record(models.Model):
|
||||||
SOA: (validators.validate_soa_record,),
|
SOA: (validators.validate_soa_record,),
|
||||||
}
|
}
|
||||||
|
|
||||||
domain = models.ForeignKey(Domain, verbose_name=_("domain"), related_name='records')
|
domain = models.ForeignKey(Domain, verbose_name=_("domain"), related_name='records', on_delete=models.CASCADE)
|
||||||
ttl = models.CharField(_("TTL"), max_length=8, blank=True,
|
ttl = models.CharField(_("TTL"), max_length=8, blank=True,
|
||||||
help_text=_("Record TTL, defaults to %s") % settings.DOMAINS_DEFAULT_TTL,
|
help_text=_("Record TTL, defaults to %s") % settings.DOMAINS_DEFAULT_TTL,
|
||||||
validators=[validators.validate_zone_interval])
|
validators=[validators.validate_zone_interval])
|
||||||
|
|
|
@ -4,7 +4,7 @@ import socket
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from selenium.webdriver.support.select import Select
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from orchestra.contrib.orchestration.models import Server, Route
|
from orchestra.contrib.orchestration.models import Server, Route
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import orchestra.models.fields
|
import orchestra.models.fields
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -20,7 +21,7 @@ class Migration(migrations.Migration):
|
||||||
('author_name', models.CharField(blank=True, max_length=256, verbose_name='author name')),
|
('author_name', models.CharField(blank=True, max_length=256, verbose_name='author name')),
|
||||||
('content', models.TextField(verbose_name='content')),
|
('content', models.TextField(verbose_name='content')),
|
||||||
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='created on')),
|
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='created on')),
|
||||||
('author', models.ForeignKey(related_name='ticket_messages', to=settings.AUTH_USER_MODEL, verbose_name='author')),
|
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_messages', to=settings.AUTH_USER_MODEL, verbose_name='author')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'get_latest_by': 'id',
|
'get_latest_by': 'id',
|
||||||
|
@ -48,9 +49,9 @@ class Migration(migrations.Migration):
|
||||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created')),
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created')),
|
||||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='modified')),
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='modified')),
|
||||||
('cc', models.TextField(blank=True, help_text='emails to send a carbon copy to', verbose_name='CC')),
|
('cc', models.TextField(blank=True, help_text='emails to send a carbon copy to', verbose_name='CC')),
|
||||||
('creator', models.ForeignKey(related_name='tickets_created', null=True, to=settings.AUTH_USER_MODEL, verbose_name='created by')),
|
('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tickets_created', null=True, to=settings.AUTH_USER_MODEL, verbose_name='created by')),
|
||||||
('owner', models.ForeignKey(blank=True, related_name='tickets_owned', null=True, to=settings.AUTH_USER_MODEL, verbose_name='assigned to')),
|
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, blank=True, related_name='tickets_owned', null=True, to=settings.AUTH_USER_MODEL, verbose_name='assigned to')),
|
||||||
('queue', models.ForeignKey(blank=True, related_name='tickets', null=True, to='issues.Queue')),
|
('queue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, blank=True, related_name='tickets', null=True, to='issues.Queue')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['-updated_at'],
|
'ordering': ['-updated_at'],
|
||||||
|
@ -60,14 +61,14 @@ class Migration(migrations.Migration):
|
||||||
name='TicketTracker',
|
name='TicketTracker',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
|
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
|
||||||
('ticket', models.ForeignKey(related_name='trackers', to='issues.Ticket', verbose_name='ticket')),
|
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='trackers', to='issues.Ticket', verbose_name='ticket')),
|
||||||
('user', models.ForeignKey(related_name='ticket_trackers', to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_trackers', to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='message',
|
model_name='message',
|
||||||
name='ticket',
|
name='ticket',
|
||||||
field=models.ForeignKey(related_name='messages', to='issues.Ticket', verbose_name='ticket'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='issues.Ticket', verbose_name='ticket'),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='tickettracker',
|
name='tickettracker',
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.utils.timezone import utc
|
||||||
|
import orchestra.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('issues', '0001_initial'), ('issues', '0002_auto_20150709_1018'), ('issues', '0003_auto_20160320_1127'), ('issues', '0004_auto_20170528_2011')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Message',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('author_name', models.CharField(blank=True, max_length=256, verbose_name='author name')),
|
||||||
|
('content', models.TextField(verbose_name='content')),
|
||||||
|
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='created on')),
|
||||||
|
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_messages', to=settings.AUTH_USER_MODEL, verbose_name='author')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'get_latest_by': 'id',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Queue',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=128, unique=True, verbose_name='name')),
|
||||||
|
('verbose_name', models.CharField(blank=True, max_length=128, verbose_name='verbose_name')),
|
||||||
|
('default', models.BooleanField(default=False, verbose_name='default')),
|
||||||
|
('notify', orchestra.models.fields.MultiSelectField(blank=True, choices=[('SUPPORT', 'Support tickets'), ('ADMIN', 'Administrative'), ('BILLING', 'Billing'), ('TECH', 'Technical'), ('ADDS', 'Announcements'), ('EMERGENCY', 'Emergency contact')], default=('SUPPORT', 'ADMIN', 'BILLING', 'TECH', 'ADDS', 'EMERGENCY'), help_text='Contacts to notify by email', max_length=256, verbose_name='notify')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Ticket',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('creator_name', models.CharField(blank=True, max_length=256, verbose_name='creator name')),
|
||||||
|
('subject', models.CharField(max_length=256, verbose_name='subject')),
|
||||||
|
('description', models.TextField(verbose_name='description')),
|
||||||
|
('priority', models.CharField(choices=[('HIGH', 'High'), ('MEDIUM', 'Medium'), ('LOW', 'Low')], default='MEDIUM', max_length=32, verbose_name='priority')),
|
||||||
|
('state', models.CharField(choices=[('NEW', 'New'), ('IN_PROGRESS', 'In Progress'), ('RESOLVED', 'Resolved'), ('FEEDBACK', 'Feedback'), ('REJECTED', 'Rejected'), ('CLOSED', 'Closed')], default='NEW', max_length=32, verbose_name='state')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='modified')),
|
||||||
|
('cc', models.TextField(blank=True, help_text='emails to send a carbon copy to', verbose_name='CC')),
|
||||||
|
('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tickets_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')),
|
||||||
|
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tickets_owned', to=settings.AUTH_USER_MODEL, verbose_name='assigned to')),
|
||||||
|
('queue', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tickets', to='issues.Queue')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['-updated_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TicketTracker',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='trackers', to='issues.Ticket', verbose_name='ticket')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_trackers', to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='message',
|
||||||
|
name='ticket',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='issues.Ticket', verbose_name='ticket'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='tickettracker',
|
||||||
|
unique_together=set([('ticket', 'user')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ticket',
|
||||||
|
name='created_at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='created'),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='message',
|
||||||
|
name='created_on',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='message',
|
||||||
|
name='created_at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2016, 3, 20, 10, 27, 45, 766388, tzinfo=utc), verbose_name='created at'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ticket',
|
||||||
|
name='creator',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tickets_created', to=settings.AUTH_USER_MODEL, verbose_name='created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ticket',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tickets_owned', to=settings.AUTH_USER_MODEL, verbose_name='assigned to'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ticket',
|
||||||
|
name='queue',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tickets', to='issues.Queue'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -161,10 +161,10 @@ class Ticket(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Message(models.Model):
|
class Message(models.Model):
|
||||||
ticket = models.ForeignKey('issues.Ticket', verbose_name=_("ticket"),
|
ticket = models.ForeignKey('issues.Ticket', on_delete=models.CASCADE,
|
||||||
related_name='messages')
|
verbose_name=_("ticket"), related_name='messages')
|
||||||
author = models.ForeignKey(djsettings.AUTH_USER_MODEL, verbose_name=_("author"),
|
author = models.ForeignKey(djsettings.AUTH_USER_MODEL, on_delete=models.CASCADE,
|
||||||
related_name='ticket_messages')
|
verbose_name=_("author"), related_name='ticket_messages')
|
||||||
author_name = models.CharField(_("author name"), max_length=256, blank=True)
|
author_name = models.CharField(_("author name"), max_length=256, blank=True)
|
||||||
content = models.TextField(_("content"))
|
content = models.TextField(_("content"))
|
||||||
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
|
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
|
||||||
|
@ -191,9 +191,10 @@ class Message(models.Model):
|
||||||
|
|
||||||
class TicketTracker(models.Model):
|
class TicketTracker(models.Model):
|
||||||
""" Keeps track of user read tickets """
|
""" Keeps track of user read tickets """
|
||||||
ticket = models.ForeignKey(Ticket, verbose_name=_("ticket"), related_name='trackers')
|
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE,
|
||||||
user = models.ForeignKey(djsettings.AUTH_USER_MODEL, verbose_name=_("user"),
|
verbose_name=_("ticket"), related_name='trackers')
|
||||||
related_name='ticket_trackers')
|
user = models.ForeignKey(djsettings.AUTH_USER_MODEL, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("user"), related_name='ticket_trackers')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (
|
unique_together = (
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import django.db.models.deletion
|
||||||
import orchestra.core.validators
|
import orchestra.core.validators
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,8 +23,8 @@ class Migration(migrations.Migration):
|
||||||
('address_name', models.CharField(max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='address name', blank=True)),
|
('address_name', models.CharField(max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='address name', blank=True)),
|
||||||
('admin_email', models.EmailField(max_length=254, verbose_name='admin email', help_text='Administration email address')),
|
('admin_email', models.EmailField(max_length=254, verbose_name='admin email', help_text='Administration email address')),
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='active', help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.')),
|
('is_active', models.BooleanField(default=True, verbose_name='active', help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.')),
|
||||||
('account', models.ForeignKey(related_name='lists', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lists', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
|
||||||
('address_domain', models.ForeignKey(null=True, blank=True, to='domains.Domain', verbose_name='address domain')),
|
('address_domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, null=True, blank=True, to='domains.Domain', verbose_name='address domain')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import orchestra.core.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('lists', '0001_initial'), ('lists', '0002_auto_20160912_1221'), ('lists', '0003_auto_20160912_1241'), ('lists', '0004_auto_20210330_1049')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('domains', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='List',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Default list address <name>@lists.orchestra.lan', max_length=128, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
|
||||||
|
('address_name', models.CharField(blank=True, max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='address name')),
|
||||||
|
('admin_email', models.EmailField(help_text='Administration email address', max_length=254, verbose_name='admin email')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lists', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
|
||||||
|
('address_domain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='domains.Domain', verbose_name='address domain')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='list',
|
||||||
|
unique_together=set([('address_name', 'address_domain')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='address_domain',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='domains.Domain', verbose_name='address domain'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='address_name',
|
||||||
|
field=models.CharField(blank=True, max_length=52, validators=[orchestra.core.validators.validate_name], verbose_name='address name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Default list address <name>@grups.pangea.org', max_length=52, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='address_name',
|
||||||
|
field=models.CharField(blank=True, max_length=64, validators=[orchestra.core.validators.validate_name], verbose_name='address name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Default list address <name>@grups.pangea.org', max_length=64, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Default list address <name>@lists.orchestra.lan', max_length=64, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-03-30 10:49
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import orchestra.core.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('lists', '0003_auto_20160912_1241'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Default list address <name>@lists.orchestra.lan', max_length=64, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -30,7 +30,7 @@ class List(models.Model):
|
||||||
admin_email = models.EmailField(_("admin email"),
|
admin_email = models.EmailField(_("admin email"),
|
||||||
help_text=_("Administration email address"))
|
help_text=_("Administration email address"))
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||||
related_name='lists')
|
related_name='lists', on_delete=models.CASCADE)
|
||||||
# TODO also admin
|
# TODO also admin
|
||||||
is_active = models.BooleanField(_("active"), default=True,
|
is_active = models.BooleanField(_("active"), default=True,
|
||||||
help_text=_("Designates whether this account should be treated as active. "
|
help_text=_("Designates whether this account should be treated as active. "
|
||||||
|
|
|
@ -12,7 +12,7 @@ from .models import List
|
||||||
|
|
||||||
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = List.address_domain.field.rel.to
|
model = List.address_domain.field.model
|
||||||
fields = ('url', 'id', 'name')
|
fields = ('url', 'id', 'name')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,26 @@
|
||||||
import os
|
import os
|
||||||
import smtplib
|
import smtplib
|
||||||
import time
|
import time
|
||||||
import requests
|
import unittest
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
|
import requests
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from selenium.webdriver.support.select import Select
|
|
||||||
|
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
from orchestra.contrib.domains.models import Domain
|
from orchestra.contrib.domains.models import Domain
|
||||||
from orchestra.contrib.orchestration.models import Server, Route
|
from orchestra.contrib.orchestration.models import Route, Server
|
||||||
from orchestra.utils.sys import sshrun
|
from orchestra.utils.sys import sshrun
|
||||||
from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii, snapshot_on_error,
|
from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii,
|
||||||
save_response_on_error)
|
save_response_on_error, snapshot_on_error)
|
||||||
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from ... import backends, settings
|
from ... import backends, settings
|
||||||
from ...models import List
|
from ...models import List
|
||||||
|
|
||||||
|
TEST_REST_API = int(os.getenv('TEST_REST_API', '0'))
|
||||||
|
|
||||||
|
|
||||||
class ListMixin(object):
|
class ListMixin(object):
|
||||||
MASTER_SERVER = os.environ.get('ORCHESTRA_SLAVE_SERVER', 'localhost')
|
MASTER_SERVER = os.environ.get('ORCHESTRA_SLAVE_SERVER', 'localhost')
|
||||||
|
@ -158,6 +160,7 @@ class ListMixin(object):
|
||||||
self.validate_delete(name)
|
self.validate_delete(name)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(TEST_REST_API, "REST API tests")
|
||||||
class RESTListMixin(ListMixin):
|
class RESTListMixin(ListMixin):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RESTListMixin, self).setUp()
|
super(RESTListMixin, self).setUp()
|
||||||
|
|
|
@ -3,7 +3,7 @@ from urllib.parse import parse_qs
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import F, Count, Value as V
|
from django.db.models import F, Count, Value as V
|
||||||
from django.db.models.functions import Concat
|
from django.db.models.functions import Concat
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import django.db.models.deletion
|
||||||
import orchestra.contrib.mailboxes.validators
|
import orchestra.contrib.mailboxes.validators
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
|
|
||||||
|
@ -21,8 +22,8 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('name', models.CharField(verbose_name='name', validators=[orchestra.contrib.mailboxes.validators.validate_emailname], blank=True, help_text='Address name, left blank for a <i>catch-all</i> address', max_length=64)),
|
('name', models.CharField(verbose_name='name', validators=[orchestra.contrib.mailboxes.validators.validate_emailname], blank=True, help_text='Address name, left blank for a <i>catch-all</i> address', max_length=64)),
|
||||||
('forward', models.CharField(verbose_name='forward', validators=[orchestra.contrib.mailboxes.validators.validate_forward], blank=True, help_text='Space separated email addresses or mailboxes', max_length=256)),
|
('forward', models.CharField(verbose_name='forward', validators=[orchestra.contrib.mailboxes.validators.validate_forward], blank=True, help_text='Space separated email addresses or mailboxes', max_length=256)),
|
||||||
('account', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='addresses', verbose_name='Account')),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, related_name='addresses', verbose_name='Account')),
|
||||||
('domain', models.ForeignKey(to='domains.Domain', related_name='addresses', verbose_name='domain')),
|
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='domains.Domain', related_name='addresses', verbose_name='domain')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'addresses',
|
'verbose_name_plural': 'addresses',
|
||||||
|
@ -35,7 +36,7 @@ class Migration(migrations.Migration):
|
||||||
('subject', models.CharField(verbose_name='subject', max_length=256)),
|
('subject', models.CharField(verbose_name='subject', max_length=256)),
|
||||||
('message', models.TextField(verbose_name='message')),
|
('message', models.TextField(verbose_name='message')),
|
||||||
('enabled', models.BooleanField(verbose_name='enabled', default=False)),
|
('enabled', models.BooleanField(verbose_name='enabled', default=False)),
|
||||||
('address', models.OneToOneField(to='mailboxes.Address', related_name='autoresponse', verbose_name='address')),
|
('address', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='mailboxes.Address', related_name='autoresponse', verbose_name='address')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
@ -47,7 +48,7 @@ class Migration(migrations.Migration):
|
||||||
('filtering', models.CharField(choices=[('CUSTOM', 'Custom filtering'), ('REDIRECT', 'Archive spam (X-Spam-Score≥9)'), ('DISABLE', 'Disable'), ('REJECT', 'Reject spam (X-Spam-Score≥9)')], max_length=16, default='REDIRECT')),
|
('filtering', models.CharField(choices=[('CUSTOM', 'Custom filtering'), ('REDIRECT', 'Archive spam (X-Spam-Score≥9)'), ('DISABLE', 'Disable'), ('REJECT', 'Reject spam (X-Spam-Score≥9)')], max_length=16, default='REDIRECT')),
|
||||||
('custom_filtering', models.TextField(verbose_name='filtering', validators=[orchestra.contrib.mailboxes.validators.validate_sieve], blank=True, help_text='Arbitrary email filtering in sieve language. This overrides any automatic junk email filtering')),
|
('custom_filtering', models.TextField(verbose_name='filtering', validators=[orchestra.contrib.mailboxes.validators.validate_sieve], blank=True, help_text='Arbitrary email filtering in sieve language. This overrides any automatic junk email filtering')),
|
||||||
('is_active', models.BooleanField(verbose_name='active', default=True)),
|
('is_active', models.BooleanField(verbose_name='active', default=True)),
|
||||||
('account', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='mailboxes', verbose_name='account')),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, related_name='mailboxes', verbose_name='account')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'mailboxes',
|
'verbose_name_plural': 'mailboxes',
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import orchestra.contrib.mailboxes.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('mailboxes', '0001_initial'), ('mailboxes', '0002_auto_20160219_1032'), ('mailboxes', '0003_auto_20170528_2011')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('domains', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Address',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(blank=True, help_text='Address name, left blank for a <i>catch-all</i> address', max_length=64, validators=[orchestra.contrib.mailboxes.validators.validate_emailname], verbose_name='name')),
|
||||||
|
('forward', models.CharField(blank=True, help_text='Space separated email addresses or mailboxes', max_length=256, validators=[orchestra.contrib.mailboxes.validators.validate_forward], verbose_name='forward')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
|
||||||
|
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to='domains.Domain', verbose_name='domain')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'addresses',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Autoresponse',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('subject', models.CharField(max_length=256, verbose_name='subject')),
|
||||||
|
('message', models.TextField(verbose_name='message')),
|
||||||
|
('enabled', models.BooleanField(default=False, verbose_name='enabled')),
|
||||||
|
('address', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='autoresponse', to='mailboxes.Address', verbose_name='address')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Mailbox',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(db_index=True, help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid mailbox name.')], verbose_name='name')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('filtering', models.CharField(choices=[('CUSTOM', 'Custom filtering'), ('DISABLE', 'Disable'), ('REDIRECT', 'Archive spam (Score≥8)'), ('REDIRECT5', 'Archive spam (Score≥5)'), ('REJECT', 'Reject spam (Score≥8)'), ('REJECT5', 'Reject spam (Score≥5)')], default='REDIRECT', max_length=16)),
|
||||||
|
('custom_filtering', models.TextField(blank=True, help_text="Arbitrary email filtering in <a href='https://tty1.net/blog/2011/sieve-tutorial_en.html'>sieve language</a>. This overrides any automatic junk email filtering", validators=[orchestra.contrib.mailboxes.validators.validate_sieve], verbose_name='filtering')),
|
||||||
|
('is_active', models.BooleanField(default=True, verbose_name='active')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mailboxes', to=settings.AUTH_USER_MODEL, verbose_name='account')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'mailboxes',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='address',
|
||||||
|
name='mailboxes',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='addresses', to='mailboxes.Mailbox', verbose_name='mailboxes'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='address',
|
||||||
|
unique_together=set([('name', 'domain')]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -23,7 +23,7 @@ class Mailbox(models.Model):
|
||||||
])
|
])
|
||||||
password = models.CharField(_("password"), max_length=128)
|
password = models.CharField(_("password"), max_length=128)
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
||||||
related_name='mailboxes')
|
related_name='mailboxes', on_delete=models.CASCADE)
|
||||||
filtering = models.CharField(max_length=16,
|
filtering = models.CharField(max_length=16,
|
||||||
default=settings.MAILBOXES_MAILBOX_DEFAULT_FILTERING,
|
default=settings.MAILBOXES_MAILBOX_DEFAULT_FILTERING,
|
||||||
choices=[(k, v[0]) for k,v in sorted(settings.MAILBOXES_MAILBOX_FILTERINGS.items())])
|
choices=[(k, v[0]) for k,v in sorted(settings.MAILBOXES_MAILBOX_FILTERINGS.items())])
|
||||||
|
@ -44,7 +44,7 @@ class Mailbox(models.Model):
|
||||||
def active(self):
|
def active(self):
|
||||||
try:
|
try:
|
||||||
return self.is_active and self.account.is_active
|
return self.is_active and self.account.is_active
|
||||||
except type(self).account.field.rel.to.DoesNotExist:
|
except type(self).account.field.model.DoesNotExist:
|
||||||
return self.is_active
|
return self.is_active
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
|
@ -97,14 +97,14 @@ class Address(models.Model):
|
||||||
validators=[validators.validate_emailname],
|
validators=[validators.validate_emailname],
|
||||||
help_text=_("Address name, left blank for a <i>catch-all</i> address"))
|
help_text=_("Address name, left blank for a <i>catch-all</i> address"))
|
||||||
domain = models.ForeignKey(settings.MAILBOXES_DOMAIN_MODEL,
|
domain = models.ForeignKey(settings.MAILBOXES_DOMAIN_MODEL,
|
||||||
verbose_name=_("domain"), related_name='addresses')
|
verbose_name=_("domain"), related_name='addresses', on_delete=models.CASCADE)
|
||||||
mailboxes = models.ManyToManyField(Mailbox, verbose_name=_("mailboxes"),
|
mailboxes = models.ManyToManyField(Mailbox, verbose_name=_("mailboxes"),
|
||||||
related_name='addresses', blank=True)
|
related_name='addresses', blank=True)
|
||||||
forward = models.CharField(_("forward"), max_length=256, blank=True,
|
forward = models.CharField(_("forward"), max_length=256, blank=True,
|
||||||
validators=[validators.validate_forward],
|
validators=[validators.validate_forward],
|
||||||
help_text=_("Space separated email addresses or mailboxes"))
|
help_text=_("Space separated email addresses or mailboxes"))
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||||
related_name='addresses')
|
related_name='addresses', on_delete=models.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = _("addresses")
|
verbose_name_plural = _("addresses")
|
||||||
|
@ -168,7 +168,7 @@ class Address(models.Model):
|
||||||
|
|
||||||
class Autoresponse(models.Model):
|
class Autoresponse(models.Model):
|
||||||
address = models.OneToOneField(Address, verbose_name=_("address"),
|
address = models.OneToOneField(Address, verbose_name=_("address"),
|
||||||
related_name='autoresponse')
|
related_name='autoresponse', on_delete=models.CASCADE)
|
||||||
# TODO initial_date
|
# TODO initial_date
|
||||||
subject = models.CharField(_("subject"), max_length=256)
|
subject = models.CharField(_("subject"), max_length=256)
|
||||||
message = models.TextField(_("message"))
|
message = models.TextField(_("message"))
|
||||||
|
|
|
@ -8,7 +8,7 @@ from .models import Mailbox, Address
|
||||||
|
|
||||||
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Address.domain.field.rel.to
|
model = Address.domain.field.model
|
||||||
fields = ('url', 'id', 'name')
|
fields = ('url', 'id', 'name')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ def create_local_address(sender, *args, **kwargs):
|
||||||
mbox = kwargs['instance']
|
mbox = kwargs['instance']
|
||||||
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
||||||
if not mbox.pk and local_domain:
|
if not mbox.pk and local_domain:
|
||||||
Domain = Address._meta.get_field('domain').rel.to
|
Domain = Address._meta.get_field('domain').remote_field.model
|
||||||
try:
|
try:
|
||||||
domain = Domain.objects.get(name=local_domain)
|
domain = Domain.objects.get(name=local_domain)
|
||||||
except Domain.DoesNotExist:
|
except Domain.DoesNotExist:
|
||||||
|
|
|
@ -4,13 +4,14 @@ import poplib
|
||||||
import smtplib
|
import smtplib
|
||||||
import time
|
import time
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import unittest
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from selenium.webdriver.support.select import Select
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from orchestra.contrib.orchestration.models import Server, Route
|
from orchestra.contrib.orchestration.models import Server, Route
|
||||||
|
@ -21,6 +22,8 @@ from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii, snapshot
|
||||||
from ... import backends, settings
|
from ... import backends, settings
|
||||||
from ...models import Mailbox
|
from ...models import Mailbox
|
||||||
|
|
||||||
|
TEST_REST_API = int(os.getenv('TEST_REST_API', '0'))
|
||||||
|
|
||||||
|
|
||||||
class MailboxMixin(object):
|
class MailboxMixin(object):
|
||||||
MASTER_SERVER = os.environ.get('ORCHESTRA_SLAVE_SERVER', 'localhost')
|
MASTER_SERVER = os.environ.get('ORCHESTRA_SLAVE_SERVER', 'localhost')
|
||||||
|
@ -39,7 +42,7 @@ class MailboxMixin(object):
|
||||||
|
|
||||||
def add_route(self):
|
def add_route(self):
|
||||||
server = Server.objects.create(name=self.MASTER_SERVER)
|
server = Server.objects.create(name=self.MASTER_SERVER)
|
||||||
backend = backends.PasswdVirtualUserBackend.get_name()
|
backend = backends.RoundcubeIdentityController.get_name()
|
||||||
Route.objects.create(backend=backend, match=True, host=server)
|
Route.objects.create(backend=backend, match=True, host=server)
|
||||||
backend = backends.PostfixAddressController.get_name()
|
backend = backends.PostfixAddressController.get_name()
|
||||||
Route.objects.create(backend=backend, match=True, host=server)
|
Route.objects.create(backend=backend, match=True, host=server)
|
||||||
|
@ -235,6 +238,7 @@ class MailboxMixin(object):
|
||||||
# TODO test autoreply
|
# TODO test autoreply
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(TEST_REST_API, "REST API tests")
|
||||||
class RESTMailboxMixin(MailboxMixin):
|
class RESTMailboxMixin(MailboxMixin):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RESTMailboxMixin, self).setUp()
|
super(RESTMailboxMixin, self).setUp()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import email
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class Migration(migrations.Migration):
|
||||||
('result', models.CharField(choices=[('SUCCESS', 'Success'), ('FAILURE', 'Failure')], default='SUCCESS', max_length=16)),
|
('result', models.CharField(choices=[('SUCCESS', 'Success'), ('FAILURE', 'Failure')], default='SUCCESS', max_length=16)),
|
||||||
('date', models.DateTimeField(auto_now_add=True)),
|
('date', models.DateTimeField(auto_now_add=True)),
|
||||||
('log_message', models.TextField()),
|
('log_message', models.TextField()),
|
||||||
('message', models.ForeignKey(to='mailer.Message', editable=False, related_name='logs')),
|
('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mailer.Message', editable=False, related_name='logs')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:28
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('mailer', '0001_initial'), ('mailer', '0002_auto_20150617_1021'), ('mailer', '0003_auto_20150617_1024'), ('mailer', '0004_auto_20150805_1328'), ('mailer', '0005_auto_20160219_1056')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Message',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('state', models.CharField(choices=[('QUEUED', 'Queued'), ('SENT', 'Sent'), ('DEFERRED', 'Deferred'), ('FAILED', 'Failes')], default='QUEUED', max_length=16, verbose_name='State')),
|
||||||
|
('priority', models.PositiveIntegerField(choices=[(0, 'Critical (not queued)'), (1, 'High'), (2, 'Normal'), (3, 'Low')], default=2, verbose_name='Priority')),
|
||||||
|
('to_address', models.CharField(max_length=256)),
|
||||||
|
('from_address', models.CharField(max_length=256)),
|
||||||
|
('subject', models.CharField(max_length=256, verbose_name='subject')),
|
||||||
|
('content', models.TextField(verbose_name='content')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')),
|
||||||
|
('retries', models.PositiveIntegerField(default=0, verbose_name='retries')),
|
||||||
|
('last_retry', models.DateTimeField(auto_now=True, verbose_name='last try')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SMTPLog',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('result', models.CharField(choices=[('SUCCESS', 'Success'), ('FAILURE', 'Failure')], default='SUCCESS', max_length=16)),
|
||||||
|
('date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('log_message', models.TextField()),
|
||||||
|
('message', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='mailer.Message')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='message',
|
||||||
|
old_name='last_retry',
|
||||||
|
new_name='last_try',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='last_try',
|
||||||
|
field=models.DateTimeField(verbose_name='last try'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='subject',
|
||||||
|
field=models.TextField(verbose_name='subject'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='last_try',
|
||||||
|
field=models.DateTimeField(null=True, verbose_name='last try'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='state',
|
||||||
|
field=models.CharField(choices=[('QUEUED', 'Queued'), ('SENT', 'Sent'), ('DEFERRED', 'Deferred'), ('FAILED', 'Failed')], default='QUEUED', max_length=16, verbose_name='State'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='last_try',
|
||||||
|
field=models.DateTimeField(db_index=True, null=True, verbose_name='last try'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='priority',
|
||||||
|
field=models.PositiveIntegerField(choices=[(0, 'Critical (not queued)'), (1, 'High'), (2, 'Normal'), (3, 'Low')], db_index=True, default=2, verbose_name='Priority'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='retries',
|
||||||
|
field=models.PositiveIntegerField(db_index=True, default=0, verbose_name='retries'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='message',
|
||||||
|
name='state',
|
||||||
|
field=models.CharField(choices=[('QUEUED', 'Queued'), ('SENT', 'Sent'), ('DEFERRED', 'Deferred'), ('FAILED', 'Failed')], db_index=True, default='QUEUED', max_length=16, verbose_name='State'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -67,7 +67,7 @@ class SMTPLog(models.Model):
|
||||||
(SUCCESS, _("Success")),
|
(SUCCESS, _("Success")),
|
||||||
(FAILURE, _("Failure")),
|
(FAILURE, _("Failure")),
|
||||||
)
|
)
|
||||||
message = models.ForeignKey(Message, editable=False, related_name='logs')
|
message = models.ForeignKey(Message, editable=False, related_name='logs', on_delete=models.CASCADE)
|
||||||
result = models.CharField(max_length=16, choices=RESULTS, default=SUCCESS)
|
result = models.CharField(max_length=16, choices=RESULTS, default=SUCCESS)
|
||||||
date = models.DateTimeField(auto_now_add=True)
|
date = models.DateTimeField(auto_now_add=True)
|
||||||
log_message = models.TextField()
|
log_message = models.TextField()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
import django.db.models.deletion
|
||||||
import orchestra.core.validators
|
import orchestra.core.validators
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import orchestra.models.fields
|
import orchestra.models.fields
|
||||||
|
@ -22,7 +23,7 @@ class Migration(migrations.Migration):
|
||||||
('description', models.TextField(blank=True, verbose_name='description')),
|
('description', models.TextField(blank=True, verbose_name='description')),
|
||||||
('amount', models.PositiveIntegerField(default=1, verbose_name='amount')),
|
('amount', models.PositiveIntegerField(default=1, verbose_name='amount')),
|
||||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this service should be treated as active. Unselect this instead of deleting services.', verbose_name='active')),
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this service should be treated as active. Unselect this instead of deleting services.', verbose_name='active')),
|
||||||
('account', models.ForeignKey(related_name='miscellaneous', verbose_name='account', to=settings.AUTH_USER_MODEL)),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='miscellaneous', verbose_name='account', to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'miscellaneous',
|
'verbose_name_plural': 'miscellaneous',
|
||||||
|
@ -43,6 +44,6 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='miscellaneous',
|
model_name='miscellaneous',
|
||||||
name='service',
|
name='service',
|
||||||
field=models.ForeignKey(related_name='instances', verbose_name='service', to='miscellaneous.MiscService'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', verbose_name='service', to='miscellaneous.MiscService'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:28
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import orchestra.core.validators
|
||||||
|
import orchestra.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('miscellaneous', '0001_initial'), ('miscellaneous', '0002_auto_20150723_1252')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Miscellaneous',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('identifier', orchestra.models.fields.NullableCharField(help_text='A unique identifier for this service.', max_length=256, null=True, unique=True, verbose_name='identifier')),
|
||||||
|
('description', models.TextField(blank=True, verbose_name='description')),
|
||||||
|
('amount', models.PositiveIntegerField(default=1, verbose_name='amount')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this service should be treated as active. Unselect this instead of deleting services.', verbose_name='active')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='miscellaneous', to=settings.AUTH_USER_MODEL, verbose_name='account')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'miscellaneous',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='MiscService',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Raw name used for internal referenciation, i.e. service match definition', max_length=32, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
|
||||||
|
('verbose_name', models.CharField(blank=True, help_text='Human readable name', max_length=256, verbose_name='verbose name')),
|
||||||
|
('description', models.TextField(blank=True, help_text='Optional description', verbose_name='description')),
|
||||||
|
('has_identifier', models.BooleanField(default=True, help_text='Designates if this service has a <b>unique text</b> field that identifies it or not.', verbose_name='has identifier')),
|
||||||
|
('has_amount', models.BooleanField(default=False, help_text='Designates whether this service has <tt>amount</tt> property or not.', verbose_name='has amount')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Whether new instances of this service can be created or not. Unselect this instead of deleting services.', verbose_name='active')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='miscellaneous',
|
||||||
|
name='service',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='miscellaneous.MiscService', verbose_name='service'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='miscellaneous',
|
||||||
|
name='identifier',
|
||||||
|
field=orchestra.models.fields.NullableCharField(db_index=True, help_text='A unique identifier for this service.', max_length=256, null=True, unique=True, verbose_name='identifier'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -42,10 +42,10 @@ class MiscService(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Miscellaneous(models.Model):
|
class Miscellaneous(models.Model):
|
||||||
service = models.ForeignKey(MiscService, verbose_name=_("service"),
|
service = models.ForeignKey(MiscService, on_delete=models.CASCADE,
|
||||||
related_name='instances')
|
verbose_name=_("service"), related_name='instances')
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
|
||||||
related_name='miscellaneous')
|
verbose_name=_("account"), related_name='miscellaneous')
|
||||||
identifier = NullableCharField(_("identifier"), max_length=256, null=True, unique=True,
|
identifier = NullableCharField(_("identifier"), max_length=256, null=True, unique=True,
|
||||||
db_index=True, help_text=_("A unique identifier for this service."))
|
db_index=True, help_text=_("A unique identifier for this service."))
|
||||||
description = models.TextField(_("description"), blank=True)
|
description = models.TextField(_("description"), blank=True)
|
||||||
|
|
|
@ -39,10 +39,10 @@ class Operation():
|
||||||
self.routes = routes
|
self.routes = routes
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls, operations, serialize=False, async=None):
|
def execute(cls, operations, serialize=False, run_async=None):
|
||||||
from . import manager
|
from . import manager
|
||||||
scripts, backend_serialize = manager.generate(operations)
|
scripts, backend_serialize = manager.generate(operations)
|
||||||
return manager.execute(scripts, serialize=(serialize or backend_serialize), async=async)
|
return manager.execute(scripts, serialize=(serialize or backend_serialize), run_async=run_async)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_for_action(cls, instances, action):
|
def create_for_action(cls, instances, action):
|
||||||
|
|
|
@ -30,14 +30,14 @@ STATE_COLORS = {
|
||||||
|
|
||||||
class RouteAdmin(ExtendedModelAdmin):
|
class RouteAdmin(ExtendedModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
'display_backend', 'host', 'match', 'display_model', 'display_actions', 'async',
|
'display_backend', 'host', 'match', 'display_model', 'display_actions', 'run_async',
|
||||||
'is_active'
|
'is_active'
|
||||||
)
|
)
|
||||||
list_editable = ('host', 'match', 'async', 'is_active')
|
list_editable = ('host', 'match', 'run_async', 'is_active')
|
||||||
list_filter = ('host', 'is_active', 'async', 'backend')
|
list_filter = ('host', 'is_active', 'run_async', 'backend')
|
||||||
list_prefetch_related = ('host',)
|
list_prefetch_related = ('host',)
|
||||||
ordering = ('backend',)
|
ordering = ('backend',)
|
||||||
add_fields = ('backend', 'host', 'match', 'async', 'is_active')
|
add_fields = ('backend', 'host', 'match', 'run_async', 'is_active')
|
||||||
change_form = RouteForm
|
change_form = RouteForm
|
||||||
actions = (orchestrate,)
|
actions = (orchestrate,)
|
||||||
change_view_actions = actions
|
change_view_actions = actions
|
||||||
|
|
|
@ -182,7 +182,7 @@ class ServiceBackend(plugins.Plugin, metaclass=ServiceMount):
|
||||||
log = manager.create(backend=self.get_name(), state=state, server=server)
|
log = manager.create(backend=self.get_name(), state=state, server=server)
|
||||||
return log
|
return log
|
||||||
|
|
||||||
def execute(self, server, async=False, log=None):
|
def execute(self, server, run_async=False, log=None):
|
||||||
from .models import BackendLog
|
from .models import BackendLog
|
||||||
if log is None:
|
if log is None:
|
||||||
log = self.create_log(server)
|
log = self.create_log(server)
|
||||||
|
@ -190,7 +190,7 @@ class ServiceBackend(plugins.Plugin, metaclass=ServiceMount):
|
||||||
if run:
|
if run:
|
||||||
scripts = self.scripts
|
scripts = self.scripts
|
||||||
for method, commands in scripts:
|
for method, commands in scripts:
|
||||||
method(log, server, commands, async)
|
method(log, server, commands, run_async)
|
||||||
if log.state != BackendLog.SUCCESS:
|
if log.state != BackendLog.SUCCESS:
|
||||||
break
|
break
|
||||||
return log
|
return log
|
||||||
|
|
|
@ -2,7 +2,7 @@ import textwrap
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.mail import mail_admins
|
from django.core.mail import mail_admins
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
@ -105,7 +105,7 @@ def get_backend_url(ids):
|
||||||
|
|
||||||
def get_messages(logs):
|
def get_messages(logs):
|
||||||
messages = []
|
messages = []
|
||||||
total, successes, async = 0, 0, 0
|
total, successes, run_async = 0, 0, 0
|
||||||
ids = []
|
ids = []
|
||||||
async_ids = []
|
async_ids = []
|
||||||
for log in logs:
|
for log in logs:
|
||||||
|
@ -118,17 +118,17 @@ def get_messages(logs):
|
||||||
if log.is_success:
|
if log.is_success:
|
||||||
successes += 1
|
successes += 1
|
||||||
elif not log.has_finished:
|
elif not log.has_finished:
|
||||||
async += 1
|
run_async += 1
|
||||||
async_ids.append(log.id)
|
async_ids.append(log.id)
|
||||||
errors = total-successes-async
|
errors = total-successes-run_async
|
||||||
url = get_backend_url(ids)
|
url = get_backend_url(ids)
|
||||||
async_url = get_backend_url(async_ids)
|
async_url = get_backend_url(async_ids)
|
||||||
async_msg = ''
|
async_msg = ''
|
||||||
if async:
|
if run_async:
|
||||||
async_msg = ungettext(
|
async_msg = ungettext(
|
||||||
_('<a href="{async_url}">{name}</a> is running on the background'),
|
_('<a href="{async_url}">{name}</a> is running on the background'),
|
||||||
_('<a href="{async_url}">{async} backends</a> are running on the background'),
|
_('<a href="{async_url}">{run_async} backends</a> are running on the background'),
|
||||||
async)
|
run_async)
|
||||||
if errors:
|
if errors:
|
||||||
if total == 1:
|
if total == 1:
|
||||||
msg = _('<a href="{url}">{name}</a> has fail to execute')
|
msg = _('<a href="{url}">{name}</a> has fail to execute')
|
||||||
|
@ -139,7 +139,7 @@ def get_messages(logs):
|
||||||
errors)
|
errors)
|
||||||
if async_msg:
|
if async_msg:
|
||||||
msg += ', ' + str(async_msg)
|
msg += ', ' + str(async_msg)
|
||||||
msg = msg.format(errors=errors, async=async, async_url=async_url, total=total, url=url,
|
msg = msg.format(errors=errors, run_async=run_async, async_url=async_url, total=total, url=url,
|
||||||
name=log.backend)
|
name=log.backend)
|
||||||
messages.append(('error', msg + '.'))
|
messages.append(('error', msg + '.'))
|
||||||
elif successes:
|
elif successes:
|
||||||
|
@ -158,12 +158,12 @@ def get_messages(logs):
|
||||||
_('<a href="{url}">{total} backends</a> have been executed'),
|
_('<a href="{url}">{total} backends</a> have been executed'),
|
||||||
total)
|
total)
|
||||||
msg = msg.format(
|
msg = msg.format(
|
||||||
total=total, url=url, async_url=async_url, async=async, successes=successes,
|
total=total, url=url, async_url=async_url, run_async=run_async, successes=successes,
|
||||||
name=log.backend
|
name=log.backend
|
||||||
)
|
)
|
||||||
messages.append(('success', msg + '.'))
|
messages.append(('success', msg + '.'))
|
||||||
else:
|
else:
|
||||||
msg = async_msg.format(url=url, async_url=async_url, async=async, name=log.backend)
|
msg = async_msg.format(url=url, async_url=async_url, run_async=run_async, name=log.backend)
|
||||||
messages.append(('success', msg + '.'))
|
messages.append(('success', msg + '.'))
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ class Command(BaseCommand):
|
||||||
if not confirm("\n\nAre your sure to execute the previous scripts on %(servers)s (yes/no)? " % context):
|
if not confirm("\n\nAre your sure to execute the previous scripts on %(servers)s (yes/no)? " % context):
|
||||||
return
|
return
|
||||||
if not dry:
|
if not dry:
|
||||||
logs = manager.execute(scripts, serialize=serialize, async=True)
|
logs = manager.execute(scripts, serialize=serialize, run_async=True)
|
||||||
running = list(logs)
|
running = list(logs)
|
||||||
stdout = 0
|
stdout = 0
|
||||||
stderr = 0
|
stderr = 0
|
||||||
|
|
|
@ -97,12 +97,12 @@ def generate(operations):
|
||||||
return scripts, serialize
|
return scripts, serialize
|
||||||
|
|
||||||
|
|
||||||
def execute(scripts, serialize=False, async=None):
|
def execute(scripts, serialize=False, run_async=None):
|
||||||
"""
|
"""
|
||||||
executes the operations on the servers
|
executes the operations on the servers
|
||||||
|
|
||||||
serialize: execute one backend at a time
|
serialize: execute one backend at a time
|
||||||
async: do not join threads (overrides route.async)
|
run_async: do not join threads (overrides route.run_async)
|
||||||
"""
|
"""
|
||||||
if settings.ORCHESTRATION_DISABLE_EXECUTION:
|
if settings.ORCHESTRATION_DISABLE_EXECUTION:
|
||||||
logger.info('Orchestration execution is dissabled by ORCHESTRATION_DISABLE_EXECUTION.')
|
logger.info('Orchestration execution is dissabled by ORCHESTRATION_DISABLE_EXECUTION.')
|
||||||
|
@ -115,12 +115,12 @@ def execute(scripts, serialize=False, async=None):
|
||||||
route, __, async_action = key
|
route, __, async_action = key
|
||||||
backend, operations = value
|
backend, operations = value
|
||||||
args = (route.host,)
|
args = (route.host,)
|
||||||
if async is None:
|
if run_async is None:
|
||||||
is_async = not serialize and (route.async or async_action)
|
is_async = not serialize and (route.run_async or async_action)
|
||||||
else:
|
else:
|
||||||
is_async = not serialize and (async or async_action)
|
is_async = not serialize and (run_async or async_action)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'async': is_async,
|
'run_async': is_async,
|
||||||
}
|
}
|
||||||
# we clone the connection just in case we are isolated inside a transaction
|
# we clone the connection just in case we are isolated inside a transaction
|
||||||
with db.clone(model=BackendLog) as handle:
|
with db.clone(model=BackendLog) as handle:
|
||||||
|
|
|
@ -17,7 +17,7 @@ from . import settings
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def Paramiko(backend, log, server, cmds, async=False, paramiko_connections={}):
|
def Paramiko(backend, log, server, cmds, run_async=False, paramiko_connections={}):
|
||||||
"""
|
"""
|
||||||
Executes cmds to remote server using Pramaiko
|
Executes cmds to remote server using Pramaiko
|
||||||
"""
|
"""
|
||||||
|
@ -55,7 +55,7 @@ def Paramiko(backend, log, server, cmds, async=False, paramiko_connections={}):
|
||||||
channel.shutdown_write()
|
channel.shutdown_write()
|
||||||
# Log results
|
# Log results
|
||||||
logger.debug('%s running on %s' % (backend, server))
|
logger.debug('%s running on %s' % (backend, server))
|
||||||
if async:
|
if run_async:
|
||||||
second = False
|
second = False
|
||||||
while True:
|
while True:
|
||||||
# Non-blocking is the secret ingridient in the async sauce
|
# Non-blocking is the secret ingridient in the async sauce
|
||||||
|
@ -97,7 +97,7 @@ def Paramiko(backend, log, server, cmds, async=False, paramiko_connections={}):
|
||||||
channel.close()
|
channel.close()
|
||||||
|
|
||||||
|
|
||||||
def OpenSSH(backend, log, server, cmds, async=False):
|
def OpenSSH(backend, log, server, cmds, run_async=False):
|
||||||
"""
|
"""
|
||||||
Executes cmds to remote server using SSH with connection resuse for maximum performance
|
Executes cmds to remote server using SSH with connection resuse for maximum performance
|
||||||
"""
|
"""
|
||||||
|
@ -110,9 +110,9 @@ def OpenSSH(backend, log, server, cmds, async=False):
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
ssh = sshrun(server.get_address(), script, executable=backend.script_executable,
|
ssh = sshrun(server.get_address(), script, executable=backend.script_executable,
|
||||||
persist=True, async=async, silent=True)
|
persist=True, run_async=run_async, silent=True)
|
||||||
logger.debug('%s running on %s' % (backend, server))
|
logger.debug('%s running on %s' % (backend, server))
|
||||||
if async:
|
if run_async:
|
||||||
for state in ssh:
|
for state in ssh:
|
||||||
log.stdout += state.stdout.decode('utf8')
|
log.stdout += state.stdout.decode('utf8')
|
||||||
log.stderr += state.stderr.decode('utf8')
|
log.stderr += state.stderr.decode('utf8')
|
||||||
|
@ -148,7 +148,7 @@ def SSH(*args, **kwargs):
|
||||||
return method(*args, **kwargs)
|
return method(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def Python(backend, log, server, cmds, async=False):
|
def Python(backend, log, server, cmds, run_async=False):
|
||||||
script = ''
|
script = ''
|
||||||
functions = set()
|
functions = set()
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
|
@ -170,7 +170,7 @@ def Python(backend, log, server, cmds, async=False):
|
||||||
log.stdout += line + '\n'
|
log.stdout += line + '\n'
|
||||||
if result:
|
if result:
|
||||||
log.stdout += '# Result: %s\n' % result
|
log.stdout += '# Result: %s\n' % result
|
||||||
if async:
|
if run_async:
|
||||||
log.save(update_fields=('stdout', 'updated_at'))
|
log.save(update_fields=('stdout', 'updated_at'))
|
||||||
except:
|
except:
|
||||||
log.exit_code = 1
|
log.exit_code = 1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from threading import local
|
from threading import local
|
||||||
|
|
||||||
from django.contrib.admin.models import LogEntry
|
from django.contrib.admin.models import LogEntry
|
||||||
from django.core.urlresolvers import resolve
|
from django.urls import resolve
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models.signals import pre_delete, post_save, m2m_changed
|
from django.db.models.signals import pre_delete, post_save, m2m_changed
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import orchestra.models.fields
|
import orchestra.models.fields
|
||||||
|
|
||||||
|
@ -38,8 +39,8 @@ class Migration(migrations.Migration):
|
||||||
('backend', models.CharField(max_length=256, verbose_name='backend')),
|
('backend', models.CharField(max_length=256, verbose_name='backend')),
|
||||||
('action', models.CharField(max_length=64, verbose_name='action')),
|
('action', models.CharField(max_length=64, verbose_name='action')),
|
||||||
('object_id', models.PositiveIntegerField()),
|
('object_id', models.PositiveIntegerField()),
|
||||||
('content_type', models.ForeignKey(to='contenttypes.ContentType')),
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||||
('log', models.ForeignKey(related_name='operations', to='orchestration.BackendLog')),
|
('log', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='operations', to='orchestration.BackendLog')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'Operations',
|
'verbose_name_plural': 'Operations',
|
||||||
|
@ -68,12 +69,12 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='route',
|
model_name='route',
|
||||||
name='host',
|
name='host',
|
||||||
field=models.ForeignKey(to='orchestration.Server', verbose_name='host'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='host'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='backendlog',
|
model_name='backendlog',
|
||||||
name='server',
|
name='server',
|
||||||
field=models.ForeignKey(related_name='execution_logs', to='orchestration.Server', verbose_name='server'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='execution_logs', to='orchestration.Server', verbose_name='server'),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='route',
|
name='route',
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import orchestra.core.validators
|
||||||
|
import orchestra.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('orchestration', '0001_initial'), ('orchestration', '0002_auto_20150506_1420'), ('orchestration', '0003_auto_20150512_1512'), ('orchestration', '0004_route_async_actions'), ('orchestration', '0005_auto_20150709_1016'), ('orchestration', '0006_auto_20160219_1110'), ('orchestration', '0007_auto_20170528_2011'), ('orchestration', '0008_auto_20190805_1134'), ('orchestration', '0009_rename_route_async_run_async')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='BackendLog',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('backend', models.CharField(max_length=256, verbose_name='backend')),
|
||||||
|
('state', models.CharField(choices=[('RECEIVED', 'RECEIVED'), ('TIMEOUT', 'TIMEOUT'), ('STARTED', 'STARTED'), ('SUCCESS', 'SUCCESS'), ('FAILURE', 'FAILURE'), ('ERROR', 'ERROR'), ('ABORTED', 'ABORTED'), ('REVOKED', 'REVOKED')], default='RECEIVED', max_length=16, verbose_name='state')),
|
||||||
|
('script', models.TextField(verbose_name='script')),
|
||||||
|
('stdout', models.TextField(verbose_name='stdout')),
|
||||||
|
('stderr', models.TextField(verbose_name='stdin')),
|
||||||
|
('traceback', models.TextField(verbose_name='traceback')),
|
||||||
|
('exit_code', models.IntegerField(null=True, verbose_name='exit code')),
|
||||||
|
('task_id', models.CharField(help_text='Celery task ID when used as execution backend', max_length=36, null=True, unique=True, verbose_name='task ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='updated')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'get_latest_by': 'id',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='BackendOperation',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('backend', models.CharField(max_length=256, verbose_name='backend')),
|
||||||
|
('action', models.CharField(max_length=64, verbose_name='action')),
|
||||||
|
('object_id', models.PositiveIntegerField(null=True)),
|
||||||
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||||
|
('log', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='operations', to='orchestration.BackendLog')),
|
||||||
|
('instance_repr', models.CharField(default='', max_length=256, verbose_name='instance representation')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Operations',
|
||||||
|
'verbose_name': 'Operation',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Route',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('backend', models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('Apache2Controller', '[S] Apache 2'), ('BSCWController', '[S] BSCW SaaS'), ('Bind9MasterDomainController', '[S] Bind9 master domain'), ('Bind9SlaveDomainController', '[S] Bind9 slave domain'), ('DokuWikiMuController', '[S] DokuWiki multisite'), ('DovecotPostfixPasswdVirtualUserController', '[S] Dovecot-Postfix virtualuser'), ('DrupalMuController', '[S] Drupal multisite'), ('GitLabSaaSController', '[S] GitLab SaaS'), ('AutoresponseController', '[S] Mail autoresponse'), ('MailmanController', '[S] Mailman'), ('MySQLController', '[S] MySQL database'), ('MySQLUserController', '[S] MySQL user'), ('PHPController', '[S] PHP FPM/FCGID'), ('PostfixAddressController', '[S] Postfix address'), ('uWSGIPythonController', '[S] Python uWSGI'), ('StaticController', '[S] Static'), ('SymbolicLinkController', '[S] Symbolic link webapp'), ('UNIXUserMaildirController', '[S] UNIX maildir user'), ('UNIXUserController', '[S] UNIX user'), ('WebalizerAppController', '[S] Webalizer App'), ('WebalizerController', '[S] Webalizer Content'), ('WordPressController', '[S] Wordpress'), ('WordpressMuController', '[S] Wordpress multisite'), ('PhpListSaaSController', '[S] phpList SaaS')], max_length=256, verbose_name='backend')),
|
||||||
|
('match', models.CharField(blank=True, default='True', help_text='Python expression used for selecting the targe host, <em>instance</em> referes to the current object.', max_length=256, verbose_name='match')),
|
||||||
|
('is_active', models.BooleanField(default=True, verbose_name='active')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Server',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Verbose name or hostname of this server.', max_length=256, unique=True, verbose_name='name')),
|
||||||
|
('address', orchestra.models.fields.NullableCharField(blank=True, help_text='Optional IP address or domain name. If blank, name field will be used for address resolution.<br>If the IP address never changes you can set this field and save DNS requests.', max_length=256, null=True, unique=True, validators=[orchestra.core.validators.OrValidator(orchestra.core.validators.validate_ip_address, orchestra.core.validators.validate_hostname)], verbose_name='address')),
|
||||||
|
('description', models.TextField(blank=True, verbose_name='description')),
|
||||||
|
('os', models.CharField(choices=[('LINUX', 'Linux')], default='LINUX', max_length=32, verbose_name='operative system')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='route',
|
||||||
|
name='host',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='host'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='backendlog',
|
||||||
|
name='server',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='execution_logs', to='orchestration.Server', verbose_name='server'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='route',
|
||||||
|
name='run_async',
|
||||||
|
field=models.BooleanField(default=False, help_text='Whether or not block the request/response cycle waitting this backend to finish its execution. Usually you want slave servers to run asynchronously.'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='route',
|
||||||
|
unique_together=set([('backend', 'host')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='backendlog',
|
||||||
|
name='state',
|
||||||
|
field=models.CharField(choices=[('RECEIVED', 'RECEIVED'), ('TIMEOUT', 'TIMEOUT'), ('STARTED', 'STARTED'), ('SUCCESS', 'SUCCESS'), ('FAILURE', 'FAILURE'), ('ERROR', 'ERROR'), ('ABORTED', 'ABORTED'), ('REVOKED', 'REVOKED'), ('NOTHING', 'NOTHING')], default='RECEIVED', max_length=16, verbose_name='state'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='backendlog',
|
||||||
|
name='stderr',
|
||||||
|
field=models.TextField(verbose_name='stderr'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='backend',
|
||||||
|
field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('Apache2Controller', '[S] Apache 2'), ('BSCWController', '[S] BSCW SaaS'), ('Bind9MasterDomainController', '[S] Bind9 master domain'), ('Bind9SlaveDomainController', '[S] Bind9 slave domain'), ('DokuWikiMuController', '[S] DokuWiki multisite'), ('DovecotPostfixPasswdVirtualUserController', '[S] Dovecot-Postfix virtualuser'), ('DrupalMuController', '[S] Drupal multisite'), ('GitLabSaaSController', '[S] GitLab SaaS'), ('AutoresponseController', '[S] Mail autoresponse'), ('MailmanController', '[S] Mailman'), ('MailmanVirtualDomainController', '[S] Mailman virtdomain-only'), ('MySQLController', '[S] MySQL database'), ('MySQLUserController', '[S] MySQL user'), ('PHPController', '[S] PHP FPM/FCGID'), ('PostfixAddressController', '[S] Postfix address'), ('PostfixAddressVirtualDomainController', '[S] Postfix address virtdomain-only'), ('uWSGIPythonController', '[S] Python uWSGI'), ('StaticController', '[S] Static'), ('SymbolicLinkController', '[S] Symbolic link webapp'), ('SyncBind9MasterDomainController', '[S] Sync Bind9 master domain'), ('SyncBind9SlaveDomainController', '[S] Sync Bind9 slave domain'), ('UNIXUserMaildirController', '[S] UNIX maildir user'), ('UNIXUserController', '[S] UNIX user'), ('WebalizerAppController', '[S] Webalizer App'), ('WebalizerController', '[S] Webalizer Content'), ('WordPressController', '[S] Wordpress'), ('WordpressMuController', '[S] Wordpress multisite'), ('PhpListSaaSController', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='route',
|
||||||
|
name='async_actions',
|
||||||
|
field=orchestra.models.fields.MultiSelectField(blank=True, help_text='Specify individual actions to be executed asynchronoulsy.', max_length=256),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='backendlog',
|
||||||
|
name='created_at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='created'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='backend',
|
||||||
|
field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic'), ('Apache2Controller', '[S] Apache 2'), ('BSCWController', '[S] BSCW SaaS'), ('Bind9MasterDomainController', '[S] Bind9 master domain'), ('Bind9SlaveDomainController', '[S] Bind9 slave domain'), ('DokuWikiMuController', '[S] DokuWiki multisite'), ('DrupalMuController', '[S] Drupal multisite'), ('GitLabSaaSController', '[S] GitLab SaaS'), ('AutoresponseController', '[S] Mail autoresponse'), ('MailScannerSpamRuleController', '[S] MailScanner ruleset'), ('MailmanController', '[S] Mailman'), ('MailmanVirtualDomainController', '[S] Mailman virtdomain-only'), ('MoodleController', '[S] Moodle'), ('MoodleWWWRootController', '[S] Moodle WWWRoot (required)'), ('MoodleMuController', '[S] Moodle multisite'), ('MySQLController', '[S] MySQL database'), ('MySQLUserController', '[S] MySQL user'), ('PHPController', '[S] PHP FPM/FCGID'), ('PangeaProxmoxOVZ', '[S] PangeaProxmoxOVZ'), ('PostfixAddressController', '[S] Postfix address'), ('PostfixAddressVirtualDomainController', '[S] Postfix address virtdomain-only'), ('PostfixRecipientAccessController', '[S] Postfix recipient access'), ('ProxmoxOVZ', '[S] ProxmoxOVZ'), ('uWSGIPythonController', '[S] Python uWSGI'), ('StaticController', '[S] Static'), ('SymbolicLinkController', '[S] Symbolic link webapp'), ('SyncBind9MasterDomainController', '[S] Sync Bind9 master domain'), ('SyncBind9SlaveDomainController', '[S] Sync Bind9 slave domain'), ('UNIXUserMaildirController', '[S] UNIX maildir user'), ('UNIXUserController', '[S] UNIX user'), ('WebalizerAppController', '[S] Webalizer App'), ('WebalizerController', '[S] Webalizer Content'), ('WordPressURLController', '[S] WordPress URL'), ('WordPressController', '[S] Wordpress'), ('WordpressMuController', '[S] Wordpress multisite'), ('OwnCloudController', '[S] ownCloud SaaS'), ('PhpListSaaSController', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
|
||||||
|
),
|
||||||
|
migrations.AlterIndexTogether(
|
||||||
|
name='backendoperation',
|
||||||
|
index_together=set([('content_type', 'object_id')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='backend',
|
||||||
|
field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('ProxmoxOpenVZTraffic', '[M] ProxmoxOpenVZTraffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('NextCloudDiskQuota', '[M] nextCloud SaaS Disk Quota'), ('NextcloudTraffic', '[M] nextCloud SaaS Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic'), ('Apache2Controller', '[S] Apache 2'), ('BSCWController', '[S] BSCW SaaS'), ('Bind9MasterDomainController', '[S] Bind9 master domain'), ('Bind9SlaveDomainController', '[S] Bind9 slave domain'), ('DokuWikiMuController', '[S] DokuWiki multisite'), ('DrupalMuController', '[S] Drupal multisite'), ('GitLabSaaSController', '[S] GitLab SaaS'), ('LetsEncryptController', "[S] Let's encrypt!"), ('LxcController', '[S] LxcController'), ('AutoresponseController', '[S] Mail autoresponse'), ('MailScannerSpamRuleController', '[S] MailScanner ruleset'), ('MailmanController', '[S] Mailman'), ('MailmanVirtualDomainController', '[S] Mailman virtdomain-only'), ('MoodleController', '[S] Moodle'), ('MoodleWWWRootController', '[S] Moodle WWWRoot (required)'), ('MoodleMuController', '[S] Moodle multisite'), ('MySQLController', '[S] MySQL database'), ('MySQLUserController', '[S] MySQL user'), ('PHPController', '[S] PHP FPM/FCGID'), ('PangeaProxmoxOVZ', '[S] PangeaProxmoxOVZ'), ('PostfixAddressController', '[S] Postfix address'), ('PostfixAddressVirtualDomainController', '[S] Postfix address virtdomain-only'), ('PostfixRecipientAccessController', '[S] Postfix recipient access'), ('ProxmoxOVZ', '[S] ProxmoxOVZ'), ('uWSGIPythonController', '[S] Python uWSGI'), ('StaticController', '[S] Static'), ('SymbolicLinkController', '[S] Symbolic link webapp'), ('SyncBind9MasterDomainController', '[S] Sync Bind9 master domain'), ('SyncBind9SlaveDomainController', '[S] Sync Bind9 slave domain'), ('UNIXUserMaildirController', '[S] UNIX maildir user'), ('UNIXUserController', '[S] UNIX user'), ('WebalizerAppController', '[S] Webalizer App'), ('WebalizerController', '[S] Webalizer Content'), ('WordPressForceSSLController', '[S] WordPress Force SSL'), ('WordPressURLController', '[S] WordPress URL'), ('WordPressController', '[S] Wordpress'), ('WordpressMuController', '[S] Wordpress multisite'), ('NextCloudController', '[S] nextCloud SaaS'), ('OwnCloudController', '[S] ownCloud SaaS'), ('PhpListSaaSController', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='host',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='routes', to='orchestration.Server', verbose_name='host'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='backend',
|
||||||
|
field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('ProxmoxOpenVZTraffic', '[M] ProxmoxOpenVZTraffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('NextCloudDiskQuota', '[M] nextCloud SaaS Disk Quota'), ('NextcloudTraffic', '[M] nextCloud SaaS Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic'), ('Apache2Controller', '[S] Apache 2'), ('BSCWController', '[S] BSCW SaaS'), ('Bind9MasterDomainController', '[S] Bind9 master domain'), ('Bind9SlaveDomainController', '[S] Bind9 slave domain'), ('DokuWikiMuController', '[S] DokuWiki multisite'), ('DrupalMuController', '[S] Drupal multisite'), ('GitLabSaaSController', '[S] GitLab SaaS'), ('LetsEncryptController', "[S] Let's encrypt!"), ('LxcController', '[S] LxcController'), ('AutoresponseController', '[S] Mail autoresponse'), ('MailScannerSpamRuleController', '[S] MailScanner ruleset'), ('MailmanController', '[S] Mailman'), ('MailmanVirtualDomainController', '[S] Mailman virtdomain-only'), ('MoodleController', '[S] Moodle'), ('MoodleWWWRootController', '[S] Moodle WWWRoot (required)'), ('MoodleMuController', '[S] Moodle multisite'), ('MySQLController', '[S] MySQL database'), ('MySQLUserController', '[S] MySQL user'), ('PHPController', '[S] PHP FPM/FCGID'), ('PangeaProxmoxOVZ', '[S] PangeaProxmoxOVZ'), ('PostfixAddressController', '[S] Postfix address'), ('PostfixAddressVirtualDomainController', '[S] Postfix address virtdomain-only'), ('PostfixRecipientAccessController', '[S] Postfix recipient access'), ('ProxmoxOVZ', '[S] ProxmoxOVZ'), ('uWSGIPythonController', '[S] Python uWSGI'), ('RoundcubeIdentityController', '[S] Roundcube Identity Controller'), ('StaticController', '[S] Static'), ('SymbolicLinkController', '[S] Symbolic link webapp'), ('SyncBind9MasterDomainController', '[S] Sync Bind9 master domain'), ('SyncBind9SlaveDomainController', '[S] Sync Bind9 slave domain'), ('UNIXUserMaildirController', '[S] UNIX maildir user'), ('UNIXUserController', '[S] UNIX user'), ('WebalizerAppController', '[S] Webalizer App'), ('WebalizerController', '[S] Webalizer Content'), ('WordPressForceSSLController', '[S] WordPress Force SSL'), ('WordPressURLController', '[S] WordPress URL'), ('WordPressController', '[S] Wordpress'), ('WordpressMuController', '[S] Wordpress multisite'), ('NextCloudController', '[S] nextCloud SaaS'), ('OwnCloudController', '[S] ownCloud SaaS'), ('PhpListSaaSController', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='backend',
|
||||||
|
field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('ProxmoxOpenVZTraffic', '[M] ProxmoxOpenVZTraffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('NextCloudDiskQuota', '[M] nextCloud SaaS Disk Quota'), ('NextcloudTraffic', '[M] nextCloud SaaS Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic'), ('Apache2Controller', '[S] Apache 2'), ('BSCWController', '[S] BSCW SaaS'), ('Bind9MasterDomainController', '[S] Bind9 master domain'), ('Bind9SlaveDomainController', '[S] Bind9 slave domain'), ('DokuWikiMuController', '[S] DokuWiki multisite'), ('DrupalMuController', '[S] Drupal multisite'), ('GitLabSaaSController', '[S] GitLab SaaS'), ('LetsEncryptController', "[S] Let's encrypt!"), ('LxcController', '[S] LxcController'), ('AutoresponseController', '[S] Mail autoresponse'), ('MailmanController', '[S] Mailman'), ('MailmanVirtualDomainController', '[S] Mailman virtdomain-only'), ('MoodleController', '[S] Moodle'), ('MoodleWWWRootController', '[S] Moodle WWWRoot (required)'), ('MoodleMuController', '[S] Moodle multisite'), ('MySQLController', '[S] MySQL database'), ('MySQLUserController', '[S] MySQL user'), ('PHPController', '[S] PHP FPM/FCGID'), ('PostfixAddressController', '[S] Postfix address'), ('PostfixAddressVirtualDomainController', '[S] Postfix address virtdomain-only'), ('ProxmoxOVZ', '[S] ProxmoxOVZ'), ('uWSGIPythonController', '[S] Python uWSGI'), ('RoundcubeIdentityController', '[S] Roundcube Identity Controller'), ('StaticController', '[S] Static'), ('SymbolicLinkController', '[S] Symbolic link webapp'), ('UNIXUserMaildirController', '[S] UNIX maildir user'), ('UNIXUserController', '[S] UNIX user'), ('WebalizerAppController', '[S] Webalizer App'), ('WebalizerController', '[S] Webalizer Content'), ('WordPressForceSSLController', '[S] WordPress Force SSL'), ('WordPressURLController', '[S] WordPress URL'), ('WordPressController', '[S] Wordpress'), ('WordpressMuController', '[S] Wordpress multisite'), ('NextCloudController', '[S] nextCloud SaaS'), ('OwnCloudController', '[S] ownCloud SaaS'), ('PhpListSaaSController', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-03-30 10:49
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('orchestration', '0008_auto_20190805_1134'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='route',
|
||||||
|
old_name='async',
|
||||||
|
new_name='run_async',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='backend',
|
||||||
|
field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('ProxmoxOpenVZTraffic', '[M] ProxmoxOpenVZTraffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('NextCloudDiskQuota', '[M] nextCloud SaaS Disk Quota'), ('NextcloudTraffic', '[M] nextCloud SaaS Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic'), ('Apache2Controller', '[S] Apache 2'), ('BSCWController', '[S] BSCW SaaS'), ('Bind9MasterDomainController', '[S] Bind9 master domain'), ('Bind9SlaveDomainController', '[S] Bind9 slave domain'), ('DokuWikiMuController', '[S] DokuWiki multisite'), ('DrupalMuController', '[S] Drupal multisite'), ('GitLabSaaSController', '[S] GitLab SaaS'), ('LetsEncryptController', "[S] Let's encrypt!"), ('LxcController', '[S] LxcController'), ('AutoresponseController', '[S] Mail autoresponse'), ('MailmanController', '[S] Mailman'), ('MailmanVirtualDomainController', '[S] Mailman virtdomain-only'), ('MoodleController', '[S] Moodle'), ('MoodleWWWRootController', '[S] Moodle WWWRoot (required)'), ('MoodleMuController', '[S] Moodle multisite'), ('MySQLController', '[S] MySQL database'), ('MySQLUserController', '[S] MySQL user'), ('PHPController', '[S] PHP FPM/FCGID'), ('PostfixAddressController', '[S] Postfix address'), ('PostfixAddressVirtualDomainController', '[S] Postfix address virtdomain-only'), ('ProxmoxOVZ', '[S] ProxmoxOVZ'), ('uWSGIPythonController', '[S] Python uWSGI'), ('RoundcubeIdentityController', '[S] Roundcube Identity Controller'), ('StaticController', '[S] Static'), ('SymbolicLinkController', '[S] Symbolic link webapp'), ('UNIXUserMaildirController', '[S] UNIX maildir user'), ('UNIXUserController', '[S] UNIX user'), ('WebalizerAppController', '[S] Webalizer App'), ('WebalizerController', '[S] Webalizer Content'), ('WordPressForceSSLController', '[S] WordPress Force SSL'), ('WordPressURLController', '[S] WordPress URL'), ('WordPressController', '[S] Wordpress'), ('WordpressMuController', '[S] Wordpress multisite'), ('NextCloudController', '[S] nextCloud SaaS'), ('OwnCloudController', '[S] ownCloud SaaS'), ('PhpListSaaSController', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -90,7 +90,7 @@ class BackendLog(models.Model):
|
||||||
|
|
||||||
backend = models.CharField(_("backend"), max_length=256)
|
backend = models.CharField(_("backend"), max_length=256)
|
||||||
state = models.CharField(_("state"), max_length=16, choices=STATES, default=RECEIVED)
|
state = models.CharField(_("state"), max_length=16, choices=STATES, default=RECEIVED)
|
||||||
server = models.ForeignKey(Server, verbose_name=_("server"), related_name='execution_logs')
|
server = models.ForeignKey(Server, verbose_name=_("server"), related_name='execution_logs', on_delete=models.CASCADE)
|
||||||
script = models.TextField(_("script"))
|
script = models.TextField(_("script"))
|
||||||
stdout = models.TextField(_("stdout"))
|
stdout = models.TextField(_("stdout"))
|
||||||
stderr = models.TextField(_("stderr"))
|
stderr = models.TextField(_("stderr"))
|
||||||
|
@ -135,10 +135,10 @@ class BackendOperation(models.Model):
|
||||||
"""
|
"""
|
||||||
Encapsulates an operation, storing its related object, the action and the backend.
|
Encapsulates an operation, storing its related object, the action and the backend.
|
||||||
"""
|
"""
|
||||||
log = models.ForeignKey('orchestration.BackendLog', related_name='operations')
|
log = models.ForeignKey('orchestration.BackendLog', related_name='operations', on_delete=models.CASCADE)
|
||||||
backend = models.CharField(_("backend"), max_length=256)
|
backend = models.CharField(_("backend"), max_length=256)
|
||||||
action = models.CharField(_("action"), max_length=64)
|
action = models.CharField(_("action"), max_length=64)
|
||||||
content_type = models.ForeignKey(ContentType)
|
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||||
object_id = models.PositiveIntegerField(null=True)
|
object_id = models.PositiveIntegerField(null=True)
|
||||||
instance_repr = models.CharField(_("instance representation"), max_length=256)
|
instance_repr = models.CharField(_("instance representation"), max_length=256)
|
||||||
|
|
||||||
|
@ -199,11 +199,11 @@ class Route(models.Model):
|
||||||
"""
|
"""
|
||||||
backend = models.CharField(_("backend"), max_length=256,
|
backend = models.CharField(_("backend"), max_length=256,
|
||||||
choices=ServiceBackend.get_choices())
|
choices=ServiceBackend.get_choices())
|
||||||
host = models.ForeignKey(Server, verbose_name=_("host"), related_name='routes')
|
host = models.ForeignKey(Server, verbose_name=_("host"), related_name='routes', on_delete=models.CASCADE)
|
||||||
match = models.CharField(_("match"), max_length=256, blank=True, default='True',
|
match = models.CharField(_("match"), max_length=256, blank=True, default='True',
|
||||||
help_text=_("Python expression used for selecting the targe host, "
|
help_text=_("Python expression used for selecting the targe host, "
|
||||||
"<em>instance</em> referes to the current object."))
|
"<em>instance</em> referes to the current object."))
|
||||||
async = models.BooleanField(default=False,
|
run_async = models.BooleanField(default=False,
|
||||||
help_text=_("Whether or not block the request/response cycle waitting this backend to "
|
help_text=_("Whether or not block the request/response cycle waitting this backend to "
|
||||||
"finish its execution. Usually you want slave servers to run asynchronously."))
|
"finish its execution. Usually you want slave servers to run asynchronously."))
|
||||||
async_actions = MultiSelectField(max_length=256, blank=True,
|
async_actions = MultiSelectField(max_length=256, blank=True,
|
||||||
|
|
|
@ -12,7 +12,7 @@ class RouterTests(BaseTestCase):
|
||||||
|
|
||||||
def test_list_backends(self):
|
def test_list_backends(self):
|
||||||
# TODO count actual, register and compare
|
# TODO count actual, register and compare
|
||||||
choices = list(Route._meta.get_field('backend')._choices)
|
choices = list(Route._meta.get_field('backend').choices)
|
||||||
self.assertLess(1, len(choices))
|
self.assertLess(1, len(choices))
|
||||||
|
|
||||||
def test_get_instances(self):
|
def test_get_instances(self):
|
||||||
|
@ -25,7 +25,7 @@ class RouterTests(BaseTestCase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
choices = backends.ServiceBackend.get_choices()
|
choices = backends.ServiceBackend.get_choices()
|
||||||
Route._meta.get_field('backend')._choices = choices
|
Route._meta.get_field('backend').choices = choices
|
||||||
backend = TestBackend.get_name()
|
backend = TestBackend.get_name()
|
||||||
|
|
||||||
route = Route.objects.create(backend=backend, host=self.host, match='True')
|
route = Route.objects.create(backend=backend, host=self.host, match='True')
|
||||||
|
|
|
@ -6,9 +6,9 @@ def retrieve_state(servers):
|
||||||
pings = []
|
pings = []
|
||||||
for server in servers:
|
for server in servers:
|
||||||
address = server.get_address()
|
address = server.get_address()
|
||||||
ping = run('ping -c 1 -w 1 %s' % address, async=True)
|
ping = run('ping -c 1 -w 1 %s' % address, run_async=True)
|
||||||
pings.append(ping)
|
pings.append(ping)
|
||||||
uptime = sshrun(address, 'uptime', persist=True, async=True, options={'ConnectTimeout': 1})
|
uptime = sshrun(address, 'uptime', persist=True, run_async=True, options={'ConnectTimeout': 1})
|
||||||
uptimes.append(uptime)
|
uptimes.append(uptime)
|
||||||
|
|
||||||
state = {}
|
state = {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
|
|
@ -16,7 +16,7 @@ def get_related_object(origin, max_depth=2):
|
||||||
if hasattr(field, 'ct_field'):
|
if hasattr(field, 'ct_field'):
|
||||||
yield getattr(node, field.name)
|
yield getattr(node, field.name)
|
||||||
for field in node._meta.fields:
|
for field in node._meta.fields:
|
||||||
if field.rel:
|
if field.remote_field:
|
||||||
try:
|
try:
|
||||||
yield getattr(node, field.name)
|
yield getattr(node, field.name)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,9 +39,9 @@ class Migration(migrations.Migration):
|
||||||
('billed_until', models.DateField(blank=True, verbose_name='billed until', null=True)),
|
('billed_until', models.DateField(blank=True, verbose_name='billed until', null=True)),
|
||||||
('ignore', models.BooleanField(default=False, verbose_name='ignore')),
|
('ignore', models.BooleanField(default=False, verbose_name='ignore')),
|
||||||
('description', models.TextField(blank=True, verbose_name='description')),
|
('description', models.TextField(blank=True, verbose_name='description')),
|
||||||
('account', models.ForeignKey(verbose_name='account', related_name='orders', to=settings.AUTH_USER_MODEL)),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='account', related_name='orders', to=settings.AUTH_USER_MODEL)),
|
||||||
('content_type', models.ForeignKey(to='contenttypes.ContentType')),
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||||
('service', models.ForeignKey(verbose_name='service', related_name='orders', to='services.Service')),
|
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='service', related_name='orders', to='services.Service')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'get_latest_by': 'id',
|
'get_latest_by': 'id',
|
||||||
|
@ -49,6 +50,6 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='metricstorage',
|
model_name='metricstorage',
|
||||||
name='order',
|
name='order',
|
||||||
field=models.ForeignKey(verbose_name='order', related_name='metrics', to='orders.Order'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='order', related_name='metrics', to='orders.Order'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:09
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('orders', '0001_initial'), ('orders', '0002_auto_20150709_1018'), ('orders', '0003_order_content_object_repr'), ('orders', '0004_auto_20150729_0945'), ('orders', '0005_auto_20160219_1107'), ('orders', '0006_auto_20210422_1108')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
|
('services', '__first__'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='MetricStorage',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('value', models.DecimalField(decimal_places=2, max_digits=16, verbose_name='value')),
|
||||||
|
('created_on', models.DateField(auto_now_add=True, verbose_name='created')),
|
||||||
|
('updated_on', models.DateTimeField(verbose_name='updated')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'get_latest_by': 'id',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Order',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('object_id', models.PositiveIntegerField(null=True)),
|
||||||
|
('registered_on', models.DateField(db_index=True, default=django.utils.timezone.now, verbose_name='registered')),
|
||||||
|
('cancelled_on', models.DateField(blank=True, null=True, verbose_name='cancelled')),
|
||||||
|
('billed_on', models.DateField(blank=True, null=True, verbose_name='billed')),
|
||||||
|
('billed_until', models.DateField(blank=True, null=True, verbose_name='billed until')),
|
||||||
|
('ignore', models.BooleanField(default=False, verbose_name='ignore')),
|
||||||
|
('description', models.TextField(blank=True, verbose_name='description')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL, verbose_name='account')),
|
||||||
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||||
|
('service', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='orders', to='services.Service', verbose_name='service')),
|
||||||
|
('content_object_repr', models.CharField(editable=False, help_text='Used for searches.', max_length=256, verbose_name='content object representation')),
|
||||||
|
('billed_metric', models.DecimalField(blank=True, decimal_places=2, max_digits=16, null=True, verbose_name='billed metric')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'get_latest_by': 'id',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='metricstorage',
|
||||||
|
name='order',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='metrics', to='orders.Order', verbose_name='order'),
|
||||||
|
),
|
||||||
|
migrations.AlterIndexTogether(
|
||||||
|
name='order',
|
||||||
|
index_together=set([('content_type', 'object_id')]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -150,12 +150,12 @@ class OrderQuerySet(models.QuerySet):
|
||||||
|
|
||||||
|
|
||||||
class Order(models.Model):
|
class Order(models.Model):
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
|
||||||
related_name='orders')
|
verbose_name=_("account"), related_name='orders')
|
||||||
content_type = models.ForeignKey(ContentType)
|
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||||
object_id = models.PositiveIntegerField(null=True)
|
object_id = models.PositiveIntegerField(null=True)
|
||||||
service = models.ForeignKey(settings.ORDERS_SERVICE_MODEL, verbose_name=_("service"),
|
service = models.ForeignKey(settings.ORDERS_SERVICE_MODEL, on_delete=models.PROTECT,
|
||||||
related_name='orders')
|
verbose_name=_("service"), related_name='orders')
|
||||||
registered_on = models.DateField(_("registered"), default=timezone.now, db_index=True)
|
registered_on = models.DateField(_("registered"), default=timezone.now, db_index=True)
|
||||||
cancelled_on = models.DateField(_("cancelled"), null=True, blank=True)
|
cancelled_on = models.DateField(_("cancelled"), null=True, blank=True)
|
||||||
billed_on = models.DateField(_("billed"), null=True, blank=True)
|
billed_on = models.DateField(_("billed"), null=True, blank=True)
|
||||||
|
@ -294,7 +294,8 @@ class MetricStorageQuerySet(models.QuerySet):
|
||||||
|
|
||||||
class MetricStorage(models.Model):
|
class MetricStorage(models.Model):
|
||||||
""" Stores metric state for future billing """
|
""" Stores metric state for future billing """
|
||||||
order = models.ForeignKey(Order, verbose_name=_("order"), related_name='metrics')
|
order = models.ForeignKey(Order, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("order"), related_name='metrics')
|
||||||
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
|
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
|
||||||
created_on = models.DateField(_("created"), auto_now_add=True, editable=True)
|
created_on = models.DateField(_("created"), auto_now_add=True, editable=True)
|
||||||
# TODO time field?
|
# TODO time field?
|
||||||
|
|
|
@ -15,7 +15,7 @@ def cancel_orders(sender, **kwargs):
|
||||||
if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS:
|
if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS:
|
||||||
instance = kwargs['instance']
|
instance = kwargs['instance']
|
||||||
# Account delete will delete all related orders, no need to maintain order consistency
|
# Account delete will delete all related orders, no need to maintain order consistency
|
||||||
if isinstance(instance, Order.account.field.rel.to):
|
if isinstance(instance, Order.account.field.model):
|
||||||
return
|
return
|
||||||
if type(instance) in services:
|
if type(instance) in services:
|
||||||
for order in Order.objects.by_object(instance).active():
|
for order in Order.objects.by_object(instance).active():
|
||||||
|
|
|
@ -2,7 +2,7 @@ from functools import partial
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import actions
|
from django.contrib.admin import actions
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import jsonfield.fields
|
import jsonfield.fields
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -21,7 +22,7 @@ class Migration(migrations.Migration):
|
||||||
('method', models.CharField(choices=[('CreditCard', 'Credit card'), ('SEPADirectDebit', 'SEPA Direct Debit')], verbose_name='method', max_length=32)),
|
('method', models.CharField(choices=[('CreditCard', 'Credit card'), ('SEPADirectDebit', 'SEPA Direct Debit')], verbose_name='method', max_length=32)),
|
||||||
('data', jsonfield.fields.JSONField(verbose_name='data', default={})),
|
('data', jsonfield.fields.JSONField(verbose_name='data', default={})),
|
||||||
('is_active', models.BooleanField(verbose_name='active', default=True)),
|
('is_active', models.BooleanField(verbose_name='active', default=True)),
|
||||||
('account', models.ForeignKey(verbose_name='account', related_name='paymentsources', to=settings.AUTH_USER_MODEL)),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='account', related_name='paymentsources', to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
@ -33,7 +34,7 @@ class Migration(migrations.Migration):
|
||||||
('currency', models.CharField(max_length=10, default='Eur')),
|
('currency', models.CharField(max_length=10, default='Eur')),
|
||||||
('created_at', models.DateTimeField(verbose_name='created', auto_now_add=True)),
|
('created_at', models.DateTimeField(verbose_name='created', auto_now_add=True)),
|
||||||
('modified_at', models.DateTimeField(verbose_name='modified', auto_now=True)),
|
('modified_at', models.DateTimeField(verbose_name='modified', auto_now=True)),
|
||||||
('bill', models.ForeignKey(verbose_name='bill', related_name='transactions', to='bills.Bill')),
|
('bill', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='bill', related_name='transactions', to='bills.Bill')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
@ -53,11 +54,11 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='transaction',
|
model_name='transaction',
|
||||||
name='process',
|
name='process',
|
||||||
field=models.ForeignKey(verbose_name='process', null=True, blank=True, related_name='transactions', to='payments.TransactionProcess'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='process', null=True, blank=True, related_name='transactions', to='payments.TransactionProcess'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='transaction',
|
model_name='transaction',
|
||||||
name='source',
|
name='source',
|
||||||
field=models.ForeignKey(verbose_name='source', null=True, blank=True, related_name='transactions', to='payments.PaymentSource'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='source', null=True, blank=True, related_name='transactions', to='payments.PaymentSource'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import jsonfield.fields
|
||||||
|
import orchestra.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('payments', '0001_initial'), ('payments', '0002_auto_20150709_1018'), ('payments', '0003_auto_20170528_2011'), ('payments', '0004_auto_20210330_1049')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('bills', '0002_auto_20150429_1417'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PaymentSource',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('method', models.CharField(choices=[('CreditCard', 'Credit card'), ('SEPADirectDebit', 'SEPA Direct Debit')], max_length=32, verbose_name='method')),
|
||||||
|
('data', jsonfield.fields.JSONField(default={}, verbose_name='data')),
|
||||||
|
('is_active', models.BooleanField(default=True, verbose_name='active')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='paymentsources', to=settings.AUTH_USER_MODEL, verbose_name='account')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Transaction',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('state', models.CharField(choices=[('WAITTING_PROCESSING', 'Waitting processing'), ('WAITTING_EXECUTION', 'Waitting execution'), ('EXECUTED', 'Executed'), ('SECURED', 'Secured'), ('REJECTED', 'Rejected')], default='WAITTING_PROCESSING', max_length=32, verbose_name='state')),
|
||||||
|
('amount', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='amount')),
|
||||||
|
('currency', models.CharField(default='Eur', max_length=10)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created')),
|
||||||
|
('modified_at', models.DateTimeField(auto_now=True, verbose_name='modified')),
|
||||||
|
('bill', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transactions', to='bills.Bill', verbose_name='bill')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TransactionProcess',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('data', jsonfield.fields.JSONField(blank=True, verbose_name='data')),
|
||||||
|
('file', orchestra.models.fields.PrivateFileField(blank=True, upload_to='', verbose_name='file')),
|
||||||
|
('state', models.CharField(choices=[('CREATED', 'Created'), ('EXECUTED', 'Executed'), ('ABORTED', 'Aborted'), ('COMMITED', 'Commited')], default='CREATED', max_length=16, verbose_name='state')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='created')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='updated')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Transaction processes',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transaction',
|
||||||
|
name='process',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='transactions', to='payments.TransactionProcess', verbose_name='process'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transaction',
|
||||||
|
name='source',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='transactions', to='payments.PaymentSource', verbose_name='source'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-03-30 10:49
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('payments', '0003_auto_20170528_2011'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='paymentsource',
|
||||||
|
name='method',
|
||||||
|
field=models.CharField(choices=[('CreditCard', 'Credit card'), ('SEPADirectDebit', 'SEPA Direct Debit')], max_length=32, verbose_name='method'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -18,7 +18,7 @@ class PaymentSourcesQueryset(models.QuerySet):
|
||||||
|
|
||||||
class PaymentSource(models.Model):
|
class PaymentSource(models.Model):
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
||||||
related_name='paymentsources')
|
related_name='paymentsources', on_delete=models.CASCADE)
|
||||||
method = models.CharField(_("method"), max_length=32,
|
method = models.CharField(_("method"), max_length=32,
|
||||||
choices=PaymentMethod.get_choices())
|
choices=PaymentMethod.get_choices())
|
||||||
data = JSONField(_("data"), default={})
|
data = JSONField(_("data"), default={})
|
||||||
|
@ -109,7 +109,7 @@ class Transaction(models.Model):
|
||||||
"should be created for recharging."),
|
"should be created for recharging."),
|
||||||
}
|
}
|
||||||
|
|
||||||
bill = models.ForeignKey('bills.bill', verbose_name=_("bill"),
|
bill = models.ForeignKey('bills.bill', on_delete=models.CASCADE, verbose_name=_("bill"),
|
||||||
related_name='transactions')
|
related_name='transactions')
|
||||||
source = models.ForeignKey(PaymentSource, null=True, blank=True, on_delete=models.SET_NULL,
|
source = models.ForeignKey(PaymentSource, null=True, blank=True, on_delete=models.SET_NULL,
|
||||||
verbose_name=_("source"), related_name='transactions')
|
verbose_name=_("source"), related_name='transactions')
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
import django.db.models.deletion
|
||||||
import orchestra.core.validators
|
import orchestra.core.validators
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ class Migration(migrations.Migration):
|
||||||
name='ContractedPlan',
|
name='ContractedPlan',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
|
||||||
('account', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='plans', verbose_name='account')),
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, related_name='plans', verbose_name='account')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'plans',
|
'verbose_name_plural': 'plans',
|
||||||
|
@ -42,14 +43,14 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
|
||||||
('quantity', models.PositiveIntegerField(help_text='See rate algorihm help text.', blank=True, verbose_name='quantity', null=True)),
|
('quantity', models.PositiveIntegerField(help_text='See rate algorihm help text.', blank=True, verbose_name='quantity', null=True)),
|
||||||
('price', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='price')),
|
('price', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='price')),
|
||||||
('plan', models.ForeignKey(to='plans.Plan', related_name='rates', verbose_name='plan')),
|
('plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='plans.Plan', related_name='rates', verbose_name='plan')),
|
||||||
('service', models.ForeignKey(to='services.Service', related_name='rates', verbose_name='service')),
|
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='services.Service', related_name='rates', verbose_name='service')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='contractedplan',
|
model_name='contractedplan',
|
||||||
name='plan',
|
name='plan',
|
||||||
field=models.ForeignKey(to='plans.Plan', related_name='contracts', verbose_name='plan'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='plans.Plan', related_name='contracts', verbose_name='plan'),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='rate',
|
name='rate',
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2021-04-22 11:09
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import orchestra.core.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('plans', '0001_initial'), ('plans', '0002_auto_20160114_1713'), ('plans', '0003_auto_20210422_1108')]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('services', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ContractedPlan',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='plans', to=settings.AUTH_USER_MODEL, verbose_name='account')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'plans',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Plan',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=32, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
|
||||||
|
('verbose_name', models.CharField(blank=True, max_length=128, verbose_name='verbose_name')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('is_default', models.BooleanField(default=False, help_text='Designates whether this plan is used by default or not.', verbose_name='default')),
|
||||||
|
('is_combinable', models.BooleanField(default=True, help_text='Designates whether this plan can be combined with other plans or not.', verbose_name='combinable')),
|
||||||
|
('allow_multiple', models.BooleanField(default=False, help_text='Designates whether this plan allow for multiple contractions.', verbose_name='allow multiple')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Rate',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('quantity', models.PositiveIntegerField(blank=True, help_text='See rate algorihm help text.', null=True, verbose_name='quantity')),
|
||||||
|
('price', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='price')),
|
||||||
|
('plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rates', to='plans.Plan', verbose_name='plan')),
|
||||||
|
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rates', to='services.Service', verbose_name='service')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='contractedplan',
|
||||||
|
name='plan',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contracts', to='plans.Plan', verbose_name='plan'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='rate',
|
||||||
|
unique_together=set([('service', 'plan', 'quantity')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rate',
|
||||||
|
name='plan',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='rates', to='plans.Plan', verbose_name='plan'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rate',
|
||||||
|
name='plan',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rates', to='plans.Plan', verbose_name='plan'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -14,6 +15,6 @@ class Migration(migrations.Migration):
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='rate',
|
model_name='rate',
|
||||||
name='plan',
|
name='plan',
|
||||||
field=models.ForeignKey(related_name='rates', to='plans.Plan', blank=True, null=True, verbose_name='plan'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rates', to='plans.Plan', blank=True, null=True, verbose_name='plan'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue