diff --git a/orchestra/apps/orders/helpers.py b/orchestra/apps/orders/helpers.py index 241c097a..e543e0f0 100644 --- a/orchestra/apps/orders/helpers.py +++ b/orchestra/apps/orders/helpers.py @@ -1,4 +1,5 @@ from orchestra.apps.accounts.models import Account +from orchestra.core import services def get_related_objects(origin, max_depth=2): @@ -9,7 +10,6 @@ def get_related_objects(origin, max_depth=2): flexibility. A more comprehensive approach may be considered if a use-case calls for it. """ - def related_iterator(node): for field in node._meta.virtual_fields: if hasattr(field, 'ct_field'): @@ -26,7 +26,7 @@ def get_related_objects(origin, max_depth=2): return None node = models[-1] if len(models) > 1: - if hasattr(node, 'account') or isinstance(node, Account): + if type(node) in services: return node for related in related_iterator(node): if related and related not in models: diff --git a/orchestra/apps/orders/models.py b/orchestra/apps/orders/models.py index e3ad3f29..17d01cc3 100644 --- a/orchestra/apps/orders/models.py +++ b/orchestra/apps/orders/models.py @@ -14,7 +14,7 @@ from django.contrib.contenttypes.models import ContentType from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from orchestra.core import accounts +from orchestra.core import accounts, services from orchestra.models import queryset from orchestra.utils.python import import_class @@ -71,6 +71,7 @@ class OrderQuerySet(models.QuerySet): for order in orders: bp = service.handler.get_billing_point(order, **options) end = max(end, bp) + # FIXME exclude cancelled except cancelled and billed > ini qs = qs | Q( Q(service=service, account=account_id, registered_on__lt=end) & Q(Q(billed_until__isnull=True) | Q(billed_until__lt=end)) @@ -236,27 +237,26 @@ class MetricStorage(models.Model): accounts.register(Order) -_excluded_models = (MetricStorage, LogEntry, Order, ContentType, MigrationRecorder.Migration) - +# TODO build a cache hash table {model: related, model: None} @receiver(post_delete, dispatch_uid="orders.cancel_orders") def cancel_orders(sender, **kwargs): - if sender not in _excluded_models: + if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS: instance = kwargs['instance'] - if hasattr(instance, 'account'): + if type(instance) in services: for order in Order.objects.by_object(instance).active(): order.cancel() - else: + elif not hasattr(instance, 'account'): related = helpers.get_related_objects(instance) if related and related != instance: Order.update_orders(related) @receiver(post_save, dispatch_uid="orders.update_orders") def update_orders(sender, **kwargs): - if sender not in _excluded_models: + if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS: instance = kwargs['instance'] - if hasattr(instance, 'account'): + if type(instance) in services: Order.update_orders(instance) - else: + elif not hasattr(instance, 'account'): related = helpers.get_related_objects(instance) if related and related != instance: Order.update_orders(related) diff --git a/orchestra/apps/orders/settings.py b/orchestra/apps/orders/settings.py index 539a8614..60f031c5 100644 --- a/orchestra/apps/orders/settings.py +++ b/orchestra/apps/orders/settings.py @@ -6,3 +6,16 @@ ORDERS_BILLING_BACKEND = getattr(settings, 'ORDERS_BILLING_BACKEND', ORDERS_SERVICE_MODEL = getattr(settings, 'ORDERS_SERVICE_MODEL', 'services.Service') + + +ORDERS_EXCLUDED_APPS = getattr(settings, 'ORDERS_EXCLUDED_APPS', ( + 'orders', + 'admin', + 'contenttypes', + 'auth', + 'migrations', + 'sessions', + 'orchestration', + 'bills', + # Do not put services here (plans) +)) diff --git a/orchestra/apps/resources/models.py b/orchestra/apps/resources/models.py index 7967ac14..384a79d3 100644 --- a/orchestra/apps/resources/models.py +++ b/orchestra/apps/resources/models.py @@ -177,8 +177,7 @@ def create_resource_relation(): data = self.obj.resource_set.get(resource__name=attr) except ResourceData.DoesNotExist: model = self.obj._meta.model_name - resource = Resource.objects.get(content_type__model=model, - name=attr, is_active=True) + resource = Resource.objects.get(content_type__model=model, name=attr, is_active=True) data = ResourceData(content_object=self.obj, resource=resource) return data diff --git a/orchestra/apps/services/tests/functional_tests/__init__.py b/orchestra/apps/services/tests/functional_tests/__init__.py index feac27d8..2196813d 100644 --- a/orchestra/apps/services/tests/functional_tests/__init__.py +++ b/orchestra/apps/services/tests/functional_tests/__init__.py @@ -1,16 +1,9 @@ from orchestra.apps.accounts.models import Account -from orchestra.apps.users.models import User from orchestra.utils.tests import BaseTestCase, random_ascii +# TODO remove this shit class BaseBillingTest(BaseTestCase): - def create_account(self): - account = Account.objects.create() - username = 'account_%s' % random_ascii(5) - user = User.objects.create_user(username=username, account=account) - account.user = user - account.save() - return account - + pass # TODO web disk diff --git a/orchestra/apps/services/tests/functional_tests/test_ftp.py b/orchestra/apps/services/tests/functional_tests/test_ftp.py index 398d3e14..b44789c2 100644 --- a/orchestra/apps/services/tests/functional_tests/test_ftp.py +++ b/orchestra/apps/services/tests/functional_tests/test_ftp.py @@ -5,7 +5,7 @@ from dateutil.relativedelta import relativedelta from django.contrib.contenttypes.models import ContentType from django.utils import timezone -from orchestra.apps.users.models import User +from orchestra.apps.systemusers.models import SystemUser from orchestra.utils.tests import random_ascii from ... import settings @@ -18,8 +18,8 @@ class FTPBillingTest(BaseBillingTest): def create_ftp_service(self): return Service.objects.create( description="FTP Account", - content_type=ContentType.objects.get_for_model(User), - match='not user.is_main and user.has_posix()', + content_type=ContentType.objects.get_for_model(SystemUser), + match='not systemuser.is_main', billing_period=Service.ANUAL, billing_point=Service.FIXED_DATE, is_fee=False, @@ -36,10 +36,7 @@ class FTPBillingTest(BaseBillingTest): if not account: account = self.create_account() username = '%s_ftp' % random_ascii(10) - user = User.objects.create_user(username=username, account=account) - POSIX = user._meta.get_field_by_name('posix')[0].model - POSIX.objects.create(user=user) - return user + return SystemUser.objects.create_user(username, account=account) def test_ftp_account_1_year_fiexed(self): service = self.create_ftp_service() diff --git a/orchestra/apps/services/tests/functional_tests/test_traffic.py b/orchestra/apps/services/tests/functional_tests/test_traffic.py index 0a2df271..d6e5f0a4 100644 --- a/orchestra/apps/services/tests/functional_tests/test_traffic.py +++ b/orchestra/apps/services/tests/functional_tests/test_traffic.py @@ -13,7 +13,7 @@ from . import BaseBillingTest class BaseTrafficBillingTest(BaseBillingTest): - METRIC = 'account.resources.traffic.used' + TRAFFIC_METRIC = 'account.resources.traffic.used' def create_traffic_service(self): service = Service.objects.create( @@ -23,7 +23,7 @@ class BaseTrafficBillingTest(BaseBillingTest): billing_period=Service.MONTHLY, billing_point=Service.FIXED_DATE, is_fee=False, - metric=self.METRIC, + metric=self.TRAFFIC_METRIC, pricing_period=Service.BILLING_PERIOD, rate_algorithm=Service.STEP_PRICE, on_cancel=Service.NOTHING, @@ -50,7 +50,7 @@ class BaseTrafficBillingTest(BaseBillingTest): return self.resource def report_traffic(self, account, value): - MonitorData.objects.create(monitor='FTPTraffic', content_object=account.user, value=value) + MonitorData.objects.create(monitor='FTPTraffic', content_object=account.systemusers.get(), value=value) data = ResourceData.get_or_create(account, self.resource) data.update() @@ -90,7 +90,7 @@ class TrafficBillingTest(BaseTrafficBillingTest): class TrafficPrepayBillingTest(BaseTrafficBillingTest): - METRIC = ("max(" + TRAFFIC_METRIC = ("max(" "(account.resources.traffic.used or 0) - " "getattr(account.miscellaneous.filter(is_active=True, service__name='traffic prepay').last(), 'amount', 0)" ", 0)" @@ -126,8 +126,8 @@ class TrafficPrepayBillingTest(BaseTrafficBillingTest): def test_traffic_prepay(self): self.create_traffic_service() self.create_prepay_service() - account = self.create_account() self.create_traffic_resource() + account = self.create_account() now = timezone.now() self.create_prepay(10, account=account) diff --git a/orchestra/apps/services/tests/test_handler.py b/orchestra/apps/services/tests/test_handler.py index 8628e1b8..af3207ed 100644 --- a/orchestra/apps/services/tests/test_handler.py +++ b/orchestra/apps/services/tests/test_handler.py @@ -5,8 +5,8 @@ from django.contrib.contenttypes.models import ContentType from django.utils import timezone from orchestra.apps.accounts.models import Account -from orchestra.apps.users.models import User -from orchestra.utils.tests import BaseTestCase +from orchestra.apps.systemusers.models import SystemUser +from orchestra.utils.tests import BaseTestCase, random_ascii from .. import helpers from ..models import Service, Plan @@ -28,22 +28,14 @@ class Order(object): class HandlerTests(BaseTestCase): DEPENDENCIES = ( 'orchestra.apps.orders', - 'orchestra.apps.users', - 'orchestra.apps.users.roles.posix', + 'orchestra.apps.systemusers', ) - def create_account(self): - account = Account.objects.create() - user = User.objects.create_user(username='rata_palida', account=account) - account.user = user - account.save() - return account - def create_ftp_service(self): service = Service.objects.create( description="FTP Account", - content_type=ContentType.objects.get_for_model(User), - match='not user.is_main and user.has_posix()', + content_type=ContentType.objects.get_for_model(SystemUser), + match='not systemuser.is_main', billing_period=Service.ANUAL, billing_point=Service.FIXED_DATE, is_fee=False, diff --git a/orchestra/apps/systemusers/models.py b/orchestra/apps/systemusers/models.py index 100e2ed9..ba31fef9 100644 --- a/orchestra/apps/systemusers/models.py +++ b/orchestra/apps/systemusers/models.py @@ -15,6 +15,7 @@ class SystemUserQuerySet(models.QuerySet): user = super(SystemUserQuerySet, self).create(username=username, **kwargs) user.set_password(password) user.save(update_fields=['password']) + return user class SystemUser(models.Model): diff --git a/orchestra/apps/systemusers/tests/functional_tests/tests.py b/orchestra/apps/systemusers/tests/functional_tests/tests.py index 0d4ac1d0..6d76e90c 100644 --- a/orchestra/apps/systemusers/tests/functional_tests/tests.py +++ b/orchestra/apps/systemusers/tests/functional_tests/tests.py @@ -2,6 +2,7 @@ from functools import partial from django.conf import settings from django.core.urlresolvers import reverse +from selenium.webdriver.support.select import Select from orchestra.apps.accounts.models import Account from orchestra.apps.orchestration.models import Server, Route @@ -9,6 +10,7 @@ from orchestra.utils.system import run from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii from ... import backends +from ...models import SystemUser r = partial(run, silent=True, display=False) @@ -40,6 +42,9 @@ class SystemUserMixin(object): def update(self): raise NotImplementedError + def disable(self): + raise NotImplementedError + def test_create_systemuser(self): username = '%s_systemuser' % random_ascii(10) password = '@!?%spppP001' % random_ascii(5) @@ -55,7 +60,16 @@ class SystemUserMixin(object): self.assertEqual(0, r("id %s" % username).return_code) self.delete(username) self.assertEqual(1, r("id %s" % username, error_codes=[0,1]).return_code) + + def test_update_systemuser(self): + pass + # TODO + + def test_disable_systemuser(self): + pass + # TODO +# TODO test with ftp and ssh clients? class RESTSystemUserMixin(SystemUserMixin): def setUp(self): @@ -73,16 +87,38 @@ class RESTSystemUserMixin(SystemUserMixin): pass -# TODO class AdminSystemUserMixin(SystemUserMixin): def setUp(self): super(AdminSystemUserMixin, self).setUp() self.admin_login() def add(self, username, password): - pass + url = self.live_server_url + reverse('admin:systemusers_systemuser_add') + self.selenium.get(url) + + username_field = self.selenium.find_element_by_id('id_username') + username_field.send_keys(username) + + password_field = self.selenium.find_element_by_id('id_password1') + password_field.send_keys(password) + password_field = self.selenium.find_element_by_id('id_password2') + password_field.send_keys(password) + + account_input = self.selenium.find_element_by_id('id_account') + account_select = Select(account_input) + account_select.select_by_value(str(self.account.pk)) + + username_field.submit() + self.assertNotEqual(url, self.selenium.current_url) def delete(self, username): + user = SystemUser.objects.get(username=username) + url = self.live_server_url + reverse('admin:systemusers_systemuser_delete', args=(user.pk,)) + self.selenium.get(url) + confirmation = self.selenium.find_element_by_name('post') + confirmation.submit() + + def disable(self, username): pass def update(self): diff --git a/orchestra/utils/tests.py b/orchestra/utils/tests.py index e4b58f86..f0093339 100644 --- a/orchestra/utils/tests.py +++ b/orchestra/utils/tests.py @@ -12,6 +12,10 @@ from xvfbwrapper import Xvfb from orchestra.apps.accounts.models import Account +def random_ascii(length): + return ''.join([random.choice(string.hexdigits) for i in range(0, length)]).lower() + + class AppDependencyMixin(object): DEPENDENCIES = () @@ -49,13 +53,15 @@ class AppDependencyMixin(object): class BaseTestCase(TestCase, AppDependencyMixin): - pass + def create_account(self, superuser=False): + username = '%s_superaccount' % random_ascii(5) + password = 'orchestra' + if superuser: + return Account.objects.create_superuser(username, password=password, email='orchestra@orchestra.org') + return Account.objects.create_user(username, password=password, email='orchestra@orchestra.org') class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase): - ACCOUNT_USERNAME = 'orchestra' - ACCOUNT_PASSWORD = 'orchestra' - @classmethod def setUpClass(cls): cls.vdisplay = Xvfb() @@ -70,11 +76,11 @@ class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase): super(BaseLiveServerTestCase, cls).tearDownClass() def create_account(self, superuser=False): + username = '%s_superaccount' % random_ascii(5) + password = 'orchestra' if superuser: - return Account.objects.create_superuser(self.ACCOUNT_USERNAME, - password=self.ACCOUNT_PASSWORD, email='orchestra@orchestra.org') - return Account.objects.create_user(self.ACCOUNT_USERNAME, - password=self.ACCOUNT_PASSWORD, email='orchestra@orchestra.org') + return Account.objects.create_superuser(username, password=password, email='orchestra@orchestra.org') + return Account.objects.create_user(username, password=password, email='orchestra@orchestra.org') def setUp(self): super(BaseLiveServerTestCase, self).setUp() @@ -96,7 +102,3 @@ class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase): def rest_login(self): self.rest.login(username=self.ACCOUNT_USERNAME, password=self.ACCOUNT_PASSWORD) - - -def random_ascii(length): - return ''.join([random.choice(string.hexdigits) for i in range(0, length)]).lower()