Replace float with decimal
Floating point calculations are inaccurate, often lose precision even when storing into database. So we replace float with decimal. Change-Id: I3782264015e0b0e260f0c046dc7c3ea94eab0b52
This commit is contained in:
parent
5025328bc1
commit
ff26ed98e7
@ -15,7 +15,9 @@ Utilities module.
|
||||
'''
|
||||
|
||||
import datetime
|
||||
import decimal
|
||||
import random
|
||||
import six
|
||||
import string
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
@ -27,6 +29,7 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from bilean.common import exception
|
||||
from bilean.common.i18n import _
|
||||
@ -156,3 +159,38 @@ def format_time(value):
|
||||
value = value.replace(microsecond=0)
|
||||
value = value.isoformat()
|
||||
return value
|
||||
|
||||
|
||||
def format_time_to_seconds(t):
|
||||
"""Format datetime to seconds from 1970-01-01 00:00:00 UTC."""
|
||||
epoch = datetime.datetime.utcfromtimestamp(0)
|
||||
if isinstance(t, datetime.datetime):
|
||||
return (t - epoch).total_seconds()
|
||||
if isinstance(t, six.string_types):
|
||||
dt = timeutils.parse_strtime(t)
|
||||
return (dt - epoch).total_seconds()
|
||||
return t
|
||||
|
||||
|
||||
def make_decimal(value):
|
||||
"""Format float to decimal."""
|
||||
if isinstance(value, decimal.Decimal):
|
||||
return value
|
||||
if isinstance(value, float):
|
||||
return decimal.Decimal.from_float(value)
|
||||
return decimal.Decimal(str(value))
|
||||
|
||||
|
||||
def format_decimal(value, num=8):
|
||||
"""Format decimal and keep num decimals."""
|
||||
if not isinstance(value, decimal.Decimal):
|
||||
value = make_decimal(value)
|
||||
dec = "0.%s" % ('0' * num)
|
||||
return value.quantize(decimal.Decimal(dec))
|
||||
|
||||
|
||||
def dec2str(value):
|
||||
"""Decimal to str and keep 2 decimals."""
|
||||
if not isinstance(value, decimal.Decimal):
|
||||
value = make_decimal(value)
|
||||
return str(value.quantize(decimal.Decimal('0.00')))
|
||||
|
@ -29,10 +29,10 @@ def upgrade(migrate_engine):
|
||||
sqlalchemy.String(36),
|
||||
sqlalchemy.ForeignKey('policy.id'),
|
||||
nullable=True),
|
||||
sqlalchemy.Column('balance', sqlalchemy.Float),
|
||||
sqlalchemy.Column('rate', sqlalchemy.Float),
|
||||
sqlalchemy.Column('balance', sqlalchemy.Numeric(20, 8)),
|
||||
sqlalchemy.Column('rate', sqlalchemy.Numeric(20, 8)),
|
||||
sqlalchemy.Column('credit', sqlalchemy.Integer),
|
||||
sqlalchemy.Column('last_bill', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('last_bill', sqlalchemy.Numeric(24, 8)),
|
||||
sqlalchemy.Column('status', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('status_reason', sqlalchemy.Text),
|
||||
sqlalchemy.Column('created_at', sqlalchemy.DateTime),
|
||||
@ -83,9 +83,9 @@ def upgrade(migrate_engine):
|
||||
sqlalchemy.Column('rule_id', sqlalchemy.String(36), nullable=False),
|
||||
sqlalchemy.Column('resource_type', sqlalchemy.String(36),
|
||||
nullable=False),
|
||||
sqlalchemy.Column('last_bill', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('last_bill', sqlalchemy.Numeric(24, 8)),
|
||||
sqlalchemy.Column('properties', types.Dict),
|
||||
sqlalchemy.Column('rate', sqlalchemy.Float, nullable=False),
|
||||
sqlalchemy.Column('rate', sqlalchemy.Numeric(20, 8), nullable=False),
|
||||
sqlalchemy.Column('created_at', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('deleted_at', sqlalchemy.DateTime),
|
||||
@ -118,10 +118,10 @@ def upgrade(migrate_engine):
|
||||
sqlalchemy.Column('user_id', sqlalchemy.String(36)),
|
||||
sqlalchemy.Column('resource_id', sqlalchemy.String(36)),
|
||||
sqlalchemy.Column('resource_type', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('start_time', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('end_time', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('rate', sqlalchemy.Float),
|
||||
sqlalchemy.Column('cost', sqlalchemy.Float),
|
||||
sqlalchemy.Column('start_time', sqlalchemy.Numeric(24, 8)),
|
||||
sqlalchemy.Column('end_time', sqlalchemy.Numeric(24, 8)),
|
||||
sqlalchemy.Column('rate', sqlalchemy.Numeric(20, 8)),
|
||||
sqlalchemy.Column('cost', sqlalchemy.Numeric(20, 8)),
|
||||
sqlalchemy.Column('meta_data', types.Dict),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8'
|
||||
@ -134,7 +134,7 @@ def upgrade(migrate_engine):
|
||||
sqlalchemy.Column('user_id', sqlalchemy.String(36)),
|
||||
sqlalchemy.Column('type', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('timestamp', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('value', sqlalchemy.Float),
|
||||
sqlalchemy.Column('value', sqlalchemy.Numeric(20, 8)),
|
||||
sqlalchemy.Column('meta_data', types.Dict),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8'
|
||||
@ -150,8 +150,8 @@ def upgrade(migrate_engine):
|
||||
sqlalchemy.Column('action', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('cause', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('owner', sqlalchemy.String(36)),
|
||||
sqlalchemy.Column('start_time', sqlalchemy.Float(precision='24,8')),
|
||||
sqlalchemy.Column('end_time', sqlalchemy.Float(precision='24,8')),
|
||||
sqlalchemy.Column('start_time', sqlalchemy.Numeric(24, 8)),
|
||||
sqlalchemy.Column('end_time', sqlalchemy.Numeric(24, 8)),
|
||||
sqlalchemy.Column('timeout', sqlalchemy.Integer),
|
||||
sqlalchemy.Column('inputs', types.Dict),
|
||||
sqlalchemy.Column('outputs', types.Dict),
|
||||
|
@ -102,11 +102,10 @@ class User(BASE, BileanBase, SoftDelete, StateAware, models.TimestampMixin):
|
||||
sqlalchemy.String(36),
|
||||
sqlalchemy.ForeignKey('policy.id'),
|
||||
nullable=True)
|
||||
balance = sqlalchemy.Column(sqlalchemy.Float, default=0.0)
|
||||
rate = sqlalchemy.Column(sqlalchemy.Float, default=0.0)
|
||||
balance = sqlalchemy.Column(sqlalchemy.Numeric(20, 8), default=0.0)
|
||||
rate = sqlalchemy.Column(sqlalchemy.Numeric(20, 8), default=0.0)
|
||||
credit = sqlalchemy.Column(sqlalchemy.Integer, default=0)
|
||||
last_bill = sqlalchemy.Column(
|
||||
sqlalchemy.DateTime, default=timeutils.utcnow())
|
||||
last_bill = sqlalchemy.Column(sqlalchemy.Numeric(24, 8))
|
||||
|
||||
|
||||
class Policy(BASE, BileanBase, SoftDelete, models.TimestampMixin):
|
||||
@ -146,8 +145,8 @@ class Resource(BASE, BileanBase, SoftDelete, models.TimestampMixin):
|
||||
rule_id = sqlalchemy.Column(sqlalchemy.String(36), nullable=True)
|
||||
user = relationship(User, backref=backref('resources'))
|
||||
resource_type = sqlalchemy.Column(sqlalchemy.String(36), nullable=False)
|
||||
rate = sqlalchemy.Column(sqlalchemy.Float, nullable=False)
|
||||
last_bill = sqlalchemy.Column(sqlalchemy.DateTime)
|
||||
rate = sqlalchemy.Column(sqlalchemy.Numeric(20, 8), nullable=False)
|
||||
last_bill = sqlalchemy.Column(sqlalchemy.Numeric(24, 8))
|
||||
properties = sqlalchemy.Column(types.Dict)
|
||||
|
||||
|
||||
@ -163,8 +162,8 @@ class Action(BASE, BileanBase, StateAware, models.TimestampMixin):
|
||||
action = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
cause = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
owner = sqlalchemy.Column(sqlalchemy.String(36))
|
||||
start_time = sqlalchemy.Column(sqlalchemy.Float(precision='24,8'))
|
||||
end_time = sqlalchemy.Column(sqlalchemy.Float(precision='24,8'))
|
||||
start_time = sqlalchemy.Column(sqlalchemy.Numeric(24, 8))
|
||||
end_time = sqlalchemy.Column(sqlalchemy.Numeric(24, 8))
|
||||
timeout = sqlalchemy.Column(sqlalchemy.Integer)
|
||||
inputs = sqlalchemy.Column(types.Dict)
|
||||
outputs = sqlalchemy.Column(types.Dict)
|
||||
@ -212,10 +211,10 @@ class Consumption(BASE, BileanBase):
|
||||
user_id = sqlalchemy.Column(sqlalchemy.String(36))
|
||||
resource_id = sqlalchemy.Column(sqlalchemy.String(36))
|
||||
resource_type = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
start_time = sqlalchemy.Column(sqlalchemy.DateTime)
|
||||
end_time = sqlalchemy.Column(sqlalchemy.DateTime)
|
||||
rate = sqlalchemy.Column(sqlalchemy.Float)
|
||||
cost = sqlalchemy.Column(sqlalchemy.Float)
|
||||
start_time = sqlalchemy.Column(sqlalchemy.Numeric(24, 8))
|
||||
end_time = sqlalchemy.Column(sqlalchemy.Numeric(24, 8))
|
||||
rate = sqlalchemy.Column(sqlalchemy.Numeric(20, 8))
|
||||
cost = sqlalchemy.Column(sqlalchemy.Numeric(20, 8))
|
||||
meta_data = sqlalchemy.Column(types.Dict)
|
||||
|
||||
|
||||
@ -229,7 +228,7 @@ class Recharge(BASE, BileanBase):
|
||||
user_id = sqlalchemy.Column(sqlalchemy.String(36))
|
||||
type = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
timestamp = sqlalchemy.Column(sqlalchemy.DateTime)
|
||||
value = sqlalchemy.Column(sqlalchemy.Float)
|
||||
value = sqlalchemy.Column(sqlalchemy.Numeric(20, 8))
|
||||
meta_data = sqlalchemy.Column(types.Dict)
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@ from bilean.common import context as req_context
|
||||
from bilean.common import exception
|
||||
from bilean.common.i18n import _
|
||||
from bilean.common.i18n import _LE
|
||||
from bilean.common import utils
|
||||
from bilean.db import api as db_api
|
||||
from bilean.engine import event as EVENT
|
||||
|
||||
@ -104,8 +105,8 @@ class Action(object):
|
||||
# working on the action. It also serves as a lock.
|
||||
self.owner = kwargs.get('owner', None)
|
||||
|
||||
self.start_time = kwargs.get('start_time', None)
|
||||
self.end_time = kwargs.get('end_time', None)
|
||||
self.start_time = utils.make_decimal(kwargs.get('start_time', 0))
|
||||
self.end_time = utils.make_decimal(kwargs.get('end_time', 0))
|
||||
|
||||
# Timeout is a placeholder in case some actions may linger too long
|
||||
self.timeout = kwargs.get('timeout', cfg.CONF.default_action_timeout)
|
||||
@ -141,8 +142,8 @@ class Action(object):
|
||||
'action': self.action,
|
||||
'cause': self.cause,
|
||||
'owner': self.owner,
|
||||
'start_time': self.start_time,
|
||||
'end_time': self.end_time,
|
||||
'start_time': utils.format_decimal(self.start_time),
|
||||
'end_time': utils.format_decimal(self.end_time),
|
||||
'timeout': self.timeout,
|
||||
'status': self.status,
|
||||
'status_reason': self.status_reason,
|
||||
@ -364,8 +365,8 @@ class Action(object):
|
||||
'target': self.target,
|
||||
'cause': self.cause,
|
||||
'owner': self.owner,
|
||||
'start_time': self.start_time,
|
||||
'end_time': self.end_time,
|
||||
'start_time': utils.dec2str(self.start_time),
|
||||
'end_time': utils.dec2str(self.end_time),
|
||||
'timeout': self.timeout,
|
||||
'status': self.status,
|
||||
'status_reason': self.status_reason,
|
||||
|
@ -30,10 +30,10 @@ class Consumption(object):
|
||||
self.resource_id = kwargs.get('resource_id')
|
||||
self.resource_type = kwargs.get('resource_type')
|
||||
|
||||
self.start_time = kwargs.get('start_time')
|
||||
self.end_time = kwargs.get('end_time')
|
||||
self.rate = kwargs.get('rate')
|
||||
self.cost = kwargs.get('cost')
|
||||
self.start_time = utils.make_decimal(kwargs.get('start_time', 0))
|
||||
self.end_time = utils.make_decimal(kwargs.get('end_time', 0))
|
||||
self.rate = utils.make_decimal(kwargs.get('rate', 0))
|
||||
self.cost = utils.make_decimal(kwargs.get('cost', 0))
|
||||
self.metadata = kwargs.get('metadata')
|
||||
|
||||
@classmethod
|
||||
@ -87,10 +87,10 @@ class Consumption(object):
|
||||
'user_id': self.user_id,
|
||||
'resource_id': self.resource_id,
|
||||
'resource_type': self.resource_type,
|
||||
'start_time': self.start_time,
|
||||
'end_time': self.end_time,
|
||||
'rate': self.rate,
|
||||
'cost': self.cost,
|
||||
'start_time': utils.format_decimal(self.start_time),
|
||||
'end_time': utils.format_decimal(self.end_time),
|
||||
'rate': utils.format_decimal(self.rate),
|
||||
'cost': utils.format_decimal(self.cost),
|
||||
'meta_data': self.metadata,
|
||||
}
|
||||
|
||||
@ -109,10 +109,10 @@ class Consumption(object):
|
||||
'user_id': self.user_id,
|
||||
'resource_id': self.resource_id,
|
||||
'resource_type': self.resource_type,
|
||||
'start_time': utils.format_time(self.start_time),
|
||||
'end_time': utils.format_time(self.end_time),
|
||||
'rate': self.rate,
|
||||
'cost': self.cost,
|
||||
'start_time': utils.dec2str(self.start_time),
|
||||
'end_time': utils.dec2str(self.end_time),
|
||||
'rate': utils.dec2str(self.rate),
|
||||
'cost': utils.dec2str(self.cost),
|
||||
'metadata': self.metadata,
|
||||
}
|
||||
return consumption
|
||||
|
@ -22,6 +22,7 @@ from taskflow.types import failure as ft
|
||||
|
||||
from bilean.common import exception
|
||||
from bilean.common.i18n import _LE
|
||||
from bilean.common import utils
|
||||
from bilean.engine import policy as policy_mod
|
||||
from bilean.engine import user as user_mod
|
||||
from bilean.plugins import base as plugin_base
|
||||
@ -77,7 +78,7 @@ class CreateResourceTask(task.Task):
|
||||
|
||||
# Update resource with rule_id and rate
|
||||
resource.rule_id = rule.id
|
||||
resource.rate = rule.get_price(resource)
|
||||
resource.rate = utils.make_decimal(rule.get_price(resource))
|
||||
resource.store(context)
|
||||
|
||||
def revert(self, context, resource, result, **kwargs):
|
||||
@ -96,7 +97,7 @@ class UpdateResourceTask(task.Task):
|
||||
old_rate = resource.rate
|
||||
resource.properties = values.get('properties')
|
||||
rule = plugin_base.Rule.load(context, rule_id=resource.rule_id)
|
||||
resource.rate = rule.get_price(resource)
|
||||
resource.rate = utils.make_decimal(rule.get_price(resource))
|
||||
resource.delta_rate = resource.rate - old_rate
|
||||
resource.store(context)
|
||||
|
||||
@ -177,8 +178,7 @@ class UpdateUserRateTask(task.Task):
|
||||
|
||||
def execute(self, context, user_obj, user_bak, resource, *args, **kwargs):
|
||||
user_obj.update_rate(context, resource.delta_rate,
|
||||
timestamp=resource.last_bill,
|
||||
delayed_cost=resource.delayed_cost)
|
||||
timestamp=resource.last_bill)
|
||||
|
||||
def revert(self, context, user_obj, user_bak, resource, result,
|
||||
*args, **kwargs):
|
||||
|
@ -12,6 +12,7 @@
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
import time
|
||||
|
||||
from bilean.common import exception
|
||||
from bilean.common.i18n import _
|
||||
@ -24,8 +25,8 @@ from bilean.plugins import base as plugin_base
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
|
||||
wallclock = time.time
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -38,16 +39,14 @@ class User(object):
|
||||
'INIT', 'FREE', 'ACTIVE', 'WARNING', 'FREEZE',
|
||||
)
|
||||
|
||||
ALLOW_DELAY_TIME = 10
|
||||
|
||||
def __init__(self, user_id, **kwargs):
|
||||
self.id = user_id
|
||||
self.name = kwargs.get('name')
|
||||
self.policy_id = kwargs.get('policy_id')
|
||||
self.balance = kwargs.get('balance', 0)
|
||||
self.rate = kwargs.get('rate', 0.0)
|
||||
self.balance = utils.make_decimal(kwargs.get('balance', 0))
|
||||
self.rate = utils.make_decimal(kwargs.get('rate', 0))
|
||||
self.credit = kwargs.get('credit', 0)
|
||||
self.last_bill = kwargs.get('last_bill')
|
||||
self.last_bill = utils.make_decimal(kwargs.get('last_bill', 0))
|
||||
|
||||
self.status = kwargs.get('status', self.INIT)
|
||||
self.status_reason = kwargs.get('status_reason', 'Init user')
|
||||
@ -65,10 +64,10 @@ class User(object):
|
||||
values = {
|
||||
'name': self.name,
|
||||
'policy_id': self.policy_id,
|
||||
'balance': self.balance,
|
||||
'rate': self.rate,
|
||||
'balance': utils.format_decimal(self.balance),
|
||||
'rate': utils.format_decimal(self.rate),
|
||||
'credit': self.credit,
|
||||
'last_bill': self.last_bill,
|
||||
'last_bill': utils.format_decimal(self.last_bill),
|
||||
'status': self.status,
|
||||
'status_reason': self.status_reason,
|
||||
'created_at': self.created_at,
|
||||
@ -156,7 +155,7 @@ class User(object):
|
||||
if not realtime:
|
||||
return u
|
||||
if u.rate > 0 and u.status != u.FREEZE:
|
||||
seconds = (timeutils.utcnow() - u.last_bill).total_seconds()
|
||||
seconds = utils.make_decimal(wallclock()) - u.last_bill
|
||||
u.balance -= u.rate * seconds
|
||||
return u
|
||||
|
||||
@ -194,10 +193,10 @@ class User(object):
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'policy_id': self.policy_id,
|
||||
'balance': self.balance,
|
||||
'rate': self.rate,
|
||||
'balance': utils.dec2str(self.balance),
|
||||
'rate': utils.dec2str(self.rate),
|
||||
'credit': self.credit,
|
||||
'last_bill': utils.format_time(self.last_bill),
|
||||
'last_bill': utils.dec2str(self.last_bill),
|
||||
'status': self.status,
|
||||
'status_reason': self.status_reason,
|
||||
'created_at': utils.format_time(self.created_at),
|
||||
@ -213,7 +212,7 @@ class User(object):
|
||||
self.status_reason = reason
|
||||
self.store(context)
|
||||
|
||||
def update_rate(self, context, delta_rate, timestamp=None, delayed_cost=0):
|
||||
def update_rate(self, context, delta_rate, timestamp=None):
|
||||
"""Update user's rate and update user status.
|
||||
|
||||
:param context: The request context.
|
||||
@ -223,18 +222,15 @@ class User(object):
|
||||
adjust balance by delayed_cost.
|
||||
"""
|
||||
|
||||
if delta_rate == 0 and delayed_cost == 0:
|
||||
return
|
||||
|
||||
# Settle account before update rate
|
||||
self._settle_account(context, timestamp=timestamp,
|
||||
delayed_cost=delayed_cost)
|
||||
self._settle_account(context, delta_rate=delta_rate,
|
||||
timestamp=timestamp)
|
||||
|
||||
old_rate = self.rate
|
||||
new_rate = old_rate + delta_rate
|
||||
if old_rate == 0 and new_rate > 0:
|
||||
# Set last_bill when status change to 'ACTIVE' from 'FREE'
|
||||
self.last_bill = timeutils.utcnow()
|
||||
self.last_bill = timestamp or wallclock()
|
||||
reason = _("Status change to 'ACTIVE' cause resource creation.")
|
||||
self.status = self.ACTIVE
|
||||
self.status_reason = reason
|
||||
@ -262,7 +258,7 @@ class User(object):
|
||||
param timestamp: Record when recharge action occurs.
|
||||
param metadata: Some other keyword.
|
||||
"""
|
||||
self.balance += value
|
||||
self.balance += utils.make_decimal(value)
|
||||
if self.status == self.INIT and self.balance > 0:
|
||||
self.status = self.FREE
|
||||
self.status_reason = "Recharged"
|
||||
@ -293,31 +289,31 @@ class User(object):
|
||||
'bilean.scheduler.cron_scheduler',
|
||||
group='scheduler')
|
||||
prior_notify_time = cfg.CONF.scheduler.prior_notify_time * 3600
|
||||
rest_usage = prior_notify_time * self.rate
|
||||
if self.balance > rest_usage:
|
||||
return False
|
||||
return True
|
||||
rest_usage = utils.make_decimal(prior_notify_time) * self.rate
|
||||
return self.balance < rest_usage
|
||||
|
||||
def do_delete(self, context):
|
||||
db_api.user_delete(context, self.id)
|
||||
return True
|
||||
|
||||
def _settle_account(self, context, timestamp=None, delayed_cost=0):
|
||||
if self.rate == 0 and delayed_cost == 0:
|
||||
def _settle_account(self, context, delta_rate=0, timestamp=None):
|
||||
if self.rate == 0:
|
||||
LOG.info(_LI("Ignore settlement action because user is in '%s' "
|
||||
"status."), self.status)
|
||||
return
|
||||
|
||||
# Calculate user's cost before last_bill and now
|
||||
cost = 0
|
||||
if self.rate > 0 and self.last_bill:
|
||||
timestamp = timestamp or timeutils.utcnow()
|
||||
total_seconds = (timestamp - self.last_bill).total_seconds()
|
||||
cost = self.rate * total_seconds
|
||||
# Calculate user's cost between last_bill and now
|
||||
now = utils.make_decimal(wallclock())
|
||||
delayed_cost = 0
|
||||
if delta_rate != 0:
|
||||
delayed_seconds = now - timestamp
|
||||
delayed_cost = delayed_seconds * utils.make_decimal(delta_rate)
|
||||
usage_seconds = now - self.last_bill
|
||||
cost = self.rate * usage_seconds
|
||||
total_cost = cost + delayed_cost
|
||||
|
||||
self.balance -= total_cost
|
||||
self.last_bill = timestamp
|
||||
self.last_bill = now
|
||||
|
||||
def settle_account(self, context, task=None):
|
||||
'''Settle account for user.'''
|
||||
|
@ -10,6 +10,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import time
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
@ -22,6 +24,7 @@ from bilean.db import api as db_api
|
||||
from bilean.engine import consumption as consumption_mod
|
||||
from bilean.engine import environment
|
||||
|
||||
wallclock = time.time
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -233,8 +236,6 @@ class Resource(object):
|
||||
something else.
|
||||
"""
|
||||
|
||||
ALLOW_DELAY_TIME = 10
|
||||
|
||||
def __new__(cls, id, user_id, res_type, properties, **kwargs):
|
||||
"""Create a new resource of the appropriate class.
|
||||
|
||||
@ -258,8 +259,8 @@ class Resource(object):
|
||||
self.properties = properties
|
||||
|
||||
self.rule_id = kwargs.get('rule_id')
|
||||
self.rate = kwargs.get('rate', 0)
|
||||
self.last_bill = kwargs.get('last_bill')
|
||||
self.rate = utils.make_decimal(kwargs.get('rate', 0))
|
||||
self.last_bill = utils.make_decimal(kwargs.get('last_bill', 0))
|
||||
|
||||
self.created_at = kwargs.get('created_at')
|
||||
self.updated_at = kwargs.get('updated_at')
|
||||
@ -267,7 +268,6 @@ class Resource(object):
|
||||
|
||||
# Properties pass to user to help settle account, not store to db
|
||||
self.delta_rate = 0
|
||||
self.delayed_cost = 0
|
||||
self.consumption = None
|
||||
|
||||
def store(self, context):
|
||||
@ -278,8 +278,8 @@ class Resource(object):
|
||||
'resource_type': self.resource_type,
|
||||
'properties': self.properties,
|
||||
'rule_id': self.rule_id,
|
||||
'rate': self.rate,
|
||||
'last_bill': self.last_bill,
|
||||
'rate': utils.format_decimal(self.rate),
|
||||
'last_bill': utils.format_decimal(self.last_bill),
|
||||
'created_at': self.created_at,
|
||||
'updated_at': self.updated_at,
|
||||
'deleted_at': self.deleted_at,
|
||||
@ -304,19 +304,13 @@ class Resource(object):
|
||||
self.created_at = resource.created_at
|
||||
return
|
||||
|
||||
now = timeutils.utcnow()
|
||||
self.last_bill = now
|
||||
self.last_bill = utils.make_decimal(wallclock())
|
||||
create_time = self.properties.get('created_at')
|
||||
if create_time is not None:
|
||||
created_at = timeutils.parse_strtime(create_time)
|
||||
delayed_seconds = (now - created_at).total_seconds()
|
||||
# Engine handle resource creation is delayed because of something,
|
||||
# we suppose less than ALLOW_DELAY_TIME is acceptable.
|
||||
if delayed_seconds > self.ALLOW_DELAY_TIME:
|
||||
self.delayed_cost = self.delta_rate * delayed_seconds
|
||||
self.last_bill = created_at
|
||||
sec = utils.format_time_to_seconds(create_time)
|
||||
self.last_bill = utils.make_decimal(sec)
|
||||
|
||||
values.update(last_bill=self.last_bill)
|
||||
values.update(last_bill=utils.format_decimal(self.last_bill))
|
||||
resource = db_api.resource_create(context, values)
|
||||
self.created_at = resource.created_at
|
||||
|
||||
@ -326,19 +320,14 @@ class Resource(object):
|
||||
return
|
||||
|
||||
update_time = self.properties.get('updated_at')
|
||||
now = timeutils.utcnow()
|
||||
updated_at = now
|
||||
updated_at = utils.make_decimal(wallclock())
|
||||
if update_time is not None:
|
||||
updated_at = timeutils.parse_strtime(update_time)
|
||||
delayed_seconds = (now - updated_at).total_seconds()
|
||||
# Engine handle resource update is delayed because of something,
|
||||
# we suppose less than ALLOW_DELAY_TIME is acceptable.
|
||||
if delayed_seconds > self.ALLOW_DELAY_TIME:
|
||||
self.delayed_cost = self.delta_rate * delayed_seconds
|
||||
sec = utils.format_time_to_seconds(update_time)
|
||||
updated_at = utils.make_decimal(sec)
|
||||
|
||||
# Generate consumption between lass bill and update time
|
||||
old_rate = self.rate - self.delta_rate
|
||||
cost = (updated_at - self.last_bill).total_seconds() * old_rate
|
||||
cost = (updated_at - self.last_bill) * old_rate
|
||||
params = {'resource_id': self.id,
|
||||
'resource_type': self.resource_type,
|
||||
'start_time': self.last_bill,
|
||||
@ -349,7 +338,7 @@ class Resource(object):
|
||||
self.consumption = consumption_mod.Consumption(self.user_id, **params)
|
||||
|
||||
self.last_bill = updated_at
|
||||
values.update(last_bill=updated_at)
|
||||
values.update(last_bill=utils.format_decimal(updated_at))
|
||||
db_api.resource_update(context, self.id, values)
|
||||
|
||||
def _delete(self, context, soft_delete=True):
|
||||
@ -359,18 +348,13 @@ class Resource(object):
|
||||
return
|
||||
|
||||
delete_time = self.properties.get('deleted_at')
|
||||
now = timeutils.utcnow()
|
||||
deleted_at = now
|
||||
deleted_at = utils.make_decimal(wallclock())
|
||||
if delete_time is not None:
|
||||
deleted_at = timeutils.parse_strtime(delete_time)
|
||||
delayed_seconds = (now - deleted_at).total_seconds()
|
||||
# Engine handle resource deletion is delayed because of something,
|
||||
# we suppose less than ALLOW_DELAY_TIME is acceptable.
|
||||
if delayed_seconds > self.ALLOW_DELAY_TIME:
|
||||
self.delayed_cost = self.delta_rate * delayed_seconds
|
||||
sec = utils.format_time_to_seconds(delete_time)
|
||||
deleted_at = utils.make_decimal(sec)
|
||||
|
||||
# Generate consumption between lass bill and delete time
|
||||
cost = (deleted_at - self.last_bill).total_seconds() * self.rate
|
||||
cost = (deleted_at - self.last_bill) * self.rate
|
||||
params = {'resource_id': self.id,
|
||||
'resource_type': self.resource_type,
|
||||
'start_time': self.last_bill,
|
||||
@ -447,8 +431,8 @@ class Resource(object):
|
||||
'resource_type': self.resource_type,
|
||||
'properties': self.properties,
|
||||
'rule_id': self.rule_id,
|
||||
'rate': self.rate,
|
||||
'last_bill': utils.format_time(self.last_bill),
|
||||
'rate': utils.dec2str(self.rate),
|
||||
'last_bill': utils.dec2str(self.last_bill),
|
||||
'created_at': utils.format_time(self.created_at),
|
||||
'updated_at': utils.format_time(self.updated_at),
|
||||
'deleted_at': utils.format_time(self.deleted_at),
|
||||
|
@ -179,7 +179,7 @@ class CronScheduler(object):
|
||||
def _add_notify_job(self, user):
|
||||
if user.rate == 0:
|
||||
return False
|
||||
total_seconds = user.balance / user.rate
|
||||
total_seconds = float(user.balance / user.rate)
|
||||
prior_notify_time = cfg.CONF.scheduler.prior_notify_time * 3600
|
||||
notify_seconds = total_seconds - prior_notify_time
|
||||
notify_seconds = notify_seconds if notify_seconds > 0 else 0
|
||||
@ -198,7 +198,7 @@ class CronScheduler(object):
|
||||
def _add_freeze_job(self, user):
|
||||
if user.rate == 0:
|
||||
return False
|
||||
total_seconds = user.balance / user.rate
|
||||
total_seconds = float(user.balance / user.rate)
|
||||
run_date = timeutils.utcnow() + timedelta(seconds=total_seconds)
|
||||
job_params = {'run_date': run_date}
|
||||
job_id = self._generate_job_id(user.id, self.FREEZE)
|
||||
|
Loading…
Reference in New Issue
Block a user