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:
lvdongbing 2016-06-22 21:11:09 -04:00
parent 5025328bc1
commit ff26ed98e7
9 changed files with 139 additions and 121 deletions

View File

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

View File

@ -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),

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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):

View File

@ -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.'''

View File

@ -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),

View File

@ -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)