import ast import logging from django.utils.dateparse import parse_datetime from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ from . import settings as musician_settings from .utils import get_bootstraped_percent logger = logging.getLogger(__name__) class OrchestraModel: """ Base class from which all orchestra models will inherit. """ api_name = None verbose_name = None fields = () param_defaults = {} id = None def __init__(self, **kwargs): if self.verbose_name is None: self.verbose_name = self.api_name for (param, default) in self.param_defaults.items(): setattr(self, param, kwargs.get(param, default)) @classmethod def new_from_json(cls, data, **kwargs): """ Create a new instance based on a JSON dict. Any kwargs should be supplied by the inherited, calling class. Args: data: A JSON dict, as converted from the JSON in the orchestra API. """ if data is None: return cls() json_data = data.copy() if kwargs: for key, val in kwargs.items(): json_data[key] = val c = cls(**json_data) c._json = data return c def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self) def __str__(self): return '%s object (%s)' % (self.__class__.__name__, self.id) class Bill(OrchestraModel): api_name = 'bill' param_defaults = { "id": None, "number": "1", "type": "INVOICE", "total": 0.0, "is_sent": False, "created_on": "", "due_on": "", "comments": "", } class BillingContact(OrchestraModel): param_defaults = { 'name': None, 'address': None, 'city': None, 'zipcode': None, 'country': None, 'vat': None, } class PaymentSource(OrchestraModel): api_name = 'payment-source' param_defaults = { "method": None, "data": [], "is_active": False, } def __init__(self, **kwargs): super().__init__(**kwargs) # payment details are passed as a plain string # try to convert to a python structure try: self.data = ast.literal_eval(self.data) except (ValueError, SyntaxError) as e: logger.error(e) class UserAccount(OrchestraModel): api_name = 'accounts' param_defaults = { 'username': None, 'type': None, 'language': None, 'short_name': None, 'full_name': None, 'billing': {}, 'last_login': None, } @classmethod def new_from_json(cls, data, **kwargs): billing = None language = None last_login = None if 'billcontact' in data: billing = BillingContact.new_from_json(data['billcontact']) # Django expects that language code is lowercase if 'language' in data: language = data['language'].lower() last_login = data.get('last_login') if last_login is not None: last_login = parse_datetime(last_login) return super().new_from_json(data=data, billing=billing, language=language, last_login=last_login) class DatabaseUser(OrchestraModel): api_name = 'databaseusers' fields = ('username',) param_defaults = { 'username': None, } class DatabaseService(OrchestraModel): api_name = 'database' verbose_name = _('Databases') description = _('Description details for databases page.') fields = ('name', 'type', 'users') param_defaults = { "id": None, "name": None, "type": None, "users": None, "usage": {}, } @classmethod def new_from_json(cls, data, **kwargs): users = None if 'users' in data: users = [DatabaseUser.new_from_json(user_data) for user_data in data['users']] usage = cls.get_usage(data) return super().new_from_json(data=data, users=users, usage=usage) @classmethod def get_usage(cls, data): try: resources = data['resources'] resource_disk = {} for r in resources: if r['name'] == 'disk': resource_disk = r break details = { 'usage': float(resource_disk['used']), 'total': resource_disk['allocated'], 'unit': resource_disk['unit'], } except (IndexError, KeyError): return {} percent = get_bootstraped_percent( details['usage'], details['total'] ) details['percent'] = percent return details @property def manager_url(self): return musician_settings.URL_DB_PHPMYADMIN class Domain(OrchestraModel): api_name = 'domain' param_defaults = { "id": None, "name": None, "records": [], "mails": [], "usage": {}, "websites": [], "url": None, } @classmethod def new_from_json(cls, data, **kwargs): records = cls.param_defaults.get("records") if 'records' in data: records = [DomainRecord.new_from_json(record_data) for record_data in data['records']] return super().new_from_json(data=data, records=records) def __str__(self): return self.name class DomainRecord(OrchestraModel): param_defaults = { "type": None, "value": None, } def __str__(self): return '<%s: %s>' % (self.type, self.value) class Address(OrchestraModel): api_name = 'address' verbose_name = _('Mail addresses') description = _('Description details for mail addresses page.') fields = ('mail_address', 'aliases', 'type', 'type_detail') param_defaults = { "id": None, "domain": None, "mailboxes": [], "forward": None, } FORWARD = 'forward' MAILBOX = 'mailbox' def __init__(self, **kwargs): self.data = kwargs super().__init__(**kwargs) def deserialize(self): data = { 'name': self.data['name'], 'domain': self.data['domain']['url'], 'mailboxes': [mbox['url'] for mbox in self.data['mailboxes']], 'forward': self.data['forward'], } return data @property def aliases(self): return [ name + '@' + self.data['domain']['name'] for name in self.data['names'][1:] ] @property def mail_address(self): return self.data['names'][0] + '@' + self.data['domain']['name'] @property def type(self): if self.data['forward']: return self.FORWARD return self.MAILBOX @property def type_detail(self): if self.type == self.FORWARD: return self.data['forward'] # retrieve mailbox usage try: resource = self.data['mailboxes'][0]['resources'] resource_disk = {} for r in resource: if r['name'] == 'disk': resource_disk = r break mailbox_details = { 'usage': float(resource_disk['used']), 'total': resource_disk['allocated'], 'unit': resource_disk['unit'], } percent = get_bootstraped_percent( mailbox_details['used'], mailbox_details['total'] ) mailbox_details['percent'] = percent except (IndexError, KeyError): mailbox_details = {} return mailbox_details class Mailbox(OrchestraModel): api_name = 'mailbox' verbose_name = _('Mailbox') description = _('Description details for mailbox page.') fields = ('name', 'filtering', 'addresses', 'active') param_defaults = { 'id': None, 'name': None, 'filtering': None, 'is_active': True, 'addresses': [], 'url': None, } @classmethod def new_from_json(cls, data, **kwargs): addresses = [Address.new_from_json(addr) for addr in data.get('addresses', [])] return super().new_from_json(data=data, addresses=addresses) class MailinglistService(OrchestraModel): api_name = 'mailinglist' verbose_name = _('Mailing list') description = _('Description details for mailinglist page.') fields = ('name', 'status', 'address_name', 'admin_email', 'configure') param_defaults = { 'name': None, 'is_active': True, 'admin_email': None, } def __init__(self, **kwargs): self.data = kwargs super().__init__(**kwargs) @property def address_name(self): address_domain = self.data['address_domain'] if address_domain is None: return self.data['address_name'] return "{}@{}".format(self.data['address_name'], address_domain['name']) @property def manager_url(self): return musician_settings.URL_MAILTRAIN class SaasService(OrchestraModel): api_name = 'saas' verbose_name = _('Software as a Service (SaaS)') description = _('Description details for SaaS page.') param_defaults = { 'name': None, 'service': None, 'is_active': True, 'data': {}, } @property def manager_url(self): URLS = { 'gitlab': musician_settings.URL_SAAS_GITLAB, 'owncloud': musician_settings.URL_SAAS_OWNCLOUD, 'wordpress': musician_settings.URL_SAAS_WORDPRESS, } return URLS.get(self.service, '#none') class WebSite(OrchestraModel): api_name = 'website' param_defaults = { "id": None, "name": None, "protocol": None, "is_active": True, "domains": [], "contents": [], } @classmethod def new_from_json(cls, data, **kwargs): domains = cls.param_defaults.get("domains") if 'domains' in data: domains = [Domain.new_from_json(domain_data) for domain_data in data['domains']] return super().new_from_json(data=data, domains=domains)