Basic fixes to get artifice working. Most just hacky workarounds to get it running for demo purposes.
Some few useful bug fixes such as replacement of password without "\n", and issues with "flavor.name" vs "instance_type".
This commit is contained in:
parent
b816b1de49
commit
67aaf5f5ab
@ -6,13 +6,13 @@ from decimal import *
|
|||||||
|
|
||||||
class Csv(invoice.RatesFileMixin, invoice.NamesFileMixin, invoice.Invoice):
|
class Csv(invoice.RatesFileMixin, invoice.NamesFileMixin, invoice.Invoice):
|
||||||
|
|
||||||
def __init__(self, tenant, config):
|
def __init__(self, tenant, start, end, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.tenant = tenant
|
self.tenant = tenant
|
||||||
self.lines = []
|
self.lines = []
|
||||||
self.closed = False
|
self.closed = False
|
||||||
self.start = None
|
self.start = start
|
||||||
self.end = None
|
self.end = end
|
||||||
# This has been moved to the mixin
|
# This has been moved to the mixin
|
||||||
# try:
|
# try:
|
||||||
# fh = open(config["rates"][ "file" ])
|
# fh = open(config["rates"][ "file" ])
|
||||||
@ -37,6 +37,8 @@ class Csv(invoice.RatesFileMixin, invoice.NamesFileMixin, invoice.Invoice):
|
|||||||
appendee.append(None)
|
appendee.append(None)
|
||||||
continue
|
continue
|
||||||
# What do we expect element to be?
|
# What do we expect element to be?
|
||||||
|
if key == "rate":
|
||||||
|
appendee.append(self.rate(self.pretty_name(element.type)))
|
||||||
if key == "type":
|
if key == "type":
|
||||||
# Fetch the 'pretty' name from the mappings, if any
|
# Fetch the 'pretty' name from the mappings, if any
|
||||||
# The default is that this returns the internal name
|
# The default is that this returns the internal name
|
||||||
@ -50,8 +52,17 @@ class Csv(invoice.RatesFileMixin, invoice.NamesFileMixin, invoice.Invoice):
|
|||||||
x = self.config["row_layout"].index("cost")
|
x = self.config["row_layout"].index("cost")
|
||||||
appendee[ x ] = element.amount.volume() * \
|
appendee[ x ] = element.amount.volume() * \
|
||||||
self.rate( self.pretty_name(element.type) )
|
self.rate( self.pretty_name(element.type) )
|
||||||
print self.rate(self.pretty_name(element.type))
|
# print (str(appendee[1]) + " - From: " + str(appendee[2]) +
|
||||||
print appendee
|
# ", Until: " + str(appendee[3]))
|
||||||
|
print ("type: " + str(self.pretty_name(element.get("type"))))
|
||||||
|
try:
|
||||||
|
print " - name : " + str(element.get("name"))
|
||||||
|
except:
|
||||||
|
# Just means it isn't a VM
|
||||||
|
pass
|
||||||
|
print " - usage: " + str(element.amount.volume())
|
||||||
|
print " - rate: " + str(self.rate(self.pretty_name(element.type)))
|
||||||
|
print " - cost: " + str(appendee[ x ])
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Not in this array. Well okay.
|
# Not in this array. Well okay.
|
||||||
@ -70,10 +81,11 @@ class Csv(invoice.RatesFileMixin, invoice.NamesFileMixin, invoice.Invoice):
|
|||||||
def filename(self):
|
def filename(self):
|
||||||
fn = os.path.join(
|
fn = os.path.join(
|
||||||
self.config["output_path"],
|
self.config["output_path"],
|
||||||
self.config["output_file"] % dict(tenant=self.tenant,
|
self.config["output_file"] % dict(tenant=self.tenant.tenant['name'],
|
||||||
start=self.start, end=self.end)
|
start=self.start, end=self.end)
|
||||||
)
|
)
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
try:
|
try:
|
||||||
read = open( self.filename )
|
read = open( self.filename )
|
||||||
@ -81,12 +93,24 @@ class Csv(invoice.RatesFileMixin, invoice.NamesFileMixin, invoice.Invoice):
|
|||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
fh = open(self.filename, "w")
|
fh = open(self.filename, "w")
|
||||||
|
|
||||||
csvwriter = writer(fh, dialect='excel', delimiter=',')
|
csvwriter = writer(fh, dialect='excel', delimiter=',')
|
||||||
|
|
||||||
|
csvwriter.writerow(["", "from", "until"])
|
||||||
|
csvwriter.writerow(["usage range: ", str(self.start), str(self.end)])
|
||||||
|
csvwriter.writerow([])
|
||||||
|
|
||||||
|
|
||||||
|
csvwriter.writerow(self.config["row_layout"])
|
||||||
for line in self.lines:
|
for line in self.lines:
|
||||||
# Line is expected to be an iterable row
|
# Line is expected to be an iterable row
|
||||||
csvwriter.writerow(line)
|
csvwriter.writerow(line)
|
||||||
|
|
||||||
|
# write a blank line
|
||||||
|
csvwriter.writerow([])
|
||||||
|
# write total
|
||||||
|
total = ["total: ", self.total()]
|
||||||
|
csvwriter.writerow(total)
|
||||||
|
|
||||||
fh.close()
|
fh.close()
|
||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import requests
|
import requests
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
# from artifice import NotFound
|
from artifice import NotFound
|
||||||
|
|
||||||
|
|
||||||
class ClerkNamesMixin(object):
|
class ClerkNamesMixin(object):
|
||||||
@ -13,8 +13,8 @@ class ClerkNamesMixin(object):
|
|||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return str(response.json()['pretty_name'])
|
return str(response.json()['pretty_name'])
|
||||||
elif response.status_code == 404:
|
elif response.status_code == 404:
|
||||||
# raise NotFound
|
|
||||||
print "not found"
|
print "not found"
|
||||||
|
raise NotFound
|
||||||
|
|
||||||
|
|
||||||
class ClerkRatesMixin(object):
|
class ClerkRatesMixin(object):
|
||||||
@ -28,5 +28,5 @@ class ClerkRatesMixin(object):
|
|||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return Decimal(response.json()['rate'])
|
return Decimal(response.json()['rate'])
|
||||||
elif response.status_code == 404:
|
elif response.status_code == 404:
|
||||||
# raise NotFound
|
|
||||||
print "not found"
|
print "not found"
|
||||||
|
raise NotFound
|
||||||
|
@ -140,7 +140,7 @@ class Artifice(object):
|
|||||||
"""
|
"""
|
||||||
# :raises: AttributeError, KeyError
|
# :raises: AttributeError, KeyError
|
||||||
# How does this get implemented ? Should there be a module injection?
|
# How does this get implemented ? Should there be a module injection?
|
||||||
return host # For the moment, passthrough
|
return "Data Center 1" # For the moment, passthrough
|
||||||
# TODO: FIXME.
|
# TODO: FIXME.
|
||||||
|
|
||||||
def tenant(self, name):
|
def tenant(self, name):
|
||||||
@ -218,22 +218,12 @@ class Tenant(object):
|
|||||||
funct = getattr(_package, call)
|
funct = getattr(_package, call)
|
||||||
self.invoice_type = funct
|
self.invoice_type = funct
|
||||||
config = self.conn.config["invoice_object"]
|
config = self.conn.config["invoice_object"]
|
||||||
invoice = self.invoice_type(self, config)
|
invoice = self.invoice_type(self, start, end, config)
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
def resources(self, start, end):
|
def resources(self, start, end):
|
||||||
if not self._resources:
|
if not self._resources:
|
||||||
date_fields = [{
|
date_fields = [{ "field": "project_id",
|
||||||
"field": "timestamp",
|
|
||||||
"op": "ge",
|
|
||||||
"value": start.strftime(date_format)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "timestamp",
|
|
||||||
"op": "lt",
|
|
||||||
"value": end.strftime(date_format)
|
|
||||||
},
|
|
||||||
{ "field": "project_id",
|
|
||||||
"op": "eq",
|
"op": "eq",
|
||||||
"value": self.tenant["id"]
|
"value": self.tenant["id"]
|
||||||
},
|
},
|
||||||
@ -553,7 +543,18 @@ class Cumulative(Artifact):
|
|||||||
def volume(self):
|
def volume(self):
|
||||||
measurements = self.usage
|
measurements = self.usage
|
||||||
measurements = sorted( measurements, key= lambda x: x["timestamp"] )
|
measurements = sorted( measurements, key= lambda x: x["timestamp"] )
|
||||||
total_usage = measurements[-1]["counter_volume"] - measurements[0]["counter_volume"]
|
count = 1
|
||||||
|
usage = 0
|
||||||
|
last_measurement = None
|
||||||
|
for measurement in measurements:
|
||||||
|
if measurement["counter_volume"] <= 0 and last_measurement is not None:
|
||||||
|
usage = usage + last_measurement["counter_volume"]
|
||||||
|
count = count + 1
|
||||||
|
last_measurement = measurement
|
||||||
|
|
||||||
|
usage = usage + measurements[-1]["counter_volume"]
|
||||||
|
|
||||||
|
total_usage = usage - measurements[0]["counter_volume"]
|
||||||
return total_usage
|
return total_usage
|
||||||
|
|
||||||
|
|
||||||
@ -575,7 +576,7 @@ class Gauge(Artifact):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
last["timestamp"] = datetime.datetime.strptime(last["timestamp"], other_date_format)
|
last["timestamp"] = datetime.datetime.strptime(last["timestamp"], other_date_format)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for val in usage[1:]:
|
for val in usage[1:]:
|
||||||
try:
|
try:
|
||||||
@ -603,5 +604,54 @@ class Gauge(Artifact):
|
|||||||
# print totals
|
# print totals
|
||||||
return sum(totals)
|
return sum(totals)
|
||||||
|
|
||||||
|
def uptime(self):
|
||||||
|
"""THIS IS AN OVERRIDE METHOD FOR A QUICK AND DIRTY DEMO!
|
||||||
|
DO NOT ACTAULLY USE THIS IN PROD. DELETE IT QUICKLY."""
|
||||||
|
usage = sorted(self.usage, key=lambda x: x["timestamp"])
|
||||||
|
|
||||||
|
blocks = []
|
||||||
|
curr = [usage[0]]
|
||||||
|
last = usage[0]
|
||||||
|
try:
|
||||||
|
last["timestamp"] = datetime.datetime.strptime(last["timestamp"], date_format)
|
||||||
|
except ValueError:
|
||||||
|
last["timestamp"] = datetime.datetime.strptime(last["timestamp"], other_date_format)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
next_hour = True
|
||||||
|
|
||||||
|
for val in usage[1:]:
|
||||||
|
try:
|
||||||
|
val["timestamp"] = datetime.datetime.strptime(val["timestamp"], date_format)
|
||||||
|
except ValueError:
|
||||||
|
val["timestamp"] = datetime.datetime.strptime(val["timestamp"], other_date_format)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if (val['timestamp'] - last["timestamp"]) > datetime.timedelta(hours=1):
|
||||||
|
blocks.append(curr)
|
||||||
|
curr = [val]
|
||||||
|
last = val
|
||||||
|
next_hour = False
|
||||||
|
else:
|
||||||
|
curr.append(val)
|
||||||
|
next_hour = True
|
||||||
|
|
||||||
|
# We are now sorted into 1-hour blocks
|
||||||
|
totals = []
|
||||||
|
for block in blocks:
|
||||||
|
usage = max( [v["counter_volume"] for v in block])
|
||||||
|
totals.append( usage )
|
||||||
|
|
||||||
|
# totals = [max(x, key=lambda val: val["counter_volume"] ) for x in blocks]
|
||||||
|
# totals is now an array of max values per hour for a given month.
|
||||||
|
# print totals
|
||||||
|
if next_hour:
|
||||||
|
return sum(totals) + 1
|
||||||
|
else:
|
||||||
|
return sum(totals)
|
||||||
|
|
||||||
|
|
||||||
class Delta(Artifact):
|
class Delta(Artifact):
|
||||||
pass
|
pass
|
||||||
|
@ -56,7 +56,7 @@ class Invoice(object):
|
|||||||
|
|
||||||
# __metaclass__ = requirements
|
# __metaclass__ = requirements
|
||||||
|
|
||||||
def __init__(self, tenant, config):
|
def __init__(self, tenant, start, end, config):
|
||||||
self.tenant = tenant
|
self.tenant = tenant
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ class NamesFileMixin(object):
|
|||||||
if not self.__names:
|
if not self.__names:
|
||||||
self.__names = {}
|
self.__names = {}
|
||||||
try:
|
try:
|
||||||
fh = open(self.config["rates"][ "name" ])
|
fh = open(self.config["rates"][ "names" ])
|
||||||
reader = csv.reader(fh, delimiter="|") # Makes no opinions on the file structure
|
reader = csv.reader(fh, delimiter="|") # Makes no opinions on the file structure
|
||||||
for row in reader:
|
for row in reader:
|
||||||
# The default layout is expected to be:
|
# The default layout is expected to be:
|
||||||
|
@ -34,7 +34,6 @@ class BaseModelConstruct(object):
|
|||||||
# Returns a given name value thing?
|
# Returns a given name value thing?
|
||||||
# Based on patterning, this is expected to be a dict of usage information
|
# Based on patterning, this is expected to be a dict of usage information
|
||||||
# based on a meter, I guess?
|
# based on a meter, I guess?
|
||||||
|
|
||||||
return getattr(self, name)
|
return getattr(self, name)
|
||||||
|
|
||||||
def _fetch_meter_name(self, name):
|
def _fetch_meter_name(self, name):
|
||||||
@ -68,27 +67,52 @@ class VM(BaseModelConstruct):
|
|||||||
# The only relevant meters of interest are the type of the interest
|
# The only relevant meters of interest are the type of the interest
|
||||||
# and the amount of network we care about.
|
# and the amount of network we care about.
|
||||||
# Oh, and floating IPs.
|
# Oh, and floating IPs.
|
||||||
relevant_meters = ["instance:<type>", "network.incoming.bytes", "network.outgoing.bytes"]
|
relevant_meters = [ "instance", "cpu", "instance:<type>", "network.incoming.bytes", "network.outgoing.bytes"]
|
||||||
|
|
||||||
def _fetch_meter_name(self, name):
|
def _fetch_meter_name(self, name):
|
||||||
if name == "instance:<type>":
|
if name == "instance:<type>":
|
||||||
return "instance:%s" % self.type
|
return "instance:%s" % self.type
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uptime(self):
|
||||||
|
return self.amount
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amount(self):
|
def amount(self):
|
||||||
|
# cpu_usage = self.usage()['cpu'].volume()
|
||||||
|
# cpu_usage_in_s = Decimal(cpu_usage / 1000000000)
|
||||||
|
|
||||||
|
# class Amount(object):
|
||||||
|
# def volume(self):
|
||||||
|
# return Decimal(cpu_usage_in_s)
|
||||||
|
# def __str__(self):
|
||||||
|
# return str(cpu_usage_in_s) + " s"
|
||||||
|
# def __repr__(self):
|
||||||
|
# return str(self)
|
||||||
|
|
||||||
|
|
||||||
|
uptime = self.usage()['instance'].uptime()
|
||||||
class Amount(object):
|
class Amount(object):
|
||||||
def volume(self):
|
def volume(self):
|
||||||
return Decimal(1.0)
|
return Decimal(uptime)
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "1.0"
|
return str(uptime) + " hr"
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self)
|
return str(self)
|
||||||
return Amount()
|
return Amount()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
return self._raw["metadata"]["nistance_type"]
|
# TODO FIgure out what the hell is going on with ceilometer here,
|
||||||
|
# and why flavor.name isn't always there, and why sometimes instance_type
|
||||||
|
# is needed instead....
|
||||||
|
try:
|
||||||
|
# print "\"flavor.name\" was used"
|
||||||
|
return self._raw["metadata"]["flavor.name"]
|
||||||
|
except KeyError:
|
||||||
|
# print "\"instance_type\" was used"
|
||||||
|
return self._raw["metadata"]["instance_type"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self):
|
def size(self):
|
||||||
|
21
bin/bill.py
21
bin/bill.py
@ -37,7 +37,13 @@ if __name__ == '__main__':
|
|||||||
# Ranging
|
# Ranging
|
||||||
# We want to get stuff from, to.
|
# We want to get stuff from, to.
|
||||||
|
|
||||||
parser.add_argument("--from", dest="start", help="When to start our range, date format %s", type=date_fmt_fnc)
|
parser.add_argument(
|
||||||
|
"--from",
|
||||||
|
dest="start",
|
||||||
|
help="When to start our range, date format %s",
|
||||||
|
type=date_fmt_fnc,
|
||||||
|
default=datetime.datetime.now() - datetime.timedelta(days=31)
|
||||||
|
)
|
||||||
parser.add_argument("--to", dest="end", help="When to end our date range. Defaults to yesterday.",
|
parser.add_argument("--to", dest="end", help="When to end our date range. Defaults to yesterday.",
|
||||||
type=date_fmt_fnc, default=datetime.datetime.now() - datetime.timedelta(days=1) )
|
type=date_fmt_fnc, default=datetime.datetime.now() - datetime.timedelta(days=1) )
|
||||||
|
|
||||||
@ -59,13 +65,16 @@ if __name__ == '__main__':
|
|||||||
password = fh.read()
|
password = fh.read()
|
||||||
fh.close()
|
fh.close()
|
||||||
# Make ourselves a nice interaction object
|
# Make ourselves a nice interaction object
|
||||||
conf["database"]["password"] = password
|
conf["database"]["password"] = password.replace("\n", "")
|
||||||
n = interface.Artifice(conf)
|
n = interface.Artifice(conf)
|
||||||
tenants = args.tenants
|
tenants = args.tenants
|
||||||
if not args.tenants:
|
if not args.tenants:
|
||||||
# only parse this list of tenants
|
# only parse this list of tenants
|
||||||
tenants = n.tenants
|
tenants = n.tenants
|
||||||
|
|
||||||
|
print "\n # ----------------- bill summary for: ----------------- # "
|
||||||
|
print "Range: %s -> %s" % (args.start, args.end)
|
||||||
|
|
||||||
for tenant_name in tenants:
|
for tenant_name in tenants:
|
||||||
# artifact = n.tenant(tenant_name).section(section).usage(args.start, args.end)
|
# artifact = n.tenant(tenant_name).section(section).usage(args.start, args.end)
|
||||||
# data should now be an artifact-like construct.
|
# data should now be an artifact-like construct.
|
||||||
@ -75,9 +84,8 @@ if __name__ == '__main__':
|
|||||||
tenant = n.tenant(tenant_name)
|
tenant = n.tenant(tenant_name)
|
||||||
# Makes a new invoice up for this tenant.
|
# Makes a new invoice up for this tenant.
|
||||||
invoice = tenant.invoice(args.start, args.end)
|
invoice = tenant.invoice(args.start, args.end)
|
||||||
print "Tenant: %s" % tenant.name
|
print "\n# ------------------------ Tenant: %s ------------------------ #" % tenant.name
|
||||||
print "Range: %s -> %s" % (args.start, args.end)
|
|
||||||
|
|
||||||
# usage = tenant.usage(start, end)
|
# usage = tenant.usage(start, end)
|
||||||
usage = tenant.usage(args.start, args.end)
|
usage = tenant.usage(args.start, args.end)
|
||||||
# A Usage set is the entirety of time for this Tenant.
|
# A Usage set is the entirety of time for this Tenant.
|
||||||
@ -86,10 +94,11 @@ if __name__ == '__main__':
|
|||||||
usage.save()
|
usage.save()
|
||||||
invoice.bill(usage.vms)
|
invoice.bill(usage.vms)
|
||||||
invoice.bill(usage.volumes)
|
invoice.bill(usage.volumes)
|
||||||
invoice.bill(usage.objects)
|
# invoice.bill(usage.objects)
|
||||||
invoice.close()
|
invoice.close()
|
||||||
|
|
||||||
print invoice.total()
|
print invoice.total()
|
||||||
|
print "# --------------------- End of Tenant: %s --------------------- #" % tenant.name
|
||||||
|
|
||||||
# for datacenter, sections in usage.iteritems():
|
# for datacenter, sections in usage.iteritems():
|
||||||
# # DC is the name of the DC/region. Or the internal code. W/E.
|
# # DC is the name of the DC/region. Or the internal code. W/E.
|
||||||
|
139
bin/bill.py~
Normal file
139
bin/bill.py~
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
try:
|
||||||
|
from artifice import interface
|
||||||
|
except ImportError:
|
||||||
|
loc, fn = os.path.split(__file__)
|
||||||
|
print loc
|
||||||
|
here = os.path.abspath(os.path.join(loc +"/../"))
|
||||||
|
sys.path.insert(0, here)
|
||||||
|
# # Are we potentially in a virtualenv? Add that in.
|
||||||
|
# if os.path.exists( os.path.join(here, "lib/python2.7" ) ):
|
||||||
|
# sys.path.insert(1, os.path.join(here, "lib/python2.7"))
|
||||||
|
from artifice import interface
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
date_format = "%Y-%m-%dT%H:%M:%S"
|
||||||
|
other_date_format = "%Y-%m-%dT%H:%M:%S.%f"
|
||||||
|
|
||||||
|
date_fmt_fnc = lambda x: datetime.datetime.strptime(date_format)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import argparse
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
# Takes names to display.
|
||||||
|
# none means display them all.
|
||||||
|
parser.add_argument("-t", "--tenant", dest="tenants", help='Tenant to display', action="append", default=[])
|
||||||
|
|
||||||
|
# Add some sections to show data from.
|
||||||
|
# Empty is display all
|
||||||
|
parser.add_argument("-s", "--section", dest="sections", help="Sections to display", action="append")
|
||||||
|
|
||||||
|
|
||||||
|
# Ranging
|
||||||
|
# We want to get stuff from, to.
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--from",
|
||||||
|
dest="start",
|
||||||
|
help="When to start our range, date format %s",
|
||||||
|
type=date_fmt_fnc,
|
||||||
|
default=datetime.datetime.now() - datetime.timedelta(days=31)
|
||||||
|
)
|
||||||
|
parser.add_argument("--to", dest="end", help="When to end our date range. Defaults to yesterday.",
|
||||||
|
type=date_fmt_fnc, default=datetime.datetime.now() - datetime.timedelta(days=1) )
|
||||||
|
|
||||||
|
parser.add_argument("--config", dest="config", help="Config file", default="/opt/stack/artifice/etc/artifice/conf.yaml")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
conf = yaml.load(open(args.config).read())
|
||||||
|
except IOError:
|
||||||
|
# Whoops
|
||||||
|
print "couldn't load %s " % args.config
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
# Make ourselves a nice interaction object
|
||||||
|
# Password needs to be loaded from /etc/artifice/database
|
||||||
|
fh = open(conf["database"]["password_path"])
|
||||||
|
password = fh.read()
|
||||||
|
fh.close()
|
||||||
|
# Make ourselves a nice interaction object
|
||||||
|
conf["database"]["password"] = password
|
||||||
|
n = interface.Artifice(conf)
|
||||||
|
tenants = args.tenants
|
||||||
|
if not args.tenants:
|
||||||
|
# only parse this list of tenants
|
||||||
|
tenants = n.tenants
|
||||||
|
|
||||||
|
print "\n # ----------------- bill summary for: ----------------- # "
|
||||||
|
print "Range: %s -> %s" % (args.start, args.end)
|
||||||
|
|
||||||
|
for tenant_name in tenants:
|
||||||
|
# artifact = n.tenant(tenant_name).section(section).usage(args.start, args.end)
|
||||||
|
# data should now be an artifact-like construct.
|
||||||
|
# Data also knows about Samples, where as an Artifact doesn't.
|
||||||
|
|
||||||
|
# An artifact knows its section
|
||||||
|
tenant = n.tenant(tenant_name)
|
||||||
|
# Makes a new invoice up for this tenant.
|
||||||
|
invoice = tenant.invoice(args.start, args.end)
|
||||||
|
print "\n# ------------------------ Tenant: %s ------------------------ #" % tenant.name
|
||||||
|
|
||||||
|
# usage = tenant.usage(start, end)
|
||||||
|
usage = tenant.usage(args.start, args.end)
|
||||||
|
# A Usage set is the entirety of time for this Tenant.
|
||||||
|
# It's not time-limited at all.
|
||||||
|
# But the
|
||||||
|
usage.save()
|
||||||
|
invoice.bill(usage.vms)
|
||||||
|
invoice.bill(usage.volumes)
|
||||||
|
# invoice.bill(usage.objects)
|
||||||
|
invoice.close()
|
||||||
|
|
||||||
|
print invoice.total()
|
||||||
|
print "# --------------------- End of Tenant: %s --------------------- #" % tenant.name
|
||||||
|
|
||||||
|
# for datacenter, sections in usage.iteritems():
|
||||||
|
# # DC is the name of the DC/region. Or the internal code. W/E.
|
||||||
|
# print datacenter
|
||||||
|
|
||||||
|
# for section_name in args.sections:
|
||||||
|
# assert section in sections
|
||||||
|
|
||||||
|
# # section = sections[ section ]
|
||||||
|
# print sections[section_name]
|
||||||
|
# for resources in sections[section_name]:
|
||||||
|
# for resource in resources:
|
||||||
|
# print resource
|
||||||
|
# for meter in resource.meters:
|
||||||
|
# usage = meter.usage(start, end)
|
||||||
|
# if usage.has_been_saved():
|
||||||
|
# continue
|
||||||
|
# print usage.volume()
|
||||||
|
# print usage.cost()
|
||||||
|
# usage.save()
|
||||||
|
# # Finally, bill it.
|
||||||
|
# # All of these things need to be converted to the
|
||||||
|
# # publicly-viewable version now.
|
||||||
|
# invoice.bill(datacenter, resource, meter, usage)
|
||||||
|
|
||||||
|
# # Section is going to be in the set of vm, network, storage, image
|
||||||
|
# # # or just all of them.
|
||||||
|
# # # It's not going to be an individual meter name.
|
||||||
|
# # artifacts = section.usage(args.start, args.end)
|
||||||
|
# # for artifact in artifacts:
|
||||||
|
# # if artifact.has_been_saved:
|
||||||
|
# # # Does this artifact exist in the DB?
|
||||||
|
# # continue
|
||||||
|
# # artifact.save() # Save to the Artifact storage
|
||||||
|
# # # Saves to the invoice.
|
||||||
|
# # invoice.bill ( artifact )
|
||||||
|
# # # artifact.bill( invoice.id )
|
||||||
|
# # print "%s: %s" % (section.name, artifact.volume)
|
@ -52,6 +52,7 @@ if __name__ == '__main__':
|
|||||||
parser.add_argument("-c", "--config", dest="config", help="Config file", default="/opt/stack/artifice/etc/artifice/conf.yaml")
|
parser.add_argument("-c", "--config", dest="config", help="Config file", default="/opt/stack/artifice/etc/artifice/conf.yaml")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
print "\n # ----------------- usage summary for: ----------------- # "
|
||||||
print "Range: %s -> %s" % (args.start, args.end)
|
print "Range: %s -> %s" % (args.start, args.end)
|
||||||
try:
|
try:
|
||||||
conf = yaml.load(open(args.config).read())
|
conf = yaml.load(open(args.config).read())
|
||||||
@ -64,7 +65,7 @@ if __name__ == '__main__':
|
|||||||
password = fh.read()
|
password = fh.read()
|
||||||
fh.close()
|
fh.close()
|
||||||
# Make ourselves a nice interaction object
|
# Make ourselves a nice interaction object
|
||||||
conf["database"]["password"] = password
|
conf["database"]["password"] = password.replace("\n", "")
|
||||||
instance = interface.Artifice(conf)
|
instance = interface.Artifice(conf)
|
||||||
tenants = args.tenants
|
tenants = args.tenants
|
||||||
if not args.tenants:
|
if not args.tenants:
|
||||||
@ -80,7 +81,7 @@ if __name__ == '__main__':
|
|||||||
tenant = instance.tenant(tenant_name)
|
tenant = instance.tenant(tenant_name)
|
||||||
# Makes a new invoice up for this tenant.
|
# Makes a new invoice up for this tenant.
|
||||||
invoice = tenant.invoice(args.start, args.end)
|
invoice = tenant.invoice(args.start, args.end)
|
||||||
print "Tenant: %s" % tenant.name
|
print "\n# ------------------------ Tenant: %s ------------------------ #" % tenant.name
|
||||||
|
|
||||||
|
|
||||||
# usage = tenant.usage(start, end)
|
# usage = tenant.usage(start, end)
|
||||||
@ -90,6 +91,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
invoice.bill(usage.vms)
|
invoice.bill(usage.vms)
|
||||||
invoice.bill(usage.volumes)
|
invoice.bill(usage.volumes)
|
||||||
invoice.bill(usage.objects)
|
# invoice.bill(usage.objects)
|
||||||
|
|
||||||
print "Total invoice value: %s" % invoice.total()
|
print "Total invoice value: %s" % invoice.total()
|
||||||
|
print "# --------------------- End of Tenant: %s --------------------- #" % tenant.name
|
||||||
|
97
bin/usage.py~
Normal file
97
bin/usage.py~
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
from artifice import interface
|
||||||
|
except ImportError:
|
||||||
|
loc, fn = os.path.split(__file__)
|
||||||
|
print loc
|
||||||
|
here = os.path.abspath(os.path.join(loc +"/../"))
|
||||||
|
sys.path.insert(0, here)
|
||||||
|
# # Are we potentially in a virtualenv? Add that in.
|
||||||
|
# if os.path.exists( os.path.join(here, "lib/python2.7" ) ):
|
||||||
|
# sys.path.insert(1, os.path.join(here, "lib/python2.7"))
|
||||||
|
from artifice import interface
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
date_format = "%Y-%m-%dT%H:%M:%S"
|
||||||
|
other_date_format = "%Y-%m-%dT%H:%M:%S.%f"
|
||||||
|
date_fmt = "%Y-%m-%d"
|
||||||
|
|
||||||
|
def date_fmt_fnc(val):
|
||||||
|
return datetime.datetime.strptime(val, date_fmt)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import argparse
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
# Takes names to display.
|
||||||
|
# none means display them all.
|
||||||
|
parser.add_argument("-t", "--tenant", dest="tenants", help='Tenant to display', action="append", default=[])
|
||||||
|
|
||||||
|
# Add some sections to show data from.
|
||||||
|
# Empty is display all
|
||||||
|
parser.add_argument("-s", "--section", dest="sections", help="Sections to display", action="append")
|
||||||
|
|
||||||
|
|
||||||
|
# Ranging
|
||||||
|
# We want to get stuff from, to.
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--from",
|
||||||
|
dest="start",
|
||||||
|
help="When to start our range, date format %s",
|
||||||
|
type=date_fmt_fnc,
|
||||||
|
default=datetime.datetime.now() - datetime.timedelta(days=31)
|
||||||
|
)
|
||||||
|
parser.add_argument("--to", dest="end", help="When to end our date range. Defaults to yesterday.",
|
||||||
|
type=date_fmt_fnc, default=datetime.datetime.now() - datetime.timedelta(days=1) )
|
||||||
|
|
||||||
|
parser.add_argument("-c", "--config", dest="config", help="Config file", default="/opt/stack/artifice/etc/artifice/conf.yaml")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
print "\n # ----------------- usage summary for: ----------------- # "
|
||||||
|
print "Range: %s -> %s" % (args.start, args.end)
|
||||||
|
try:
|
||||||
|
conf = yaml.load(open(args.config).read())
|
||||||
|
except IOError:
|
||||||
|
# Whoops
|
||||||
|
print "couldn't load %s " % args.config
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
fh = open(conf["database"]["password_path"])
|
||||||
|
password = fh.read()
|
||||||
|
fh.close()
|
||||||
|
# Make ourselves a nice interaction object
|
||||||
|
conf["database"]["password"] = password
|
||||||
|
instance = interface.Artifice(conf)
|
||||||
|
tenants = args.tenants
|
||||||
|
if not args.tenants:
|
||||||
|
# only parse this list of tenants
|
||||||
|
tenants = instance.tenants
|
||||||
|
|
||||||
|
for tenant_name in tenants:
|
||||||
|
# artifact = n.tenant(tenant_name).section(section).usage(args.start, args.end)
|
||||||
|
# data should now be an artifact-like construct.
|
||||||
|
# Data also knows about Samples, where as an Artifact doesn't.
|
||||||
|
|
||||||
|
# An artifact knows its section
|
||||||
|
tenant = instance.tenant(tenant_name)
|
||||||
|
# Makes a new invoice up for this tenant.
|
||||||
|
invoice = tenant.invoice(args.start, args.end)
|
||||||
|
print "\n# ------------------------ Tenant: %s ------------------------ #" % tenant.name
|
||||||
|
|
||||||
|
|
||||||
|
# usage = tenant.usage(start, end)
|
||||||
|
usage = tenant.usage(args.start, args.end)
|
||||||
|
# A Usage set is the entirety of time for this Tenant.
|
||||||
|
# It's not time-limited at all.
|
||||||
|
|
||||||
|
invoice.bill(usage.vms)
|
||||||
|
invoice.bill(usage.volumes)
|
||||||
|
# invoice.bill(usage.objects)
|
||||||
|
|
||||||
|
print "Total invoice value: %s" % invoice.total()
|
||||||
|
print "# --------------------- End of Tenant: %s --------------------- #" % tenant.name
|
Loading…
x
Reference in New Issue
Block a user