Example #1
0
    def update_price_for_resource(cls, resource, delete=False):
        try:
            cost_tracking_backend = CostTrackingRegister.get_resource_backend(
                resource)
            monthly_cost = float(
                cost_tracking_backend.get_monthly_cost_estimate(resource))
        except ServiceBackendNotImplemented:
            return
        except ServiceBackendError as e:
            logger.error("Failed to get cost estimate for resource %s: %s",
                         resource, e)
        except Exception as e:
            logger.exception("Failed to get cost estimate for resource %s: %s",
                             resource, e)
        else:
            logger.info("Update cost estimate for resource %s: %s", resource,
                        monthly_cost)

            now = timezone.now()
            created = resource.created

            days_in_month = calendar.monthrange(created.year, created.month)[1]
            month_start = created.replace(day=1, hour=0, minute=0, second=0)
            month_end = month_start + timezone.timedelta(days=days_in_month)
            seconds_in_month = (month_end - month_start).total_seconds()
            seconds_of_work = (month_end - created).total_seconds()

            creation_month_cost = round(
                monthly_cost * seconds_of_work / seconds_in_month, 2)
            update = functools.partial(cls.update_price_for_scope, resource)

            if delete:
                monthly_cost *= -1
                creation_month_cost *= -1

            if created.month == now.month and created.year == now.year:
                # update only current month estimate
                update(now.month, now.year, creation_month_cost)
            else:
                # update current month estimate
                update(now.month, now.year, monthly_cost)
                # update first month estimate
                update(created.month,
                       created.year,
                       creation_month_cost,
                       update_if_exists=False)
                # update price estimate for previous months if it does not exist:
                date = now - relativedelta(months=+1)
                while not (date.month == created.month
                           and date.year == created.year):
                    update(date.month,
                           date.year,
                           monthly_cost,
                           update_if_exists=False)
                    date -= relativedelta(months=+1)
Example #2
0
def update_projected_estimate(customer_uuid=None, resource_uuid=None):

    if customer_uuid and resource_uuid:
        raise RuntimeError("Either customer_uuid or resource_uuid could be supplied, both received.")

    def get_resource_creation_month_cost(resource, monthly_cost):
        month_start = resource.created.replace(day=1, hour=0, minute=0, second=0)
        month_end = month_start.replace(month=month_start.month+1)
        seconds_in_month = (month_end - month_start).total_seconds()
        seconds_of_work = (month_end - resource.created).total_seconds()
        return round(monthly_cost * seconds_of_work / seconds_in_month, 2)

    for model in Resource.get_all_models():
        queryset = model.objects.exclude(state=model.States.ERRED)
        if customer_uuid:
            queryset = queryset.filter(customer__uuid=customer_uuid)
        elif resource_uuid:
            queryset = queryset.filter(uuid=resource_uuid)

        for instance in queryset.iterator():
            try:
                cost_tracking_backend = CostTrackingRegister.get_resource_backend(instance)
                if not cost_tracking_backend:
                    continue
                monthly_cost = float(cost_tracking_backend.get_monthly_cost_estimate(instance))
            except ServiceBackendNotImplemented as e:
                continue
            except ServiceBackendError as e:
                logger.error("Failed to get cost estimate for resource %s: %s", instance, e)
            except Exception as e:
                logger.exception("Failed to get cost estimate for resource %s: %s", instance, e)
            else:
                logger.info("Update cost estimate for resource %s: %s", instance, monthly_cost)

                creation_month_cost = get_resource_creation_month_cost(instance, monthly_cost)

                now = timezone.now()
                created = instance.created
                if created.month == now.month and created.year == now.year:
                    # update only current month estimate
                    PriceEstimate.update_price_for_scope(instance, now.month, now.year, creation_month_cost)
                else:
                    # update current month estimate
                    PriceEstimate.update_price_for_scope(instance, now.month, now.year, monthly_cost)
                    # update first month estimate
                    PriceEstimate.update_price_for_scope(instance, created.month, created.year, creation_month_cost,
                                                         update_if_exists=False)
                    # update price estimate for previous months if it does not exist:
                    date = now - relativedelta(months=+1)
                    while not (date.month == created.month and date.year == created.year):
                        PriceEstimate.update_price_for_scope(instance, date.month, date.year, monthly_cost,
                                                             update_if_exists=False)
                        date -= relativedelta(months=+1)
def update_today_usage_of_resource(resource_str):
    # XXX: this method does ignores cases then VM was offline or online for small periods of time.
    # It could to be rewritten if more accurate calculation will be needed
    with transaction.atomic():
        resource = next(Resource.from_string(resource_str))
        cs_backend = CostTrackingRegister.get_resource_backend(resource)
        used_items = cs_backend.get_used_items(resource)

        if not resource.billing_backend_id:
            logger.warning(
                "Can't update usage for resource %s which is not subscribed to backend",
                resource_str)
            return

        numerical = ['storage', 'users'
                     ]  # XXX: use consistent method for usage calculation
        content_type = ContentType.objects.get_for_model(resource)

        units = {(item.item_type,
                  None if item.item_type in numerical else item.key):
                 item.units
                 for item in DefaultPriceListItem.objects.filter(
                     resource_content_type=content_type)}

        now = timezone.now()
        last_update_time = resource.last_usage_update_time or resource.created
        minutes_from_last_usage_update = (
            now - last_update_time).total_seconds() / 60

        usage = {}
        for item_type, key, val in used_items:
            if val:
                try:
                    unit = units[item_type,
                                 None if item_type in numerical else key]
                    usage_per_min = int(
                        round(val * minutes_from_last_usage_update))
                    if usage_per_min:
                        usage[unit] = usage_per_min
                except KeyError:
                    logger.error("Can't find price for usage item %s:%s", key,
                                 val)

        kb_backend = KillBillBackend()
        kb_backend.add_usage_data(resource, usage)

        resource.last_usage_update_time = timezone.now()
        resource.save(update_fields=['last_usage_update_time'])