Example #1
0
def recalculate_estimate(recalculate_total=False):
    """ Recalculate price of consumables that were used by resource until now.

        Regular task. It is too expensive to calculate consumed price on each
        request, so we store cached price each hour.
        If recalculate_total is True - task also recalculates total estimate
        for current month.
    """
    # Celery does not import server.urls and does not discover cost tracking modules.
    # So they should be discovered implicitly.
    CostTrackingRegister.autodiscover()
    # Step 1. Recalculate resources estimates.
    for resource_model in CostTrackingRegister.registered_resources:
        for resource in resource_model.objects.all():
            _update_resource_consumed(resource, recalculate_total=recalculate_total)
    # Step 2. Move from down to top and recalculate consumed estimate for each
    #         object based on its children.
    ancestors_models = [
        m
        for m in models.PriceEstimate.get_estimated_models()
        if not issubclass(m, structure_models.ResourceMixin)
    ]
    for model in ancestors_models:
        for ancestor in model.objects.all():
            _update_ancestor_consumed(ancestor)
Example #2
0
    def reinit_configurations(self, request):
        """ Re-initialize configuration for resource if it has been changed.

            This method should be called if resource consumption strategy was changed.
        """
        now = timezone.now()

        # Step 1. Collect all resources with changed configuration.
        changed_resources = []
        for resource_model in CostTrackingRegister.registered_resources:
            for resource in resource_model.objects.all():
                try:
                    pe = models.PriceEstimate.objects.get(
                        scope=resource, month=now.month, year=now.year
                    )
                except models.PriceEstimate.DoesNotExist:
                    changed_resources.append(resource)
                else:
                    new_configuration = CostTrackingRegister.get_configuration(resource)
                    if new_configuration != pe.consumption_details.configuration:
                        changed_resources.append(resource)

        # Step 2. Re-init configuration and recalculate estimate for changed resources.
        for resource in changed_resources:
            models.PriceEstimate.update_resource_estimate(
                resource, CostTrackingRegister.get_configuration(resource)
            )

        message = _('Configuration was reinitialized for %(count)s resources') % {
            'count': len(changed_resources)
        }
        self.message_user(request, message)

        return redirect(reverse('admin:cost_tracking_defaultpricelistitem_changelist'))
Example #3
0
 def setUp(self):
     CostTrackingRegister.register_strategy(
         factories.TestNewInstanceCostTrackingStrategy)
     resource_content_type = ContentType.objects.get_for_model(
         TestNewInstance)
     self.price_list_item = models.DefaultPriceListItem.objects.create(
         item_type='storage',
         key='1 MB',
         value=0.5,
         resource_content_type=resource_content_type)
Example #4
0
    def delete_not_registered(self, request):
        deleted_items_names = []

        for price_list_item in models.DefaultPriceListItem.objects.all():
            try:
                resource_class = price_list_item.resource_content_type.model_class(
                )
                consumable_items = CostTrackingRegister.get_consumable_items(
                    resource_class)
                next(item for item in consumable_items
                     if item.key == price_list_item.key
                     and item.item_type == price_list_item.item_type)
            except (ResourceNotRegisteredError, StopIteration):
                deleted_items_names.append(price_list_item.name)
                price_list_item.delete()

        if deleted_items_names:
            message = ungettext(
                _('Price item was deleted: %s.') % deleted_items_names[0],
                _('Price items were deleted: %s.') %
                ', '.join(item for item in deleted_items_names),
                len(deleted_items_names))
            self.message_user(request, message)
        else:
            self.message_user(
                request,
                _('Nothing to delete. All default price items are registered.')
            )

        return redirect(
            reverse('admin:cost_tracking_defaultpricelistitem_changelist'))
Example #5
0
 def setUp(self):
     resource_content_type = ContentType.objects.get_for_model(
         TestNewInstance)
     self.price_list_item = models.DefaultPriceListItem.objects.create(
         item_type='storage',
         key='1 MB',
         resource_content_type=resource_content_type,
         value=2)
     CostTrackingRegister.register_strategy(
         factories.TestNewInstanceCostTrackingStrategy)
     self.start_time = datetime.datetime(2016, 8, 8, 11, 0)
     with freeze_time(self.start_time):
         self.resource = structure_factories.TestNewInstanceFactory(
             disk=20 * 1024)
     self.spl = self.resource.service_project_link
     self.project = self.spl.project
     self.customer = self.project.customer
     self.service = self.spl.service
def resource_quota_update(sender, instance, **kwargs):
    """ Update resource consumption details and price estimate if its configuration has changed """
    quota = instance
    resource = quota.scope
    try:
        new_configuration = CostTrackingRegister.get_configuration(resource)
    except ResourceNotRegisteredError:
        return
    models.PriceEstimate.update_resource_estimate(
        resource, new_configuration, raise_exception=not _is_in_celery_task())
 def handle(self, *args, **options):
     today = timezone.now()
     with transaction.atomic():
         # Delete current month price estimates
         models.PriceEstimate.objects.filter(month=today.month, year=today.year).delete()
         # Create new estimates for resources and ancestors
         for resource_model in CostTrackingRegister.registered_resources:
             for resource in resource_model.objects.all():
                 configuration = CostTrackingRegister.get_configuration(resource)
                 date = max(core_utils.month_start(today), resource.created)
                 models.PriceEstimate.create_historical(resource, configuration, date)
         # recalculate consumed estimate
         tasks.recalculate_estimate()
