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 listener_list(project_id, region_name=None, project_name=None): client = get_neutronclient(region_name) if project_id: listeners = client.list_listeners( tenant_id=project_id).get('listeners') else: listeners = client.list_listeners().get('listeners') formatted_listeners = [] for listener in listeners: status = utils.transform_status(listener['status']) admin_state = (const.STATE_RUNNING if listener['admin_state_up'] else const.STATE_STOPPED) created_at = utils.format_datetime( listener.get('created_at', timeutils.strtime())) formatted_listeners.append( Listener(id=listener['id'], name=listener['name'], admin_state_up=listener['admin_state_up'], admin_state=admin_state, connection_limit=listener['connection_limit'], project_id=listener['tenant_id'], project_name=project_name, resource_type=const.RESOURCE_LISTENER, status=status, original_status=listener['status'], created_at=created_at)) return formatted_listeners
def to_message(self): msg = { "event_type": "router.create.end.again", "payload": {"router": {"id": self.id, "name": self.name, "tenant_id": self.project_id}}, "timestamp": utils.format_datetime(timeutils.strtime()), } return msg
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 listener_list(project_id, region_name=None, project_name=None): client = get_neutronclient(region_name) if project_id: listeners = client.list_listeners(tenant_id=project_id).get("listeners") else: listeners = client.list_listeners().get("listeners") formatted_listeners = [] for listener in listeners: status = utils.transform_status(listener["status"]) admin_state = const.STATE_RUNNING if listener["admin_state_up"] else const.STATE_STOPPED created_at = utils.format_datetime(listener.get("created_at", timeutils.strtime())) formatted_listeners.append( Listener( id=listener["id"], name=listener["name"], admin_state_up=listener["admin_state_up"], admin_state=admin_state, connection_limit=listener["connection_limit"], project_id=listener["tenant_id"], project_name=project_name, resource_type=const.RESOURCE_LISTENER, status=status, original_status=listener["status"], created_at=created_at, ) ) return formatted_listeners
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 restore_resource(self, data): action_time = \ gringutils.format_datetime(timeutils.strtime(timeutils.utcnow())) remarks = '%s Has Been Restored' % data.resource_type.capitalize() self.master_api.resource_restore(request.context, self._id, action_time, remarks)
def delete_resource(self, data): """Update the order when delete the resource""" action_time = \ gringutils.format_datetime(timeutils.strtime(timeutils.utcnow())) remarks = '%s Has Been Deleted' % data.resource_type.capitalize() self.master_api.resource_deleted(request.context, self._id, action_time, remarks)
def stop_resource(self, data): action_time = \ gringutils.format_datetime(timeutils.strtime(timeutils.utcnow())) if data.resource_type == 'instance': self.master_api.instance_stopped(request.context, self._id, action_time) else: remarks = '%s Has Been Stopped' % data.resource_type.capitalize() self.master_api.resource_stopped(request.context, self._id, action_time, remarks)
def _check_order_to_resource(self, resource_id, order, try_to_fix): # if the order is billed by month or year, we don't check it if order['unit'] in ['month', 'year']: LOG.warn("The monthly/yearly billing resource %s has been " "deleted, the order is %s, please check it manually", resource_id, order['order_id']) else: # Situation 4: The resource of the order has been deleted deleted_at = utils.format_datetime(timeutils.strtime()) try_to_fix['4'].append(Situation4Item(order['order_id'], deleted_at, order['project_id']))
def to_message(self): msg = { 'event_type': 'image.activate.again', 'payload': { 'id': self.id, 'name': self.name, 'size': self.size, 'owner': self.project_id, }, 'timestamp': utils.format_datetime(timeutils.strtime()) } return msg
def to_message(self): msg = { "event_type": "alarm.creation.again", "payload": { "alarm_id": self.id, "detail": {"name": self.name}, "user_id": self.user_id, "project_id": self.project_id, }, "timestamp": utils.format_datetime(timeutils.strtime()), } return msg
def to_message(self): msg = { 'event_type': 'router.create.end.again', 'payload': { 'router': { 'id': self.id, 'name': self.name, 'tenant_id': self.project_id } }, 'timestamp': utils.format_datetime(timeutils.strtime()) } return msg
def to_message(self): msg = { 'event_type': 'snapshot.create.end.again', 'payload': { 'snapshot_id': self.id, 'display_name': self.name, 'volume_size': self.size, 'user_id': self.user_id, 'tenant_id': self.project_id }, 'timestamp': utils.format_datetime(timeutils.strtime()) } return msg
def to_message(self): msg = { 'event_type': 'alarm.creation.again', 'payload': { 'alarm_id': self.id, 'detail': { 'name': self.name, }, 'user_id': self.user_id, 'project_id': self.project_id }, 'timestamp': utils.format_datetime(timeutils.strtime()) } return msg
def to_message(self): msg = { "event_type": "listener.create.end", "payload": { "listener": { "id": self.id, "name": self.name, "admin_state_up": self.admin_state_up, "connection_limit": self.connection_limit, "tenant_id": self.project_id, } }, "timestamp": utils.format_datetime(timeutils.strtime()), } return msg
def to_message(self): msg = { "event_type": "floatingipset.create.end.again", "payload": { "floatingipset": { "id": self.id, "uos:name": self.name, "uos:service_provider": self.providers, "rate_limit": self.size, "tenant_id": self.project_id, } }, "timestamp": utils.format_datetime(timeutils.strtime()), } return msg
def to_message(self): msg = { 'event_type': 'listener.create.end', 'payload': { 'listener': { 'id': self.id, 'name': self.name, 'admin_state_up': self.admin_state_up, 'connection_limit': self.connection_limit, 'tenant_id': self.project_id } }, 'timestamp': utils.format_datetime(timeutils.strtime()) } return msg
def to_message(self): msg = { 'event_type': 'floatingipset.create.end.again', 'payload': { 'floatingipset': { 'id': self.id, 'uos:name': self.name, 'uos:service_provider': self.providers, 'rate_limit': self.size, 'tenant_id': self.project_id } }, 'timestamp': utils.format_datetime(timeutils.strtime()) } return msg
def resize_resource(self, data): """Update the order when resize the resource""" action_time = \ gringutils.format_datetime(timeutils.strtime(timeutils.utcnow())) remarks = '%s Has Been Resized' % data.resource_type.capitalize() self._validate_resize(data.as_dict()) if data.resource_type == 'instance': self.master_api.instance_resized(request.context, self._id, action_time, data.new_flavor, data.old_flavor, data.service, data.region_id, remarks) else: self.master_api.resource_resized(request.context, self._id, action_time, data.quantity, remarks)
def to_message(self): msg = { 'event_type': 'compute.instance.create.end.again', 'payload': { 'instance_type': self.flavor_name, 'disk_gb': self.disk_gb, 'instance_id': self.id, 'display_name': self.name, 'user_id': self.user_id, 'tenant_id': self.project_id, 'image_name': self.image_name, 'image_meta': { 'base_image_ref': self.image_id } }, 'timestamp': utils.format_datetime(timeutils.strtime()) } return msg
def post(self, data): conn = pecan.request.db_conn try: order = conn.create_order(request.context, **data.as_dict()) if order.unit in ['month', 'year']: self.master_api.create_monthly_job( request.context, order.order_id, timeutils.isotime(order.cron_time)) else: action_time = \ gringutils.format_datetime( timeutils.strtime(timeutils.utcnow())) remarks = '%s Has Been Created.' % order.type.capitalize() self.master_api.resource_created(request.context, order.order_id, action_time, remarks) except Exception as e: LOG.exception('Fail to create order: %s, for reason %s' % (data.as_dict(), e))
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 get_all(self, output_format='xlsx', type=None, status=None, start_time=None, end_time=None, limit=None, offset=None, region_id=None, project_id=None, user_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. """ limit_user_id = acl.get_limited_to_user(request.headers, 'export_orders') if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") 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 and project_id in _project_ids: project_ids = [project_id] 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)) users = {} projects = {} def _get_user(user_id): user = users.get(user_id) if user: return user contact = keystone.get_uos_user(user_id) user_name = contact['name'] if contact else None users[user_id] = models.User(user_id=user_id, user_name=user_name) return users[user_id] def _get_project(project_id): project = projects.get(project_id) if project: return project project = keystone.get_project(project_id) project_name = project.name if project else None projects[project_id] = models.SimpleProject( project_id=project_id, project_name=project_name ) return projects[project_id] MAP = [ {"running": u"运行中", "stopped": u"暂停中", "deleted": u"被删除"}, {"instance": u"虚拟机", "image": u"镜像", "snapshot": u"硬盘快照", "volume": u"云硬盘", "share": u"共享文件", "floatingip": u"公网IP", "listener": u"负载均衡监听器", "router": u"路由器", "alarm": u"监控报警"}, ] headers = (u"资源ID", u"资源名称", u"资源类型", u"资源状态", u"单价(元/小时)", u"金额(元)", u"区域", u"用户ID", u"用户名称", u"项目ID", u"项目名称", u"创建时间") data = [] adata = (u"过滤条件: 资源类型: %s, 资源状态: %s,用户ID: %s, 项目ID: %s, 区域: %s, 起始时间: %s, 结束时间: %s" % (type, status, user_id, project_id, region_id, start_time, end_time), "", "", "", "", "", "", "", "", "", "", "") data.append(adata) 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, user_id=user_id, project_ids=project_ids) for order in orders_db: price = self._get_order_price(order, start_time=start_time, end_time=end_time) user = _get_user(order.user_id) project = _get_project(order.project_id) order.created_at += datetime.timedelta(hours=8) created_at = \ timeutils.strtime(order.created_at, fmt=OUTPUT_TIME_FORMAT) adata = (order.resource_id, order.resource_name, MAP[1][order.type], MAP[0][order.status], order.unit_price, price, order.region_id, user.user_id, user.user_name, project.project_id, project.project_name, created_at) data.append(adata) data = tablib.Dataset(*data, headers=headers) response.content_type = "application/binary; charset=UTF-8" response.content_disposition = \ "attachment; filename=orders.%s" % output_format content = getattr(data, output_format) if output_format == 'csv': content = content.decode("utf-8").encode("gb2312") response.write(content) return response
def start_resource(self, data): action_time = \ gringutils.format_datetime(timeutils.strtime(timeutils.utcnow())) remarks = '%s Has Been Started' % data.resource_type.capitalize() self.master_api.resource_started(request.context, self._id, action_time, remarks)
def get_all(self, output_format='xlsx', type=None, status=None, start_time=None, end_time=None, limit=None, offset=None, region_id=None, project_id=None, user_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. """ limit_user_id = acl.get_limited_to_user(request.headers, 'export_orders') if limit and limit < 0: raise exception.InvalidParameterValue(err="Invalid limit") if offset and offset < 0: raise exception.InvalidParameterValue(err="Invalid offset") 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 and project_id in _project_ids: project_ids = [project_id] 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)) users = {} projects = {} def _get_user(user_id): user = users.get(user_id) if user: return user contact = kunkka.get_uos_user(user_id) user_name = contact['name'] if contact else None users[user_id] = models.User(user_id=user_id, user_name=user_name) return users[user_id] def _get_project(project_id): project = projects.get(project_id) if project: return project try: project = keystone.get_project(project_id) project_name = project.name if project else None projects[project_id] = models.SimpleProject( project_id=project_id, project_name=project_name) return projects[project_id] except Exception as e: # Note(chengkun): some project was deleted from keystone, # But the project's order still in the gringotts. so when # we get the order it will raise 404 project not found error LOG.error('error to get project: %s' % e) return None MAP = [ { "running": u"运行中", "stopped": u"暂停中", "deleted": u"被删除" }, { "instance": u"虚拟机", "image": u"镜像", "snapshot": u"硬盘快照", "volume": u"云硬盘", "share": u"共享文件", "floatingip": u"公网IP", "listener": u"负载均衡监听器", "router": u"路由器", "alarm": u"监控报警" }, ] headers = (u"资源ID", u"资源名称", u"资源类型", u"资源状态", u"单价(元/小时)", u"金额(元)", u"区域", u"用户ID", u"用户名称", u"项目ID", u"项目名称", u"创建时间") data = [] adata = ( u"过滤条件: 资源类型: %s, 资源状态: %s,用户ID: %s, 项目ID: %s, 区域: %s, 起始时间: %s, 结束时间: %s" % (type, status, user_id, project_id, region_id, start_time, end_time), "", "", "", "", "", "", "", "", "", "", "") data.append(adata) 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, user_id=user_id, project_ids=project_ids) for order in orders_db: price = self._get_order_price(order, start_time=start_time, end_time=end_time) user = _get_user(order.user_id) project = _get_project(order.project_id) if project is None: continue order.created_at += datetime.timedelta(hours=8) created_at = \ timeutils.strtime(order.created_at, fmt=OUTPUT_TIME_FORMAT) adata = (order.resource_id, order.resource_name, MAP[1][order.type], MAP[0][order.status], order.unit_price, price, order.region_id, user.user_id, user.user_name, project.project_id, project.project_name, created_at) data.append(adata) data = tablib.Dataset(*data, headers=headers) response.content_type = "application/binary; charset=UTF-8" response.content_disposition = \ "attachment; filename=orders.%s" % output_format content = getattr(data, output_format) if output_format == 'csv': content = content.decode("utf-8").encode("gb2312") response.write(content) return response
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 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_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 get(self, output_format='xlsx', user_id=None, start_time=None, end_time=None, limit=None, offset=None): """Export all charges of special user, output formats supported: * Excel (Sets + Books) * JSON (Sets + Books) * YAML (Sets + Books) * HTML (Sets) * TSV (Sets) * CSV (Sets) """ if output_format.lower() not in ["xls", "xlsx", "csv", "json", "yaml"]: raise exception.InvalidOutputFormat(output_format=output_format) 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, 'export_charges') if limit_user_id: user_id = limit_user_id headers = (u"充值记录ID", u"充值对象用户名", u"充值对象ID", u"充值对象真实姓名", u"充值对象邮箱", u"充值对象公司", u"充值金额", u"充值类型", u"充值来源", u"充值人员ID", u"充值人员用户名", u"充值时间", u"状态") data = [] 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') or 'unknown' mobile = contact.get('phone') or 'unknown' company = contact.get('company') or 'unknown' 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, limit=limit, offset=offset, start_time=start_time, end_time=end_time) for charge in charges: charge.charge_time += datetime.timedelta(hours=8) acharge = models.Charge.from_db_model(charge) acharge.actor = _get_user(charge.operator) acharge.target = _get_user(charge.user_id) charge_time = \ timeutils.strtime(charge.charge_time, fmt=OUTPUT_TIME_FORMAT) adata = (acharge.charge_id, acharge.target.user_name, acharge.target.user_id, acharge.target.real_name, acharge.target.email, acharge.target.company, str(acharge.value), acharge.type, acharge.come_from, acharge.actor.user_id, acharge.actor.user_name, charge_time, u"正常") data.append(adata) data = tablib.Dataset(*data, headers=headers) response.content_type = "application/binary; charset=UTF-8" response.content_disposition = \ "attachment; filename=charges.%s" % output_format content = getattr(data, output_format) if output_format == 'csv': content = content.decode("utf-8").encode("gb2312") response.write(content) return response
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 get(self, output_format='xlsx', user_id=None, start_time=None, end_time=None, limit=None, offset=None): """Export all charges of special user, output formats supported: * Excel (Sets + Books) * JSON (Sets + Books) * YAML (Sets + Books) * HTML (Sets) * TSV (Sets) * CSV (Sets) """ if output_format.lower() not in ["xls", "xlsx", "csv", "json", "yaml"]: raise exception.InvalidOutputFormat(output_format=output_format) 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, 'export_charges') if limit_user_id: user_id = limit_user_id headers = (u"充值记录ID", u"充值对象用户名", u"充值对象ID", u"充值对象真实姓名", u"充值对象邮箱", u"充值对象公司", u"充值金额", u"充值类型", u"充值来源", u"充值人员ID", u"充值人员用户名", u"充值时间", u"状态") data = [] 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') or 'unknown' mobile = contact.get('mobile_number') or 'unknown' company = contact.get('company') or 'unknown' 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, limit=limit, offset=offset, start_time=start_time, end_time=end_time) for charge in charges: charge.charge_time += datetime.timedelta(hours=8) acharge = models.Charge.from_db_model(charge) acharge.actor = _get_user(charge.operator) acharge.target = _get_user(charge.user_id) charge_time = \ timeutils.strtime(charge.charge_time, fmt=OUTPUT_TIME_FORMAT) adata = (acharge.charge_id, acharge.target.user_name, acharge.target.user_id, acharge.target.real_name, acharge.target.email, acharge.target.company, str(acharge.value), acharge.type, acharge.come_from, acharge.actor.user_id, acharge.actor.user_name, charge_time, u"正常") data.append(adata) data = tablib.Dataset(*data, headers=headers) response.content_type = "application/binary; charset=UTF-8" response.content_disposition = \ "attachment; filename=charges.%s" % output_format content = getattr(data, output_format) if output_format == 'csv': content = content.decode("utf-8").encode("gb2312") response.write(content) return response
def to_primitive(value, convert_instances=False, convert_datetime=True, level=0, max_depth=3): """Convert a complex object into primitives. Handy for JSON serialization. We can optionally handle instances, but since this is a recursive function, we could have cyclical data structures. To handle cyclical data structures we could track the actual objects visited in a set, but not all objects are hashable. Instead we just track the depth of the object inspections and don't go too deep. Therefore, convert_instances=True is lossy ... be aware. """ # handle obvious types first - order of basic types determined by running # full tests on nova project, resulting in the following counts: # 572754 <type 'NoneType'> # 460353 <type 'int'> # 379632 <type 'unicode'> # 274610 <type 'str'> # 199918 <type 'dict'> # 114200 <type 'datetime.datetime'> # 51817 <type 'bool'> # 26164 <type 'list'> # 6491 <type 'float'> # 283 <type 'tuple'> # 19 <type 'long'> if isinstance(value, _simple_types): return value if isinstance(value, datetime.datetime): if convert_datetime: return timeutils.strtime(value) else: return value # value of itertools.count doesn't get caught by nasty_type_tests # and results in infinite loop when list(value) is called. if type(value) == itertools.count: return six.text_type(value) # FIXME(vish): Workaround for LP bug 852095. Without this workaround, # tests that raise an exception in a mocked method that # has a @wrap_exception with a notifier will fail. If # we up the dependency to 0.5.4 (when it is released) we # can remove this workaround. if getattr(value, '__module__', None) == 'mox': return 'mock' if level > max_depth: return '?' # The try block may not be necessary after the class check above, # but just in case ... try: recursive = functools.partial(to_primitive, convert_instances=convert_instances, convert_datetime=convert_datetime, level=level, max_depth=max_depth) if isinstance(value, dict): return dict((k, recursive(v)) for k, v in value.iteritems()) elif isinstance(value, (list, tuple)): return [recursive(lv) for lv in value] # It's not clear why xmlrpclib created their own DateTime type, but # for our purposes, make it a datetime type which is explicitly # handled if xmlrpclib and isinstance(value, xmlrpclib.DateTime): value = datetime.datetime(*tuple(value.timetuple())[:6]) if convert_datetime and isinstance(value, datetime.datetime): return timeutils.strtime(value) elif isinstance(value, gettextutils.Message): return value.data elif hasattr(value, 'iteritems'): return recursive(dict(value.iteritems()), level=level + 1) elif hasattr(value, '__iter__'): return recursive(list(value)) elif convert_instances and hasattr(value, '__dict__'): # Likely an instance of something. Watch for cycles. # Ignore class member vars. return recursive(value.__dict__, level=level + 1) elif netaddr and isinstance(value, netaddr.IPAddress): return six.text_type(value) else: if any(test(value) for test in _nasty_type_tests): return six.text_type(value) return value except TypeError: # Class objects are tricky since they may define something like # __iter__ defined but it isn't callable as list(). return six.text_type(value)