def regenerate_sales_order_range(tenant_id, start, end): """For all sales orders in a given range, generate sales order dicts, and return them.""" session = Session() db = database.Database(session) rates = RatesFile(config.rates_config) valid_tenant = validate_tenant_id(tenant_id, session) if isinstance(valid_tenant, tuple): return valid_tenant sales_orders = db.get_sales_orders(tenant_id, start, end) tenants = [] for sales_order in sales_orders: usage = db.usage(sales_order.start, sales_order.end, tenant_id) # Transform the query result into a billable dict. tenant_dict = build_tenant_dict(valid_tenant, usage, db) tenant_dict = add_costs_for_tenant(tenant_dict, rates) # add sales order range: tenant_dict['start'] = str(sales_order.start) tenant_dict['end'] = str(sales_order.end) tenants.append(tenant_dict) return 200, tenants
def regenerate_sales_order(tenant_id, target): """Finds a sales order entry nearest to the target, and returns a salesorder dict based on the entry.""" session = Session() db = database.Database(session) rates = RatesFile(config.rates_config) valid_tenant = validate_tenant_id(tenant_id, session) if isinstance(valid_tenant, tuple): return valid_tenant try: sales_order = db.get_sales_orders(tenant_id, target, target)[0] except IndexError: return 400, {"errors": ["Given date not in existing sales orders."]} usage = db.usage(sales_order.start, sales_order.end, tenant_id) # Transform the query result into a billable dict. tenant_dict = build_tenant_dict(valid_tenant, usage, db) tenant_dict = add_costs_for_tenant(tenant_dict, rates) # add sales order range: tenant_dict['start'] = str(sales_order.start) tenant_dict['end'] = str(sales_order.end) return 200, tenant_dict
def get_usage(): """ Get raw aggregated usage for a tenant, in a given timespan. - No rates are applied. - No conversion from collection unit to billing unit - No rounding """ tenant_id = flask.request.args.get('tenant') start = flask.request.args.get('start') end = flask.request.args.get('end') LOG.info("get_usage for %s %s %s" % (tenant_id, start, end)) try: start_dt = datetime.strptime(end, iso_time) except ValueError: return 400, {'error': 'Invalid start datetime'} try: end_dt = datetime.strptime(end, iso_time) except ValueError: return 400, {'error': 'Invalid end datetime'} if end_dt < start_dt: return 400, {'error': 'End must be after start'} session = Session() db = database.Database(session) valid_tenant = validate_tenant_id(tenant_id, session) if isinstance(valid_tenant, tuple): return valid_tenant LOG.info("parameter validation ok") if memcache is not None: key = make_key("raw_usage", tenant_id, start, end) data = memcache.get(key) if data is not None: LOG.info("Returning memcache raw data for %s in range: %s - %s" % (tenant_id, start, end)) return 200, data LOG.info("Calculating raw data for %s in range: %s - %s" % (tenant_id, start, end)) # aggregate usage usage = db.usage(start, end, tenant_id) tenant_dict = build_tenant_dict(valid_tenant, usage, db) response_json = json.dumps({'usage': make_serializable(tenant_dict)}) if memcache is not None: memcache.set(key, response_json) return 200, response_json
def test_get_from_db(self): """Test to ensure the data in the database matches the data entered.""" num_resources = 32 num_tenants = 5 utils.init_db(self.session, num_tenants, num_resources, self.end) db = database.Database(self.session) for i in range(num_tenants): usage = db.usage(self.start, self.start + timedelta(days=60), "tenant_id_" + str(i)) self.assertEqual(usage.count(), num_resources)
def run_usage_collection(): """Run usage collection on all tenants present in Keystone.""" try: log.info("Usage collection run started.") session = Session() interface = Interface() reset_cache() db = database.Database(session) end = datetime.utcnow().\ replace(minute=0, second=0, microsecond=0) tenants = interface.tenants resp = {"tenants": [], "errors": 0} run_once = False for tenant in tenants: if collect_usage(tenant, db, session, resp, end): run_once = True if (run_once): session.begin() last_run = session.query(_Last_Run) if last_run.count() == 0: last_run = _Last_Run(last_run=end) session.add(last_run) session.commit() else: last_run[0].last_run = end session.commit() session.close() log.info("Usage collection run complete.") return json.dumps(resp) except Exception as e: import traceback trace = traceback.format_exc() log.critical('Exception escaped! %s \nTrace: \n%s' % (e, trace))
def calculate_rated_data(tenant, start, end, session): """Calculate a rated data dict from the given range.""" db = database.Database(session) global RATES if not RATES: RATES = RatesFile(config.rates_config) usage = db.usage(start, end, tenant.id) # Transform the query result into a billable dict. tenant_dict = build_tenant_dict(tenant, usage, db) tenant_dict = add_costs_for_tenant(tenant_dict, RATES) # add sales order range: tenant_dict['start'] = str(start) tenant_dict['end'] = str(end) return tenant_dict
def get_usage(): """ Get raw aggregated usage for a tenant, in a given timespan. - No rates are applied. - No conversion from collection unit to billing unit - No rounding """ tenant_id = flask.request.args.get('tenant') start = flask.request.args.get('start') end = flask.request.args.get('end') log.info("get_usage for %s %s %s" % (tenant_id, start, end)) try: start_dt = datetime.strptime(end, iso_time) except ValueError: return 400, {'error': 'Invalid start datetime'} try: end_dt = datetime.strptime(end, iso_time) except ValueError: return 400, {'error': 'Invalid end datetime'} if end_dt < start_dt: return 400, {'error': 'End must be after start'} session = Session() db = database.Database(session) valid_tenant = validate_tenant_id(tenant_id, session) if isinstance(valid_tenant, tuple): return valid_tenant log.info("parameter validation ok") # aggregate usage usage = db.usage(start, end, tenant_id) tenant_dict = build_tenant_dict(valid_tenant, usage, db) return 200, {'usage': make_serializable(tenant_dict)}
def generate_sales_order(draft, tenant_id, end): """Generates a sales order dict, and unless draft is true, creates a database entry for sales_order.""" session = Session() db = database.Database(session) valid_tenant = validate_tenant_id(tenant_id, session) if isinstance(valid_tenant, tuple): return valid_tenant rates = RatesFile(config.rates_config) # Get the last sales order for this tenant, to establish # the proper ranging start = session.query(func.max(SalesOrder.end).label('end')).\ filter(SalesOrder.tenant_id == tenant_id).first().end if not start: start = dawn_of_time # these coditionals need work, also some way to # ensure all given timedate values are in UTC? if end <= start: return 400, { "errors": [ "end date must be greater than " + "the end of the last sales order range." ] } if end > datetime.utcnow(): return 400, {"errors": ["end date cannot be a future date."]} usage = db.usage(start, end, tenant_id) session.begin() if not draft: order = SalesOrder(tenant_id=tenant_id, start=start, end=end) session.add(order) try: # Commit the record before we generate the bill, to mark this as a # billed region of data. Avoids race conditions by marking a tenant # BEFORE we start to generate the data for it. session.commit() # Transform the query result into a billable dict. tenant_dict = build_tenant_dict(valid_tenant, usage, db) tenant_dict = add_costs_for_tenant(tenant_dict, rates) # add sales order range: tenant_dict['start'] = str(start) tenant_dict['end'] = str(end) session.close() if not draft: log.info("Sales Order #%s Generated for %s in range: %s - %s" % (order.id, tenant_id, start, end)) return 200, tenant_dict except (IntegrityError, OperationalError): session.rollback() session.close() log.warning("IntegrityError creating sales-order for " + "%s %s in range: %s - %s " % (valid_tenant.name, valid_tenant.id, start, end)) return 400, { "id": tenant_id, "error": "IntegrityError, existing sales_order overlap." }