Ported to python 3.4
This commit is contained in:
parent
b29c554878
commit
b3946168f3
31
TODO.md
31
TODO.md
|
@ -236,7 +236,6 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl
|
||||||
* display subline links on billlines, to show that they exists.
|
* display subline links on billlines, to show that they exists.
|
||||||
* update service orders on a celery task? because it take alot
|
* update service orders on a celery task? because it take alot
|
||||||
|
|
||||||
*
|
|
||||||
* billline quantity eval('10x100') instead of miningless description '(10*100)'
|
* billline quantity eval('10x100') instead of miningless description '(10*100)'
|
||||||
|
|
||||||
* IMPORTANT do more test, make sure billed until doesn't get uodated whhen services are billed with les metric, and don't upgrade billed_until when undoing under this circumstances
|
* IMPORTANT do more test, make sure billed until doesn't get uodated whhen services are billed with les metric, and don't upgrade billed_until when undoing under this circumstances
|
||||||
|
@ -245,8 +244,6 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl
|
||||||
* threshold for significative metric accountancy on services.handler
|
* threshold for significative metric accountancy on services.handler
|
||||||
* http://orchestra.pangea.org/admin/orders/order/6418/
|
* http://orchestra.pangea.org/admin/orders/order/6418/
|
||||||
* http://orchestra.pangea.org/admin/orders/order/6495/bill_selected_orders/
|
* http://orchestra.pangea.org/admin/orders/order/6495/bill_selected_orders/
|
||||||
* >>> round(float(decimal.Decimal('2.63'))/0.5)*0.5
|
|
||||||
* >>> round(float(str(decimal.Decimal('2.99')).split('.')[0]))/1*1
|
|
||||||
|
|
||||||
* move normurlpath to orchestra.utils from websites.utils
|
* move normurlpath to orchestra.utils from websites.utils
|
||||||
|
|
||||||
|
@ -286,23 +283,13 @@ translation.activate('ca')
|
||||||
ugettext("Description")
|
ugettext("Description")
|
||||||
|
|
||||||
|
|
||||||
Object = disk*15
|
|
||||||
bscw quota
|
|
||||||
root@web:/home/pangea/bscw/bin ./bsadmin quota report
|
|
||||||
Disk Objects
|
|
||||||
User usage soft hard time usage soft hard time
|
|
||||||
xxx2 -- 0 20M 22M 9 200 300
|
|
||||||
xxxxxxxxxxxxx -- 0 20M 22M 8 200 300
|
|
||||||
xxxxx -- 0 20M 22M 7 200 300
|
|
||||||
xxxxx -- 0 20M 22M 7 200 300
|
|
||||||
|
|
||||||
|
|
||||||
* saas validate_creation generic approach, for all backends. standard output
|
* saas validate_creation generic approach, for all backends. standard output
|
||||||
|
|
||||||
* html code x: ×
|
* html code x: ×
|
||||||
|
|
||||||
|
|
||||||
* cleanup backendlogs, monitor data and metricstorage
|
* periodic task to cleanup backendlogs, monitor data and metricstorage
|
||||||
* create orchestrate databases.Database pk=1 -n --dry-run | --noinput --action save (default)|delete --backend name (limit to this backend) --help
|
* create orchestrate databases.Database pk=1 -n --dry-run | --noinput --action save (default)|delete --backend name (limit to this backend) --help
|
||||||
|
|
||||||
* uwsgi --max-requests=5000 \ # respawn processes after serving 5000 requests and
|
* uwsgi --max-requests=5000 \ # respawn processes after serving 5000 requests and
|
||||||
|
@ -313,3 +300,19 @@ celery max-tasks-per-child
|
||||||
* postupgradeorchestra send signals in order to hook custom stuff
|
* postupgradeorchestra send signals in order to hook custom stuff
|
||||||
|
|
||||||
* make base home for systemusers that ara homed into main account systemuser
|
* make base home for systemusers that ara homed into main account systemuser
|
||||||
|
|
||||||
|
|
||||||
|
* user force_text instead of unicode for _()
|
||||||
|
|
||||||
|
* autoscale celery workers http://docs.celeryproject.org/en/latest/userguide/workers.html#autoscaling
|
||||||
|
|
||||||
|
|
||||||
|
* Delete transaction middleware
|
||||||
|
|
||||||
|
|
||||||
|
* webapp has_website list filter
|
||||||
|
|
||||||
|
|
||||||
|
apt-get install python3 python3-pip
|
||||||
|
cp /usr/local/lib/python2.7/dist-packages/orchestra.pth /usr/local/lib/python3.4/dist-packages/
|
||||||
|
glic3rinu's django-fluent-dashboard
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# django-orchestra documentation build configuration file, created by
|
# django-orchestra documentation build configuration file, created by
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
from options import *
|
from .options import *
|
||||||
from dashboard import *
|
from .dashboard import *
|
||||||
|
|
|
@ -100,7 +100,7 @@ class SendEmail(object):
|
||||||
'content_message': _(
|
'content_message': _(
|
||||||
"Are you sure you want to send the following message to the following %s?"
|
"Are you sure you want to send the following message to the following %s?"
|
||||||
) % self.opts.verbose_name_plural,
|
) % self.opts.verbose_name_plural,
|
||||||
'display_objects': [u"%s (%s)" % (contact, contact.email) for contact in self.queryset],
|
'display_objects': ["%s (%s)" % (contact, contact.email) for contact in self.queryset],
|
||||||
'form': form,
|
'form': form,
|
||||||
'subject': subject,
|
'subject': subject,
|
||||||
'message': message,
|
'message': message,
|
||||||
|
|
|
@ -5,7 +5,7 @@ from orchestra.core import services
|
||||||
|
|
||||||
def generate_services_group():
|
def generate_services_group():
|
||||||
models = []
|
models = []
|
||||||
for model, options in services.get().iteritems():
|
for model, options in services.get().items():
|
||||||
if options.get('menu', True):
|
if options.get('menu', True):
|
||||||
models.append("%s.%s" % (model.__module__, model._meta.object_name))
|
models.append("%s.%s" % (model.__module__, model._meta.object_name))
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ class AdminFormMixin(object):
|
||||||
def as_admin(self):
|
def as_admin(self):
|
||||||
prepopulated_fields = {}
|
prepopulated_fields = {}
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(None, {'fields': self.fields.keys()})
|
(None, {'fields': list(self.fields.keys())})
|
||||||
]
|
]
|
||||||
adminform = helpers.AdminForm(self, fieldsets, prepopulated_fields)
|
adminform = helpers.AdminForm(self, fieldsets, prepopulated_fields)
|
||||||
template = Template(
|
template = Template(
|
||||||
|
@ -32,7 +32,7 @@ class AdminFormSet(BaseModelFormSet):
|
||||||
def as_admin(self):
|
def as_admin(self):
|
||||||
prepopulated = {}
|
prepopulated = {}
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(None, {'fields': self.form().fields.keys()})
|
(None, {'fields': list(self.form().fields.keys())})
|
||||||
]
|
]
|
||||||
readonly = getattr(self.form.Meta, 'readonly_fields', ())
|
readonly = getattr(self.form.Meta, 'readonly_fields', ())
|
||||||
if not hasattr(self.modeladmin, 'verbose_name_plural'):
|
if not hasattr(self.modeladmin, 'verbose_name_plural'):
|
||||||
|
@ -114,7 +114,11 @@ class AdminPasswordChangeForm(forms.Form):
|
||||||
if password:
|
if password:
|
||||||
self.user.set_password(password)
|
self.user.set_password(password)
|
||||||
if commit:
|
if commit:
|
||||||
self.user.save(update_fields=['password'])
|
try:
|
||||||
|
self.user.save(update_fields=['password'])
|
||||||
|
except ValueError:
|
||||||
|
# password is not a field but an attribute
|
||||||
|
self.user.save() # Trigger the backend
|
||||||
for ix, rel in enumerate(self.related):
|
for ix, rel in enumerate(self.related):
|
||||||
password = self.cleaned_data['password1_%s' % ix]
|
password = self.cleaned_data['password1_%s' % ix]
|
||||||
if password:
|
if password:
|
||||||
|
|
|
@ -32,7 +32,7 @@ def api_link(context):
|
||||||
|
|
||||||
def get_services():
|
def get_services():
|
||||||
childrens = []
|
childrens = []
|
||||||
for model, options in services.get().iteritems():
|
for model, options in services.get().items():
|
||||||
if options.get('menu', True):
|
if options.get('menu', True):
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
url = reverse('admin:{}_{}_changelist'.format(
|
url = reverse('admin:{}_{}_changelist'.format(
|
||||||
|
@ -50,7 +50,7 @@ def get_accounts():
|
||||||
if isinstalled('orchestra.apps.issues'):
|
if isinstalled('orchestra.apps.issues'):
|
||||||
url = reverse('admin:issues_ticket_changelist')
|
url = reverse('admin:issues_ticket_changelist')
|
||||||
childrens.append(items.MenuItem(_("Tickets"), url))
|
childrens.append(items.MenuItem(_("Tickets"), url))
|
||||||
for model, options in accounts.get().iteritems():
|
for model, options in accounts.get().items():
|
||||||
if options.get('menu', True):
|
if options.get('menu', True):
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
url = reverse('admin:{}_{}_changelist'.format(
|
url = reverse('admin:{}_{}_changelist'.format(
|
||||||
|
|
|
@ -80,7 +80,7 @@ class ChangeViewActionsMixin(object):
|
||||||
""" allow customization on modelamdin """
|
""" allow customization on modelamdin """
|
||||||
views = []
|
views = []
|
||||||
for action in self.change_view_actions:
|
for action in self.change_view_actions:
|
||||||
if isinstance(action, basestring):
|
if isinstance(action, str):
|
||||||
action = getattr(self, action)
|
action = getattr(self, action)
|
||||||
view = action_to_view(action, self)
|
view = action_to_view(action, self)
|
||||||
view.url_name = getattr(action, 'url_name', action.__name__)
|
view.url_name = getattr(action, 'url_name', action.__name__)
|
||||||
|
|
|
@ -20,7 +20,7 @@ from .decorators import admin_field
|
||||||
|
|
||||||
def get_modeladmin(model, import_module=True):
|
def get_modeladmin(model, import_module=True):
|
||||||
""" returns the modeladmin registred for model """
|
""" returns the modeladmin registred for model """
|
||||||
for k,v in admin.site._registry.iteritems():
|
for k,v in admin.site._registry.items():
|
||||||
if k is model:
|
if k is model:
|
||||||
return v
|
return v
|
||||||
if import_module:
|
if import_module:
|
||||||
|
@ -97,7 +97,7 @@ def change_url(obj):
|
||||||
@admin_field
|
@admin_field
|
||||||
def admin_link(*args, **kwargs):
|
def admin_link(*args, **kwargs):
|
||||||
instance = args[-1]
|
instance = args[-1]
|
||||||
if kwargs['field'] in ['id', 'pk', '__unicode__']:
|
if kwargs['field'] in ['id', 'pk', '__str__']:
|
||||||
obj = instance
|
obj = instance
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
from options import *
|
from .options import *
|
||||||
from actions import *
|
from .actions import *
|
||||||
|
|
|
@ -10,7 +10,7 @@ class SetPasswordApiMixin(object):
|
||||||
def set_password(self, request, pk):
|
def set_password(self, request, pk):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
data = request.DATA
|
data = request.DATA
|
||||||
if isinstance(data, basestring):
|
if isinstance(data, str):
|
||||||
data = {
|
data = {
|
||||||
'password': data
|
'password': data
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,17 @@ class OptionField(serializers.WritableField):
|
||||||
properties = serializers.RelationsList()
|
properties = serializers.RelationsList()
|
||||||
if value:
|
if value:
|
||||||
model = getattr(parent.opts.model, self.source or 'options').related.model
|
model = getattr(parent.opts.model, self.source or 'options').related.model
|
||||||
if isinstance(value, basestring):
|
if isinstance(value, str):
|
||||||
try:
|
try:
|
||||||
value = json.loads(value)
|
value = json.loads(value)
|
||||||
except:
|
except:
|
||||||
raise exceptions.ParseError("Malformed property: %s" % str(value))
|
raise exceptions.ParseError("Malformed property: %s" % str(value))
|
||||||
if not related_manager:
|
if not related_manager:
|
||||||
# POST (new parent object)
|
# POST (new parent object)
|
||||||
return [ model(name=n, value=v) for n,v in value.iteritems() ]
|
return [ model(name=n, value=v) for n,v in value.items() ]
|
||||||
# PUT
|
# PUT
|
||||||
to_save = []
|
to_save = []
|
||||||
for (name, value) in value.iteritems():
|
for (name, value) in value.items():
|
||||||
try:
|
try:
|
||||||
# Update existing property
|
# Update existing property
|
||||||
prop = related_manager.get(name=name)
|
prop = related_manager.get(name=name)
|
||||||
|
|
|
@ -24,7 +24,7 @@ class HyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
""" removes postonly_fields from attrs when not posting """
|
""" removes postonly_fields from attrs when not posting """
|
||||||
model_attrs = dict(**attrs)
|
model_attrs = dict(**attrs)
|
||||||
if instance is not None:
|
if instance is not None:
|
||||||
for attr, value in attrs.iteritems():
|
for attr, value in attrs.items():
|
||||||
if attr in self.opts.postonly_fields:
|
if attr in self.opts.postonly_fields:
|
||||||
model_attrs.pop(attr)
|
model_attrs.pop(attr)
|
||||||
return super(HyperlinkedModelSerializer, self).restore_object(model_attrs, instance)
|
return super(HyperlinkedModelSerializer, self).restore_object(model_attrs, instance)
|
||||||
|
|
|
@ -44,7 +44,7 @@ def service_report(modeladmin, request, queryset):
|
||||||
accounts = []
|
accounts = []
|
||||||
fields = []
|
fields = []
|
||||||
# First we get related manager names to fire a prefetch related
|
# First we get related manager names to fire a prefetch related
|
||||||
for name, field in queryset.model._meta._name_map.iteritems():
|
for name, field in queryset.model._meta._name_map.items():
|
||||||
model = field[0].model
|
model = field[0].model
|
||||||
if model in services.get() and model != queryset.model:
|
if model in services.get() and model != queryset.model:
|
||||||
fields.append((model, name))
|
fields.append((model, name))
|
||||||
|
@ -63,3 +63,7 @@ def service_report(modeladmin, request, queryset):
|
||||||
'date': timezone.now().today()
|
'date': timezone.now().today()
|
||||||
}
|
}
|
||||||
return render(request, settings.ACCOUNTS_SERVICE_REPORT_TEMPLATE, context)
|
return render(request, settings.ACCOUNTS_SERVICE_REPORT_TEMPLATE, context)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_related_services(modeladmin, request, queryset):
|
||||||
|
pass
|
||||||
|
|
|
@ -62,12 +62,12 @@ def create_account_creation_form():
|
||||||
field_name = 'create_%s' % model._meta.model_name
|
field_name = 'create_%s' % model._meta.model_name
|
||||||
if self.cleaned_data[field_name]:
|
if self.cleaned_data[field_name]:
|
||||||
kwargs = {
|
kwargs = {
|
||||||
key: eval(value, {'account': account}) for key, value in related_kwargs.iteritems()
|
key: eval(value, {'account': account}) for key, value in related_kwargs.items()
|
||||||
}
|
}
|
||||||
model.objects.create(account=account, **kwargs)
|
model.objects.create(account=account, **kwargs)
|
||||||
|
|
||||||
fields.update({
|
fields.update({
|
||||||
'create_related_fields': fields.keys(),
|
'create_related_fields': list(fields.keys()),
|
||||||
'clean': clean,
|
'clean': clean,
|
||||||
'save_model': save_model,
|
'save_model': save_model,
|
||||||
'save_related': save_related,
|
'save_related': save_related,
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Account(auth.AbstractBaseUser):
|
||||||
USERNAME_FIELD = 'username'
|
USERNAME_FIELD = 'username'
|
||||||
REQUIRED_FIELDS = ['email']
|
REQUIRED_FIELDS = ['email']
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -11,6 +11,7 @@ ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', (
|
||||||
('COMPANY', _("Company")),
|
('COMPANY', _("Company")),
|
||||||
('PUBLICBODY', _("Public body")),
|
('PUBLICBODY', _("Public body")),
|
||||||
('STAFF', _("Staff")),
|
('STAFF', _("Staff")),
|
||||||
|
('FRIEND', _("Friend")),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import StringIO
|
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
|
@ -20,7 +20,7 @@ from .helpers import validate_contact
|
||||||
|
|
||||||
def download_bills(modeladmin, request, queryset):
|
def download_bills(modeladmin, request, queryset):
|
||||||
if queryset.count() > 1:
|
if queryset.count() > 1:
|
||||||
stringio = StringIO.StringIO()
|
stringio = StringIO()
|
||||||
archive = zipfile.ZipFile(stringio, 'w')
|
archive = zipfile.ZipFile(stringio, 'w')
|
||||||
for bill in queryset:
|
for bill in queryset:
|
||||||
pdf = html_to_pdf(bill.html or bill.render())
|
pdf = html_to_pdf(bill.html or bill.render())
|
||||||
|
@ -122,7 +122,7 @@ def undo_billing(modeladmin, request, queryset):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
group[line.order] = [line]
|
group[line.order] = [line]
|
||||||
# TODO force incomplete info
|
# TODO force incomplete info
|
||||||
for order, lines in group.iteritems():
|
for order, lines in group.items():
|
||||||
# Find path from ini to end
|
# Find path from ini to end
|
||||||
for attr in ['order_id', 'order_billed_on', 'order_billed_until']:
|
for attr in ['order_id', 'order_billed_on', 'order_billed_until']:
|
||||||
if not getattr(self, attr):
|
if not getattr(self, attr):
|
||||||
|
@ -131,7 +131,7 @@ def undo_billing(modeladmin, request, queryset):
|
||||||
if 'a' != order.billed_on:
|
if 'a' != order.billed_on:
|
||||||
raise ValidationError(_("Dates don't match"))
|
raise ValidationError(_("Dates don't match"))
|
||||||
prev = order.billed_on
|
prev = order.billed_on
|
||||||
for ix in xrange(0, len(lines)):
|
for ix in range(0, len(lines)):
|
||||||
if lines[ix].order_b: # TODO we need to look at the periods here
|
if lines[ix].order_b: # TODO we need to look at the periods here
|
||||||
pass
|
pass
|
||||||
order.billed_until = self.order_billed_until
|
order.billed_until = self.order_billed_until
|
||||||
|
|
|
@ -210,8 +210,8 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
def get_inline_instances(self, request, obj=None):
|
def get_inline_instances(self, request, obj=None):
|
||||||
inlines = super(BillAdmin, self).get_inline_instances(request, obj)
|
inlines = super(BillAdmin, self).get_inline_instances(request, obj)
|
||||||
if obj and not obj.is_open:
|
if obj and not obj.is_open:
|
||||||
return [inline for inline in inlines if not type(inline) == BillLineInline]
|
return [inline for inline in inlines if not isinstance(inline, BillLineInline)]
|
||||||
return [inline for inline in inlines if not type(inline) == ClosedBillLineInline]
|
return [inline for inline in inlines if not isinstance(inline, ClosedBillLineInline)]
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
|
|
|
@ -33,7 +33,7 @@ class SelectSourceForm(forms.ModelForm):
|
||||||
choices.append((source.pk, str(source)))
|
choices.append((source.pk, str(source)))
|
||||||
self.fields['source'].choices = choices
|
self.fields['source'].choices = choices
|
||||||
self.fields['source'].initial = choices[-1][0]
|
self.fields['source'].initial = choices[-1][0]
|
||||||
self.fields['bill_link'].initial = admin_link('__unicode__')(bill)
|
self.fields['bill_link'].initial = admin_link('__str__')(bill)
|
||||||
self.fields['display_type'].initial = bill.get_type_display()
|
self.fields['display_type'].initial = bill.get_type_display()
|
||||||
|
|
||||||
def clean_source(self):
|
def clean_source(self):
|
||||||
|
|
|
@ -31,7 +31,7 @@ class BillContact(models.Model):
|
||||||
default=settings.BILLS_CONTACT_DEFAULT_COUNTRY)
|
default=settings.BILLS_CONTACT_DEFAULT_COUNTRY)
|
||||||
vat = models.CharField(_("VAT number"), max_length=64)
|
vat = models.CharField(_("VAT number"), max_length=64)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
|
@ -98,7 +98,7 @@ class Bill(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.number
|
return self.number
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -235,7 +235,7 @@ class Bill(models.Model):
|
||||||
|
|
||||||
def get_total(self):
|
def get_total(self):
|
||||||
total = 0
|
total = 0
|
||||||
for tax, subtotal in self.get_subtotals().iteritems():
|
for tax, subtotal in self.get_subtotals().items():
|
||||||
subtotal, taxes = subtotal
|
subtotal, taxes = subtotal
|
||||||
total += subtotal + taxes
|
total += subtotal + taxes
|
||||||
return total
|
return total
|
||||||
|
@ -287,7 +287,7 @@ class BillLine(models.Model):
|
||||||
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)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "#%i" % self.number
|
return "#%i" % self.number
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -90,7 +90,7 @@ BILLS_CONTACT_DEFAULT_CITY = getattr(settings, 'BILLS_CONTACT_DEFAULT_CITY',
|
||||||
|
|
||||||
|
|
||||||
BILLS_CONTACT_COUNTRIES = getattr(settings, 'BILLS_CONTACT_COUNTRIES',
|
BILLS_CONTACT_COUNTRIES = getattr(settings, 'BILLS_CONTACT_COUNTRIES',
|
||||||
((k,v) for k,v in data.COUNTRIES.iteritems())
|
((k,v) for k,v in data.COUNTRIES.items())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
actions = [SendEmail(),]
|
actions = [SendEmail(),]
|
||||||
|
|
||||||
def dispaly_name(self, contact):
|
def dispaly_name(self, contact):
|
||||||
return unicode(contact)
|
return str(contact)
|
||||||
dispaly_name.short_description = _("Name")
|
dispaly_name.short_description = _("Name")
|
||||||
dispaly_name.admin_order_field = 'short_name'
|
dispaly_name.admin_order_field = 'short_name'
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Contact(models.Model):
|
||||||
choices=settings.CONTACTS_COUNTRIES,
|
choices=settings.CONTACTS_COUNTRIES,
|
||||||
default=settings.CONTACTS_DEFAULT_COUNTRY)
|
default=settings.CONTACTS_DEFAULT_COUNTRY)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.full_name or self.short_name
|
return self.full_name or self.short_name
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
|
@ -18,7 +18,7 @@ CONTACTS_DEFAULT_CITY = getattr(settings, 'CONTACTS_DEFAULT_CITY',
|
||||||
|
|
||||||
|
|
||||||
CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', (
|
CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', (
|
||||||
(k,v) for k,v in data.COUNTRIES.iteritems()
|
(k,v) for k,v in data.COUNTRIES.items()
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Database(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('name', 'type')
|
unique_together = ('name', 'type')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s" % self.name
|
return "%s" % self.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -59,7 +59,7 @@ class DatabaseUser(models.Model):
|
||||||
verbose_name_plural = _("DB users")
|
verbose_name_plural = _("DB users")
|
||||||
unique_together = ('username', 'type')
|
unique_together = ('username', 'type')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
def get_username(self):
|
def get_username(self):
|
||||||
|
@ -68,7 +68,7 @@ class DatabaseUser(models.Model):
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
if self.type == self.MYSQL:
|
if self.type == self.MYSQL:
|
||||||
# MySQL stores sha1(sha1(password).binary).hex
|
# MySQL stores sha1(sha1(password).binary).hex
|
||||||
binary = hashlib.sha1(password).digest()
|
binary = hashlib.sha1(password.encode('utf-8')).digest()
|
||||||
hexdigest = hashlib.sha1(binary).hexdigest()
|
hexdigest = hashlib.sha1(binary).hexdigest()
|
||||||
self.password = '*%s' % hexdigest.upper()
|
self.password = '*%s' % hexdigest.upper()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -41,7 +41,7 @@ class DomainInline(admin.TabularInline):
|
||||||
extra = 0
|
extra = 0
|
||||||
verbose_name_plural = _("Subdomains")
|
verbose_name_plural = _("Subdomains")
|
||||||
|
|
||||||
domain_link = admin_link('__unicode__')
|
domain_link = admin_link('__str__')
|
||||||
domain_link.short_description = _("Name")
|
domain_link.short_description = _("Name")
|
||||||
account_link = admin_link('account')
|
account_link = admin_link('account')
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Domain(models.Model):
|
||||||
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial,
|
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial,
|
||||||
help_text=_("Serial number"))
|
help_text=_("Serial number"))
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -228,7 +228,7 @@ class Record(models.Model):
|
||||||
type = models.CharField(_("type"), max_length=32, choices=TYPE_CHOICES)
|
type = models.CharField(_("type"), max_length=32, choices=TYPE_CHOICES)
|
||||||
value = models.CharField(_("value"), max_length=256)
|
value = models.CharField(_("value"), max_length=256)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s %s IN %s %s" % (self.domain, self.get_ttl(), self.type, self.value)
|
return "%s %s IN %s %s" % (self.domain, self.get_ttl(), self.type, self.value)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf.urls import patterns
|
from django.conf.urls import patterns
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -267,8 +265,8 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin):
|
||||||
changes = get_ticket_changes(self, request, ticket)
|
changes = get_ticket_changes(self, request, ticket)
|
||||||
if changes:
|
if changes:
|
||||||
content = markdown_formated_changes(changes)
|
content = markdown_formated_changes(changes)
|
||||||
content += request.POST[u'messages-2-0-content']
|
content += request.POST['messages-2-0-content']
|
||||||
request.POST[u'messages-2-0-content'] = content
|
request.POST['messages-2-0-content'] = content
|
||||||
ticket.mark_as_read_by(request.user)
|
ticket.mark_as_read_by(request.user)
|
||||||
context = {'title': "Issue #%i - %s" % (ticket.id, ticket.subject)}
|
context = {'title': "Issue #%i - %s" % (ticket.id, ticket.subject)}
|
||||||
context.update(extra_context or {})
|
context.update(extra_context or {})
|
||||||
|
|
|
@ -22,7 +22,7 @@ class MyTicketsListFilter(SimpleListFilter):
|
||||||
def choices(self, cl):
|
def choices(self, cl):
|
||||||
""" Remove default All """
|
""" Remove default All """
|
||||||
choices = iter(super(MyTicketsListFilter, self).choices(cl))
|
choices = iter(super(MyTicketsListFilter, self).choices(cl))
|
||||||
choices.next()
|
next(choices)
|
||||||
return choices
|
return choices
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,6 +52,6 @@ class TicketStateListFilter(SimpleListFilter):
|
||||||
def choices(self, cl):
|
def choices(self, cl):
|
||||||
""" Remove default All """
|
""" Remove default All """
|
||||||
choices = iter(super(TicketStateListFilter, self).choices(cl))
|
choices = iter(super(TicketStateListFilter, self).choices(cl))
|
||||||
choices.next()
|
next(choices)
|
||||||
return choices
|
return choices
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ def filter_actions(modeladmin, ticket, request):
|
||||||
del_actions.append('take')
|
del_actions.append('take')
|
||||||
exclude = lambda a: not (a == action or a.url_name == action)
|
exclude = lambda a: not (a == action or a.url_name == action)
|
||||||
for action in del_actions:
|
for action in del_actions:
|
||||||
actions = filter(exclude, actions)
|
actions = list(filter(exclude, actions))
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ class Queue(models.Model):
|
||||||
default=contacts_settings.CONTACTS_DEFAULT_EMAIL_USAGES,
|
default=contacts_settings.CONTACTS_DEFAULT_EMAIL_USAGES,
|
||||||
help_text=_("Contacts to notify by email"))
|
help_text=_("Contacts to notify by email"))
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.verbose_name or self.name
|
return self.verbose_name or self.name
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -77,8 +77,8 @@ class Ticket(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['-updated_at']
|
ordering = ['-updated_at']
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return unicode(self.pk)
|
return str(self.pk)
|
||||||
|
|
||||||
def get_notification_emails(self):
|
def get_notification_emails(self):
|
||||||
""" Get emails of the users related to the ticket """
|
""" Get emails of the users related to the ticket """
|
||||||
|
@ -164,8 +164,8 @@ class Message(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"#%i" % self.id
|
return "#%i" % self.id
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
""" notify stakeholders of ticket update """
|
""" notify stakeholders of ticket update """
|
||||||
|
|
|
@ -295,7 +295,7 @@ class MailmanTraffic(ServiceMonitor):
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
sys.stderr.write(e)
|
sys.stderr.write(e)
|
||||||
|
|
||||||
for list_name, opts in lists.iteritems():
|
for list_name, opts in lists.items():
|
||||||
__, object_id, size = opts
|
__, object_id, size = opts
|
||||||
if size:
|
if size:
|
||||||
cmd = ' '.join(('list_members', list_name, '| wc -l'))
|
cmd = ' '.join(('list_members', list_name, '| wc -l'))
|
||||||
|
|
|
@ -30,7 +30,7 @@ class List(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('address_name', 'address_domain')
|
unique_together = ('address_name', 'address_domain')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import copy
|
import copy
|
||||||
from urlparse import parse_qs
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
|
@ -431,7 +431,7 @@ class PostfixTraffic(ServiceMonitor):
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
sys.stderr.write(e)
|
sys.stderr.write(e)
|
||||||
|
|
||||||
for username, opts in users.iteritems():
|
for username, opts in users.items():
|
||||||
size = 0
|
size = 0
|
||||||
for req_id in reverse[username]:
|
for req_id in reverse[username]:
|
||||||
size += targets[req_id][1] * counter.get(req_id, 0)
|
size += targets[req_id][1] * counter.get(req_id, 0)
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Mailbox(models.Model):
|
||||||
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=[
|
choices=[
|
||||||
(k, v[0]) for k,v in settings.MAILBOXES_MAILBOX_FILTERINGS.iteritems()
|
(k, v[0]) for k,v in settings.MAILBOXES_MAILBOX_FILTERINGS.items()
|
||||||
])
|
])
|
||||||
custom_filtering = models.TextField(_("filtering"), blank=True,
|
custom_filtering = models.TextField(_("filtering"), blank=True,
|
||||||
validators=[validators.validate_sieve],
|
validators=[validators.validate_sieve],
|
||||||
|
@ -36,7 +36,7 @@ class Mailbox(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = _("mailboxes")
|
verbose_name_plural = _("mailboxes")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -62,7 +62,7 @@ class Mailbox(models.Model):
|
||||||
|
|
||||||
def get_filtering(self):
|
def get_filtering(self):
|
||||||
__, filtering = settings.MAILBOXES_MAILBOX_FILTERINGS[self.filtering]
|
__, filtering = settings.MAILBOXES_MAILBOX_FILTERINGS[self.filtering]
|
||||||
if isinstance(filtering, basestring):
|
if isinstance(filtering, str):
|
||||||
return filtering
|
return filtering
|
||||||
return filtering(self)
|
return filtering(self)
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class Address(models.Model):
|
||||||
verbose_name_plural = _("addresses")
|
verbose_name_plural = _("addresses")
|
||||||
unique_together = ('name', 'domain')
|
unique_together = ('name', 'domain')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.email
|
return self.email
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -154,7 +154,7 @@ class Autoresponse(models.Model):
|
||||||
message = models.TextField(_("message"))
|
message = models.TextField(_("message"))
|
||||||
enabled = models.BooleanField(_("enabled"), default=False)
|
enabled = models.BooleanField(_("enabled"), default=False)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.address
|
return self.address
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ class MiscServiceAdmin(ExtendedModelAdmin):
|
||||||
|
|
||||||
class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelAdmin):
|
class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
'__unicode__', 'service_link', 'amount', 'dispaly_active', 'account_link'
|
'__str__', 'service_link', 'amount', 'dispaly_active', 'account_link'
|
||||||
)
|
)
|
||||||
list_filter = ('service__name', 'is_active')
|
list_filter = ('service__name', 'is_active')
|
||||||
list_select_related = ('service', 'account')
|
list_select_related = ('service', 'account')
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import orchestra.core.validators
|
import orchestra.core.validators
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import orchestra.models.fields
|
import orchestra.models.fields
|
||||||
|
|
|
@ -25,7 +25,7 @@ class MiscService(models.Model):
|
||||||
help_text=_("Whether new instances of this service can be created "
|
help_text=_("Whether new instances of this service can be created "
|
||||||
"or not. Unselect this instead of deleting services."))
|
"or not. Unselect this instead of deleting services."))
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -51,7 +51,7 @@ class Miscellaneous(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = _("miscellaneous")
|
verbose_name_plural = _("miscellaneous")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.identifier or self.description[:32] or str(self.service)
|
return self.identifier or self.description[:32] or str(self.service)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -18,7 +18,7 @@ class ServiceMount(plugins.PluginMount):
|
||||||
super(ServiceMount, cls).__init__(name, bases, attrs)
|
super(ServiceMount, cls).__init__(name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
class ServiceBackend(plugins.Plugin):
|
class ServiceBackend(plugins.Plugin, metaclass=ServiceMount):
|
||||||
"""
|
"""
|
||||||
Service management backend base class
|
Service management backend base class
|
||||||
|
|
||||||
|
@ -37,13 +37,8 @@ class ServiceBackend(plugins.Plugin):
|
||||||
default_route_match = 'True'
|
default_route_match = 'True'
|
||||||
block = False # Force the backend manager to block in multiple backend executions and execute them synchronously
|
block = False # Force the backend manager to block in multiple backend executions and execute them synchronously
|
||||||
|
|
||||||
__metaclass__ = ServiceMount
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return type(self).__name__
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return unicode(self)
|
return type(self).__name__
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.head = []
|
self.head = []
|
||||||
|
@ -138,7 +133,7 @@ class ServiceBackend(plugins.Plugin):
|
||||||
scripts[method] += commands
|
scripts[method] += commands
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return list(scripts.iteritems())
|
return list(scripts.items())
|
||||||
|
|
||||||
def get_banner(self):
|
def get_banner(self):
|
||||||
time = timezone.now().strftime("%h %d, %Y %I:%M:%S %Z")
|
time = timezone.now().strftime("%h %d, %Y %I:%M:%S %Z")
|
||||||
|
@ -159,7 +154,7 @@ class ServiceBackend(plugins.Plugin):
|
||||||
|
|
||||||
def append(self, *cmd):
|
def append(self, *cmd):
|
||||||
# aggregate commands acording to its execution method
|
# aggregate commands acording to its execution method
|
||||||
if isinstance(cmd[0], basestring):
|
if isinstance(cmd[0], str):
|
||||||
method = self.script_method
|
method = self.script_method
|
||||||
cmd = cmd[0]
|
cmd = cmd[0]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -7,10 +7,9 @@ from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
def send_report(method, args, log):
|
def send_report(method, args, log):
|
||||||
backend = method.im_class().get_name()
|
|
||||||
server = args[0]
|
server = args[0]
|
||||||
subject = '[Orchestra] %s execution %s on %s'
|
backend = method.__self__.__class__.__name__
|
||||||
subject = subject % (backend, log.state, server)
|
subject = '[Orchestra] %s execution %s on %s' % (backend, log.state, server)
|
||||||
separator = "\n%s\n\n" % ('~ '*40,)
|
separator = "\n%s\n\n" % ('~ '*40,)
|
||||||
message = separator.join([
|
message = separator.join([
|
||||||
"[EXIT CODE] %s" % log.exit_code,
|
"[EXIT CODE] %s" % log.exit_code,
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Command(BaseCommand):
|
||||||
scripts, block = manager.generate(operations)
|
scripts, block = manager.generate(operations)
|
||||||
servers = []
|
servers = []
|
||||||
# Print scripts
|
# Print scripts
|
||||||
for key, value in scripts.iteritems():
|
for key, value in scripts.items():
|
||||||
server, __ = key
|
server, __ = key
|
||||||
backend, operations = value
|
backend, operations = value
|
||||||
servers.append(server.name)
|
servers.append(server.name)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -47,7 +48,7 @@ def close_connection(execute):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
log = execute(*args, **kwargs)
|
log = execute(*args, **kwargs)
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
wrapper.log = log
|
wrapper.log = log
|
||||||
|
@ -86,7 +87,7 @@ def generate(operations):
|
||||||
post_action.send(**kwargs)
|
post_action.send(**kwargs)
|
||||||
if backend.block:
|
if backend.block:
|
||||||
block = True
|
block = True
|
||||||
for value in scripts.itervalues():
|
for value in scripts.values():
|
||||||
backend, operations = value
|
backend, operations = value
|
||||||
backend.commit()
|
backend.commit()
|
||||||
return scripts, block
|
return scripts, block
|
||||||
|
@ -97,13 +98,13 @@ def execute(scripts, block=False, async=False):
|
||||||
# Execute scripts on each server
|
# Execute scripts on each server
|
||||||
threads = []
|
threads = []
|
||||||
executions = []
|
executions = []
|
||||||
for key, value in scripts.iteritems():
|
for key, value in scripts.items():
|
||||||
server, __ = key
|
server, __ = key
|
||||||
backend, operations = value
|
backend, operations = value
|
||||||
execute = as_task(backend.execute)
|
execute = as_task(backend.execute)
|
||||||
logger.debug('%s is going to be executed on %s' % (backend, server))
|
logger.debug('%s is going to be executed on %s' % (backend, server))
|
||||||
if block:
|
if block:
|
||||||
# Execute one bakend at a time, no need for threads
|
# Execute one backend at a time, no need for threads
|
||||||
execute(server, async=async)
|
execute(server, async=async)
|
||||||
else:
|
else:
|
||||||
execute = close_connection(execute)
|
execute = close_connection(execute)
|
||||||
|
|
|
@ -29,7 +29,8 @@ def SSH(backend, log, server, cmds, async=False):
|
||||||
"""
|
"""
|
||||||
script = '\n'.join(cmds)
|
script = '\n'.join(cmds)
|
||||||
script = script.replace('\r', '')
|
script = script.replace('\r', '')
|
||||||
digest = hashlib.md5(script).hexdigest()
|
bscript = script.encode('utf-8')
|
||||||
|
digest = hashlib.md5(bscript).hexdigest()
|
||||||
path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_PATH, digest)
|
path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_PATH, digest)
|
||||||
remote_path = "%s.remote" % path
|
remote_path = "%s.remote" % path
|
||||||
log.script = '# %s\n%s' % (remote_path, script)
|
log.script = '# %s\n%s' % (remote_path, script)
|
||||||
|
@ -41,8 +42,8 @@ def SSH(backend, log, server, cmds, async=False):
|
||||||
try:
|
try:
|
||||||
# Avoid "Argument list too long" on large scripts by genereting a file
|
# Avoid "Argument list too long" on large scripts by genereting a file
|
||||||
# and scping it to the remote server
|
# and scping it to the remote server
|
||||||
with os.fdopen(os.open(path, os.O_WRONLY | os.O_CREAT, 0600), 'w') as handle:
|
with os.fdopen(os.open(path, os.O_WRONLY | os.O_CREAT, 0o600), 'wb') as handle:
|
||||||
handle.write(script)
|
handle.write(bscript)
|
||||||
|
|
||||||
# ssh connection
|
# ssh connection
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
|
@ -62,7 +63,7 @@ def SSH(backend, log, server, cmds, async=False):
|
||||||
# Copy script to remote server
|
# Copy script to remote server
|
||||||
sftp = paramiko.SFTPClient.from_transport(transport)
|
sftp = paramiko.SFTPClient.from_transport(transport)
|
||||||
sftp.put(path, remote_path)
|
sftp.put(path, remote_path)
|
||||||
sftp.chmod(remote_path, 0600)
|
sftp.chmod(remote_path, 0o600)
|
||||||
sftp.close()
|
sftp.close()
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ def SSH(backend, log, server, cmds, async=False):
|
||||||
|
|
||||||
def Python(backend, log, server, cmds, async=False):
|
def Python(backend, log, server, cmds, async=False):
|
||||||
# TODO collect stdout?
|
# TODO collect stdout?
|
||||||
script = [ str(cmd.func.func_name) + str(cmd.args) for cmd in cmds ]
|
script = [ str(cmd.func.__name__) + str(cmd.args) for cmd in cmds ]
|
||||||
script = json.dumps(script, indent=4).replace('"', '')
|
script = json.dumps(script, indent=4).replace('"', '')
|
||||||
log.script = '\n'.join([log.script, script])
|
log.script = '\n'.join([log.script, script])
|
||||||
log.save(update_fields=['script'])
|
log.save(update_fields=['script'])
|
||||||
|
@ -133,7 +134,7 @@ def Python(backend, log, server, cmds, async=False):
|
||||||
with CaptureStdout() as stdout:
|
with CaptureStdout() as stdout:
|
||||||
result = cmd(server)
|
result = cmd(server)
|
||||||
for line in stdout:
|
for line in stdout:
|
||||||
log.stdout += unicode(line, errors='replace') + '\n'
|
log.stdout += line + '\n'
|
||||||
if async:
|
if async:
|
||||||
log.save(update_fields=['stdout'])
|
log.save(update_fields=['stdout'])
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from threading import local
|
from threading import local
|
||||||
|
|
||||||
from django.core.urlresolvers import resolve
|
from django.core.urlresolvers import resolve
|
||||||
|
from django.db import connection, 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
|
||||||
from django.http.response import HttpResponseServerError
|
from django.http.response import HttpResponseServerError
|
||||||
|
@ -36,6 +37,11 @@ class OperationsMiddleware(object):
|
||||||
"""
|
"""
|
||||||
Stores all the operations derived from save and delete signals and executes them
|
Stores all the operations derived from save and delete signals and executes them
|
||||||
at the end of the request/response cycle
|
at the end of the request/response cycle
|
||||||
|
|
||||||
|
It also works as a transaction middleware. Each view function will be run
|
||||||
|
with commit_on_response activated - that way a save() doesn't do a direct
|
||||||
|
commit, the commit is done when a successful response is created. If an
|
||||||
|
exception happens, the database is rolled back.
|
||||||
"""
|
"""
|
||||||
# Thread local is used because request object is not available on model signals
|
# Thread local is used because request object is not available on model signals
|
||||||
thread_locals = local()
|
thread_locals = local()
|
||||||
|
@ -71,16 +77,55 @@ class OperationsMiddleware(object):
|
||||||
instance = kwargs.pop('instance')
|
instance = kwargs.pop('instance')
|
||||||
manager.collect(instance, action, **kwargs)
|
manager.collect(instance, action, **kwargs)
|
||||||
|
|
||||||
|
def commit_transaction(self):
|
||||||
|
if not transaction.get_autocommit():
|
||||||
|
if transaction.is_dirty():
|
||||||
|
# Note: it is possible that the commit fails. If the reason is
|
||||||
|
# closed connection or some similar reason, then there is
|
||||||
|
# little hope to proceed nicely. However, in some cases (
|
||||||
|
# deferred foreign key checks for exampl) it is still possible
|
||||||
|
# to rollback().
|
||||||
|
try:
|
||||||
|
transaction.commit()
|
||||||
|
except Exception:
|
||||||
|
# If the rollback fails, the transaction state will be
|
||||||
|
# messed up. It doesn't matter, the connection will be set
|
||||||
|
# to clean state after the request finishes. And, we can't
|
||||||
|
# clean the state here properly even if we wanted to, the
|
||||||
|
# connection is in transaction but we can't rollback...
|
||||||
|
transaction.rollback()
|
||||||
|
transaction.leave_transaction_management()
|
||||||
|
raise
|
||||||
|
transaction.leave_transaction_management()
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
""" Store request on a thread local variable """
|
""" Store request on a thread local variable """
|
||||||
type(self).thread_locals.request = request
|
type(self).thread_locals.request = request
|
||||||
|
# Enters transaction management
|
||||||
|
transaction.enter_transaction_management()
|
||||||
|
|
||||||
|
def process_exception(self, request, exception):
|
||||||
|
"""Rolls back the database and leaves transaction management"""
|
||||||
|
if transaction.is_dirty():
|
||||||
|
# This rollback might fail because of network failure for example.
|
||||||
|
# If rollback isn't possible it is impossible to clean the
|
||||||
|
# connection's state. So leave the connection in dirty state and
|
||||||
|
# let request_finished signal deal with cleaning the connection.
|
||||||
|
transaction.rollback()
|
||||||
|
transaction.leave_transaction_management()
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
""" Processes pending backend operations """
|
""" Processes pending backend operations """
|
||||||
if not isinstance(response, HttpResponseServerError):
|
if not isinstance(response, HttpResponseServerError):
|
||||||
operations = type(self).get_pending_operations()
|
operations = type(self).get_pending_operations()
|
||||||
if operations:
|
if operations:
|
||||||
logs = Operation.execute(operations)
|
scripts, block = manager.generate(operations)
|
||||||
|
# We commit transaction just before executing operations
|
||||||
|
# because here is when IntegrityError show up
|
||||||
|
self.commit_transaction()
|
||||||
|
logs = manager.execute(scripts, block=block)
|
||||||
if logs and resolve(request.path).app_name == 'admin':
|
if logs and resolve(request.path).app_name == 'admin':
|
||||||
message_user(request, logs)
|
message_user(request, logs)
|
||||||
|
return response
|
||||||
|
self.commit_transaction()
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Server(models.Model):
|
||||||
choices=settings.ORCHESTRATION_OS_CHOICES,
|
choices=settings.ORCHESTRATION_OS_CHOICES,
|
||||||
default=settings.ORCHESTRATION_DEFAULT_OS)
|
default=settings.ORCHESTRATION_DEFAULT_OS)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_address(self):
|
def get_address(self):
|
||||||
|
@ -83,7 +83,7 @@ class BackendLog(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s@%s" % (self.backend, self.server)
|
return "%s@%s" % (self.backend, self.server)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -116,7 +116,7 @@ class BackendOperation(models.Model):
|
||||||
verbose_name = _("Operation")
|
verbose_name = _("Operation")
|
||||||
verbose_name_plural = _("Operations")
|
verbose_name_plural = _("Operations")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return '%s.%s(%s)' % (self.backend, self.action, self.instance)
|
return '%s.%s(%s)' % (self.backend, self.action, self.instance)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
|
@ -184,7 +184,7 @@ class Route(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('backend', 'host')
|
unique_together = ('backend', 'host')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s@%s" % (self.backend, self.host)
|
return "%s@%s" % (self.backend, self.host)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -218,7 +218,7 @@ class Route(models.Model):
|
||||||
if not self.match:
|
if not self.match:
|
||||||
self.match = 'True'
|
self.match = 'True'
|
||||||
if self.backend:
|
if self.backend:
|
||||||
backend_model = self.backend_class.model
|
backend_model = self.backend_class.model_class()
|
||||||
try:
|
try:
|
||||||
obj = backend_model.objects.all()[0]
|
obj = backend_model.objects.all()[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
@ -227,8 +227,7 @@ class Route(models.Model):
|
||||||
bool(self.matches(obj))
|
bool(self.matches(obj))
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
name = type(exception).__name__
|
name = type(exception).__name__
|
||||||
message = exception.message
|
raise ValidationError(': '.join((name, exception)))
|
||||||
raise ValidationError(': '.join((name, message)))
|
|
||||||
|
|
||||||
def matches(self, instance):
|
def matches(self, instance):
|
||||||
safe_locals = {
|
safe_locals = {
|
||||||
|
|
|
@ -29,8 +29,8 @@ class BillSelectedOptionsForm(AdminFormMixin, forms.Form):
|
||||||
|
|
||||||
def selected_related_choices(queryset):
|
def selected_related_choices(queryset):
|
||||||
for order in queryset:
|
for order in queryset:
|
||||||
verbose = u'<a href="{order_url}">{description}</a> '
|
verbose = '<a href="{order_url}">{description}</a> '
|
||||||
verbose += u'<a class="account" href="{account_url}">{account}</a>'
|
verbose += '<a class="account" href="{account_url}">{account}</a>'
|
||||||
verbose = verbose.format(
|
verbose = verbose.format(
|
||||||
order_url=change_url(order), description=order.description,
|
order_url=change_url(order), description=order.description,
|
||||||
account_url=change_url(order.account), account=str(order.account)
|
account_url=change_url(order.account), account=str(order.account)
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.db import models
|
||||||
from django.db.migrations.recorder import MigrationRecorder
|
from django.db.migrations.recorder import MigrationRecorder
|
||||||
from django.db.models import F, Q
|
from django.db.models import F, Q
|
||||||
from django.db.models.loading import get_model
|
from django.db.models.loading import get_model
|
||||||
from django.db.models.signals import post_delete, post_save
|
from django.db.models.signals import post_delete, post_save, pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.contrib.admin.models import LogEntry
|
from django.contrib.admin.models import LogEntry
|
||||||
from django.contrib.contenttypes import generic
|
from django.contrib.contenttypes import generic
|
||||||
|
@ -32,9 +32,9 @@ class OrderQuerySet(models.QuerySet):
|
||||||
bill_backend = Order.get_bill_backend()
|
bill_backend = Order.get_bill_backend()
|
||||||
qs = self.select_related('account', 'service')
|
qs = self.select_related('account', 'service')
|
||||||
commit = options.get('commit', True)
|
commit = options.get('commit', True)
|
||||||
for account, services in qs.group_by('account', 'service').iteritems():
|
for account, services in qs.group_by('account', 'service').items():
|
||||||
bill_lines = []
|
bill_lines = []
|
||||||
for service, orders in services.iteritems():
|
for service, orders in services.items():
|
||||||
for order in orders:
|
for order in orders:
|
||||||
# Saved for undoing support
|
# Saved for undoing support
|
||||||
order.old_billed_on = order.billed_on
|
order.old_billed_on = order.billed_on
|
||||||
|
@ -65,8 +65,8 @@ class OrderQuerySet(models.QuerySet):
|
||||||
conflictive = conflictive.exclude(service__billing_period=Service.NEVER)
|
conflictive = conflictive.exclude(service__billing_period=Service.NEVER)
|
||||||
conflictive = conflictive.select_related('service').group_by('account_id', 'service')
|
conflictive = conflictive.select_related('service').group_by('account_id', 'service')
|
||||||
qs = Q()
|
qs = Q()
|
||||||
for account_id, services in conflictive.iteritems():
|
for account_id, services in conflictive.items():
|
||||||
for service, orders in services.iteritems():
|
for service, orders in services.items():
|
||||||
if not service.rates.exists():
|
if not service.rates.exists():
|
||||||
continue
|
continue
|
||||||
ini = datetime.date.max
|
ini = datetime.date.max
|
||||||
|
@ -127,8 +127,8 @@ class Order(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return unicode(self.service)
|
return str(self.service)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_orders(cls, instance, service=None, commit=True):
|
def update_orders(cls, instance, service=None, commit=True):
|
||||||
|
@ -178,7 +178,7 @@ class Order(models.Model):
|
||||||
MetricStorage.store(self, metric)
|
MetricStorage.store(self, metric)
|
||||||
metric = ', metric:{}'.format(metric)
|
metric = ', metric:{}'.format(metric)
|
||||||
description = handler.get_order_description(instance)
|
description = handler.get_order_description(instance)
|
||||||
logger.info(u"UPDATED order id:{id}, description:{description}{metric}".format(
|
logger.info("UPDATED order id:{id}, description:{description}{metric}".format(
|
||||||
id=self.id, description=description, metric=metric).encode('ascii', 'ignore')
|
id=self.id, description=description, metric=metric).encode('ascii', 'ignore')
|
||||||
)
|
)
|
||||||
if self.description != description:
|
if self.description != description:
|
||||||
|
@ -247,8 +247,8 @@ class MetricStorage(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return unicode(self.order)
|
return str(self.order)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def store(cls, order, value):
|
def store(cls, order, value):
|
||||||
|
@ -268,12 +268,27 @@ class MetricStorage(models.Model):
|
||||||
|
|
||||||
accounts.register(Order)
|
accounts.register(Order)
|
||||||
|
|
||||||
|
@receiver(pre_delete, dispatch_uid="orders.account_orders")
|
||||||
|
def account_orders(sender, **kwargs):
|
||||||
|
account = kwargs['instance']
|
||||||
|
if isinstance(account, Order.account.field.rel.to):
|
||||||
|
account._deleted = True
|
||||||
|
|
||||||
|
|
||||||
# TODO build a cache hash table {model: related, model: None}
|
# TODO build a cache hash table {model: related, model: None}
|
||||||
@receiver(post_delete, dispatch_uid="orders.cancel_orders")
|
@receiver(post_delete, dispatch_uid="orders.cancel_orders")
|
||||||
def cancel_orders(sender, **kwargs):
|
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
|
||||||
|
if isinstance(instance, Order.account.field.rel.to):
|
||||||
|
# print 'aaaaaaaaaaaaaAAAAAAAAAAAAAAAAaa'
|
||||||
|
return
|
||||||
|
# print 'delete', sender, kwargs
|
||||||
|
try:
|
||||||
|
print(instance.account.pk)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
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():
|
||||||
order.cancel()
|
order.cancel()
|
||||||
|
@ -286,6 +301,7 @@ def cancel_orders(sender, **kwargs):
|
||||||
def update_orders(sender, **kwargs):
|
def update_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']
|
||||||
|
# print 'save', sender, kwargs
|
||||||
if type(instance) in services:
|
if type(instance) in services:
|
||||||
Order.update_orders(instance)
|
Order.update_orders(instance)
|
||||||
elif not hasattr(instance, 'account'):
|
elif not hasattr(instance, 'account'):
|
||||||
|
|
|
@ -21,7 +21,7 @@ def process_transactions(modeladmin, request, queryset):
|
||||||
msg = _("Selected transactions must be on '{state}' state")
|
msg = _("Selected transactions must be on '{state}' state")
|
||||||
messages.error(request, msg.format(state=Transaction.WAITTING_PROCESSING))
|
messages.error(request, msg.format(state=Transaction.WAITTING_PROCESSING))
|
||||||
return
|
return
|
||||||
for method, transactions in queryset.group_by('source__method').iteritems():
|
for method, transactions in queryset.group_by('source__method').items():
|
||||||
if method is not None:
|
if method is not None:
|
||||||
method = PaymentMethod.get(method)
|
method = PaymentMethod.get(method)
|
||||||
procs = method.process(transactions)
|
procs = method.process(transactions)
|
||||||
|
|
|
@ -39,7 +39,7 @@ class TransactionInline(admin.TabularInline):
|
||||||
)
|
)
|
||||||
readonly_fields = fields
|
readonly_fields = fields
|
||||||
|
|
||||||
transaction_link = admin_link('__unicode__', short_description=_("ID"))
|
transaction_link = admin_link('__str__', short_description=_("ID"))
|
||||||
bill_link = admin_link('bill')
|
bill_link = admin_link('bill')
|
||||||
source_link = admin_link('source')
|
source_link = admin_link('source')
|
||||||
display_state = admin_colored('state', colors=STATE_COLORS)
|
display_state = admin_colored('state', colors=STATE_COLORS)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import lxml.builder
|
||||||
import os
|
import os
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from lxml.builder import E
|
from lxml.builder import E
|
||||||
from StringIO import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
|
@ -26,7 +26,7 @@ class PaymentSource(models.Model):
|
||||||
|
|
||||||
objects = PaymentSourcesQueryset.as_manager()
|
objects = PaymentSourcesQueryset.as_manager()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s (%s)" % (self.label, self.method_class.verbose_name)
|
return "%s (%s)" % (self.label, self.method_class.verbose_name)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -76,7 +76,7 @@ class TransactionQuerySet(models.QuerySet):
|
||||||
return self.exclude(state=Transaction.REJECTED)
|
return self.exclude(state=Transaction.REJECTED)
|
||||||
|
|
||||||
def amount(self):
|
def amount(self):
|
||||||
return self.aggregate(models.Sum('amount')).values()[0]
|
return next(iter(self.aggregate(models.Sum('amount')).values()))
|
||||||
|
|
||||||
def processing(self):
|
def processing(self):
|
||||||
return self.filter(state__in=[Transaction.EXECUTED, Transaction.WAITTING_EXECUTION])
|
return self.filter(state__in=[Transaction.EXECUTED, Transaction.WAITTING_EXECUTION])
|
||||||
|
@ -111,7 +111,7 @@ class Transaction(models.Model):
|
||||||
|
|
||||||
objects = TransactionQuerySet.as_manager()
|
objects = TransactionQuerySet.as_manager()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "Transaction #{}".format(self.id)
|
return "Transaction #{}".format(self.id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -173,7 +173,7 @@ class TransactionProcess(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = _("Transaction processes")
|
verbose_name_plural = _("Transaction processes")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return '#%i' % self.id
|
return '#%i' % self.id
|
||||||
|
|
||||||
def check_state(*args):
|
def check_state(*args):
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Plan(models.Model):
|
||||||
allow_multiple = models.BooleanField(_("allow multiple"), default=False,
|
allow_multiple = models.BooleanField(_("allow multiple"), default=False,
|
||||||
help_text=_("Designates whether this plan allow for multiple contractions."))
|
help_text=_("Designates whether this plan allow for multiple contractions."))
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.get_verbose_name()
|
return self.get_verbose_name()
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -41,7 +41,7 @@ class ContractedPlan(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = _("plans")
|
verbose_name_plural = _("plans")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return str(self.plan)
|
return str(self.plan)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -80,7 +80,7 @@ class Rate(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('service', 'plan', 'quantity')
|
unique_together = ('service', 'plan', 'quantity')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "{}-{}".format(str(self.price), self.quantity)
|
return "{}-{}".format(str(self.price), self.quantity)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -90,7 +90,7 @@ class Rate(models.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_choices(cls):
|
def get_choices(cls):
|
||||||
choices = []
|
choices = []
|
||||||
for name, method in cls.RATE_METHODS.iteritems():
|
for name, method in cls.RATE_METHODS.items():
|
||||||
choices.append((name, method.verbose_name))
|
choices.append((name, method.verbose_name))
|
||||||
return choices
|
return choices
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,8 @@ def _prepend_missing(rates):
|
||||||
def step_price(rates, metric):
|
def step_price(rates, metric):
|
||||||
# Step price
|
# Step price
|
||||||
group = []
|
group = []
|
||||||
minimal = (sys.maxint, [])
|
minimal = (sys.maxsize, [])
|
||||||
for plan, rates in rates.group_by('plan').iteritems():
|
for plan, rates in rates.group_by('plan').items():
|
||||||
rates = _prepend_missing(rates)
|
rates = _prepend_missing(rates)
|
||||||
value, steps = _compute(rates, metric)
|
value, steps = _compute(rates, metric)
|
||||||
if plan.is_combinable:
|
if plan.is_combinable:
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
def run_monitor(modeladmin, request, queryset):
|
def run_monitor(modeladmin, request, queryset):
|
||||||
""" Resource and ResourceData run monitors """
|
""" Resource and ResourceData run monitors """
|
||||||
referer = request.META.get('HTTP_REFERER')
|
referer = request.META.get('HTTP_REFERER')
|
||||||
async = modeladmin.model.monitor.func_defaults[0]
|
async = modeladmin.model.monitor.__defaults__[0]
|
||||||
logs = set()
|
logs = set()
|
||||||
for resource in queryset:
|
for resource in queryset:
|
||||||
results = resource.monitor()
|
results = resource.monitor()
|
||||||
|
|
|
@ -262,7 +262,7 @@ def insert_resource_inlines():
|
||||||
if inline.__name__ == 'ResourceInline':
|
if inline.__name__ == 'ResourceInline':
|
||||||
modeladmin_class.inlines.remove(inline)
|
modeladmin_class.inlines.remove(inline)
|
||||||
resources = Resource.objects.filter(is_active=True)
|
resources = Resource.objects.filter(is_active=True)
|
||||||
for ct, resources in resources.group_by('content_type').iteritems():
|
for ct, resources in resources.group_by('content_type').items():
|
||||||
inline = resource_inline_factory(resources)
|
inline = resource_inline_factory(resources)
|
||||||
model = ct.model_class()
|
model = ct.model_class()
|
||||||
insertattr(model, 'inlines', inline)
|
insertattr(model, 'inlines', inline)
|
||||||
|
|
|
@ -7,10 +7,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from orchestra import plugins
|
from orchestra import plugins
|
||||||
|
|
||||||
|
|
||||||
class DataMethod(plugins.Plugin):
|
class DataMethod(plugins.Plugin, metaclass=plugins.PluginMount):
|
||||||
""" filters and computes dataset usage """
|
""" filters and computes dataset usage """
|
||||||
__metaclass__ = plugins.PluginMount
|
|
||||||
|
|
||||||
def filter(self, dataset):
|
def filter(self, dataset):
|
||||||
""" Filter the dataset to get the relevant data according to the period """
|
""" Filter the dataset to get the relevant data according to the period """
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import orchestra.core.validators
|
import orchestra.core.validators
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ class Resource(models.Model):
|
||||||
('verbose_name', 'content_type')
|
('verbose_name', 'content_type')
|
||||||
)
|
)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "{}-{}".format(str(self.content_type), self.name)
|
return "{}-{}".format(str(self.content_type), self.name)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -188,7 +188,7 @@ class ResourceData(models.Model):
|
||||||
unique_together = ('resource', 'content_type', 'object_id')
|
unique_together = ('resource', 'content_type', 'object_id')
|
||||||
verbose_name_plural = _("resource data")
|
verbose_name_plural = _("resource data")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s: %s" % (str(self.resource), str(self.content_object))
|
return "%s: %s" % (str(self.resource), str(self.content_object))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -278,7 +278,7 @@ class MonitorData(models.Model):
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
verbose_name_plural = _("monitor data")
|
verbose_name_plural = _("monitor data")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return str(self.monitor)
|
return str(self.monitor)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -331,7 +331,7 @@ def create_resource_relation():
|
||||||
field for field in related._meta.virtual_fields if field.rel.to != ResourceData
|
field for field in related._meta.virtual_fields if field.rel.to != ResourceData
|
||||||
]
|
]
|
||||||
|
|
||||||
for ct, resources in Resource.objects.group_by('content_type').iteritems():
|
for ct, resources in Resource.objects.group_by('content_type').items():
|
||||||
model = ct.model_class()
|
model = ct.model_class()
|
||||||
relation = GenericRelation('resources.ResourceData')
|
relation = GenericRelation('resources.ResourceData')
|
||||||
model.add_to_class('resource_set', relation)
|
model.add_to_class('resource_set', relation)
|
||||||
|
|
|
@ -41,7 +41,7 @@ def insert_resource_serializers():
|
||||||
pass
|
pass
|
||||||
viewset.serializer_class.Meta.fields = fields
|
viewset.serializer_class.Meta.fields = fields
|
||||||
# Create nested serializers on target models
|
# Create nested serializers on target models
|
||||||
for ct, resources in Resource.objects.group_by('content_type').iteritems():
|
for ct, resources in Resource.objects.group_by('content_type').items():
|
||||||
model = ct.model_class()
|
model = ct.model_class()
|
||||||
try:
|
try:
|
||||||
router.insert(model, 'resources', ResourceSerializer, required=False, many=True, source='resource_set')
|
router.insert(model, 'resources', ResourceSerializer, required=False, many=True, source='resource_set')
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin
|
||||||
from orchestra.plugins.admin import SelectPluginAdminMixin
|
from orchestra.plugins.admin import SelectPluginAdminMixin
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ from .models import SaaS
|
||||||
from .services import SoftwareService
|
from .services import SoftwareService
|
||||||
|
|
||||||
|
|
||||||
class SaaSAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
|
class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
|
||||||
list_display = ('name', 'service', 'display_site_domain', 'account_link', 'is_active')
|
list_display = ('name', 'service', 'display_site_domain', 'account_link', 'is_active')
|
||||||
list_filter = ('service', 'is_active')
|
list_filter = ('service', 'is_active')
|
||||||
change_readonly_fields = ('service',)
|
change_readonly_fields = ('service',)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import textwrap
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.apps.orchestration import ServiceController
|
from orchestra.apps.orchestration import ServiceController
|
||||||
|
@ -26,11 +28,11 @@ class BSCWBackend(ServiceController):
|
||||||
if hasattr(saas, 'password'):
|
if hasattr(saas, 'password'):
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
if [[ ! $(%(bsadmin)s register %(email)s) && ! $(%(bsadmin)s users -n %(username)s) ]]; then
|
if [[ ! $(%(bsadmin)s register %(email)s) && ! $(%(bsadmin)s users -n %(username)s) ]]; then
|
||||||
# Change password
|
|
||||||
%(bsadmin)s chpwd %(username)s '%(password)s'
|
|
||||||
else
|
|
||||||
# Create new user
|
# Create new user
|
||||||
%(bsadmin)s register -r %(email)s %(username)s '%(password)s'
|
%(bsadmin)s register -r %(email)s %(username)s '%(password)s'
|
||||||
|
else
|
||||||
|
# Change password
|
||||||
|
%(bsadmin)s chpwd %(username)s '%(password)s'
|
||||||
fi
|
fi
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
|
|
|
@ -84,7 +84,7 @@ class GitLabSaaSBackend(ServiceController):
|
||||||
user_url = self.get_user_url(saas)
|
user_url = self.get_user_url(saas)
|
||||||
response = requests.delete(user_url, headers=self.headers)
|
response = requests.delete(user_url, headers=self.headers)
|
||||||
user = self.validate_response(response, 200, 404)
|
user = self.validate_response(response, 200, 404)
|
||||||
print json.dumps(user, indent=4)
|
print(json.dumps(user, indent=4))
|
||||||
|
|
||||||
def _validate_creation(self, saas, server):
|
def _validate_creation(self, saas, server):
|
||||||
""" checks if a saas object is valid for creation on the server side """
|
""" checks if a saas object is valid for creation on the server side """
|
||||||
|
@ -95,9 +95,9 @@ class GitLabSaaSBackend(ServiceController):
|
||||||
users = json.loads(requests.get(users_url, headers=self.headers).content)
|
users = json.loads(requests.get(users_url, headers=self.headers).content)
|
||||||
for user in users:
|
for user in users:
|
||||||
if user['username'] == username:
|
if user['username'] == username:
|
||||||
print 'ValidationError: user-exists'
|
print('ValidationError: user-exists')
|
||||||
if user['email'] == email:
|
if user['email'] == email:
|
||||||
print 'ValidationError: email-exists'
|
print('ValidationError: email-exists')
|
||||||
|
|
||||||
def validate_creation(self, saas):
|
def validate_creation(self, saas):
|
||||||
self.append(self._validate_creation, saas)
|
self.append(self._validate_creation, saas)
|
||||||
|
|
|
@ -34,7 +34,7 @@ class PhpListSaaSBackend(ServiceController):
|
||||||
'adminpassword': saas.password,
|
'adminpassword': saas.password,
|
||||||
}
|
}
|
||||||
response = requests.post(install_link, data=post)
|
response = requests.post(install_link, data=post)
|
||||||
print response.content
|
print(response.content)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
raise RuntimeError("Bad status code %i" % response.status_code)
|
raise RuntimeError("Bad status code %i" % response.status_code)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -36,7 +36,7 @@ class SaaS(models.Model):
|
||||||
('name', 'service'),
|
('name', 'service'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s@%s" % (self.name, self.service)
|
return "%s@%s" % (self.name, self.service)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -7,18 +7,12 @@ from .. import settings
|
||||||
from .options import SoftwareService, SoftwareServiceForm
|
from .options import SoftwareService, SoftwareServiceForm
|
||||||
|
|
||||||
|
|
||||||
# TODO monitor quota since out of sync?
|
|
||||||
|
|
||||||
class BSCWForm(SoftwareServiceForm):
|
class BSCWForm(SoftwareServiceForm):
|
||||||
email = forms.EmailField(label=_("Email"), widget=forms.TextInput(attrs={'size':'40'}))
|
email = forms.EmailField(label=_("Email"), widget=forms.TextInput(attrs={'size':'40'}))
|
||||||
quota = forms.IntegerField(label=_("Quota"), initial=settings.SAAS_BSCW_DEFAULT_QUOTA,
|
|
||||||
help_text=_("Disk quota in MB."))
|
|
||||||
|
|
||||||
|
|
||||||
class BSCWDataSerializer(serializers.Serializer):
|
class BSCWDataSerializer(serializers.Serializer):
|
||||||
email = serializers.EmailField(label=_("Email"))
|
email = serializers.EmailField(label=_("Email"))
|
||||||
quota = serializers.IntegerField(label=_("Quota"), default=settings.SAAS_BSCW_DEFAULT_QUOTA,
|
|
||||||
help_text=_("Disk quota in MB."))
|
|
||||||
|
|
||||||
|
|
||||||
class BSCWService(SoftwareService):
|
class BSCWService(SoftwareService):
|
||||||
|
|
|
@ -3,7 +3,6 @@ from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from orchestra.apps.orchestration.models import BackendOperation as Operation
|
|
||||||
from orchestra.forms import widgets
|
from orchestra.forms import widgets
|
||||||
|
|
||||||
from .options import SoftwareService, SoftwareServiceForm
|
from .options import SoftwareService, SoftwareServiceForm
|
||||||
|
@ -35,16 +34,4 @@ class GitLabService(SoftwareService):
|
||||||
change_readonly_fileds = ('email', 'user_id',)
|
change_readonly_fileds = ('email', 'user_id',)
|
||||||
verbose_name = "GitLab"
|
verbose_name = "GitLab"
|
||||||
icon = 'orchestra/icons/apps/gitlab.png'
|
icon = 'orchestra/icons/apps/gitlab.png'
|
||||||
|
|
||||||
def clean_data(self):
|
|
||||||
data = super(GitLabService, self).clean_data()
|
|
||||||
if not self.instance.pk:
|
|
||||||
log = Operation.execute_action(self.instance, 'validate_creation')[0]
|
|
||||||
errors = {}
|
|
||||||
if 'user-exists' in log.stdout:
|
|
||||||
errors['name'] = _("User with this username already exists.")
|
|
||||||
elif 'email-exists' in log.stdout:
|
|
||||||
errors['email'] = _("User with this email address already exists.")
|
|
||||||
if errors:
|
|
||||||
raise ValidationError(errors)
|
|
||||||
return data
|
|
||||||
|
|
|
@ -4,9 +4,10 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra import plugins
|
from orchestra import plugins
|
||||||
from orchestra.plugins.forms import PluginDataForm
|
from orchestra.apps.orchestration.models import BackendOperation as Operation
|
||||||
from orchestra.core import validators
|
from orchestra.core import validators
|
||||||
from orchestra.forms import widgets
|
from orchestra.forms import widgets
|
||||||
|
from orchestra.plugins.forms import PluginDataForm
|
||||||
from orchestra.utils.functional import cached
|
from orchestra.utils.functional import cached
|
||||||
from orchestra.utils.python import import_class, random_ascii
|
from orchestra.utils.python import import_class, random_ascii
|
||||||
|
|
||||||
|
@ -91,6 +92,23 @@ class SoftwareService(plugins.Plugin):
|
||||||
(self.instance.name, self.site_base_domain)
|
(self.instance.name, self.site_base_domain)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def clean_data(self):
|
||||||
|
data = super(SoftwareService, self).clean_data()
|
||||||
|
if not self.instance.pk:
|
||||||
|
try:
|
||||||
|
log = Operation.execute_action(self.instance, 'validate_creation')[0]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
errors = {}
|
||||||
|
if 'user-exists' in log.stdout:
|
||||||
|
errors['name'] = _("User with this username already exists.")
|
||||||
|
elif 'email-exists' in log.stdout:
|
||||||
|
errors['email'] = _("User with this email address already exists.")
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
return data
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import calendar
|
import calendar
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
import math
|
||||||
|
|
||||||
from dateutil import relativedelta
|
from dateutil import relativedelta
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
@ -15,7 +16,7 @@ from orchestra.utils.python import AttrDict
|
||||||
from . import settings, helpers
|
from . import settings, helpers
|
||||||
|
|
||||||
|
|
||||||
class ServiceHandler(plugins.Plugin):
|
class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
|
||||||
"""
|
"""
|
||||||
Separates all the logic of billing handling from the model allowing to better
|
Separates all the logic of billing handling from the model allowing to better
|
||||||
customize its behaviout
|
customize its behaviout
|
||||||
|
@ -27,8 +28,6 @@ class ServiceHandler(plugins.Plugin):
|
||||||
|
|
||||||
model = None
|
model = None
|
||||||
|
|
||||||
__metaclass__ = plugins.PluginMount
|
|
||||||
|
|
||||||
def __init__(self, service):
|
def __init__(self, service):
|
||||||
self.service = service
|
self.service = service
|
||||||
|
|
||||||
|
@ -54,8 +53,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
bool(self.matches(obj))
|
bool(self.matches(obj))
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
name = type(exception).__name__
|
name = type(exception).__name__
|
||||||
message = exception.message
|
raise ValidationError(': '.join((name, exception)))
|
||||||
raise ValidationError(': '.join((name, message)))
|
|
||||||
|
|
||||||
def validate_metric(self, service):
|
def validate_metric(self, service):
|
||||||
try:
|
try:
|
||||||
|
@ -66,8 +64,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
bool(self.get_metric(obj))
|
bool(self.get_metric(obj))
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
name = type(exception).__name__
|
name = type(exception).__name__
|
||||||
message = exception.message
|
raise ValidationError(': '.join((name, exception)))
|
||||||
raise ValidationError(': '.join((name, message)))
|
|
||||||
|
|
||||||
def get_content_type(self):
|
def get_content_type(self):
|
||||||
if not self.model:
|
if not self.model:
|
||||||
|
@ -106,18 +103,26 @@ class ServiceHandler(plugins.Plugin):
|
||||||
return order.ignore
|
return order.ignore
|
||||||
|
|
||||||
def get_ignore(self, instance):
|
def get_ignore(self, instance):
|
||||||
ignore = False
|
if self.ignore_superusers:
|
||||||
account = getattr(instance, 'account', instance)
|
account = getattr(instance, 'account', instance)
|
||||||
if account.is_superuser:
|
if (account.type in settings.SERVICES_IGNORE_ACCOUNT_TYPE or
|
||||||
ignore = self.ignore_superusers
|
'superuser' in settings.SERVICES_IGNORE_ACCOUNT_TYPE):
|
||||||
return ignore
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_metric(self, instance):
|
def get_metric(self, instance):
|
||||||
if self.metric:
|
if self.metric:
|
||||||
safe_locals = {
|
safe_locals = {
|
||||||
instance._meta.model_name: instance
|
instance._meta.model_name: instance,
|
||||||
|
'instance': instance,
|
||||||
|
'math': math,
|
||||||
|
'log10': math.log10,
|
||||||
|
'Decimal': decimal.Decimal,
|
||||||
}
|
}
|
||||||
return eval(self.metric, safe_locals)
|
try:
|
||||||
|
return eval(self.metric, safe_locals)
|
||||||
|
except Exception as error:
|
||||||
|
raise type(error)("%s on '%s'" %(error, self.service))
|
||||||
|
|
||||||
def get_order_description(self, instance):
|
def get_order_description(self, instance):
|
||||||
safe_locals = {
|
safe_locals = {
|
||||||
|
@ -126,7 +131,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
instance._meta.model_name: instance,
|
instance._meta.model_name: instance,
|
||||||
}
|
}
|
||||||
if not self.order_description:
|
if not self.order_description:
|
||||||
return u'%s: %s' % (self.description, instance)
|
return '%s: %s' % (self.description, instance)
|
||||||
return eval(self.order_description, safe_locals)
|
return eval(self.order_description, safe_locals)
|
||||||
|
|
||||||
def get_billing_point(self, order, bp=None, **options):
|
def get_billing_point(self, order, bp=None, **options):
|
||||||
|
@ -359,7 +364,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
else:
|
else:
|
||||||
priced[order] = (price, cprice)
|
priced[order] = (price, cprice)
|
||||||
lines = []
|
lines = []
|
||||||
for order, prices in priced.iteritems():
|
for order, prices in priced.items():
|
||||||
discounts = ()
|
discounts = ()
|
||||||
# Generate lines and discounts from order.nominal_price
|
# Generate lines and discounts from order.nominal_price
|
||||||
price, cprice = prices
|
price, cprice = prices
|
||||||
|
|
|
@ -24,6 +24,7 @@ autodiscover_modules('handlers')
|
||||||
rate_class = import_class(settings.SERVICES_RATE_CLASS)
|
rate_class = import_class(settings.SERVICES_RATE_CLASS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Service(models.Model):
|
class Service(models.Model):
|
||||||
NEVER = ''
|
NEVER = ''
|
||||||
# DAILY = 'DAILY'
|
# DAILY = 'DAILY'
|
||||||
|
@ -46,6 +47,8 @@ class Service(models.Model):
|
||||||
PREPAY = 'PREPAY'
|
PREPAY = 'PREPAY'
|
||||||
POSTPAY = 'POSTPAY'
|
POSTPAY = 'POSTPAY'
|
||||||
|
|
||||||
|
_ignore_types = ' and '.join(', '.join(settings.SERVICES_IGNORE_ACCOUNT_TYPE).rsplit(', ', 1)).lower()
|
||||||
|
|
||||||
description = models.CharField(_("description"), max_length=256, unique=True)
|
description = models.CharField(_("description"), max_length=256, unique=True)
|
||||||
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"),
|
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"),
|
||||||
help_text=_("Content type of the related service objects."))
|
help_text=_("Content type of the related service objects."))
|
||||||
|
@ -66,8 +69,8 @@ class Service(models.Model):
|
||||||
"here allow to."),
|
"here allow to."),
|
||||||
choices=ServiceHandler.get_choices())
|
choices=ServiceHandler.get_choices())
|
||||||
is_active = models.BooleanField(_("active"), default=True)
|
is_active = models.BooleanField(_("active"), default=True)
|
||||||
ignore_superusers = models.BooleanField(_("ignore superusers"), default=True,
|
ignore_superusers = models.BooleanField(_("ignore %s") % _ignore_types, default=True,
|
||||||
help_text=_("Designates whether superuser orders are marked as ignored by default or not."))
|
help_text=_("Designates whether %s orders are marked as ignored by default or not.") % _ignore_types)
|
||||||
# Billing
|
# Billing
|
||||||
billing_period = models.CharField(_("billing period"), max_length=16,
|
billing_period = models.CharField(_("billing period"), max_length=16,
|
||||||
help_text=_("Renewal period for recurring invoicing."),
|
help_text=_("Renewal period for recurring invoicing."),
|
||||||
|
@ -133,7 +136,7 @@ class Service(models.Model):
|
||||||
rate_algorithm = models.CharField(_("rate algorithm"), max_length=16,
|
rate_algorithm = models.CharField(_("rate algorithm"), max_length=16,
|
||||||
help_text=string_concat(_("Algorithm used to interprete the rating table."), *[
|
help_text=string_concat(_("Algorithm used to interprete the rating table."), *[
|
||||||
string_concat('<br> ', method.verbose_name, ': ', method.help_text)
|
string_concat('<br> ', method.verbose_name, ': ', method.help_text)
|
||||||
for name, method in rate_class.get_methods().iteritems()
|
for name, method in rate_class.get_methods().items()
|
||||||
]), choices=rate_class.get_choices(), default=rate_class.get_choices()[0][0])
|
]), choices=rate_class.get_choices(), default=rate_class.get_choices()[0][0])
|
||||||
on_cancel = models.CharField(_("on cancel"), max_length=16,
|
on_cancel = models.CharField(_("on cancel"), max_length=16,
|
||||||
help_text=_("Defines the cancellation behaviour of this service."),
|
help_text=_("Defines the cancellation behaviour of this service."),
|
||||||
|
@ -153,7 +156,7 @@ class Service(models.Model):
|
||||||
),
|
),
|
||||||
default=PREPAY)
|
default=PREPAY)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.description
|
return self.description
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -33,3 +33,10 @@ SERVICES_RATE_CLASS = getattr(settings, 'SERVICES_RATE_CLASS',
|
||||||
SERVICES_DEFAULT_IGNORE_PERIOD = getattr(settings, 'SERVICES_DEFAULT_IGNORE_PERIOD',
|
SERVICES_DEFAULT_IGNORE_PERIOD = getattr(settings, 'SERVICES_DEFAULT_IGNORE_PERIOD',
|
||||||
'TEN_DAYS'
|
'TEN_DAYS'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
SERVICES_IGNORE_ACCOUNT_TYPE = getattr(settings, 'SERVICES_IGNORE_ACCOUNT_TYPE', (
|
||||||
|
'superuser',
|
||||||
|
'STAFF',
|
||||||
|
'FRIEND',
|
||||||
|
))
|
||||||
|
|
|
@ -222,7 +222,7 @@ class Exim4Traffic(ServiceMonitor):
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
sys.stderr.write(e)
|
sys.stderr.write(e)
|
||||||
|
|
||||||
for username, opts in users.iteritems():
|
for username, opts in users.items():
|
||||||
__, object_id, size = opts
|
__, object_id, size = opts
|
||||||
print object_id, size
|
print object_id, size
|
||||||
""").format(**context)
|
""").format(**context)
|
||||||
|
@ -317,7 +317,7 @@ class FTPTraffic(ServiceMonitor):
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
sys.stderr.write(e)
|
sys.stderr.write(e)
|
||||||
|
|
||||||
for username, opts in users.iteritems():
|
for username, opts in users.items():
|
||||||
__, object_id, size = opts
|
__, object_id, size = opts
|
||||||
print object_id, size
|
print object_id, size
|
||||||
""").format(**context)
|
""").format(**context)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
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
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ class SystemUser(models.Model):
|
||||||
|
|
||||||
objects = SystemUserQuerySet.as_manager()
|
objects = SystemUserQuerySet.as_manager()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -24,7 +24,7 @@ class VPS(models.Model):
|
||||||
verbose_name = "VPS"
|
verbose_name = "VPS"
|
||||||
verbose_name_plural = "VPSs"
|
verbose_name_plural = "VPSs"
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.hostname
|
return self.hostname
|
||||||
|
|
||||||
def set_password(self, raw_password):
|
def set_password(self, raw_password):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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.core.urlresolvers import reverse
|
||||||
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
|
@ -20,7 +21,7 @@ class WebAppOptionInline(admin.TabularInline):
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
OPTIONS_HELP_TEXT = {
|
OPTIONS_HELP_TEXT = {
|
||||||
op.name: str(unicode(op.help_text)) for op in AppOption.get_plugins()
|
op.name: force_text(op.help_text) for op in AppOption.get_plugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
|
|
|
@ -118,6 +118,7 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
|
||||||
listen.owner = {{ user }}
|
listen.owner = {{ user }}
|
||||||
listen.group = {{ group }}
|
listen.group = {{ group }}
|
||||||
pm = ondemand
|
pm = ondemand
|
||||||
|
pm.max_requests = {{ max_requests }}
|
||||||
{% if max_children %}pm.max_children = {{ max_children }}{% endif %}
|
{% if max_children %}pm.max_children = {{ max_children }}{% endif %}
|
||||||
{% if request_terminate_timeout %}request_terminate_timeout = {{ request_terminate_timeout }}{% endif %}
|
{% if request_terminate_timeout %}request_terminate_timeout = {{ request_terminate_timeout }}{% endif %}
|
||||||
{% for name, value in init_vars.iteritems %}
|
{% for name, value in init_vars.iteritems %}
|
||||||
|
@ -131,7 +132,7 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
|
||||||
# Format PHP init vars
|
# Format PHP init vars
|
||||||
init_vars = opt.get_php_init_vars(merge=self.MERGE)
|
init_vars = opt.get_php_init_vars(merge=self.MERGE)
|
||||||
if init_vars:
|
if init_vars:
|
||||||
init_vars = [ '-d %s="%s"' % (k,v) for k,v in init_vars.iteritems() ]
|
init_vars = [ '-d %s="%s"' % (k,v) for k,v in init_vars.items() ]
|
||||||
init_vars = ', '.join(init_vars)
|
init_vars = ', '.join(init_vars)
|
||||||
context.update({
|
context.update({
|
||||||
'php_binary': os.path.normpath(settings.WEBAPPS_PHP_CGI_BINARY_PATH % context),
|
'php_binary': os.path.normpath(settings.WEBAPPS_PHP_CGI_BINARY_PATH % context),
|
||||||
|
@ -144,6 +145,7 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
|
||||||
# %(banner)s
|
# %(banner)s
|
||||||
export PHPRC=%(php_rc)s
|
export PHPRC=%(php_rc)s
|
||||||
export PHP_INI_SCAN_DIR=%(php_ini_scan)s
|
export PHP_INI_SCAN_DIR=%(php_ini_scan)s
|
||||||
|
export PHP_FCGI_MAX_REQUESTS=%(max_requests)s
|
||||||
exec %(php_binary)s %(php_init_vars)s""") % context
|
exec %(php_binary)s %(php_init_vars)s""") % context
|
||||||
|
|
||||||
def get_fcgid_cmd_options(self, webapp, context):
|
def get_fcgid_cmd_options(self, webapp, context):
|
||||||
|
@ -152,7 +154,7 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
|
||||||
'IOTimeout': webapp.get_options().get('timeout', None),
|
'IOTimeout': webapp.get_options().get('timeout', None),
|
||||||
}
|
}
|
||||||
cmd_options = []
|
cmd_options = []
|
||||||
for directive, value in maps.iteritems():
|
for directive, value in maps.items():
|
||||||
if value:
|
if value:
|
||||||
cmd_options.append("%s %s" % (directive, value))
|
cmd_options.append("%s %s" % (directive, value))
|
||||||
if cmd_options:
|
if cmd_options:
|
||||||
|
@ -187,6 +189,7 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
|
||||||
context.update({
|
context.update({
|
||||||
'php_version': webapp.type_instance.get_php_version(),
|
'php_version': webapp.type_instance.get_php_version(),
|
||||||
'php_version_number': webapp.type_instance.get_php_version_number(),
|
'php_version_number': webapp.type_instance.get_php_version_number(),
|
||||||
|
'max_requests': settings.WEBAPPS_PHP_MAX_REQUESTS,
|
||||||
})
|
})
|
||||||
self.update_fcgid_context(webapp, context)
|
self.update_fcgid_context(webapp, context)
|
||||||
self.update_fpm_context(webapp, context)
|
self.update_fpm_context(webapp, context)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import orchestra.core.validators
|
import orchestra.core.validators
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import jsonfield.fields
|
import jsonfield.fields
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ class WebApp(models.Model):
|
||||||
verbose_name = _("Web App")
|
verbose_name = _("Web App")
|
||||||
verbose_name_plural = _("Web Apps")
|
verbose_name_plural = _("Web Apps")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_description(self):
|
def get_description(self):
|
||||||
|
@ -98,7 +99,7 @@ class WebAppOption(models.Model):
|
||||||
verbose_name = _("option")
|
verbose_name = _("option")
|
||||||
verbose_name_plural = _("options")
|
verbose_name_plural = _("options")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -30,6 +30,13 @@ WEBAPPS_FCGID_CMD_OPTIONS_PATH = getattr(settings, 'WEBAPPS_FCGID_CMD_OPTIONS_PA
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Greater or equal to your FcgidMaxRequestsPerProcess
|
||||||
|
# http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#examples
|
||||||
|
WEBAPPS_PHP_MAX_REQUESTS = getattr(settings, 'WEBAPPS_PHP_MAX_REQUESTS',
|
||||||
|
400
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
WEBAPPS_PHP_ERROR_LOG_PATH = getattr(settings, 'WEBAPPS_PHP_ERROR_LOG_PATH',
|
WEBAPPS_PHP_ERROR_LOG_PATH = getattr(settings, 'WEBAPPS_PHP_ERROR_LOG_PATH',
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
|
@ -92,7 +99,7 @@ WEBAPPS_UNDER_CONSTRUCTION_PATH = getattr(settings, 'WEBAPPS_UNDER_CONSTRUCTION_
|
||||||
|
|
||||||
|
|
||||||
#WEBAPPS_TYPES_OVERRIDE = getattr(settings, 'WEBAPPS_TYPES_OVERRIDE', {})
|
#WEBAPPS_TYPES_OVERRIDE = getattr(settings, 'WEBAPPS_TYPES_OVERRIDE', {})
|
||||||
#for webapp_type, value in WEBAPPS_TYPES_OVERRIDE.iteritems():
|
#for webapp_type, value in WEBAPPS_TYPES_OVERRIDE.items():
|
||||||
# if value is None:
|
# if value is None:
|
||||||
# WEBAPPS_TYPES.pop(webapp_type, None)
|
# WEBAPPS_TYPES.pop(webapp_type, None)
|
||||||
# else:
|
# else:
|
||||||
|
|
|
@ -2,7 +2,7 @@ import ftplib
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import textwrap
|
import textwrap
|
||||||
from StringIO import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -61,7 +61,7 @@ class PHPApp(AppType):
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def get_php_options(self):
|
def get_php_options(self):
|
||||||
php_version = self.get_php_version()
|
php_version = self.get_php_version_number()
|
||||||
php_options = AppOption.get_option_groups()[AppOption.PHP]
|
php_options = AppOption.get_option_groups()[AppOption.PHP]
|
||||||
return [op for op in php_options if getattr(self, 'deprecated', 999) > php_version]
|
return [op for op in php_options if getattr(self, 'deprecated', 999) > php_version]
|
||||||
|
|
||||||
|
@ -93,6 +93,9 @@ class PHPApp(AppType):
|
||||||
if function not in enabled_functions:
|
if function not in enabled_functions:
|
||||||
disabled_functions.append(function)
|
disabled_functions.append(function)
|
||||||
init_vars['dissabled_functions'] = ','.join(disabled_functions)
|
init_vars['dissabled_functions'] = ','.join(disabled_functions)
|
||||||
|
timeout = self.instance.options.filter(name='timeout').first()
|
||||||
|
if timeout:
|
||||||
|
init_vars['max_execution_time'] = timeout.value
|
||||||
if self.PHP_ERROR_LOG_PATH and 'error_log' not in init_vars:
|
if self.PHP_ERROR_LOG_PATH and 'error_log' not in init_vars:
|
||||||
context = self.get_directive_context()
|
context = self.get_directive_context()
|
||||||
error_log_path = os.path.normpath(self.PHP_ERROR_LOG_PATH % context)
|
error_log_path = os.path.normpath(self.PHP_ERROR_LOG_PATH % context)
|
||||||
|
@ -128,4 +131,4 @@ class PHPApp(AppType):
|
||||||
raise ValueError("No version number matches for '%s'" % php_version)
|
raise ValueError("No version number matches for '%s'" % php_version)
|
||||||
if len(number) > 1:
|
if len(number) > 1:
|
||||||
raise ValueError("Multiple version number matches for '%s'" % php_version)
|
raise ValueError("Multiple version number matches for '%s'" % php_version)
|
||||||
return number[0]
|
return float(number[0])
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import resolve
|
from django.core.urlresolvers import resolve
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ class WebsiteDirectiveInline(admin.TabularInline):
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
DIRECTIVES_HELP_TEXT = {
|
DIRECTIVES_HELP_TEXT = {
|
||||||
op.name: str(unicode(op.help_text)) for op in SiteDirective.get_plugins()
|
op.name: force_text(op.help_text) for op in SiteDirective.get_plugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
|
|
|
@ -219,7 +219,7 @@ class Apache2Backend(ServiceController):
|
||||||
|
|
||||||
def get_saas(self, directives):
|
def get_saas(self, directives):
|
||||||
saas = []
|
saas = []
|
||||||
for name, values in directives.iteritems():
|
for name, values in directives.items():
|
||||||
if name.endswith('-saas'):
|
if name.endswith('-saas'):
|
||||||
for value in values:
|
for value in values:
|
||||||
context = {
|
context = {
|
||||||
|
|
|
@ -47,7 +47,7 @@ class SiteDirective(Plugin):
|
||||||
options = cls.get_option_groups()
|
options = cls.get_option_groups()
|
||||||
for option in options.pop(None, ()):
|
for option in options.pop(None, ()):
|
||||||
yield (option.name, option.verbose_name)
|
yield (option.name, option.verbose_name)
|
||||||
for group, options in options.iteritems():
|
for group, options in options.items():
|
||||||
yield (group, [(op.name, op.verbose_name) for op in options])
|
yield (group, [(op.name, op.verbose_name) for op in options])
|
||||||
|
|
||||||
def validate(self, website):
|
def validate(self, website):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
from .validators import validate_domain_protocol
|
from .validators import validate_domain_protocol
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class WebsiteDirectiveInlineFormSet(forms.models.BaseInlineFormSet):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
if directive.unique_value and value in values.get(name, []):
|
if directive.unique_value and value in values.get(name, []):
|
||||||
form.add_error('value', ValidationError(
|
form.add_error('value', ValidationError(
|
||||||
_("This value is already used by other %s.") % unicode(directive.get_verbose_name())
|
_("This value is already used by other %s.") % force_text(directive.get_verbose_name())
|
||||||
))
|
))
|
||||||
try:
|
try:
|
||||||
values[name].append(value)
|
values[name].append(value)
|
||||||
|
|
|
@ -40,7 +40,7 @@ class Website(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('name', 'account')
|
unique_together = ('name', 'account')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -107,7 +107,7 @@ class WebsiteDirective(models.Model):
|
||||||
choices=SiteDirective.get_choices())
|
choices=SiteDirective.get_choices())
|
||||||
value = models.CharField(_("value"), max_length=256)
|
value = models.CharField(_("value"), max_length=256)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -133,7 +133,7 @@ class Content(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('website', 'path')
|
unique_together = ('website', 'path')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
try:
|
try:
|
||||||
return self.website.name + self.path
|
return self.website.name + self.path
|
||||||
except Website.DoesNotExist:
|
except Website.DoesNotExist:
|
||||||
|
|
|
@ -136,10 +136,10 @@ function install_requirements () {
|
||||||
|
|
||||||
PIP="django==1.7.7 \
|
PIP="django==1.7.7 \
|
||||||
django-celery-email==1.0.4 \
|
django-celery-email==1.0.4 \
|
||||||
django-fluent-dashboard==0.3.5 \
|
django-fluent-dashboard==0.4 \
|
||||||
https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \
|
https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \
|
||||||
IPy==0.81 \
|
IPy==0.81 \
|
||||||
django-extensions==1.1.1 \
|
django-extensions==1.5.2 \
|
||||||
django-transaction-signals==1.0.0 \
|
django-transaction-signals==1.0.0 \
|
||||||
django-celery==3.1.16 \
|
django-celery==3.1.16 \
|
||||||
celery==3.1.16 \
|
celery==3.1.16 \
|
||||||
|
@ -209,10 +209,10 @@ function install_requirements () {
|
||||||
# Patch passlib
|
# Patch passlib
|
||||||
IMPORT="from django.contrib.auth.hashers import mask_hash, _"
|
IMPORT="from django.contrib.auth.hashers import mask_hash, _"
|
||||||
COLLECTIONS="from collections import OrderedDict"
|
COLLECTIONS="from collections import OrderedDict"
|
||||||
sed -i "s/${IMPORT}, SortedDict/${IMPORT}\n ${COLLECTIONS}/" \
|
ls /usr/local/lib/python*/dist-packages/passlib/ext/django/utils.py \
|
||||||
/usr/local/lib/python2.7/dist-packages/passlib/ext/django/utils.py
|
| xargs sed -i "s/${IMPORT}, SortedDict/${IMPORT}\n ${COLLECTIONS}/"
|
||||||
sed -i "s/SortedDict/OrderedDict/g" \
|
ls /usr/local/lib/python*/dist-packages/passlib/ext/django/utils.py \
|
||||||
/usr/local/lib/python2.7/dist-packages/passlib/ext/django/utils.py
|
| xargs sed -i "s/SortedDict/OrderedDict/g"
|
||||||
|
|
||||||
# Patch dateutil
|
# Patch dateutil
|
||||||
sed -i "s/elif not isinstance(dt2, datetime.datetime):/else:/" \
|
sed -i "s/elif not isinstance(dt2, datetime.datetime):/else:/" \
|
||||||
|
|
|
@ -48,8 +48,7 @@ MIDDLEWARE_CLASSES = (
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'orchestra.core.caches.RequestCacheMiddleware',
|
'orchestra.core.caches.RequestCacheMiddleware',
|
||||||
# ATOMIC REQUESTS do not wrap middlewares
|
# also handles transations, ATOMIC REQUESTS does not wrap middlewares
|
||||||
'orchestra.core.middlewares.TransactionMiddleware',
|
|
||||||
'orchestra.apps.orchestration.middlewares.OperationsMiddleware',
|
'orchestra.apps.orchestration.middlewares.OperationsMiddleware',
|
||||||
# Uncomment the next line for simple clickjacking protection:
|
# Uncomment the next line for simple clickjacking protection:
|
||||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
|
|
@ -8,40 +8,40 @@ class TransactionMiddleware(object):
|
||||||
commit, the commit is done when a successful response is created. If an
|
commit, the commit is done when a successful response is created. If an
|
||||||
exception happens, the database is rolled back.
|
exception happens, the database is rolled back.
|
||||||
"""
|
"""
|
||||||
|
pass
|
||||||
def process_request(self, request):
|
# def process_request(self, request):
|
||||||
"""Enters transaction management"""
|
# """Enters transaction management"""
|
||||||
transaction.enter_transaction_management()
|
# transaction.enter_transaction_management()
|
||||||
|
#
|
||||||
def process_exception(self, request, exception):
|
# def process_exception(self, request, exception):
|
||||||
"""Rolls back the database and leaves transaction management"""
|
# """Rolls back the database and leaves transaction management"""
|
||||||
if transaction.is_dirty():
|
# if transaction.is_dirty():
|
||||||
# This rollback might fail because of network failure for example.
|
# # This rollback might fail because of network failure for example.
|
||||||
# If rollback isn't possible it is impossible to clean the
|
# # If rollback isn't possible it is impossible to clean the
|
||||||
# connection's state. So leave the connection in dirty state and
|
# # connection's state. So leave the connection in dirty state and
|
||||||
# let request_finished signal deal with cleaning the connection.
|
# # let request_finished signal deal with cleaning the connection.
|
||||||
transaction.rollback()
|
# transaction.rollback()
|
||||||
transaction.leave_transaction_management()
|
# transaction.leave_transaction_management()
|
||||||
|
#
|
||||||
def process_response(self, request, response):
|
# def process_response(self, request, response):
|
||||||
"""Commits and leaves transaction management."""
|
# """Commits and leaves transaction management."""
|
||||||
if not transaction.get_autocommit():
|
# if not transaction.get_autocommit():
|
||||||
if transaction.is_dirty():
|
# if transaction.is_dirty():
|
||||||
# Note: it is possible that the commit fails. If the reason is
|
# # Note: it is possible that the commit fails. If the reason is
|
||||||
# closed connection or some similar reason, then there is
|
# # closed connection or some similar reason, then there is
|
||||||
# little hope to proceed nicely. However, in some cases (
|
# # little hope to proceed nicely. However, in some cases (
|
||||||
# deferred foreign key checks for exampl) it is still possible
|
# # deferred foreign key checks for exampl) it is still possible
|
||||||
# to rollback().
|
# # to rollback().
|
||||||
try:
|
# try:
|
||||||
transaction.commit()
|
# transaction.commit()
|
||||||
except Exception:
|
# except Exception:
|
||||||
# If the rollback fails, the transaction state will be
|
# # If the rollback fails, the transaction state will be
|
||||||
# messed up. It doesn't matter, the connection will be set
|
# # messed up. It doesn't matter, the connection will be set
|
||||||
# to clean state after the request finishes. And, we can't
|
# # to clean state after the request finishes. And, we can't
|
||||||
# clean the state here properly even if we wanted to, the
|
# # clean the state here properly even if we wanted to, the
|
||||||
# connection is in transaction but we can't rollback...
|
# # connection is in transaction but we can't rollback...
|
||||||
transaction.rollback()
|
# transaction.rollback()
|
||||||
transaction.leave_transaction_management()
|
# transaction.leave_transaction_management()
|
||||||
raise
|
# raise
|
||||||
transaction.leave_transaction_management()
|
# transaction.leave_transaction_management()
|
||||||
return response
|
# return response
|
||||||
|
|
|
@ -15,7 +15,7 @@ from ..utils.python import import_class
|
||||||
def all_valid(kwargs):
|
def all_valid(kwargs):
|
||||||
""" helper function to merge multiple validators at once """
|
""" helper function to merge multiple validators at once """
|
||||||
errors = {}
|
errors = {}
|
||||||
for field, validator in kwargs.iteritems():
|
for field, validator in kwargs.items():
|
||||||
try:
|
try:
|
||||||
validator[0](*validator[1:])
|
validator[0](*validator[1:])
|
||||||
except ValidationError as error:
|
except ValidationError as error:
|
||||||
|
|
|
@ -19,16 +19,16 @@ class ShowTextWidget(forms.Widget):
|
||||||
if hasattr(self, 'initial'):
|
if hasattr(self, 'initial'):
|
||||||
value = self.initial
|
value = self.initial
|
||||||
if self.bold:
|
if self.bold:
|
||||||
final_value = u'<b>%s</b>' % (value)
|
final_value = '<b>%s</b>' % (value)
|
||||||
else:
|
else:
|
||||||
final_value = '<br/>'.join(value.split('\n'))
|
final_value = '<br/>'.join(value.split('\n'))
|
||||||
if self.warning:
|
if self.warning:
|
||||||
final_value = (
|
final_value = (
|
||||||
u'<ul class="messagelist"><li class="warning">%s</li></ul>'
|
'<ul class="messagelist"><li class="warning">%s</li></ul>'
|
||||||
% final_value)
|
% final_value)
|
||||||
if self.hidden:
|
if self.hidden:
|
||||||
final_value = (
|
final_value = (
|
||||||
u'%s<input type="hidden" name="%s" value="%s"/>'
|
'%s<input type="hidden" name="%s" value="%s"/>'
|
||||||
% (final_value, name, value))
|
% (final_value, name, value))
|
||||||
return mark_safe(final_value)
|
return mark_safe(final_value)
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,13 @@ class Command(makemessages.Command):
|
||||||
self.remove_database_files()
|
self.remove_database_files()
|
||||||
|
|
||||||
def get_contents(self):
|
def get_contents(self):
|
||||||
for model, fields in ModelTranslation._registry.iteritems():
|
for model, fields in ModelTranslation._registry.items():
|
||||||
for field in fields:
|
for field in fields:
|
||||||
contents = []
|
contents = []
|
||||||
for content in model.objects.values_list('id', field):
|
for content in model.objects.values_list('id', field):
|
||||||
pk, value = content
|
pk, value = content
|
||||||
contents.append(
|
contents.append(
|
||||||
(pk, u"_(u'%s')" % value)
|
(pk, "_(u'%s')" % value)
|
||||||
)
|
)
|
||||||
if contents:
|
if contents:
|
||||||
yield ('_'.join((model._meta.db_table, field)), contents)
|
yield ('_'.join((model._meta.db_table, field)), contents)
|
||||||
|
@ -38,7 +38,7 @@ class Command(makemessages.Command):
|
||||||
2) Django's makemessages will work with no modifications
|
2) Django's makemessages will work with no modifications
|
||||||
"""
|
"""
|
||||||
for name, contents in self.get_contents():
|
for name, contents in self.get_contents():
|
||||||
name = unicode(name)
|
name = str(name)
|
||||||
maximum = None
|
maximum = None
|
||||||
content = {}
|
content = {}
|
||||||
for pk, value in contents:
|
for pk, value in contents:
|
||||||
|
@ -46,9 +46,9 @@ class Command(makemessages.Command):
|
||||||
maximum = pk
|
maximum = pk
|
||||||
content[pk] = value
|
content[pk] = value
|
||||||
tmpcontent = []
|
tmpcontent = []
|
||||||
for ix in xrange(maximum+1):
|
for ix in range(maximum+1):
|
||||||
tmpcontent.append(content.get(ix, ''))
|
tmpcontent.append(content.get(ix, ''))
|
||||||
tmpcontent = u'\n'.join(tmpcontent) + '\n'
|
tmpcontent = '\n'.join(tmpcontent) + '\n'
|
||||||
filename = 'database_%s.sql.py' % name
|
filename = 'database_%s.sql.py' % name
|
||||||
self.database_files.append(filename)
|
self.database_files.append(filename)
|
||||||
with open(filename, 'w') as tmpfile:
|
with open(filename, 'w') as tmpfile:
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue