def renew(self, data): """renew the order manually 1. Caculate price needed to deduct 2. Change cron_time and deduct account in the same session 3. Change cron job in master """ self._validate_renew(data.as_dict()) conn = pecan.request.db_conn try: order, renew_price = conn.renew_order(request.context, self._id, data) self.master_api.change_monthly_job_time(request.context, self._id, timeutils.isotime( order.cron_time), clear_date_jobs=True) except exception.OrderNotFound as e: LOG.warn(e) raise except exception.OrderRenewError as e: LOG.warn(e) raise except exception.NotSufficientFund as e: LOG.warn(e) raise except Exception: msg = "Fail to renew the order %s" % self._id LOG.exception(msg) raise exception.DBError(reason=msg) result = models.Order.from_db_model(order) result.renew_price = str(renew_price) return result
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 get_all(self, user_id=None, limit=None, offset=None, sort_key='dispatched,used', sort_dir='asc'): """Get all precharges.""" context = pecan.request.context conn = pecan.request.db_conn check_policy(context, "account:precharge") if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") try: precharges = conn.get_precharges(context, user_id=user_id, limit=limit, offset=offset, sort_key=sort_key, sort_dir=sort_dir) total_count = conn.get_precharges_count(context, user_id=user_id) pecan.response.headers['X-Total-Count'] = str(total_count) except Exception as e: LOG.exception('Failed to get all precharges') raise exception.DBError(reason=e) precharges = [models.PreCharge.from_db_model(p) for p in precharges] return models.PreCharges.transform(precharges=precharges, total_count=total_count)
def delete(self): """Delete this product.""" # ensure product exists before deleting product = self._product() try: self.conn.delete_product(request.context, product.product_id) except Exception: error = 'Error while deleting product: %s' % product.product_id LOG.exception(error) raise exception.DBError(reason=error)
def put(self, data): """Charge the account """ # Check the charge value if data.value < -100000 or data.value > 100000: raise exception.InvalidChargeValue(value=data.value) self.conn = pecan.request.db_conn try: charge, _ = self.conn.update_account(request.context, None, project_id=self._id, **data.as_dict()) has_bonus = False if cfg.CONF.enable_bonus and data['type'] != 'bonus': data['type'] = 'bonus' data['come_from'] = 'system' value = gringutils.calculate_bonus(data['value']) if value > 0: data['value'] = value bonus, _ = self.conn.update_account(request.context, None, project_id=self._id, **data.as_dict()) has_bonus = True self.conn.set_charged_orders(request.context, None, project_id=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 and charge['type'] != 'bonus': account = self.conn.get_account(request.context, None, project_id=self._id).as_dict() contact = keystone.get_uos_user(account['user_id']) 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) return models.Charge.from_db_model(charge)
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(deleted=False, **data.as_dict()) except Exception: error = 'Error while turning product: %s' % data.as_dict() LOG.exception(error) raise exception.MissingRequiredParams(reason=error) # Check and process unit_price data if product_in.unit_price is not None: price_data = ProductExtraData(product_in.unit_price.as_dict()) price_data.validate() # 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: error = 'Error while creating product: %s' % data.as_dict() LOG.exception(error) raise exception.DBError(reason=error) # DB model to API model return models.Product(product_id=product.product_id, name=product.name, service=product.service, region_id=product.region_id, description=product.description, unit_price=product_in.unit_price, created_at=product.created_at, updated_at=product.updated_at)
def delete(self): """Delete the account including the projects that belong to it.""" # only admin can delete account check_policy(request.context, "account:delete") try: self.conn = pecan.request.db_conn self.conn.delete_account(request.context, self._id) except (exception.NotFound): msg = _('Could not find account whose user_id is %s' % self._id) raise exception.NotFound(msg) except Exception as e: msg = _("failed to delete account whose user_id is %s." % self._id) LOG.warn(msg) raise exception.DBError(reason=e)
def close(self): """Close this order Close means set order's status to deleted in database, then stop the cron job in apscheduler. """ conn = pecan.request.db_conn try: order = conn.close_order(request.context, self._id) except Exception: msg = "Fail to close the order %s" % self._id LOG.exception(msg) raise exception.DBError(reason=msg) self.master_api.delete_sched_jobs(request.context, self._id) return models.Order.from_db_model(order)
def level(self, level): if not isinstance(level, int) or level < 0 or level > 9: raise exception.InvalidParameterValue(err="Invalid Level") self.conn = pecan.request.db_conn try: account = self.conn.change_account_level(request.context, None, level, project_id=self._id) except Exception as e: LOG.exception('Fail to change the account level of: %s' % self._id) raise exception.DBError(reason=e) return models.UserAccount.from_db_model(account)
def level(self, level): """Update the account's level.""" check_policy(request.context, "account:level") if not isinstance(level, int) or level < 0 or level > 9: raise exception.InvalidParameterValue(err="Invalid Level") self.conn = pecan.request.db_conn try: account = self.conn.change_account_level(request.context, self._id, level) except Exception as e: LOG.exception('Fail to change the account level of: %s' % self._id) raise exception.DBError(reason=e) return models.UserAccount.from_db_model(account)
def activate_auto_renew(self, data): """Activate auto renew of this order """ self._validate_renew(data.as_dict()) conn = pecan.request.db_conn try: order = conn.activate_auto_renew(request.context, self._id, data) except exception.OrderNotFound as e: LOG.warn(e) raise except exception.OrderRenewError as e: LOG.warn(e) raise except Exception: msg = "Fail to activate auto renew of the order %s" % self._id LOG.exception(msg) raise exception.DBError(reason=msg) return models.Order.from_db_model(order)
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 put(self, data): """Modify this product. PUT method will override all the fields. """ # Ensure this product exists # NOTE(suo): we can't make product_in and product_old # point to the same object product_in = self._product() product_old = self._product() for k, v in data.as_dict().items(): product_in[k] = v product_in.updated_at = datetime.datetime.utcnow() # Check if there are other same names in the same region # except itself filters = { 'name': product_in.name, 'service': product_in.service, 'region_id': product_in.region_id } products = list( self.conn.get_products(request.context, filters=filters)) if len(products) > 0 and ( product_old.name != product_in.name or product_old.service != product_in.service or product_old.region_id != product_in.region_id): 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) # Update product model to DB try: product = self.conn.update_product(request.context, product_in) except Exception as e: error = 'Error while updating product: %s' % data.as_dict() LOG.exception(error) raise exception.DBError(reason=error) # DB model to API model return models.Product.from_db_model(product)
def get_all(self, owed=None, limit=None, offset=None, duration=None): """Get all accounts.""" 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 duration = gringutils.normalize_timedelta(duration) if duration: active_from = datetime.datetime.utcnow() - duration else: active_from = None try: accounts = self.conn.get_accounts(request.context, owed=owed, limit=limit, offset=offset, active_from=active_from) count = self.conn.get_accounts_count(request.context, owed=owed, active_from=active_from) 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) accounts = [ models.AdminAccount.from_db_model(account) for account in accounts ] return models.AdminAccounts(total_count=count, accounts=accounts)
def get_all(self, owed=None, limit=None, offset=None): """Get all accounts """ self.conn = pecan.request.db_conn try: accounts = self.conn.get_accounts(request.context, owed=owed, limit=limit, offset=offset) count = self.conn.get_accounts_count(request.context, owed=owed) pecan.response.headers['X-Total-Count'] = str(count) except exception.NotAuthorized as e: LOG.exception('Fail to get all accounts') raise exception.NotAuthorized() except Exception as e: LOG.exception('Fail to get all accounts') raise exception.DBError(reason=e) return [ models.AdminAccount.from_db_model(account) for account in accounts ]
def get(self, reqId): check_policy(request.context, "deduct:check_req") self.conn = pecan.request.db_conn status = "0" try: self.conn.get_deduct(request.context, reqId) except exception.DeductNotFound: LOG.warn('Deduct Req: %s not found' % reqId) status = "-1" except exception.NotAuthorized: LOG.exception('Fail to get the deduct req: %s' % reqId) raise exception.NotAuthorized() except Exception: msg = "Fail to get the deduct req: %s" % reqId LOG.exception(msg) raise exception.DBError(reason=msg) return models.CheckReqResponse(code="0", total="1", message="OK", data=[models.CheckReq(status=status)])
def invitees(self, limit=None, offset=None): """Get invitees of the inviter.""" if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") inviter = acl.get_limited_to_user(request.headers, 'referees_get') or self._id self.conn = pecan.request.db_conn try: _invitees, total_count = self.conn.get_invitees(request.context, inviter, limit=limit, offset=offset) except Exception as e: LOG.exception('Fail to get invitees') raise exception.DBError(reason=e) invitees = [] for invitee in _invitees: user = keystone.get_uos_user(invitee.user_id) if user: user_name = user.get('real_name') or user['email'].split( '@')[0] user_email = user['email'] else: user_name = "" user_email = "" invitees.append( models.Invitee(user_id=invitee.user_id, user_name=user_name, user_email=user_email, created_at=invitee.created_at, charged=invitee.charged, reward_value=invitee.reward_value)) return models.Invitees(total_count=total_count, invitees=invitees)
def switch_auto_renew(self, data): """Start/Stop auto renew of this order """ if data.action not in ['start', 'stop']: err = "Wrong switch renew action, should be start or stop" LOG.warn(err) raise exception.InvalidParameterValue(err=err) conn = pecan.request.db_conn try: order = conn.switch_auto_renew(request.context, self._id, data.action) except exception.OrderNotFound as e: LOG.warn(e) raise except exception.OrderRenewError as e: LOG.warn(e) raise except Exception: msg = "Fail to switch auto renew of the order %s" % self._id LOG.exception(msg) raise exception.DBError(reason=msg) return models.Order.from_db_model(order)
def put(self, data): """Modify this product. PUT method will override all the fields.""" # Ensure this product exists # NOTE(suo): we can't make product_in and product_old # point to the same object product_in = self._product() product_old = self._product() p = data.as_dict() # Default to reset subscription and order reset = p.pop("reset") if 'reset' in p else True for k, v in p.items(): product_in[k] = v product_in.updated_at = datetime.datetime.utcnow() # Check and process unit_price data if product_in.unit_price is not None: price_data = ProductExtraData(product_in.unit_price.as_dict()) price_data.validate() # Check if there are other same names in the same region # except itself filters = { 'name': product_in.name, 'service': product_in.service, 'region_id': product_in.region_id } products = list( self.conn.get_products(request.context, filters=filters)) if len(products) > 0 and ( (product_old.name != product_in.name) or (product_old.service != product_in.service) or (product_old.region_id != product_in.region_id)): 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) # Update product model to DB try: product = self.conn.update_product(request.context, product_in) except Exception: error = 'Error while updating product: %s' % data.as_dict() LOG.exception(error) raise exception.DBError(reason=error) # Reset order and subscription's unit price if reset: try: self.conn.reset_product(request.context, product, cfg.CONF.ignore_tenants) except Exception: error = "Fail to reset the product: %s" % data.as_dict() LOG.exception(error) raise exception.DBError(reason=error) # DB model to API model return models.Product(product_id=product.product_id, name=product.name, service=product.service, region_id=product.region_id, description=product.description, unit_price=product_in.unit_price, created_at=product.created_at, updated_at=product.updated_at)
def get_all(self, user_id=None, type=None, duration=None): """Get all projects.""" user_id = acl.get_limited_to_user(request.headers, 'projects_get') or user_id self.conn = pecan.request.db_conn result = [] if not type or type.lower() == 'pay': # if admin call this api, limit to admin's user_id if not user_id: user_id = request.context.user_id try: user_projects = self.conn.get_user_projects(request.context, user_id=user_id) except Exception as e: LOG.exception('Fail to get all projects') raise exception.DBError(reason=e) project_ids = [up.project_id for up in user_projects] if not project_ids: LOG.warn('User %s has no payed projects' % user_id) return [] projects = self._list_keystone_projects() for u, p in itertools.product(user_projects, projects): if u.project_id == p['id']: billing_owner = p['users']['billing_owner'] project_owner = p['users']['project_owner'] project_creator = p['users']['project_creator'] up = models.UserProject( user_id=user_id, project_id=u.project_id, project_name=p['name'], user_consumption=u.user_consumption, project_consumption=u.project_consumption, billing_owner=dict(user_id=billing_owner.get('id') if billing_owner else None, user_name=billing_owner.get('name') if billing_owner else None), project_owner=dict(user_id=project_owner.get('id') if project_owner else None, user_name=project_owner.get('name') if project_owner else None), project_creator=dict( user_id=project_creator.get('id') if project_creator else None, user_name=project_creator.get('name') if project_creator else None), is_historical=u.is_historical, created_at=timeutils.parse_isotime(p['created_at']) if p['created_at'] else None) result.append(up) elif type.lower() == 'all': # if admin call this api, limit to admin's user_id if not user_id: user_id = request.context.user_id k_projects = keystone.get_project_list(name=user_id) project_ids = [p['id'] for p in k_projects] if not project_ids: LOG.warn('User %s has no projects' % user_id) return [] try: g_projects = self.conn.get_projects_by_project_ids( request.context, project_ids) except Exception as e: LOG.exception('Fail to get all projects') raise exception.DBError(reason=e) for k, g in itertools.product(k_projects, g_projects): if k['id'] == g.project_id: billing_owner = k['users']['billing_owner'] project_owner = k['users']['project_owner'] project_creator = k['users']['project_creator'] up = models.UserProject( user_id=user_id, project_id=g.project_id, project_name=k['name'], project_consumption=g.consumption, billing_owner=dict(user_id=billing_owner.get('id') if billing_owner else None, user_name=billing_owner.get('name') if billing_owner else None), project_owner=dict(user_id=project_owner.get('id') if project_owner else None, user_name=project_owner.get('name') if project_owner else None), project_creator=dict( user_id=project_creator.get('id') if project_creator else None, user_name=project_creator.get('name') if project_creator else None), is_historical=False, created_at=timeutils.parse_isotime(k['created_at']) if k['created_at'] else None) result.append(up) elif type.lower() == 'simple': duration = gringutils.normalize_timedelta(duration) if duration: active_from = datetime.datetime.utcnow() - duration else: active_from = None g_projects = list( self.conn.get_projects(request.context, user_id=user_id, active_from=active_from)) project_ids = [p.project_id for p in g_projects] if not project_ids: LOG.warn('User %s has no payed projects' % user_id) return [] k_projects = self._list_keystone_projects() for k, g in itertools.product(k_projects, g_projects): if k.id == g.project_id: up = models.UserProject( project_id=g.project_id, project_name=k.name, domain_id=g.domain_id, billing_owner=dict(user_id=g.user_id)) result.append(up) return result
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 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)