def create_bill(self, order_id, action_time=None, remarks=None, end_time=None): if isinstance(action_time, basestring): action_time = timeutils.parse_strtime(action_time, fmt=TIMESTAMP_TIME_FORMAT) if isinstance(end_time, basestring): end_time = timeutils.parse_strtime(end_time, fmt=TIMESTAMP_TIME_FORMAT) _body = dict(order_id=order_id, action_time=action_time, remarks=remarks, end_time=end_time) resp, body = self.client.post('/bills', body=_body) return body
def load_monthly_cron_jobs(self): """Load monthly cron jobs Because owed monthly resource will not cron again, so there is no need to load owed monthly resources """ # owed="" will be tranlated to owed=False by wsme orders = self._get_cron_orders(bill_methods=['month', 'year'], owed="") for order in orders: if not order['cron_time']: continue elif isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] # Because if not specify run_date for date_time job or the # specified run_date is less than the current time, the date # time job will use the current time, so there is no need to # distinguish the danger_time self._create_monthly_job(order['order_id'], run_date=cron_time) self.locks[order['order_id']] = gthreading.Lock()
def instance_stopped(self, ctxt, order_id, action_time): """Instance stopped for a month continuously will not be charged """ with self._get_lock(order_id): LOG.debug("Instance stopped, its order_id: %s, action_time: %s", order_id, action_time) # close the old bill self._close_bill(ctxt, order_id, action_time) # Caculate cron_time after 30 days from now action_time = timeutils.parse_strtime(action_time, fmt=TIMESTAMP_TIME_FORMAT) cron_time = action_time + datetime.timedelta(days=30) # change the order's unit price and its active subscriptions self.gclient.change_order(order_id, const.STATE_STOPPED_IN_30_DAYS, cron_time=cron_time, first_change_to=const.STATE_STOPPED) # create a bill cross 30 days self.gclient.create_bill(order_id, action_time=action_time, remarks="Instance Has Been Stopped", end_time=cron_time) # create a cron job that will execute after 30 days self._create_cron_job(order_id, start_date=cron_time)
def load_date_jobs(self): states = [ const.STATE_RUNNING, const.STATE_STOPPED, const.STATE_SUSPEND ] for s in states: LOG.debug('Loading date jobs in %s state', s) orders = self.gclient.get_orders(status=s, owed=True, region_id=cfg.CONF.region_name) for order in orders: # load delete resource date job if isinstance(order['date_time'], basestring): date_time = timeutils.parse_strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: date_time = order['date_time'] danger_time = (datetime.datetime.utcnow() + datetime.timedelta(seconds=30)) if date_time > danger_time: self._create_date_job(order['order_id'], order['type'], order['resource_id'], order['region_id'], order['date_time']) else: LOG.warning( 'The resource(%s) is in danger time after ' 'master started', order['resource_id']) self._delete_owed_resource(order['type'], order['resource_id'], order['region_id']) LOG.warning('Load date jobs successfully.')
def load_hourly_cron_jobs(self): orders = self._get_cron_orders(bill_methods=['hour'], region_id=cfg.CONF.region_name) for order in orders: if not order['cron_time']: continue elif isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] # create cron job danger_time = (datetime.datetime.utcnow() + datetime.timedelta(seconds=30)) if cron_time > danger_time: self._create_cron_job(order['order_id'], start_date=cron_time) else: LOG.warning("The order(%s) is in danger time after master " "started", order['order_id']) while cron_time <= danger_time: cron_time += datetime.timedelta(hours=1) cron_time -= datetime.timedelta(hours=1) action_time = utils.format_datetime( timeutils.strtime(cron_time)) self._create_bill(self.ctxt, order['order_id'], action_time, "System Adjust") self.locks[order['order_id']] = gthreading.Lock()
def load_hourly_cron_jobs(self): orders = self._get_cron_orders(bill_methods=['hour'], region_id=cfg.CONF.region_name) for order in orders: if not order['cron_time']: continue elif isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] # create cron job danger_time = (datetime.datetime.utcnow() + datetime.timedelta(seconds=30)) if cron_time > danger_time: self._create_cron_job(order['order_id'], start_date=cron_time) else: LOG.warning( "The order(%s) is in danger time after master " "started", order['order_id']) while cron_time <= danger_time: cron_time += datetime.timedelta(hours=1) cron_time -= datetime.timedelta(hours=1) action_time = utils.format_datetime( timeutils.strtime(cron_time)) self._create_bill(self.ctxt, order['order_id'], action_time, "System Adjust") self.locks[order['order_id']] = gthreading.Lock()
def load_date_jobs(self): states = [const.STATE_RUNNING, const.STATE_STOPPED, const.STATE_SUSPEND] for s in states: LOG.debug('Loading date jobs in %s state', s) orders = self.gclient.get_orders(status=s, owed=True, region_id=cfg.CONF.region_name) for order in orders: # load delete resource date job if isinstance(order['date_time'], basestring): date_time = timeutils.parse_strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: date_time = order['date_time'] danger_time = (datetime.datetime.utcnow() + datetime.timedelta(seconds=30)) if date_time > danger_time: self._create_date_job(order['order_id'], order['type'], order['resource_id'], order['region_id'], order['date_time']) else: LOG.warning('The resource(%s) is in danger time after ' 'master started', order['resource_id']) self._delete_owed_resource(order['type'], order['resource_id'], order['region_id']) LOG.warning('Load date jobs successfully.')
def close_bill(self, order_id, action_time): if isinstance(action_time, basestring): action_time = timeutils.parse_strtime(action_time, fmt=TIMESTAMP_TIME_FORMAT) _body = dict(order_id=order_id, action_time=action_time) resp, body = self.client.put('/bills/close', body=_body) return body
def load_30_days_date_jobs(self): # load change unit price date job orders = self.gclient.get_orders(status=const.STATE_STOPPED, region_id=cfg.CONF.region_name, bill_methods=['hour'], type=const.RESOURCE_INSTANCE) for order in orders: if not order['cron_time']: LOG.warn('There is no cron_time for the stopped order: %s', order) continue if isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] danger_time = (datetime.datetime.utcnow() + datetime.timedelta(seconds=30)) cron_time = cron_time - datetime.timedelta(hours=1) if cron_time > danger_time: self._create_date_job_after_30_days(order['order_id'], cron_time) else: LOG.warning("The order(%s) is in danger time after master " "started", order['order_id']) self._change_order_unit_price(order['order_id']) LOG.warning('Load 30-days date jobs successfully.')
def _check_if_owed_resources_match_owed_orders(self, should_stop_resources, should_delete_resources, projects): for project in projects: orders = list(self.gclient.get_active_orders( region_id=self.region_name, project_id=project['project_id'], owed=True)) if not orders: continue for order in orders: if isinstance(order['date_time'], basestring): order['date_time'] = timeutils.parse_strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) if isinstance(order['cron_time'], basestring): order['cron_time'] = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) resource = self.RESOURCE_GET_MAP[order['type']]( order['resource_id'], region_name=self.region_name) if not resource: LOG.warn('The resource of the order(%s) not exists', order) continue now = datetime.datetime.utcnow() if order['date_time'] < now: should_delete_resources.append(resource) elif order['unit'] in ['month', 'year']: continue elif (order['type'] == const.RESOURCE_FLOATINGIP and not resource.is_reserved and order['owed']): should_delete_resources.append(resource) elif (resource.resource_type == const.RESOURCE_LISTENER and not resource.is_last_up and resource.admin_state != self.RESOURCE_STOPPED_STATE[order['type']]): should_stop_resources.append(resource) elif (resource.resource_type != const.RESOURCE_LISTENER and resource.status != self.RESOURCE_STOPPED_STATE[order['type']]): should_stop_resources.append(resource)
def _handle_monthly_order(self, order_id): LOG.warn("Handle monthly billing order: %s", order_id) try: with self._get_lock(order_id): # check resource and order before deduct order = self.gclient.get_order(order_id) # do not deduct doctor project for now if order['project_id'] in cfg.CONF.ignore_tenants: return method = self.RESOURCE_GET_MAP[order['type']] resource = method(order['resource_id'], order['region_id']) if not resource: LOG.warn("The resource(%s|%s) has been deleted", order['type'], order['resource_id']) return if order['renew_period'] > 1: remarks = "Renew for %s %ss" % \ (order['renew_period'], order['renew_method']) else: remarks = "Renew for %s %s" % \ (order['renew_period'], order['renew_method']) # deduct the order. result = self.gclient.create_bill(order_id, remarks=remarks) # if deduct successfully, create another monthly job. if result['type'] == const.BILL_NORMAL: if isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] months = utils.to_months(order['renew_method'], order['renew_period']) next_cron_time = utils.add_months(cron_time, months) self._create_monthly_job(order_id, next_cron_time) # if deduct failed because of not sufficient balance, # or the order's renew is not activated, stop the resource, # and create a date job to delete the resoruce. elif result['type'] == const.BILL_ORDER_OWED: self._stop_owed_resource(result['resource_type'], result['resource_id'], result['region_id']) self._create_date_job(order_id, result['resource_type'], result['resource_id'], result['region_id'], result['date_time']) except Exception as e: LOG.exception("Some exceptions happen when deducting order: %s, " "for reason: %s", order_id, e)
def _handle_monthly_order(self, order_id): LOG.warn("Handle monthly billing order: %s", order_id) try: with self._get_lock(order_id): # check resource and order before deduct order = self.gclient.get_order(order_id) # do not deduct doctor project for now if order['project_id'] in cfg.CONF.ignore_tenants: return method = self.RESOURCE_GET_MAP[order['type']] resource = method(order['resource_id'], order['region_id']) if not resource: LOG.warn("The resource(%s|%s) has been deleted", order['type'], order['resource_id']) return if order['renew_period'] > 1: remarks = "Renew for %s %ss" % \ (order['renew_period'], order['renew_method']) else: remarks = "Renew for %s %s" % \ (order['renew_period'], order['renew_method']) # deduct the order. result = self.gclient.create_bill(order_id, remarks=remarks) # if deduct successfully, create another monthly job. if result['type'] == const.BILL_NORMAL: if isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] months = utils.to_months(order['renew_method'], order['renew_period']) next_cron_time = utils.add_months(cron_time, months) self._create_monthly_job(order_id, next_cron_time) # if deduct failed because of not sufficient balance, # or the order's renew is not activated, stop the resource, # and create a date job to delete the resoruce. elif result['type'] == const.BILL_ORDER_OWED: self._stop_owed_resource(result['resource_type'], result['resource_id'], result['region_id']) self._create_date_job(order_id, result['resource_type'], result['resource_id'], result['region_id'], result['date_time']) except Exception as e: LOG.exception( "Some exceptions happen when deducting order: %s, " "for reason: %s", order_id, e)
def _pre_deduct(self, order_id): LOG.warn("Prededucting order: %s", order_id) try: with self._get_lock(order_id): # check resource and order before deduct order = self.gclient.get_order(order_id) # do not deduct doctor project for now if order['project_id'] in cfg.CONF.ignore_tenants: return method = self.RESOURCE_GET_MAP[order['type']] resource = method(order['resource_id'], order['region_id']) if not resource: LOG.warn("The resource(%s|%s) has been deleted", order['type'], order['resource_id']) return if isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] remarks = 'Hourly Billing' now = timeutils.utcnow() if now - cron_time >= datetime.timedelta(hours=1): result = self.gclient.create_bill(order_id, action_time=now, remarks=remarks) else: result = self.gclient.create_bill(order_id, remarks=remarks) # Order is owed if result['type'] == const.BILL_ORDER_OWED: self._stop_owed_resource(result['resource_type'], result['resource_id'], result['region_id']) self._create_date_job(order_id, result['resource_type'], result['resource_id'], result['region_id'], result['date_time']) # Account is charged but order is still owed elif result['type'] == const.BILL_OWED_ACCOUNT_CHARGED: self._delete_date_job(order_id) except Exception as e: LOG.warn("Some exceptions happen when deducting order: %s, " "for reason: %s", order_id, e)
def _pre_deduct(self, order_id): LOG.warn("Prededucting order: %s", order_id) try: with self._get_lock(order_id): # check resource and order before deduct order = self.gclient.get_order(order_id) # do not deduct doctor project for now if order['project_id'] in cfg.CONF.ignore_tenants: return method = self.RESOURCE_GET_MAP[order['type']] resource = method(order['resource_id'], order['region_id']) if not resource: LOG.warn("The resource(%s|%s) has been deleted", order['type'], order['resource_id']) return if isinstance(order['cron_time'], basestring): cron_time = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) else: cron_time = order['cron_time'] remarks = 'Hourly Billing' now = timeutils.utcnow() if now - cron_time >= datetime.timedelta(hours=1): result = self.gclient.create_bill(order_id, action_time=now, remarks=remarks) else: result = self.gclient.create_bill(order_id, remarks=remarks) # Order is owed if result['type'] == const.BILL_ORDER_OWED: self._stop_owed_resource(result['resource_type'], result['resource_id'], result['region_id']) self._create_date_job(order_id, result['resource_type'], result['resource_id'], result['region_id'], result['date_time']) # Account is charged but order is still owed elif result['type'] == const.BILL_OWED_ACCOUNT_CHARGED: self._delete_date_job(order_id) except Exception as e: LOG.warn( "Some exceptions happen when deducting order: %s, " "for reason: %s", order_id, e)
def _create_date_job(self, order_id, resource_type, resource_id, region_id, action_time): """Create a date job to delete the owed resource This job will be executed once, the job id is resource_id. """ job_id = self._make_date_job_id(order_id) if isinstance(action_time, basestring): action_time = timeutils.parse_strtime(action_time, fmt=ISO8601_UTC_TIME_FORMAT) self.apsched.add_job(self._delete_owed_resource, 'date', args=[resource_type, resource_id, region_id], id=job_id, run_date=action_time, replace_existing=True) LOG.warn('create date job for order: %s', order_id)
def _create_cron_job(self, order_id, action_time=None, start_date=None): """Create a interval job to deduct the order This kind of job will be executed periodically, the job id is order_id """ job_id = self._make_cron_job_id(order_id) if action_time: action_time = timeutils.parse_strtime(action_time, fmt=TIMESTAMP_TIME_FORMAT) start_date = action_time + datetime.timedelta(hours=1) self.apsched.add_job(self._pre_deduct, 'interval', args=[order_id], id=job_id, hours=1, start_date=start_date) LOG.warn('create cron job for order: %s', order_id)
def get_unit_price(self, order_id, message, status, cron_time=None): unit_price = 0 if isinstance(cron_time, basestring): cron_time = timeutils.parse_strtime(cron_time, fmt=ISO8601_UTC_TIME_FORMAT) # delta = cron_time - timeutils.utcnow() # if status == const.STATE_STOPPED and delta > datetime.timedelta(hours=1): # return 0 # didn't check the stopped instance for now if status == const.STATE_STOPPED: return for ext in self.product_items.extensions: if ext.name.startswith(status): unit_price += ext.obj.get_unit_price(order_id, message) return unit_price
def get_unit_price(self, order_id, message, status, cron_time=None): unit_price = 0 if isinstance(cron_time, basestring): cron_time = timeutils.parse_strtime(cron_time, fmt=ISO8601_UTC_TIME_FORMAT) #delta = cron_time - timeutils.utcnow() #if status == const.STATE_STOPPED and delta > datetime.timedelta(hours=1): # return 0 # didn't check the stopped instance for now if status == const.STATE_STOPPED: return for ext in self.product_items.extensions: if ext.name.startswith(status): unit_price += ext.obj.get_unit_price(order_id, message) return unit_price
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 check_owed_order_resources_and_notify(self): #noqa """Check order-billing resources and notify related accounts Send sms/email notifications for each has-owed or will-owed order. """ 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 = ['month', 'year'] for account in accounts: try: if not isinstance(account, dict): account = account.as_dict() if account['level'] == 9: 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" account['reserved_days'] = utils.cal_reserved_days(account['level']) orders = list( self.gclient.get_active_orders( user_id=account['user_id'], bill_methods=bill_methods) ) 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: # warning that 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'] order_d['owed'] = order['owed'] if isinstance(order['date_time'], basestring): order['date_time'] = timeutils.parse_strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) if isinstance(order['cron_time'], basestring): order['cron_time'] = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) # if order is owed, reserved_days represent how long resource will be reserved; # if not, reserved_days repesent how long resource will be expired now = datetime.datetime.utcnow() if order['owed']: reserved_days = (order['date_time'] - now).days else: reserved_days = (order['cron_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) order_d['cron_time'] = timeutils.strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) is_notify_will_owed = (order_d['reserved_days'] <= cfg.CONF.checker.days_to_owe) if order_d['owed'] or (not order_d['owed'] and is_notify_will_owed): self.notifier.notify_order_billing_owed(self.ctxt, account, contact, order_d, language=language) except Exception: LOG.exception("Some exceptions occurred when checking owed " "account: %s", account['user_id'])
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 check_owed_order_resources_and_notify(self): #noqa """Check order-billing resources and notify related accounts Send sms/email notifications for each has-owed or will-owed order. """ 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 = ['month', 'year'] for account in accounts: try: if not isinstance(account, dict): account = account.as_dict() if account['level'] == 9: 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" account['reserved_days'] = utils.cal_reserved_days( account['level']) orders = list( self.gclient.get_active_orders(user_id=account['user_id'], bill_methods=bill_methods)) 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: # warning that 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'] order_d['owed'] = order['owed'] if isinstance(order['date_time'], basestring): order['date_time'] = timeutils.parse_strtime( order['date_time'], fmt=ISO8601_UTC_TIME_FORMAT) if isinstance(order['cron_time'], basestring): order['cron_time'] = timeutils.parse_strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) # if order is owed, reserved_days represent how long resource will be reserved; # if not, reserved_days repesent how long resource will be expired now = datetime.datetime.utcnow() if order['owed']: reserved_days = (order['date_time'] - now).days else: reserved_days = (order['cron_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) order_d['cron_time'] = timeutils.strtime( order['cron_time'], fmt=ISO8601_UTC_TIME_FORMAT) is_notify_will_owed = (order_d['reserved_days'] <= cfg.CONF.checker.days_to_owe) if order_d['owed'] or (not order_d['owed'] and is_notify_will_owed): self.notifier.notify_order_billing_owed( self.ctxt, account, contact, order_d, language=language) except Exception: LOG.exception( "Some exceptions occurred when checking owed " "account: %s", account['user_id'])