Added service metric and match descriptions and fixed domain slaves without master
This commit is contained in:
parent
e669dcf926
commit
8f27720c69
4
TODO.md
4
TODO.md
|
@ -119,7 +119,6 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
|||
* delete main user -> delete account or prevent delete main user
|
||||
|
||||
* Ansible orchestration *method* (methods.py)
|
||||
* pip upgrade or install
|
||||
* multiple domains creation; line separated domains
|
||||
* Move MU webapps to SaaS?
|
||||
|
||||
|
@ -156,5 +155,6 @@ textwrap.dedent( \\)
|
|||
* parmiko write to a channel instead of transfering files? http://sysadmin.circularvale.com/programming/paramiko-channel-hangs/
|
||||
|
||||
* strip leading and trailing whitre spaces of most input fields
|
||||
* Examples of service.match and service.metric
|
||||
|
||||
* better modeling of the interdependency between webapps and websites (settings)
|
||||
* webapp options cfig agnostic
|
||||
|
|
|
@ -133,7 +133,7 @@ class Bind9SlaveDomainBackend(Bind9MasterDomainBackend):
|
|||
'name': domain.name,
|
||||
'banner': self.get_banner(),
|
||||
'subdomains': domain.subdomains.all(),
|
||||
'masters': '; '.join(self.get_masters(domain)) or 'none',
|
||||
'masters': '; '.join(self.get_masters(domain)) or '"none"',
|
||||
}
|
||||
context.update({
|
||||
'conf_path': settings.DOMAINS_SLAVES_PATH,
|
||||
|
|
|
@ -24,6 +24,46 @@ class DomainAdminForm(forms.ModelForm):
|
|||
return cleaned_data
|
||||
|
||||
|
||||
#class BatchDomainCreationAdminForm(DomainAdminForm):
|
||||
# # TODO
|
||||
# name = forms.CharField(widget=forms.Textarea, label=_("Names"),
|
||||
# help_text=_("Domain per line. All domains will share the same attributes."))
|
||||
#
|
||||
# def clean_name(self):
|
||||
# self.names = []
|
||||
# target = None
|
||||
# for name in self.cleaned_data['name'].splitlines():
|
||||
# name = name.strip()
|
||||
# if target is None:
|
||||
# target = name
|
||||
# else:
|
||||
# domain = Domain(name=name)
|
||||
# try:
|
||||
# domain.full_clean(exclude=['top'])
|
||||
# except ValidationError as e:
|
||||
# raise ValidationError(e.error_dict['name'])
|
||||
# self.names.append(name)
|
||||
# return target
|
||||
#
|
||||
# def save_model(self, request, obj, form, change):
|
||||
# # TODO thsi is modeladmin
|
||||
# """ batch domain creation support """
|
||||
# super(DomainAdmin, self).save_model(request, obj, form, change)
|
||||
# if not change:
|
||||
# for name in form.names:
|
||||
# domain = Domain.objects.create(name=name, account_id=obj.account_id)
|
||||
#
|
||||
# def save_related(self, request, form, formsets, change):
|
||||
# # TODO thsi is modeladmin
|
||||
# """ batch domain creation support """
|
||||
# super(DomainAdmin, self).save_related(request, form, formsets, change)
|
||||
# if not change:
|
||||
# for name in form.names:
|
||||
# for formset in formsets:
|
||||
# formset.instance = form.instance
|
||||
# self.save_formset(request, form, formset, change=change)
|
||||
|
||||
|
||||
class RecordInlineFormSet(forms.models.BaseInlineFormSet):
|
||||
def clean(self):
|
||||
""" Checks if everything is consistent """
|
||||
|
|
|
@ -11,9 +11,10 @@ from . import settings, validators, utils
|
|||
|
||||
class Domain(models.Model):
|
||||
name = models.CharField(_("name"), max_length=256, unique=True,
|
||||
validators=[validate_hostname, validators.validate_allowed_domain])
|
||||
validators=[validate_hostname, validators.validate_allowed_domain],
|
||||
help_text=_("Domain or subdomain name."))
|
||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||
related_name='domains', blank=True, help_text=_("Automatically selected for subdomains"))
|
||||
related_name='domains', blank=True, help_text=_("Automatically selected for subdomains."))
|
||||
top = models.ForeignKey('domains.Domain', null=True, related_name='subdomains')
|
||||
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial,
|
||||
help_text=_("Serial number"))
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.db import models
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core import services
|
||||
|
@ -34,6 +35,13 @@ class Miscellaneous(models.Model):
|
|||
|
||||
def __unicode__(self):
|
||||
return "{0}-{1}".format(str(self.service), str(self.account))
|
||||
|
||||
@cached_property
|
||||
def active(self):
|
||||
try:
|
||||
return self.is_active and self.account.is_active
|
||||
except type(self).account.field.rel.to.DoesNotExist:
|
||||
return self.is_active
|
||||
|
||||
|
||||
services.register(Miscellaneous)
|
||||
|
|
|
@ -45,6 +45,9 @@ class ServiceHandler(plugins.Plugin):
|
|||
return ContentType.objects.get_by_natural_key(app_label, model.lower())
|
||||
|
||||
def matches(self, instance):
|
||||
if not self.match:
|
||||
# Blank expressions always evaluate True
|
||||
return True
|
||||
safe_locals = {
|
||||
'instance': instance,
|
||||
'obj': instance,
|
||||
|
|
|
@ -100,8 +100,19 @@ class Service(models.Model):
|
|||
}
|
||||
|
||||
description = models.CharField(_("description"), max_length=256, unique=True)
|
||||
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
|
||||
match = models.CharField(_("match"), max_length=256, blank=True)
|
||||
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"),
|
||||
help_text=_("Content type of the related service objects."))
|
||||
match = models.CharField(_("match"), max_length=256, blank=True,
|
||||
help_text=_(
|
||||
"Python <a href='https://docs.python.org/2/library/functions.html#eval'>expression</a> "
|
||||
"that designates wheter a <tt>content_type</tt> object is related to this service "
|
||||
"or not, always evaluates <tt>True</tt> when left blank. "
|
||||
"Related instance can be instantiated with <tt>instance</tt> keyword or "
|
||||
"<tt>content_type.model_name</tt>.</br>"
|
||||
"<tt> databaseuser.type == 'MYSQL'</tt><br>"
|
||||
"<tt> miscellaneous.active and miscellaneous.service.name.lower() == 'domain .es'</tt><br>"
|
||||
"<tt> contractedplan.plan.name == 'association_fee''</tt><br>"
|
||||
"<tt> instance.active</tt>"))
|
||||
handler_type = models.CharField(_("handler"), max_length=256, blank=True,
|
||||
help_text=_("Handler used for processing this Service. A handler "
|
||||
"enables customized behaviour far beyond what options "
|
||||
|
@ -132,8 +143,16 @@ class Service(models.Model):
|
|||
" membership fee or not"))
|
||||
# Pricing
|
||||
metric = models.CharField(_("metric"), max_length=256, blank=True,
|
||||
help_text=_("Metric used to compute the pricing rate. "
|
||||
"Number of orders is used when left blank."))
|
||||
help_text=_(
|
||||
"Python <a href='https://docs.python.org/2/library/functions.html#eval'>expression</a> "
|
||||
"used for obtinging the <i>metric value</i> for the pricing rate computation. "
|
||||
"Number of orders is used when left blank. Related instance can be instantiated "
|
||||
"with <tt>instance</tt> keyword or <tt>content_type.model_name</tt>.<br>"
|
||||
"<tt> max((mailbox.resources.disk.allocated or 0) -1, 0)</tt><br>"
|
||||
"<tt> miscellaneous.amount</tt><br>"
|
||||
"<tt> max((account.resources.traffic.used or 0) -"
|
||||
" getattr(account.miscellaneous.filter(is_active=True,"
|
||||
" service__name='traffic prepay').last(), 'amount', 0), 0)</tt>"))
|
||||
nominal_price = models.DecimalField(_("nominal price"), max_digits=12,
|
||||
decimal_places=2)
|
||||
tax = models.PositiveIntegerField(_("tax"), choices=settings.SERVICES_SERVICE_TAXES,
|
||||
|
|
Loading…
Reference in New Issue