Ported to python 3.4

This commit is contained in:
Marc Aymerich 2015-04-02 16:14:55 +00:00
parent b29c554878
commit b3946168f3
112 changed files with 413 additions and 323 deletions

31
TODO.md
View File

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

View File

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

View File

@ -1,2 +1,2 @@
from options import * from .options import *
from dashboard import * from .dashboard import *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +1,2 @@
from options import * from .options import *
from actions import * from .actions import *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
@ -36,15 +35,3 @@ class GitLabService(SoftwareService):
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

View File

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

View File

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

View File

@ -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>&nbsp;&nbsp;', method.verbose_name, ': ', method.help_text) string_concat('<br>&nbsp;&nbsp;', 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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