From 051d2c64ff8e1cc86da3200884f812a2b63d9794 Mon Sep 17 00:00:00 2001 From: adriant Date: Wed, 15 Jan 2014 17:47:55 +1300 Subject: [PATCH] More pep8 formatting and tidying up. --- artifice/csv_mixin.py | 2 +- artifice/interface.py | 74 ++++++++++++++++++++++---------------- artifice/invoice.py | 60 +++++++++++++++++++------------ artifice/models/tenants.py | 3 +- artifice/models/usage.py | 22 ++++++------ bin/bill.py | 20 ++++++----- bin/usage.py | 52 ++++++++++++++++++++------- 7 files changed, 147 insertions(+), 86 deletions(-) diff --git a/artifice/csv_mixin.py b/artifice/csv_mixin.py index e3238a0..b0bcdc0 100644 --- a/artifice/csv_mixin.py +++ b/artifice/csv_mixin.py @@ -5,7 +5,7 @@ class CSV_File_mixin(object): def write_output(self): try: - read = open(self.filename) + open(self.filename) raise RuntimeError("Can't write to an existing file!") except IOError: pass diff --git a/artifice/interface.py b/artifice/interface.py index 6cdaa15..15a92a3 100644 --- a/artifice/interface.py +++ b/artifice/interface.py @@ -43,16 +43,17 @@ def get_meter(meter, start, end, auth): # Meter is a href; in this case, it has a set of fields with it already. # print meter.link # print dir(meter) - date_fields = [{ - "field": "timestamp", - "op": "ge", - "value": start.strftime(date_format) - }, - { - "field": "timestamp", - "op": "lt", - "value": end.strftime(date_format) - } + date_fields = [ + { + "field": "timestamp", + "op": "ge", + "value": start.strftime(date_format) + }, + { + "field": "timestamp", + "op": "lt", + "value": end.strftime(date_format) + } ] fields = [] for field in date_fields: @@ -83,7 +84,7 @@ class keystone(KeystoneClient.Client): url = "%(url)s/tenants?%(query)s" % { "url": authenticator, "query": urllib.urlencode({"name": name}) - } + } r = requests.get(url, headers={ "X-Auth-Token": self.auth_token, "Content-Type": "application/json" @@ -107,18 +108,22 @@ class Artifice(object): # This is the Keystone client connection, which provides our # OpenStack authentication self.auth = keystone( - username= config["openstack"]["username"], - password= config["openstack"]["password"], - tenant_name= config["openstack"]["default_tenant"], - auth_url= config["openstack"]["authentication_url"] + username=config["openstack"]["username"], + password=config["openstack"]["password"], + tenant_name=config["openstack"]["default_tenant"], + auth_url=config["openstack"]["authentication_url"] ) - conn_string = 'postgresql://%(username)s:%(password)s@%(host)s:%(port)s/%(database)s' % { + + conn_dict = { "username": config["database"]["username"], "password": config["database"]["password"], - "host": config["database"]["host"], - "port": config["database"]["port"], + "host": config["database"]["host"], + "port": config["database"]["port"], "database": config["database"]["database"] } + conn_string = ('postgresql://%(username)s:%(password)s@' + + '%(host)s:%(port)s/%(database)s') % conn_dict + engine = create_engine(conn_string) Session.configure(bind=engine) self.session = Session() @@ -211,7 +216,8 @@ class Tenant(object): if self.invoice_type is None: invoice_type = self.conn.config["main"]["invoice:object"] if ":" not in invoice_type: - raise AttributeError("Invoice configuration incorrect! %s" % invoice_type) + raise AttributeError("Invoice configuration incorrect! %s" % + invoice_type) module, call = invoice_type.split(":") _package = __import__(module, globals(), locals(), [call]) @@ -223,10 +229,11 @@ class Tenant(object): def resources(self, start, end): if not self._resources: - date_fields = [{"field": "project_id", - "op": "eq", - "value": self.tenant["id"] - }, + date_fields = [ + {"field": "project_id", + "op": "eq", + "value": self.tenant["id"] + }, ] # Sets up our resources as Ceilometer objects. # That's cool, I think. @@ -485,7 +492,8 @@ class Artifact(object): def save(self): """ - Persists to our database backend. Opinionatedly this is a sql datastore. + Persists to our database backend. + Opinionatedly this is a sql datastore. """ value = self.volume() session = self.resource.conn.session @@ -509,7 +517,8 @@ class Artifact(object): session.add(tenant) else: try: - res = session.query(resources.Resource).filter(resources.Resource.id == resource_id)[0] + matching = resources.Resource.id == resource_id + res = session.query(resources.Resource).filter(matching)[0] tenant = res.tenant except IndexError: res = resources.Resource() @@ -572,21 +581,26 @@ class Gauge(Artifact): curr = [usage[0]] last = usage[0] try: - last["timestamp"] = datetime.datetime.strptime(last["timestamp"], date_format) + last["timestamp"] = datetime.datetime.strptime(last["timestamp"], + date_format) except ValueError: - last["timestamp"] = datetime.datetime.strptime(last["timestamp"], other_date_format) + last["timestamp"] = datetime.datetime.strptime(last["timestamp"], + other_date_format) except TypeError: pass for val in usage[1:]: try: - val["timestamp"] = datetime.datetime.strptime(val["timestamp"], date_format) + val["timestamp"] = datetime.datetime.strptime(val["timestamp"], + date_format) except ValueError: - val["timestamp"] = datetime.datetime.strptime(val["timestamp"], other_date_format) + val["timestamp"] = datetime.datetime.strptime(val["timestamp"], + other_date_format) except TypeError: pass - if (val['timestamp'] - last["timestamp"]) > datetime.timedelta(hours=1): + difference = (val['timestamp'] - last["timestamp"]) + if difference > datetime.timedelta(hours=1): blocks.append(curr) curr = [val] last = val diff --git a/artifice/invoice.py b/artifice/invoice.py index ebd45a7..baccbff 100644 --- a/artifice/invoice.py +++ b/artifice/invoice.py @@ -12,18 +12,30 @@ an Invoice interface consists of: from decimal import * -class IntegrityViolation(BaseException): pass +import csv -class BillingOverlap(BaseException): pass -class NoSuchType(KeyError): pass +class IntegrityViolation(BaseException): + pass + + +class BillingOverlap(BaseException): + pass + + +class NoSuchType(KeyError): + pass + + +class NoSuchLocation(KeyError): + pass -class NoSuchLocation(KeyError): pass costs = { - "cpu_util" : { "nova": "1" } + "cpu_util": {"nova": "1"} } + class Costs(object): def cost(self, location, name): @@ -39,6 +51,7 @@ class Costs(object): required = ["add_line", "close"] + def requirements(name, parents, attrs): for attr_name in required: try: @@ -52,6 +65,7 @@ def requirements(name, parents, attrs): raise RuntimeError("%s is not callable" % (attr_name)) return type(name, parents, attrs) + class Invoice(object): # __metaclass__ = requirements @@ -77,17 +91,17 @@ class Invoice(object): # DC is the name of the DC/region. Or the internal code. W/E. # print datacenter self.subheading(dc["name"]) - for section in dc["sections"]: # will be vm, network, storage - self.add_section( section ) + for section in dc["sections"]: # will be vm, network, storage + self.add_section(section) meters = dc["sections"][section] for usage in meters: - cost = self.cost( dc["name"], meter["name"] ) - - self.add_line( "%s per unit " % cost, usage.volume, cost * usage.volume ) - self.commit() # Writes to OpenERP? Closes the invoice? Something. + cost = self.cost(dc["name"], meter["name"]) + self.add_line("%s per unit " % (cost, usage.volume, + cost * usage.volume)) + self.commit() # Writes to OpenERP? Closes the invoice? Something. def add_line(self, item): raise NotImplementedError("Not implemented in base class") @@ -98,7 +112,7 @@ class Invoice(object): def total(self): raise NotImplementedError("Not implemented in the base class") -import csv + class RatesFileMixin(object): # Mixin # Adds a rates file loader, expecting various things from the @@ -112,15 +126,16 @@ class RatesFileMixin(object): if not self.__rates: self.__rates = {} try: - fh = open(self.config["rates"][ "file" ]) - reader = csv.reader(fh, delimiter = "|") # Makes no opinions on the file structure + fh = open(self.config["rates"]["file"]) + # Makes no opinions on the file structure + reader = csv.reader(fh, delimiter="|") for row in reader: # The default layout is expected to be: # location | rate name | rate measurement | rate value self.__rates[row[1].strip()] = { - "cost": Decimal(row[3].strip()), - "region": row[0].strip(), - "measures": row[2].strip() + "cost": Decimal(row[3].strip()), + "region": row[0].strip(), + "measures": row[2].strip() } if not self.__rates: raise IndexError("malformed rates CSV!") @@ -134,7 +149,8 @@ class RatesFileMixin(object): except IOError: print "Couldn't open the file!" raise - return self.__rates[name]["cost"] # ignore the regions-ness for now + return self.__rates[name]["cost"] # ignore the regions-ness for now + class NamesFileMixin(object): @@ -149,12 +165,13 @@ class NamesFileMixin(object): if not self.__names: self.__names = {} try: - fh = open(self.config["rates"][ "names" ]) - reader = csv.reader(fh, delimiter="|") # Makes no opinions on the file structure + fh = open(self.config["rates"]["names"]) + # Makes no opinions on the file structure + reader = csv.reader(fh, delimiter="|") for row in reader: # The default layout is expected to be: # internal name | external name - self.__names[ row[0].strip() ] = row[1].strip() + self.__names[row[0].strip()] = row[1].strip() if not self.__names: raise IndexError("Malformed names CSV") @@ -167,4 +184,3 @@ class NamesFileMixin(object): print "Couldn't open the file!" raise return self.__names[name] - diff --git a/artifice/models/tenants.py b/artifice/models/tenants.py index 8e35aad..2e321db 100644 --- a/artifice/models/tenants.py +++ b/artifice/models/tenants.py @@ -1,10 +1,11 @@ from . import Base from sqlalchemy import Column, types, String + class Tenant(Base): __tablename__ = 'tenants' # ID is a uuid id = Column(String, primary_key=True, nullable=False) other_id = Column(String) - # Some reference data to something else? \ No newline at end of file + # Some reference data to something else? diff --git a/artifice/models/usage.py b/artifice/models/usage.py index 287315d..fcb0c3a 100644 --- a/artifice/models/usage.py +++ b/artifice/models/usage.py @@ -1,11 +1,13 @@ from . import Base from .resources import Resource -from sqlalchemy import Column, types, ForeignKey, CheckConstraint, String, Integer, type_coerce, func, Sequence -from sqlalchemy.orm import relationship, backref +from sqlalchemy import (Column, types, String, Integer, type_coerce, + func, Sequence) +from sqlalchemy.orm import relationship from sqlalchemy.schema import ForeignKeyConstraint import datetime -from sqlalchemy.dialects.postgresql import ExcludeConstraint, TSRANGE, ARRAY +from sqlalchemy.dialects.postgresql import ExcludeConstraint, TSRANGE + class TSRange(TSRANGE): @@ -45,16 +47,14 @@ class Usage(Base): ), ) - resource = relationship(Resource, primaryjoin= - resource_id == Resource.id) - tenant = relationship(Resource, primaryjoin= - tenant_id== Resource.tenant_id) + resource = relationship(Resource, + primaryjoin=(resource_id == Resource.id)) + tenant = relationship(Resource, + primaryjoin=(tenant_id == Resource.tenant_id)) # resource = relationship("Resource", backref=backref("resources", order_by=created)) # tenant = relationship("Tenant", backref=backref("usage", order_by=created)) - - def __init__(self, resource, tenant, value, start, end): assert start < end @@ -67,7 +67,7 @@ class Usage(Base): assert resource.tenant.id == tenant.id self.resource = resource - self.tenant = resource # Same resource + self.tenant = resource # Same resource self.time = "[%s,%s]" % (start, end) self.created = datetime.datetime.now() - self.volume = value \ No newline at end of file + self.volume = value diff --git a/bin/bill.py b/bin/bill.py index d49f751..c561b1f 100644 --- a/bin/bill.py +++ b/bin/bill.py @@ -7,7 +7,7 @@ try: except ImportError: loc, fn = os.path.split(__file__) print loc - here = os.path.abspath(os.path.join(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" ) ): @@ -27,12 +27,13 @@ if __name__ == '__main__': 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=[]) + 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") - + parser.add_argument("-s", "--section", dest="sections", + help="Sections to display", action="append") # Ranging # We want to get stuff from, to. @@ -44,10 +45,14 @@ if __name__ == '__main__': 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( + "--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") + parser.add_argument("--config", dest="config", help="Config file", + default="/opt/stack/artifice/etc/artifice/conf.yaml") args = parser.parse_args() @@ -58,7 +63,6 @@ if __name__ == '__main__': 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"]) diff --git a/bin/usage.py b/bin/usage.py index 153676c..aae704a 100644 --- a/bin/usage.py +++ b/bin/usage.py @@ -1,13 +1,14 @@ #!/usr/bin/env python -import os, sys +import os +import sys try: from artifice import interface except ImportError: loc, fn = os.path.split(__file__) print loc - here = os.path.abspath(os.path.join(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" ) ): @@ -21,6 +22,7 @@ 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) @@ -29,12 +31,16 @@ if __name__ == '__main__': 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=[]) + 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") - + parser.add_argument( + "-s", "--section", dest="sections", + help="Sections to display", action="append") # Ranging # We want to get stuff from, to. @@ -46,13 +52,34 @@ if __name__ == '__main__': 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( + "--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") + 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: ----------------- # " + + def format_title(name, max_length): + numb_lines = (max_length - len(name)) / 2 + lines = "" + for i in range(numb_lines): + lines = lines + "-" + if (max_length - len(name)) % 2 == 0: + lines2 = lines + else: + lines2 = lines + "-" + title = "\n # " + lines + " " + name + " " + lines2 + " # " + return title + + max_len = 60 + + print format_title("usage summary for:", max_len) print "Range: %s -> %s" % (args.start, args.end) try: conf = yaml.load(open(args.config).read()) @@ -60,7 +87,7 @@ if __name__ == '__main__': # Whoops print "couldn't load %s " % args.config sys.exit(1) - + fh = open(conf["database"]["password_path"]) password = fh.read() fh.close() @@ -81,8 +108,7 @@ if __name__ == '__main__': 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 - + print format_title("Tenant: %s" % tenant.name, max_len) # usage = tenant.usage(start, end) usage = tenant.usage(args.start, args.end) @@ -94,4 +120,4 @@ if __name__ == '__main__': # invoice.bill(usage.objects) print "Total invoice value: %s" % invoice.total() - print "# --------------------- End of Tenant: %s --------------------- #" % tenant.name + print format_title("End of Tenant: %s" % tenant.name, max_len)