def process_notification(self, message, state=None): LOG.warn("Do action for event: %s, resource_id: %s", message["event_type"], message["payload"]["instance_id"]) # Generate uuid of an order order_id = uuidutils.generate_uuid() unit_price = 0 unit = "hour" # Create subscriptions for this order for ext in self.product_items.extensions: # disk extension is used when instance been stopped and been suspend if ext.name.startswith("stopped"): sub = ext.obj.create_subscription(message, order_id, type=const.STATE_STOPPED) if sub and state == const.STATE_STOPPED: p = gringutils._quantize_decimal(sub["unit_price"]) unit_price += p * sub["quantity"] elif ext.name.startswith("running"): sub = ext.obj.create_subscription(message, order_id, type=const.STATE_RUNNING) if sub and (not state or state == const.STATE_RUNNING): p = gringutils._quantize_decimal(sub["unit_price"]) unit_price += p * sub["quantity"] # Create an order for this instance self.create_order(order_id, unit_price, unit, message, state=state) # Notify master remarks = "Instance Has Been Created." action_time = message["timestamp"] if state: self.resource_created_again(order_id, action_time, remarks) if state == const.STATE_STOPPED: self.instance_stopped(order_id, action_time) else: self.resource_created(order_id, action_time, remarks)
def process_notification(self, message, state=None): LOG.warn('Do action for event: %s, resource_id: %s', message['event_type'], message['payload']['id']) # Generate uuid of an order order_id = uuidutils.generate_uuid() unit_price = 0 unit = 'hour' # Create subscriptions for this order for ext in self.product_items.extensions: if ext.name.startswith('suspend'): sub = ext.obj.create_subscription(message, order_id, type=const.STATE_SUSPEND) if sub and state==const.STATE_SUSPEND: p = gringutils._quantize_decimal(sub['unit_price']) unit_price += p * sub['quantity'] elif ext.name.startswith('running'): sub = ext.obj.create_subscription(message, order_id, type=const.STATE_RUNNING) if sub and (not state or state==const.STATE_RUNNING): p = gringutils._quantize_decimal(sub['unit_price']) unit_price += p * sub['quantity'] # Create an order for this instance self.create_order(order_id, unit_price, unit, message, state=state) # Notify master remarks = 'Image Has Been Created.' action_time = message['timestamp'] if state: self.resource_created_again(order_id, action_time, remarks) else: self.resource_created(order_id, action_time, remarks)
def estimate_per_day(self): """Get the price per day and the remaining days that the balance can support. """ self.conn = pecan.request.db_conn user_id = acl.get_limited_to_user( request.headers, 'account_estimate') or self._id account = self._account(user_id=user_id) orders = self.conn.get_active_orders(request.context, user_id=user_id, within_one_hour=True, bill_methods=['hour']) price_per_day = gringutils._quantize_decimal(0) remaining_day = -1 if not orders: return models.Estimate(price_per_day=price_per_day, remaining_day=remaining_day) price_per_hour = 0 for order in orders: price_per_hour += gringutils._quantize_decimal(order.unit_price) if price_per_hour == 0: return models.Estimate(price_per_day=price_per_day, remaining_day=remaining_day) price_per_day = price_per_hour * 24 remaining_day = int(account.balance / price_per_day) return models.Estimate(price_per_day=price_per_day, remaining_day=remaining_day)
def _estimate_per_day(self, user_id, balance): """Get the price per day and the remaining days that the balance can support. """ self.conn = pecan.request.db_conn orders = self.conn.get_active_orders(request.context, user_id=user_id, within_one_hour=True, bill_methods=['hour']) price_per_day = gringutils._quantize_decimal(0) remaining_day = -1 if not orders: return (price_per_day, remaining_day) price_per_hour = 0 for order in orders: price_per_hour += gringutils._quantize_decimal(order.unit_price) if price_per_hour == 0: return (price_per_day, remaining_day) price_per_day = price_per_hour * 24 remaining_day = int(balance / price_per_day) return (price_per_day, remaining_day)
def estimate_per_day(self): """Get the price per day and the remaining days that the balance can support. """ self.conn = pecan.request.db_conn user_id = acl.get_limited_to_user(request.headers, 'account_estimate') or self._id account = self._account(user_id=user_id) orders = self.conn.get_active_orders(request.context, user_id=user_id, within_one_hour=True, bill_methods=['hour']) price_per_day = gringutils._quantize_decimal(0) remaining_day = -1 if not orders: return models.Estimate(price_per_day=price_per_day, remaining_day=remaining_day) price_per_hour = 0 for order in orders: price_per_hour += gringutils._quantize_decimal(order.unit_price) if price_per_hour == 0: return models.Estimate(price_per_day=price_per_day, remaining_day=remaining_day) price_per_day = price_per_hour * 24 remaining_day = int(account.balance / price_per_day) return models.Estimate(price_per_day=price_per_day, remaining_day=remaining_day)
def get(self, start_time=None, end_time=None, region_id=None): """Get summary of all kinds of orders """ # bad hack if not request.context.is_admin and not project_id: summaries = [] for order_type in ORDER_TYPE: summaries.append( models.Summary.transform( total_count=0, order_type=order_type, total_price=gringutils._quantize_decimal(0))) return models.Summaries.transform( total_price=gringutils._quantize_decimal(0), total_count=0, summaries=summaries) conn = pecan.request.db_conn # Get all orders of this particular context one time orders_db = list( conn.get_orders(request.context, start_time=start_time, end_time=end_time, region_id=region_id)) total_price = gringutils._quantize_decimal(0) total_count = 0 summaries = [] # loop all order types for order_type in ORDER_TYPE: order_total_price = gringutils._quantize_decimal(0) order_total_count = 0 # One user's order records will not be very large, so we can # traverse them directly for order in orders_db: if order.type != order_type: continue price, count = self._get_order_price_and_count( order, start_time=start_time, end_time=end_time) order_total_price += price order_total_count += count summaries.append( models.Summary.transform(total_count=order_total_count, order_type=order_type, total_price=order_total_price)) total_price += order_total_price total_count += order_total_count return models.Summaries.transform(total_price=total_price, total_count=total_count, summaries=summaries)
def get(self, start_time=None, end_time=None, region_id=None): """Get summary of all kinds of orders """ # bad hack if not request.context.is_admin and not project_id: summaries = [] for order_type in ORDER_TYPE: summaries.append(models.Summary.transform(total_count=0, order_type=order_type, total_price=gringutils._quantize_decimal(0))) return models.Summaries.transform(total_price=gringutils._quantize_decimal(0), total_count=0, summaries=summaries) conn = pecan.request.db_conn # Get all orders of this particular context one time orders_db = list(conn.get_orders(request.context, start_time=start_time, end_time=end_time, region_id=region_id)) total_price = gringutils._quantize_decimal(0) total_count = 0 summaries = [] # loop all order types for order_type in ORDER_TYPE: order_total_price = gringutils._quantize_decimal(0) order_total_count = 0 # One user's order records will not be very large, so we can # traverse them directly for order in orders_db: if order.type != order_type: continue price, count = self._get_order_price_and_count(order, start_time=start_time, end_time=end_time) order_total_price += price order_total_count += count summaries.append(models.Summary.transform(total_count=order_total_count, order_type=order_type, total_price=order_total_price)) total_price += order_total_price total_count += order_total_count return models.Summaries.transform(total_price=total_price, total_count=total_count, summaries=summaries)
def estimate(self, region_id=None): """Get estimation of specified project and region.""" limit_user_id = acl.get_limited_to_user(request.headers, 'project_estimate') if limit_user_id: # normal user projects = keystone.get_projects_by_user(limit_user_id) _project_ids = [project['id'] for project in projects] project_ids = [self._id] if self._id in _project_ids else [] else: # accountant project_ids = [self._id] # good way to go conn = pecan.request.db_conn # Get all orders of this particular context one time orders_db = list( conn.get_orders(request.context, project_ids=project_ids, region_id=region_id, read_deleted=False, bill_methods=['hour'])) total_price = gringutils._quantize_decimal(0) total_count = 0 summaries = [] # loop all order types for order_type in const.ORDER_TYPE: order_total_price = gringutils._quantize_decimal(0) order_total_count = 0 # One user's order records will not be very large, so we can # traverse them directly for order in orders_db: if order.type != order_type: continue order_total_price += order.unit_price * 24 order_total_count += 1 summaries.append( models.Summary.transform(total_count=order_total_count, order_type=order_type, total_price=order_total_price)) total_price += order_total_price total_count += order_total_count return models.Summaries.transform(total_price=total_price, total_count=total_count, summaries=summaries)
def estimate(self, region_id=None): """Get estimation of specified project and region.""" limit_user_id = acl.get_limited_to_user(request.headers, 'project_estimate') if limit_user_id: # normal user projects = keystone.get_projects_by_user(limit_user_id) _project_ids = [project['id'] for project in projects] project_ids = [self._id] if self._id in _project_ids else [] else: # accountant project_ids = [self._id] # good way to go conn = pecan.request.db_conn # Get all orders of this particular context one time orders_db = list(conn.get_orders(request.context, project_ids=project_ids, region_id=region_id, read_deleted=False, bill_methods=['hour'])) total_price = gringutils._quantize_decimal(0) total_count = 0 summaries = [] # loop all order types for order_type in const.ORDER_TYPE: order_total_price = gringutils._quantize_decimal(0) order_total_count = 0 # One user's order records will not be very large, so we can # traverse them directly for order in orders_db: if order.type != order_type: continue order_total_price += order.unit_price * 24 order_total_count += 1 summaries.append(models.Summary.transform(total_count=order_total_count, order_type=order_type, total_price=order_total_price)) total_price += order_total_price total_count += order_total_count return models.Summaries.transform(total_price=total_price, total_count=total_count, summaries=summaries)
def put(self, data): check_policy(request.context, "deduct:account_pay") if data.reqId == wsme.Unset or data.money == wsme.Unset or \ data.accountNum == wsme.Unset or data.extData == wsme.Unset or \ data.extData.order_id == wsme.Unset: raise exception.InvalidDeductParameter() data.money = gringutils._quantize_decimal(data.money) self.conn = pecan.request.db_conn try: deduct = self.conn.deduct_account(request.context, data.accountNum, **data.as_dict()) except db_exc.DBDuplicateEntry: LOG.exception('Duplicated deduct req_id: %s' % data.reqId) raise exception.DuplicatedDeduct(req_id=data.reqId) except exception.NotAuthorized: LOG.exception('Fail to deduct the account: %s' % data.accountNum) raise exception.NotAuthorized() except Exception: msg = "Fail to deduct the account: %s, charge value: %s" % ( data.accountNum, data.money) LOG.exception(msg) raise exception.DBError(reason=msg) pay = models.Pay(transactionNum=deduct.deduct_id, money=deduct.money, createDate=deduct.created_at) return models.PayResponse(code="0", total="1", message="OK", data=[pay])
def put(self, data): check_policy(request.context, "deduct:account_pay") if data.reqId == wsme.Unset or data.money == wsme.Unset or \ data.accountNum == wsme.Unset or data.extData == wsme.Unset or \ data.extData.order_id == wsme.Unset: raise exception.InvalidDeductParameter() data.money = gringutils._quantize_decimal(data.money) self.conn = pecan.request.db_conn try: deduct = self.conn.deduct_account(request.context, data.accountNum, **data.as_dict()) except db_exception.DBDuplicateEntry: LOG.exception('Duplicated deduct req_id: %s' % data.reqId) raise exception.DuplicatedDeduct(req_id=data.reqId) except exception.NotAuthorized as e: LOG.exception('Fail to deduct the account: %s' % data.accountNum) raise exception.NotAuthorized() except Exception as e: msg = "Fail to deduct the account: %s, charge value: %s" % (data.accountNum, data.money) LOG.exception(msg) raise exception.DBError(reason=msg) pay = models.Pay(transactionNum=deduct.deduct_id, money=deduct.money, createDate=deduct.created_at) return models.PayResponse(code="0", total="1", message="OK", data=[pay])
def charges(self, type=None, start_time=None, end_time=None, limit=None, offset=None): """Get this account's charge records.""" if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") user_id = acl.get_limited_to_user( request.headers, 'account_charge') or self._id self.conn = pecan.request.db_conn charges = self.conn.get_charges(request.context, user_id=user_id, type=type, limit=limit, offset=offset, start_time=start_time, end_time=end_time) charges_list = [] for charge in charges: charges_list.append(models.Charge.from_db_model(charge)) total_price, total_count = self.conn.get_charges_price_and_count( request.context, user_id=user_id, type=type, start_time=start_time, end_time=end_time) total_price = gringutils._quantize_decimal(total_price) return models.Charges.transform(total_price=total_price, total_count=total_count, charges=charges_list)
def get_all(self, start_time=None, end_time=None, type=None, project_id=None, limit=None, offset=None): if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") conn = pecan.request.db_conn bills_db = list(conn.get_bills(pecan.request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type, limit=limit, offset=offset)) total_count, total_price = conn.get_bills_count_and_sum( pecan.request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type) total_price = gringutils._quantize_decimal(total_price) bills = [] for bill in bills_db: bills.append(models.Bill.from_db_model(bill)) return models.Bills.transform(total_price=total_price, total_count=total_count, bills=bills)
def get_all(self, start_time=None, end_time=None, type=None, project_id=None, limit=None, offset=None): conn = pecan.request.db_conn bills_db = list( conn.get_bills( request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type, limit=limit, offset=offset, ) ) total_count, total_price = conn.get_bills_count_and_sum( request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type ) total_price = gringutils._quantize_decimal(total_price) bills = [] for bill in bills_db: bills.append(models.Bill.from_db_model(bill)) return models.Bills.transform(total_price=total_price, total_count=total_count, bills=bills)
def estimate(self): self.conn = pecan.request.db_conn if not cfg.CONF.enable_owe: return -1 account = self._account() if account.balance < 0: return -2 orders = self.conn.get_active_orders(request.context, project_id=self._id, within_one_hour=True) if not orders: return -1 price_per_hour = 0 for order in orders: price_per_hour += gringutils._quantize_decimal(order.unit_price) if price_per_hour == 0: return -1 price_per_day = price_per_hour * 24 days_to_owe_d = float(account.balance / price_per_day) days_to_owe = round(days_to_owe_d) if days_to_owe < days_to_owe_d: days_to_owe = days_to_owe + 1 if days_to_owe > 7: return -1 return days_to_owe
def get_all(self, start_time=None, end_time=None, type=None, project_id=None, limit=None, offset=None): conn = pecan.request.db_conn bills_db = list(conn.get_bills(request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type, limit=limit, offset=offset)) total_count, total_price = conn.get_bills_count_and_sum( request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type) total_price=gringutils._quantize_decimal(total_price) bills = [] for bill in bills_db: bills.append(models.Bill.from_db_model(bill)) return models.Bills.transform(total_price=total_price, total_count=total_count, bills=bills)
def _check_resource_to_order(self, resource, resource_to_order, bad_resources, try_to_fix): try: order = resource_to_order[resource.id] except KeyError: # Situation 1: There may exist resources that have no orders # NOTE(suo): The resource billed by month/year may also has no # order, but for now, we couldn't know which billing type it is # only from the GET resource method, on this occasion, we take # this resource as the hourly billing type, then create the order # in auto-fix process if resource.status == const.STATE_ERROR: bad_resources.append(resource) return if not resource.is_bill: return try_to_fix['1'].append(resource) else: # Situation 2: There may exist resource whose status doesn't match # with its order's if resource.status == const.STATE_ERROR: bad_resources.append(resource) elif order['unit'] in ['month', 'year']: # if the order is billed by month or year, we don't check # it for now, just pass, and set it to checked later pass elif (resource.resource_type == const.RESOURCE_LISTENER and resource.admin_state != order['status']): # for loadbalancer listener action_time = utils.format_datetime(timeutils.strtime()) try_to_fix['2'].append( Situation2Item(order['order_id'], resource.resource_type, action_time, resource.admin_state, resource.project_id)) elif (resource.resource_type != const.RESOURCE_LISTENER and resource.status != order['status']): action_time = utils.format_datetime(timeutils.strtime()) try_to_fix['2'].append( Situation2Item(order['order_id'], resource.resource_type, action_time, resource.status, resource.project_id)) # Situation 3: Resource's order has been created, but its bill not # be created by master elif (not order['cron_time'] and order['status'] != const.STATE_STOPPED): try_to_fix['3'].append( Situation3Item(order['order_id'], resource.created_at, resource.project_id)) # Situation 5: The order's unit_price is different from the # resource's actual price else: unit_price = ( self.RESOURCE_CREATE_MAP[resource.resource_type].\ get_unit_price(resource, 'hour')) order_unit_price = utils._quantize_decimal(order['unit_price']) if unit_price is not None and unit_price != order_unit_price: try_to_fix['5'].append( Situation5Item(order['order_id'], resource, unit_price, resource.project_id)) resource_to_order[resource.id]['checked'] = True
def get_all(self, type=None, status=None, start_time=None, end_time=None, limit=None, offset=None, resource_id=None, region_id=None, project_id=None, user_id=None, owed=None, bill_methods=None): """Get queried orders If start_time and end_time is not None, will get orders that have bills during start_time and end_time, or return all orders directly. """ if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") limit_user_id = acl.get_limited_to_user( request.headers, 'orders_get') if limit_user_id: # normal user user_id = None projects = keystone.get_project_list(user=limit_user_id) _project_ids = [project.id for project in projects] if project_id: project_ids = ([project_id] if project_id in _project_ids else _project_ids) else: project_ids = _project_ids else: # accountant if project_id: # look up specified project project_ids = [project_id] else: # look up all projects project_ids = None if project_ids: project_ids = list(set(project_ids) - set(cfg.CONF.ignore_tenants)) conn = pecan.request.db_conn orders_db, total_count = conn.get_orders(request.context, type=type, status=status, start_time=start_time, end_time=end_time, owed=owed, limit=limit, offset=offset, with_count=True, resource_id=resource_id, bill_methods=bill_methods, region_id=region_id, user_id=user_id, project_ids=project_ids) orders = [] for order in orders_db: price = self._get_order_price(order, start_time=start_time, end_time=end_time) order.total_price = gringutils._quantize_decimal(price) orders.append(models.Order.from_db_model(order)) return models.Orders.transform(total_count=total_count, orders=orders)
def get_all(self, start_time=None, end_time=None, type=None, project_id=None): """Get all bills, filter by type, start time, and end time """ conn = pecan.request.db_conn total_price = conn.get_bills_sum( request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type ) return models.Bills.transform(total_price=gringutils._quantize_decimal(total_price))
def get(self, user_id=None, type=None, start_time=None, end_time=None, limit=None, offset=None, sort_key='created_at', sort_dir='desc'): """Get all charges of all account.""" check_policy(request.context, "charges:all") if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") users = {} def _get_user(user_id): user = users.get(user_id) if user: return user contact = keystone.get_uos_user(user_id) or {} user_name = contact.get('name') email = contact.get('email') real_name = contact.get('real_name') mobile = contact.get('mobile_number') company = contact.get('company') users[user_id] = models.User(user_id=user_id, user_name=user_name, email=email, real_name=real_name, mobile=mobile, company=company) return users[user_id] self.conn = pecan.request.db_conn charges = self.conn.get_charges(request.context, user_id=user_id, type=type, limit=limit, offset=offset, start_time=start_time, end_time=end_time, sort_key=sort_key, sort_dir=sort_dir) charges_list = [] for charge in charges: acharge = models.Charge.from_db_model(charge) acharge.actor = _get_user(charge.operator) acharge.target = _get_user(charge.user_id) charges_list.append(acharge) total_price, total_count = self.conn.get_charges_price_and_count( request.context, user_id=user_id, type=type, start_time=start_time, end_time=end_time) total_price = gringutils._quantize_decimal(total_price) return models.Charges.transform(total_price=total_price, total_count=total_count, charges=charges_list)
def get(self, user_id=None, type=None, start_time=None, end_time=None, limit=None, offset=None, sort_key='created_at', sort_dir='desc'): """Get all charges of all account.""" check_policy(request.context, "charges:all") if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") users = {} def _get_user(user_id): user = users.get(user_id) if user: return user contact = kunkka.get_uos_user(user_id) or {} user_name = contact.get('name') email = contact.get('email') real_name = contact.get('real_name') mobile = contact.get('phone') company = contact.get('company') users[user_id] = models.User(user_id=user_id, user_name=user_name, email=email, real_name=real_name, mobile=mobile, company=company) return users[user_id] self.conn = pecan.request.db_conn charges = self.conn.get_charges(request.context, user_id=user_id, type=type, limit=limit, offset=offset, start_time=start_time, end_time=end_time, sort_key=sort_key, sort_dir=sort_dir) charges_list = [] for charge in charges: acharge = models.Charge.from_db_model(charge) acharge.actor = _get_user(charge.operator) acharge.target = _get_user(charge.user_id) charges_list.append(acharge) total_price, total_count = self.conn.get_charges_price_and_count( request.context, user_id=user_id, type=type, start_time=start_time, end_time=end_time) total_price = gringutils._quantize_decimal(total_price) return models.Charges.transform(total_price=total_price, total_count=total_count, charges=charges_list)
def get_all(self, start_time=None, end_time=None, type=None, project_id=None): """Get all bills, filter by type, start time, and end time """ conn = pecan.request.db_conn total_price = conn.get_bills_sum(request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type) return models.Bills.transform( total_price=gringutils._quantize_decimal(total_price))
def get(self): self.conn = pecan.request.db_conn account = self.conn.get_billing_owner(request.context, self.project_id) try: if cfg.CONF.external_billing.enable: external_balance = self.external_client.get_external_balance( account.user_id)['data'][0]['money'] account.balance = gringutils._quantize_decimal( external_balance) except Exception: LOG.exception("Fail to get external balance of the account: %s" % \ account.user_id) return models.UserAccount.from_db_model(account)
def get_all(self, purchases=[]): """Get price of a group of products """ conn = pecan.request.db_conn unit_price = 0 hourly_price = 0 unit = None for p in purchases: if p.product_name and p.service and p.region_id and p.quantity: filters = dict(name=p.product_name, service=p.service, region_id=p.region_id) try: product = list(conn.get_products(request.context, filters=filters))[0] hourly_price += product.unit_price * p.quantity unit_price += product.unit_price unit = product.unit except Exception as e: LOG.error('Product %s not found' % p.product_name) # NOTE(suo): Even through fail to find the product, we should't # raise Exception, emit the price to zero. #raise exception.ProductNameNotFound(product_name=p.product_name) else: raise exception.MissingRequiredParams() unit_price = gringutils._quantize_decimal(unit_price) hourly_price = gringutils._quantize_decimal(hourly_price) monthly_price = gringutils._quantize_decimal(hourly_price * 24 * 30) return models.Price.transform(unit_price=unit_price, hourly_price=hourly_price, monthly_price=monthly_price, unit=unit)
def get_all(self, purchases=[]): """Get price of a group of products """ conn = pecan.request.db_conn unit_price = 0 hourly_price = 0 unit = None for p in purchases: if p.product_name and p.service and p.region_id and p.quantity: filters = dict(name=p.product_name, service=p.service, region_id=p.region_id) try: product = list( conn.get_products(request.context, filters=filters))[0] hourly_price += product.unit_price * p.quantity unit_price += product.unit_price unit = product.unit except Exception as e: LOG.error('Product %s not found' % p.product_name) # NOTE(suo): Even through fail to find the product, we should't # raise Exception, emit the price to zero. #raise exception.ProductNameNotFound(product_name=p.product_name) else: raise exception.MissingRequiredParams() unit_price = gringutils._quantize_decimal(unit_price) hourly_price = gringutils._quantize_decimal(hourly_price) monthly_price = gringutils._quantize_decimal(hourly_price * 24 * 30) return models.Price.transform(unit_price=unit_price, hourly_price=hourly_price, monthly_price=monthly_price, unit=unit)
def get(self): self.conn = pecan.request.db_conn try: account = self.conn.get_billing_owner(request.context, self.project_id) except exception.AccountNotFound: return None try: if cfg.CONF.external_billing.enable: external_balance = self.external_client.get_external_balance( account.user_id)['data'][0]['money'] account.balance = gringutils._quantize_decimal( external_balance) except Exception: LOG.exception("Fail to get external balance of the account: %s" % \ account.user_id) return models.UserAccount.from_db_model(account)
def post(self, data): """Create a new product """ data.product_id = uuidutils.generate_uuid() conn = pecan.request.db_conn # API model to DB model try: product_in = db_models.Product(quantity=0, deleted=False, **data.as_dict()) except Exception as e: error = 'Error while turning product: %s' % data.as_dict() LOG.exception(error) raise exception.MissingRequiredParams(reason=error) # Check if there are duplicated name in the same region filters = { 'name': data.name, 'service': data.service, 'region_id': data.region_id } products = list(conn.get_products(request.context, filters=filters)) if len(products) > 0: error = "Product with name(%s) within service(%s) already "\ "exists in region_id(%s)" % \ (data.name, data.service, data.region_id) LOG.warning(error) raise exception.DuplicatedProduct(reason=error) # Write product model to DB try: product = conn.create_product(request.context, product_in) except Exception as e: error = 'Error while creating product: %s' % data.as_dict() LOG.exception(error) raise exception.DBError(reason=error) product.unit_price = gringutils._quantize_decimal(product.unit_price) # DB model to API model return models.Product.from_db_model(product)
def estimate_per_day(self): self.conn = pecan.request.db_conn account = self._account() orders = self.conn.get_active_orders(request.context, project_id=self._id, within_one_hour=True) if not orders: return 0 price_per_hour = 0 for order in orders: price_per_hour += gringutils._quantize_decimal(order.unit_price) if price_per_hour == 0: return 0 price_per_day = price_per_hour * 24 return round(float(price_per_day), 4)
def post(self, data): """Create a new product """ data.product_id = uuidutils.generate_uuid() conn = pecan.request.db_conn # API model to DB model try: product_in = db_models.Product(quantity=0, deleted=False, **data.as_dict()) except Exception as e: error = 'Error while turning product: %s' % data.as_dict() LOG.exception(error) raise exception.MissingRequiredParams(reason=error) # Check if there are duplicated name in the same region filters = {'name': data.name, 'service': data.service, 'region_id': data.region_id} products = list(conn.get_products(request.context, filters=filters)) if len(products) > 0: error = "Product with name(%s) within service(%s) already "\ "exists in region_id(%s)" % \ (data.name, data.service, data.region_id) LOG.warning(error) raise exception.DuplicatedProduct(reason=error) # Write product model to DB try: product = conn.create_product(request.context, product_in) except Exception as e: error = 'Error while creating product: %s' % data.as_dict() LOG.exception(error) raise exception.DBError(reason=error) product.unit_price = gringutils._quantize_decimal(product.unit_price) # DB model to API model return models.Product.from_db_model(product)
def _account(self, user_id=None): self.conn = pecan.request.db_conn _id = user_id or self._id try: account = self.conn.get_account(request.context, _id) if cfg.CONF.external_billing.enable: external_balance = self.external_client.get_external_balance( _id)['data'][0]['money'] external_balance = gringutils._quantize_decimal( external_balance) account.balance = external_balance except exception.AccountNotFound: LOG.error("Account %s not found" % _id) raise except exception.GetExternalBalanceFailed: raise except (Exception): LOG.error("Fail to get account %s" % _id) raise exception.AccountGetFailed(user_id=_id) return account
def get_all(self, type=None, status=None, start_time=None, end_time=None, limit=None, offset=None, region_id=None, project_id=None, owed=None): """Get queried orders If start_time and end_time is not None, will get orders that have bills during start_time and end_time, or return all orders directly. """ # bad hack if not request.context.is_admin and not project_id: return models.Orders.transform(total_count=0, orders=[]) conn = pecan.request.db_conn orders_db, total_count = conn.get_orders(request.context, type=type, status=status, start_time=start_time, end_time=end_time, owed=owed, limit=limit, offset=offset, with_count=True, region_id=region_id, project_id=project_id) orders = [] for order in orders_db: price = self._get_order_price(order, start_time=start_time, end_time=end_time) order.total_price = gringutils._quantize_decimal(price) orders.append(models.Order.from_db_model(order)) return models.Orders.transform(total_count=total_count, orders=orders)
def charges(self, start_time=None, end_time=None, limit=None, offset=None): """Return this account's charge records """ self.conn = pecan.request.db_conn charges = self.conn.get_charges(request.context, project_id=self._id, limit=limit, offset=offset, start_time=start_time, end_time=end_time) charges_list = [] for charge in charges: charges_list.append(models.Charge.from_db_model(charge)) total_price, total_count = self.conn.get_charges_price_and_count( request.context, project_id=self._id, start_time=start_time, end_time=end_time) total_price = gringutils._quantize_decimal(total_price) return models.Charges.transform(total_price=total_price, total_count=total_count, charges=charges_list)
def get_all(self, start_time=None, end_time=None, type=None, project_id=None, limit=None, offset=None): if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") conn = pecan.request.db_conn bills_db = list( conn.get_bills(pecan.request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type, limit=limit, offset=offset)) total_count, total_price = conn.get_bills_count_and_sum( pecan.request.context, project_id=project_id, start_time=start_time, end_time=end_time, type=type) total_price = gringutils._quantize_decimal(total_price) bills = [] for bill in bills_db: bills.append(models.Bill.from_db_model(bill)) return models.Bills.transform(total_price=total_price, total_count=total_count, bills=bills)
def estimate(self): """Estimate the hourly billing resources how many days to owed. """ self.conn = pecan.request.db_conn if not cfg.CONF.enable_owe: return -1 user_id = acl.get_limited_to_user( request.headers, 'account_estimate') or self._id account = self._account(user_id=user_id) if account.balance < 0: return -2 orders = self.conn.get_active_orders(request.context, user_id=user_id, within_one_hour=True, bill_methods=['hour']) if not orders: return -1 price_per_hour = 0 for order in orders: price_per_hour += gringutils._quantize_decimal(order.unit_price) if price_per_hour == 0: return -1 price_per_day = price_per_hour * 24 days_to_owe_d = float(account.balance / price_per_day) days_to_owe = round(days_to_owe_d) if days_to_owe < days_to_owe_d: days_to_owe = days_to_owe + 1 if days_to_owe > 7: return -1 return days_to_owe
def estimate(self): """Estimate the hourly billing resources how many days to owed. """ self.conn = pecan.request.db_conn if not cfg.CONF.enable_owe: return -1 user_id = acl.get_limited_to_user(request.headers, 'account_estimate') or self._id account = self._account(user_id=user_id) if account.balance < 0: return -2 orders = self.conn.get_active_orders(request.context, user_id=user_id, within_one_hour=True, bill_methods=['hour']) if not orders: return -1 price_per_hour = 0 for order in orders: price_per_hour += gringutils._quantize_decimal(order.unit_price) if price_per_hour == 0: return -1 price_per_day = price_per_hour * 24 days_to_owe_d = float(account.balance / price_per_day) days_to_owe = round(days_to_owe_d) if days_to_owe < days_to_owe_d: days_to_owe = days_to_owe + 1 if days_to_owe > 7: return -1 return days_to_owe
def quantize(self, value): return gring_utils._quantize_decimal(value)
def get_all(self, user_id=None, owed=None, limit=None, offset=None, sort_key='created_at', sort_dir='desc'): check_policy(request.context, "account:all") if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") self.conn = pecan.request.db_conn try: accounts = self.conn.get_accounts(request.context, user_id=user_id, owed=owed, limit=limit, offset=offset, sort_key=sort_key, sort_dir=sort_dir) count = self.conn.get_accounts_count(request.context, owed=owed, user_id=user_id) pecan.response.headers['X-Total-Count'] = str(count) except exception.NotAuthorized as e: LOG.exception('Failed to get all accounts') raise exception.NotAuthorized() except Exception as e: LOG.exception('Failed to get all accounts') raise exception.DBError(reason=e) results = [] user_ids = [] for account in accounts: user_ids.append(account.user_id) if account.sales_id: user_ids.append(account.sales_id) if cfg.CONF.external_billing.enable: try: external_balance = \ self.external_client.get_external_balance( account.user_id)['data'][0]['money'] external_balance = \ gringutils._quantize_decimal( external_balance) account.balance = external_balance except exception.GetExternalBalanceFailed: msg = _('Fail to get %s\'s external balance' % (account.user_id)) LOG.warn(msg) price_per_day, remaining_day = \ self._estimate_per_day(account.user_id, account.balance) result = models.AdminAccountInDetail( user=None, user_id=account.user_id, salesperson=None, sales_id=account.sales_id, balance=account.balance, consumption=account.consumption, level=account.level, project_id=account.project_id, domain_id=account.domain_id, owed=owed, inviter=account.inviter, created_at=account.created_at, price_per_day=price_per_day, remaining_day=remaining_day) results.append(result) users = keystone.get_users_by_user_ids(user_ids) for result in results: user = users.get(result.user_id) salesperson = users.get(result.sales_id) if user: result.user = models.UserInDetail(**user) if salesperson: result.salesperson = models.UserInDetail(**salesperson) return models.AdminAccountsInDetail(total_count=count, accounts=results)
def get(self, start_time=None, end_time=None, region_id=None, user_id=None, project_id=None, read_deleted=None): """Get summary of all kinds of orders.""" limit_user_id = acl.get_limited_to_user( request.headers, 'order_summary') if limit_user_id: # normal user user_id = None projects = keystone.get_projects_by_user(limit_user_id) _project_ids = [project['id'] for project in projects] if project_id: project_ids = ([project_id] if project_id in _project_ids else _project_ids) else: project_ids = _project_ids else: # accountant if project_id: # look up specified project project_ids = [project_id] else: # look up all projects project_ids = [] if project_ids: project_ids = list(set(project_ids) - set(cfg.CONF.ignore_tenants)) # good way to go conn = pecan.request.db_conn if read_deleted: if read_deleted.lower() == 'true': read_deleted = True elif read_deleted.lower() == 'false': read_deleted = False else: read_deleted = True else: read_deleted = True # Get all orders of this particular context at one time orders_db = list(conn.get_orders(request.context, start_time=start_time, end_time=end_time, user_id=user_id, project_ids=project_ids, region_id=region_id, read_deleted=read_deleted)) total_price = gringutils._quantize_decimal(0) total_count = 0 summaries = [] # loop all order types for order_type in const.ORDER_TYPE: order_total_price = gringutils._quantize_decimal(0) order_total_count = 0 # One user's order records will not be very large, so we can # traverse them directly for order in orders_db: if order.type != order_type: if (order.type == const.RESOURCE_FLOATINGIPSET and order_type == const.RESOURCE_FLOATINGIP): # floatingipset consumption belongs to floatingip pass else: continue price, count = self._get_order_price_and_count( order, start_time=start_time, end_time=end_time) order_total_price += price order_total_count += count summaries.append(models.Summary.transform( total_count=order_total_count, order_type=order_type, total_price=order_total_price) ) total_price += order_total_price total_count += order_total_count return models.Summaries.transform(total_price=total_price, total_count=total_count, summaries=summaries)
def check_owed_hour_resources_and_notify(self): # noqa """Check owed hour-billing resources and notify them Get owed accounts, send them sms/email notifications. """ try: accounts = self._assigned_accounts() except Exception: LOG.exception("Fail to get assigned accounts") accounts = [] LOG.warn("[%s] Notifying owed accounts, assigned accounts number: %s", self.member_id, len(accounts)) bill_methods = [ 'hour', ] for account in accounts: try: if not isinstance(account, dict): account = account.as_dict() if account['level'] == 9: continue if account['owed']: orders = list( self.gclient.get_active_orders( user_id=account['user_id'], owed=True, bill_methods=bill_methods)) if not orders: continue contact = keystone.get_user(account['user_id']).to_dict() _projects = self.gclient.get_projects( user_id=account['user_id'], type='simple') orders_dict = {} for project in _projects: orders_dict[project['project_id']] = [] for order in orders: # check if the resource exists resource = self.RESOURCE_GET_MAP[order['type']]( order['resource_id'], region_name=order['region_id']) if not resource: # NOTE(chengkun): just warning if the resource not exists LOG.warn( "[%s] The resource(%s|%s) has been " "deleted", self.member_id, order['type'], order['resource_id']) continue order_d = {} order_d['order_id'] = order['order_id'] order_d['region_id'] = order['region_id'] order_d['resource_id'] = order['resource_id'] order_d['resource_name'] = order['resource_name'] order_d['type'] = order['type'] if isinstance(order['date_time'], basestring): order['date_time'] = timeutils.parse_strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) now = datetime.datetime.utcnow() reserved_days = (order['date_time'] - now).days if reserved_days < 0: LOG.warn( "[%s] The order %s reserved_days is " "less than 0", self.member_id, order['order_id']) reserved_days = 0 order_d['reserved_days'] = reserved_days order_d['date_time'] = timeutils.strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) orders_dict[order['project_id']].append(order_d) projects = [] for project in _projects: if orders_dict[project['project_id']]: adict = {} adict['project_id'] = project['project_id'] adict['project_name'] = project['project_name'] adict['orders'] = ( orders_dict[project['project_id']]) projects.append(adict) if not projects: continue reserved_days = utils.cal_reserved_days(account['level']) account['reserved_days'] = reserved_days country_code = contact.get("country_code") or "86" language = "en_US" if country_code != '86' else "zh_CN" self.notifier.notify_has_owed(self.ctxt, account, contact, projects, language=language) else: orders = self.gclient.get_active_orders( user_id=account['user_id'], bill_methods=bill_methods) if not orders: continue _projects = self.gclient.get_projects( user_id=account['user_id'], type='simple') estimation = {} for project in _projects: estimation[project['project_id']] = 0 price_per_hour = 0 for order in orders: # check if the resource exists resource = self.RESOURCE_GET_MAP[order['type']]( order['resource_id'], region_name=order['region_id']) if not resource: # just warning the resource not exists LOG.warn( "[%s] The resource(%s|%s) may has been " "deleted", self.member_id, order['type'], order['resource_id']) continue price_per_hour += utils._quantize_decimal( order['unit_price']) estimation[order['project_id']] += \ utils._quantize_decimal(order['unit_price']) price_per_day = price_per_hour * 24 account_balance = utils._quantize_decimal( account['balance']) if price_per_day == 0: continue days_to_owe_d = float(account_balance / price_per_day) days_to_owe = round(days_to_owe_d) if days_to_owe > cfg.CONF.checker.days_to_owe: continue # caculate projects projects = [] for project in _projects: adict = {} adict['project_id'] = project['project_id'] adict['project_name'] = project['project_name'] adict['estimation'] = str( estimation[project['project_id']] * 24) projects.append(adict) if not projects: continue contact = keystone.get_uos_user(account['user_id']) country_code = contact.get("country_code") or "86" language = "en_US" if country_code != '86' else "zh_CN" self.notifier.notify_before_owed(self.ctxt, account, contact, projects, str(price_per_day), days_to_owe, language=language) except Exception: LOG.exception( "Some exceptions occurred when checking owed " "account: %s", account['user_id'])
def used(self): conn = pecan.request.db_conn user_id = pecan.request.context.user_id project_id = pecan.request.context.project_id key = str("gring-precharge-limit-%s" % project_id) cache = _get_cache() count = cache.get(key) max_count, lock_time = self._parse_limit_rule(cfg.CONF.precharge_limit_rule) if count is None: cache.set(key, str(max_count), lock_time) count = max_count price = utils._quantize_decimal('0') ret_code = 0 if int(count) > 0: try: precharge = conn.use_precharge(pecan.request.context, self.code, user_id=user_id, project_id=project_id) price = precharge.price except exception.PreChargeNotFound: LOG.error('The precharge %s not found' % self.code) ret_code = 1 try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) except exception.PreChargeHasUsed: LOG.error('The precharge %s has been used' % self.code) ret_code = 2 try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) except exception.PreChargeHasExpired: LOG.error('The precharge %s has been expired' % self.code) ret_code = 3 try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) except Exception as e: LOG.error('Fail to use precharge(%s), for reason: %s' % (self.code, e)) try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) raise exception.PreChargeException() else: cache.set(key, str(max_count), lock_time) left_count = int(cache.get(key)) if left_count == 0: ret_code = 4 return models.PreChargeSimple.transform(price=price, ret_code=ret_code, left_count=left_count, lock_time=lock_time/60)
def _check_resource_to_order(self, resource, resource_to_order, bad_resources, try_to_fix): try: order = resource_to_order[resource.id] except KeyError: # Situation 1: There may exist resources that have no orders # NOTE(suo): The resource billed by month/year may also has no # order, but for now, we couldn't know which billing type it is # only from the GET resource method, on this occasion, we take # this resource as the hourly billing type, then create the order # in auto-fix process if resource.status == const.STATE_ERROR: bad_resources.append(resource) return if not resource.is_bill: return try_to_fix['1'].append(resource) else: # Situation 2: There may exist resource whose status doesn't match # with its order's if resource.status == const.STATE_ERROR: bad_resources.append(resource) elif order['unit'] in ['month', 'year']: # if the order is billed by month or year, we don't check # it for now, just pass, and set it to checked later pass elif (resource.resource_type == const.RESOURCE_LISTENER and resource.admin_state != order['status']): # for loadbalancer listener action_time = utils.format_datetime(timeutils.strtime()) try_to_fix['2'].append(Situation2Item(order['order_id'], resource.resource_type, action_time, resource.admin_state, resource.project_id)) elif (resource.resource_type != const.RESOURCE_LISTENER and resource.status != order['status']): action_time = utils.format_datetime(timeutils.strtime()) try_to_fix['2'].append(Situation2Item(order['order_id'], resource.resource_type, action_time, resource.status, resource.project_id)) # Situation 3: Resource's order has been created, but its bill not # be created by master elif (not order['cron_time'] and order['status'] != const.STATE_STOPPED): try_to_fix['3'].append(Situation3Item(order['order_id'], resource.created_at, resource.project_id)) # Situation 5: The order's unit_price is different from the # resource's actual price else: unit_price = ( self.RESOURCE_CREATE_MAP[resource.resource_type].\ get_unit_price(resource, 'hour')) order_unit_price = utils._quantize_decimal(order['unit_price']) if unit_price is not None and unit_price != order_unit_price: try_to_fix['5'].append(Situation5Item(order['order_id'], resource, unit_price, resource.project_id)) resource_to_order[resource.id]['checked'] = True
def get_all(self, user_id=None, owed=None, limit=None, offset=None, sort_key='created_at', sort_dir='desc'): check_policy(request.context, "account:all") if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") self.conn = pecan.request.db_conn try: accounts = self.conn.get_accounts(request.context, user_id=user_id, owed=owed, limit=limit, offset=offset, sort_key=sort_key, sort_dir=sort_dir) count = self.conn.get_accounts_count(request.context, owed=owed, user_id=user_id) pecan.response.headers['X-Total-Count'] = str(count) except exception.NotAuthorized as e: LOG.exception('Failed to get all accounts') raise exception.NotAuthorized() except Exception as e: LOG.exception('Failed to get all accounts') raise exception.DBError(reason=e) results = [] user_ids = [] for account in accounts: user_ids.append(account.user_id) if account.sales_id: user_ids.append(account.sales_id) if cfg.CONF.external_billing.enable: try: external_balance = \ self.external_client.get_external_balance( account.user_id)['data'][0]['money'] external_balance = \ gringutils._quantize_decimal( external_balance) account.balance = external_balance except exception.GetExternalBalanceFailed: msg = _('Fail to get %s\'s external balance' % (account.user_id)) LOG.warn(msg) price_per_day, remaining_day = \ self._estimate_per_day(account.user_id, account.balance) result = models.AdminAccountInDetail( user=None, user_id=account.user_id, salesperson=None, sales_id=account.sales_id, balance=account.balance, consumption=account.consumption, level=account.level, project_id=account.project_id, domain_id=account.domain_id, owed=owed, inviter=account.inviter, created_at=account.created_at, price_per_day=price_per_day, remaining_day=remaining_day ) results.append(result) users = keystone.get_users_by_user_ids(user_ids) for result in results: user = users.get(result.user_id) salesperson = users.get(result.sales_id) if user: result.user = models.UserInDetail(**user) if salesperson: result.salesperson = models.UserInDetail(**salesperson) return models.AdminAccountsInDetail(total_count=count, accounts=results)
def get_all(self, type=None, status=None, start_time=None, end_time=None, limit=None, offset=None, resource_id=None, region_id=None, project_id=None, user_id=None, owed=None, bill_methods=None): """Get queried orders If start_time and end_time is not None, will get orders that have bills during start_time and end_time, or return all orders directly. """ if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") limit_user_id = acl.get_limited_to_user(request.headers, 'orders_get') if limit_user_id: # normal user user_id = None projects = keystone.get_project_list(user=limit_user_id) _project_ids = [project.id for project in projects] if project_id: project_ids = ([project_id] if project_id in _project_ids else _project_ids) else: project_ids = _project_ids else: # accountant if project_id: # look up specified project project_ids = [project_id] else: # look up all projects project_ids = None if project_ids: project_ids = list(set(project_ids) - set(cfg.CONF.ignore_tenants)) conn = pecan.request.db_conn orders_db, total_count = conn.get_orders(request.context, type=type, status=status, start_time=start_time, end_time=end_time, owed=owed, limit=limit, offset=offset, with_count=True, resource_id=resource_id, bill_methods=bill_methods, region_id=region_id, user_id=user_id, project_ids=project_ids) orders = [] for order in orders_db: price = self._get_order_price(order, start_time=start_time, end_time=end_time) order.total_price = gringutils._quantize_decimal(price) orders.append(models.Order.from_db_model(order)) return models.Orders.transform(total_count=total_count, orders=orders)
def get(self, today=None, type=None, region_id=None): """Get summary of all kinds of orders in the latest 12 month or 12 day :param today: Client's today wee hour :param type: month, day """ conn = pecan.request.db_conn trends = [] periods = [] if not type: type = "month" if not today: now = datetime.datetime.utcnow() today = datetime.datetime(now.year, now.month, now.day) # The latest 12 months if type == "month": if calendar.monthrange(today.year, today.month)[1] == today.day: latest_start = today else: latest_start = today - datetime.timedelta(days=today.day) next_month_days = gringutils.next_month_days(latest_start.year, latest_start.month) latest_end = latest_start + datetime.timedelta(days=next_month_days) periods = [(latest_start, latest_end)] for i in range(11): last = periods[-1][0] last_days = calendar.monthrange(last.year, last.month)[1] start_month = last - datetime.timedelta(days=last_days) periods.append((start_month, last)) LOG.debug("Latest 12 months: %s" % periods) # The latest 12 days elif type == "day": latest_end = today + datetime.timedelta(days=1) periods = [(today, latest_end)] for i in range(11): start_day = today - datetime.timedelta(days=i + 1) end_day = start_day + datetime.timedelta(days=1) periods.append((start_day, end_day)) LOG.debug("Latest 12 days: %s" % periods) # NOTE(suo): The latest period will not read cache for i in range(12): read_cache = True if i == 0: read_cache = False bills_sum = self._get_bills_sum( request.context, conn, region_id=region_id, start_time=periods[i][0], end_time=periods[i][1], read_cache=read_cache, ) bills_sum = gringutils._quantize_decimal(bills_sum) trends.insert( 0, models.Trend.transform(start_time=periods[i][0], end_time=periods[i][-1], consumption=bills_sum) ) return trends
def put(self, data): """Charge the account.""" check_policy(request.context, "account:charge") # check uos_bill_account_charge_limited charge value if "uos_bill_account_charge_limited" in request.context.roles: lscv = int(cfg.CONF.limited_support_charge_value) if data.value < -lscv or data.value > lscv: raise exception.InvalidChargeValue(value=data.value) else: # check accountant charge value lacv = int(cfg.CONF.limited_accountant_charge_value) if data.value < -lacv or data.value > lacv: raise exception.InvalidChargeValue(value=data.value) remarks = data.remarks if data.remarks != wsme.Unset else None operator = request.context.user_id self.conn = pecan.request.db_conn try: charge, is_first_charge = self.conn.update_account( request.context, self._id, operator=operator, **data.as_dict()) has_bonus = False if cfg.CONF.enable_bonus and data['type'] != 'bonus': value = gringutils.calculate_bonus(data['value']) if value > 0: bonus, _ = self.conn.update_account(request.context, self._id, type='bonus', value=value, come_from='system', operator=operator, remarks=remarks) has_bonus = True if cfg.CONF.enable_invitation and is_first_charge: _account = self._account() min_charge_value = gringutils._quantize_decimal( cfg.CONF.min_charge_value) reward_value = gringutils._quantize_decimal( cfg.CONF.reward_value) if _account.inviter \ and data.value >= min_charge_value \ and reward_value > 0: self.conn.update_account( request.context, _account.inviter, type='bonus', value=reward_value, come_from='system', operator=operator, remarks="reward because of invitation", invitee=self._id) if cfg.CONF.notify_account_charged: inviter = self.conn.get_account( request.context, _account.inviter).as_dict() contact = keystone.get_uos_user(inviter['user_id']) self.notifier = notifier.NotifierService( cfg.CONF.checker.notifier_level) self.notifier.notify_account_charged( request.context, inviter, contact, 'bonus', reward_value, bonus=0, operator=operator, operator_name=request.context.user_name, remarks="reward because of invitation") self.conn.set_charged_orders(request.context, self._id) except exception.NotAuthorized as e: LOG.exception('Fail to charge the account:%s ' 'due to not authorization' % self._id) raise exception.NotAuthorized() except Exception as e: LOG.exception('Fail to charge the account:%s, ' 'charge value: %s' % (self._id, data.value)) raise exception.DBError(reason=e) else: # Notifier account if cfg.CONF.notify_account_charged: account = self.conn.get_account( request.context, self._id).as_dict() contact = keystone.get_uos_user(account['user_id']) country_code = contact.get("country_code") or "86" language = "en_US" if country_code != '86' else "zh_CN" self.notifier = notifier.NotifierService( cfg.CONF.checker.notifier_level) self.notifier.notify_account_charged( request.context, account, contact, data['type'], charge.value, bonus=bonus.value if has_bonus else 0, operator=operator, operator_name=request.context.user_name, remarks=remarks, language=language) return models.Charge.from_db_model(charge)
def check_owed_hour_resources_and_notify(self): # noqa """Check owed hour-billing resources and notify them Get owed accounts, send them sms/email notifications. """ try: accounts = self._assigned_accounts() except Exception: LOG.exception("Fail to get assigned accounts") accounts = [] LOG.warn("[%s] Notifying owed accounts, assigned accounts number: %s", self.member_id, len(accounts)) bill_methods=['hour',] for account in accounts: try: if not isinstance(account, dict): account = account.as_dict() if account['level'] == 9: continue if account['owed']: orders = list( self.gclient.get_active_orders( user_id=account['user_id'], owed=True, bill_methods=bill_methods) ) if not orders: continue contact = keystone.get_user(account['user_id']).to_dict() _projects = self.gclient.get_projects( user_id=account['user_id'], type='simple') orders_dict = {} for project in _projects: orders_dict[project['project_id']] = [] for order in orders: # check if the resource exists resource = self.RESOURCE_GET_MAP[order['type']]( order['resource_id'], region_name=order['region_id']) if not resource: # NOTE(chengkun): just warning if the resource not exists LOG.warn("[%s] The resource(%s|%s) has been " "deleted", self.member_id, order['type'], order['resource_id']) continue order_d = {} order_d['order_id'] = order['order_id'] order_d['region_id'] = order['region_id'] order_d['resource_id'] = order['resource_id'] order_d['resource_name'] = order['resource_name'] order_d['type'] = order['type'] if isinstance(order['date_time'], basestring): order['date_time'] = timeutils.parse_strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) now = datetime.datetime.utcnow() reserved_days = (order['date_time'] - now).days if reserved_days < 0: LOG.warn("[%s] The order %s reserved_days is " "less than 0", self.member_id, order['order_id']) reserved_days = 0 order_d['reserved_days'] = reserved_days order_d['date_time'] = timeutils.strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) orders_dict[order['project_id']].append(order_d) projects = [] for project in _projects: if orders_dict[project['project_id']]: adict = {} adict['project_id'] = project['project_id'] adict['project_name'] = project['project_name'] adict['orders'] = (orders_dict[ project['project_id']]) projects.append(adict) if not projects: continue reserved_days = utils.cal_reserved_days(account['level']) account['reserved_days'] = reserved_days country_code = contact.get("country_code") or "86" language = "en_US" if country_code != '86' else "zh_CN" self.notifier.notify_has_owed(self.ctxt, account, contact, projects, language=language) else: orders = self.gclient.get_active_orders( user_id=account['user_id'], bill_methods=bill_methods) if not orders: continue _projects = self.gclient.get_projects( user_id=account['user_id'], type='simple') estimation = {} for project in _projects: estimation[project['project_id']] = 0 price_per_hour = 0 for order in orders: # check if the resource exists resource = self.RESOURCE_GET_MAP[order['type']]( order['resource_id'], region_name=order['region_id']) if not resource: # just warning the resource not exists LOG.warn("[%s] The resource(%s|%s) may has been " "deleted", self.member_id, order['type'], order['resource_id']) continue price_per_hour += utils._quantize_decimal( order['unit_price']) estimation[order['project_id']] += \ utils._quantize_decimal(order['unit_price']) price_per_day = price_per_hour * 24 account_balance = utils._quantize_decimal( account['balance']) if price_per_day == 0: continue days_to_owe_d = float(account_balance / price_per_day) days_to_owe = round(days_to_owe_d) if days_to_owe > cfg.CONF.checker.days_to_owe: continue # caculate projects projects = [] for project in _projects: adict = {} adict['project_id'] = project['project_id'] adict['project_name'] = project['project_name'] adict['estimation'] = str( estimation[project['project_id']] * 24) projects.append(adict) if not projects: continue contact = keystone.get_uos_user(account['user_id']) country_code = contact.get("country_code") or "86" language = "en_US" if country_code != '86' else "zh_CN" self.notifier.notify_before_owed(self.ctxt, account, contact, projects, str(price_per_day), days_to_owe, language=language) except Exception: LOG.exception("Some exceptions occurred when checking owed " "account: %s", account['user_id'])
def used(self): conn = pecan.request.db_conn context = pecan.request.context user_id = context.user_id project_id = context.project_id user_name = context.user_name key = str("gring-precharge-limit-%s" % user_id) cache = _get_cache() count = cache.get(key) max_count, lock_time = \ self._parse_limit_rule(cfg.CONF.precharge_limit_rule) if count is None: cache.set(key, str(max_count), lock_time) count = max_count price = utils._quantize_decimal('0') ret_code = 0 if int(count) > 0: try: precharge = conn.use_precharge(context, self.code, user_id=user_id, project_id=project_id) price = precharge.price except exception.PreChargeNotFound: LOG.error('The precharge %s not found' % self.code) ret_code = 1 try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) except exception.PreChargeHasUsed: LOG.error('The precharge %s has been used' % self.code) ret_code = 2 try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) except exception.PreChargeHasExpired: LOG.error('The precharge %s has been expired' % self.code) ret_code = 3 try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) except Exception as e: LOG.error('Fail to use precharge(%s), for reason: %s' % (self.code, e)) try: cache.decr(key) except AttributeError: cache.incr(key, delta=-1) raise exception.PreChargeException() else: cache.set(key, str(max_count), lock_time) # Notifier account if cfg.CONF.notify_account_charged: account = conn.get_account(context, user_id).as_dict() contact = kunkka.get_uos_user(user_id) self.notifier = notifier.NotifierService( cfg.CONF.checker.notifier_level) self.notifier.notify_account_charged( context, account, contact, 'coupon', price, bonus=0, operator=user_id, operator_name=user_name, remarks='coupon') left_count = int(cache.get(key)) if left_count == 0: ret_code = 4 return models.PreChargeSimple.transform(price=price, ret_code=ret_code, left_count=left_count, lock_time=lock_time / 60)
def put(self, data): """Charge the account.""" check_policy(request.context, "account:charge") # check uos_bill_account_charge_limited charge value if "uos_bill_account_charge_limited" in request.context.roles: lscv = int(cfg.CONF.limited_support_charge_value) if data.value < -lscv or data.value > lscv: raise exception.InvalidChargeValue(value=data.value) else: # check accountant charge value lacv = int(cfg.CONF.limited_accountant_charge_value) if data.value < -lacv or data.value > lacv: raise exception.InvalidChargeValue(value=data.value) remarks = data.remarks if data.remarks != wsme.Unset else None operator = request.context.user_id self.conn = pecan.request.db_conn try: charge, is_first_charge = self.conn.update_account( request.context, self._id, operator=operator, **data.as_dict()) has_bonus = False if cfg.CONF.enable_bonus and data['type'] != 'bonus': value = gringutils.calculate_bonus(data['value']) if value > 0: bonus, _ = self.conn.update_account(request.context, self._id, type='bonus', value=value, come_from='system', operator=operator, remarks=remarks) has_bonus = True if cfg.CONF.enable_invitation and is_first_charge: _account = self._account() min_charge_value = gringutils._quantize_decimal( cfg.CONF.min_charge_value) reward_value = gringutils._quantize_decimal( cfg.CONF.reward_value) if _account.inviter \ and data.value >= min_charge_value \ and reward_value > 0: self.conn.update_account( request.context, _account.inviter, type='bonus', value=reward_value, come_from='system', operator=operator, remarks="reward because of invitation", invitee=self._id) if cfg.CONF.notify_account_charged: inviter = self.conn.get_account( request.context, _account.inviter).as_dict() contact = keystone.get_uos_user(inviter['user_id']) self.notifier = notifier.NotifierService( cfg.CONF.checker.notifier_level) self.notifier.notify_account_charged( request.context, inviter, contact, 'bonus', reward_value, bonus=0, operator=operator, operator_name=request.context.user_name, remarks="reward because of invitation") self.conn.set_charged_orders(request.context, self._id) except exception.NotAuthorized as e: LOG.exception('Fail to charge the account:%s ' 'due to not authorization' % self._id) raise exception.NotAuthorized() except Exception as e: LOG.exception('Fail to charge the account:%s, ' 'charge value: %s' % (self._id, data.value)) raise exception.DBError(reason=e) else: # Notifier account if cfg.CONF.notify_account_charged: account = self.conn.get_account(request.context, self._id).as_dict() contact = keystone.get_uos_user(account['user_id']) country_code = contact.get("country_code") or "86" language = "en_US" if country_code != '86' else "zh_CN" self.notifier = notifier.NotifierService( cfg.CONF.checker.notifier_level) self.notifier.notify_account_charged( request.context, account, contact, data['type'], charge.value, bonus=bonus.value if has_bonus else 0, operator=operator, operator_name=request.context.user_name, remarks=remarks, language=language) return models.Charge.from_db_model(charge)
def get(self, start_time=None, end_time=None, region_id=None, user_id=None, project_id=None, read_deleted=None): """Get summary of all kinds of orders.""" limit_user_id = acl.get_limited_to_user(request.headers, 'order_summary') if limit_user_id: # normal user user_id = None projects = keystone.get_projects_by_user(limit_user_id) _project_ids = [project['id'] for project in projects] if project_id: project_ids = ([project_id] if project_id in _project_ids else _project_ids) else: project_ids = _project_ids else: # accountant if project_id: # look up specified project project_ids = [project_id] else: # look up all projects project_ids = [] if project_ids: project_ids = list(set(project_ids) - set(cfg.CONF.ignore_tenants)) # good way to go conn = pecan.request.db_conn if read_deleted: if read_deleted.lower() == 'true': read_deleted = True elif read_deleted.lower() == 'false': read_deleted = False else: read_deleted = True else: read_deleted = True # Get all orders of this particular context at one time orders_db = list( conn.get_orders(request.context, start_time=start_time, end_time=end_time, user_id=user_id, project_ids=project_ids, region_id=region_id, read_deleted=read_deleted)) total_price = gringutils._quantize_decimal(0) total_count = 0 summaries = [] # loop all order types for order_type in const.ORDER_TYPE: order_total_price = gringutils._quantize_decimal(0) order_total_count = 0 # One user's order records will not be very large, so we can # traverse them directly for order in orders_db: if order.type != order_type: if (order.type == const.RESOURCE_FLOATINGIPSET and order_type == const.RESOURCE_FLOATINGIP): # floatingipset consumption belongs to floatingip pass else: continue price, count = self._get_order_price_and_count( order, start_time=start_time, end_time=end_time) order_total_price += price order_total_count += count summaries.append( models.Summary.transform(total_count=order_total_count, order_type=order_type, total_price=order_total_price)) total_price += order_total_price total_count += order_total_count return models.Summaries.transform(total_price=total_price, total_count=total_count, summaries=summaries)
def get(self, today=None, type=None, user_id=None, project_id=None, region_id=None): """Get summary of all kinds of orders in the latest 12 month or 12 day :param today: Client's today wee hour :param type: month, day """ limit_user_id = acl.get_limited_to_user(pecan.request.headers, 'trends_get') if limit_user_id: user_id = limit_user_id # accountant can look up any user, if not sepcify, look up itself elif not user_id: user_id = pecan.request.context.user_id conn = pecan.request.db_conn trends = [] periods = [] if not type: type = 'month' if not today: now = datetime.datetime.utcnow() today = datetime.datetime(now.year, now.month, now.day) # The latest 12 months if type == 'month': if calendar.monthrange(today.year, today.month)[1] == today.day: latest_start = today else: latest_start = today - datetime.timedelta(days=today.day) next_month_days = gringutils.next_month_days( latest_start.year, latest_start.month) latest_end = latest_start + datetime.timedelta( days=next_month_days) periods = [(latest_start, latest_end)] for i in range(11): last = periods[-1][0] last_days = calendar.monthrange(last.year, last.month)[1] start_month = last - datetime.timedelta(days=last_days) periods.append((start_month, last)) LOG.debug('Latest 12 months: %s' % periods) # The latest 12 days elif type == 'day': latest_end = today + datetime.timedelta(days=1) periods = [(today, latest_end)] for i in range(11): start_day = today - datetime.timedelta(days=i + 1) end_day = start_day + datetime.timedelta(days=1) periods.append((start_day, end_day)) LOG.debug('Latest 12 days: %s' % periods) # NOTE(suo): The latest period will not read cache for i in range(12): read_cache = True if i == 0: read_cache = False bills_sum = self._get_bills_sum(pecan.request.context, conn, user_id=user_id, project_id=project_id, region_id=region_id, start_time=periods[i][0], end_time=periods[i][1], read_cache=read_cache) bills_sum = gringutils._quantize_decimal(bills_sum) trends.insert( 0, models.Trend.transform(start_time=periods[i][0], end_time=periods[i][-1], consumption=bills_sum)) return trends