def resource_update(sender, instance, created=False, **kwargs):
    """ Update resource consumption details and price estimate if its configuration has changed.
        Create estimates for previous months if resource was created not in current month.
    """
    resource = instance
    try:
        new_configuration = CostTrackingRegister.get_configuration(resource)
    except ResourceNotRegisteredError:
        return
    models.PriceEstimate.update_resource_estimate(
        resource, new_configuration, raise_exception=not _is_in_celery_task())
    # Try to create historical price estimates
    if created:
        _create_historical_estimates(resource, new_configuration)
Example #9
0
from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView

from waldur_core.core import WaldurExtension
from waldur_core.core import views as core_views
from waldur_core.core.routers import SortedDefaultRouter as DefaultRouter
from waldur_core.core.schemas import WaldurSchemaView
from waldur_core.cost_tracking import urls as cost_tracking_urls, CostTrackingRegister
from waldur_core.logging import urls as logging_urls
from waldur_core.monitoring import urls as monitoring_urls
from waldur_core.quotas import urls as quotas_urls
from waldur_core.structure import urls as structure_urls
from waldur_core.users import urls as users_urls

CostTrackingRegister.autodiscover()

router = DefaultRouter()
cost_tracking_urls.register_in(router)
logging_urls.register_in(router)
monitoring_urls.register_in(router)
quotas_urls.register_in(router)
structure_urls.register_in(router)
users_urls.register_in(router)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^admintools/', include('admin_tools.urls')),
]

if settings.WALDUR_CORE.get('EXTENSIONS_AUTOREGISTER'):
from . import models


class InstanceStrategy(CostTrackingStrategy):
    resource_class = models.Instance

    class Types:
        FLAVOR = 'flavor'

    @classmethod
    def get_consumable_items(cls):
        return [
            ConsumableItem(
                item_type=cls.Types.FLAVOR,
                key=size.backend_id,
                default_price=size.price,
            ) for size in models.Size.objects.all()
        ]

    @classmethod
    def get_configuration(cls, instance):
        consumables = {}
        if instance.state != models.Instance.States.ERRED:
            consumables[ConsumableItem(item_type=cls.Types.FLAVOR,
                                       key=instance.size_backend_id)] = 1
        return consumables


CostTrackingRegister.register_strategy(InstanceStrategy)
Example #11
0
from waldur_core.cost_tracking import CostTrackingRegister, CostTrackingStrategy, ConsumableItem

from . import models


class DropletStrategy(CostTrackingStrategy):
    resource_class = models.Droplet

    class Types:
        FLAVOR = 'flavor'

    @classmethod
    def get_consumable_items(cls):
        return [ConsumableItem(item_type=cls.Types.FLAVOR, key=size.name, default_price=size.price)
                for size in models.Size.objects.all()]

    @classmethod
    def get_configuration(cls, droplet):
        consumables = {}
        if droplet.state != models.Droplet.States.ERRED and droplet.size_name:
            consumables[ConsumableItem(item_type=cls.Types.FLAVOR, key=droplet.size_name)] = 1
        return consumables


CostTrackingRegister.register_strategy(DropletStrategy)
class TenantStrategy(CostTrackingStrategy):
    resource_class = openstack_models.Tenant

    @classmethod
    def get_consumable_items(cls):
        for package_template in models.PackageTemplate.objects.all():
            yield utils.get_consumable_item(package_template)

    @classmethod
    def get_configuration(cls, tenant):
        configuration = {}
        if tenant.state != tenant.States.ERRED:
            if 'package_name' not in tenant.extra_configuration:
                logger.debug(
                    'Package name is not defined in configuration of tenant %s, (PK: %s)',
                    tenant.name,
                    tenant.pk,
                )
            else:
                package_name = tenant.extra_configuration['package_name']
                configuration = {
                    ConsumableItem(item_type=utils.Types.PACKAGE_TEMPLATE,
                                   key=package_name):
                    1,
                }
        return configuration


CostTrackingRegister.register_strategy(TenantStrategy)
Example #13
0
 def setUp(self):
     CostTrackingRegister.register_strategy(
         factories.TestNewInstanceCostTrackingStrategy)
Example #14
0
    @classmethod
    def get_consumable_items(cls):
        for flavor_name in set(models.Flavor.objects.all().values_list(
                'name', flat=True)):
            yield utils.get_consumable_item(flavor_name)

    @classmethod
    def get_configuration(cls, instance):
        consumables = {}
        if instance.state != models.Instance.States.ERRED:
            consumables[ConsumableItem(item_type=cls.Types.FLAVOR,
                                       key=instance.flavor_name)] = 1
        return consumables


CostTrackingRegister.register_strategy(InstanceStrategy)


class VolumeStrategy(CostTrackingStrategy):
    resource_class = models.Volume

    class Types:
        STORAGE = PriceItemTypes.STORAGE

    class Keys:
        STORAGE = '1 GB'

    @classmethod
    def get_consumable_items(cls):
        return [
            ConsumableItem(
from waldur_core.cost_tracking import CostTrackingStrategy, ConsumableItem, CostTrackingRegister

from . import models, backend


class AzureCostTrackingStrategy(CostTrackingStrategy):
    resource_class = models.VirtualMachine

    class Types(object):
        FLAVOR = 'flavor'

    @classmethod
    def get_consumable_items(cls):
        return [
            ConsumableItem(item_type=cls.Types.FLAVOR,
                           key=size.name,
                           default_price=size.price)
            for size in backend.SizeQueryset()
        ]

    @classmethod
    def get_configuration(cls, virtual_machine):
        consumables = {}
        if virtual_machine.state != models.VirtualMachine.States.ERRED and virtual_machine.image_name:
            consumables[ConsumableItem(item_type=cls.Types.FLAVOR,
                                       key=virtual_machine.image_name)] = 1
        return consumables


CostTrackingRegister.register_strategy(AzureCostTrackingStrategy)