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)
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'])