Test cases for usages indicated an incomplete representation of the various datatypes, based on our own necessary modelling for billable information. Moving instead to specific types of constructs, similar to ceilometer's own internal data structures/divisons. New tests to follow that line.

This commit is contained in:
Aurynn Shaw 2013-08-15 16:50:14 +12:00
parent f0eff01084
commit 908ca359c1
35 changed files with 658 additions and 150 deletions

View File

@ -1,5 +1,5 @@
import yaml
from models import session
from models import Session
from interface import Artifice
default_config = "/etc/artifice/config.yaml"

View File

@ -5,18 +5,29 @@
import requests
import json
from copy import copy
from collections import defaultdict
#
import datetime
# Provides authentication against Openstack
from keystoneclient.v2_0 import client as keystone
from keystoneclient.v2_0 import client as KeystoneClient
# Provides hooks to ceilometer, which we need for data.
from ceilometerclient.v2.client import Client as ceilometer
#
# from .models import usage
from .models import Session, usage
from sqlalchemy import create_engine
from .models.usage import Usage
from .models import resources, tenants
# from .models.tenants import Tenant
# Date format Ceilometer uses
# 2013-07-03T13:34:17
# which is, as an strftime:
@ -29,8 +40,52 @@ date_format = "%Y-%m-%dT%H:%M:%S"
# Sometimes things also have milliseconds, so we look for that too.
other_date_format = "%Y-%m-%dT%H:%M:%S.%f"
def get_meter(meter, start, end, auth):
date_fields = [{
"field": "timestamp",
"op": "ge",
"value": start.strftime(date_format)
},
{
"field": "timestamp",
"op": "lt",
"value": end.strftime(date_format)
}
]
# I dislike directly calling requests here.
r = requests.get(
meter.link,
headers={
"X-Auth-Token": auth,
"Content-Type":"application/json"},
data=json.dumps({"q": date_fields})
)
return json.loads(r)
class NotFound(BaseException): pass
class keystone(KeystoneClient.Client):
def tenant_by_name(self, name):
authenticator = self.auth_url
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"
})
if r.ok:
data = json.loads(r.text)
assert data
return data
else:
if r.status_code == 404:
# couldn't find it
raise NotFound
class Artifice(object):
"""Produces billable artifacts"""
def __init__(self, config):
@ -39,7 +94,7 @@ class Artifice(object):
# This is the Keystone client connection, which provides our
# OpenStack authentication
self.auth = keystone.Client(
self.auth = keystone(
username= config["openstack"]["username"],
password= config["openstack"]["password"],
tenant_name= config["openstack"]["default_tenant"],
@ -55,9 +110,16 @@ class Artifice(object):
}
engine = create_engine(conn_string)
Session.configure(bind=engine)
session = Session()
self.session = Session()
self.artifice = None
self.ceilometer = ceilometer(
self.config["ceilometer"]["host"],
token=self.auth.auth_token
)
self._tenancy = None
def tenant(self, name):
"""
Returns a Tenant object describing the specified Tenant by name, or raises a NotFound error.
@ -65,32 +127,21 @@ class Artifice(object):
# Returns a Tenant object for the given name.
# Uses Keystone API to perform a direct name lookup,
# as this is expected to work via name.
authenticator = config["openstack"]["authentication_url"]
url = "%(url)s/tenants?%(query)s" % {
"url": authenticator,
"query": urllib.urlencode({"name":name})
}
r = requests.get(url, headers={"X-Auth-Token": self.auth.auth_token, "Content-Type": "application/json"})
if r.ok:
data = json.loads(r.text)
assert data
t = Tenant(data["tenant"], self)
return t
else:
if r.status_code == 404:
# couldn't find it
raise NotFound
data = self.auth.tenant_by_name(name)
t = Tenant(data["tenant"], self)
return t
@property
def tenants(self):
"""All the tenants in our system"""
# print "tenant list is %s" % self.auth.tenants.list()
if not self._tenancy:
self._tenancy = {}
invoice_type = __import__(self.config["invoices"]["plugin"])
for tenant in self.auth.tenants.list():
t = Tenant(tenant, self)
dict( [(t.name, Tenant(t, self)) for t in self.auth.tenants.list() ] )
self._tenancy[t.name] = t
return self._tenancy
class Tenant(object):
@ -109,8 +160,9 @@ class Tenant(object):
def __getattr__(self, attr):
if attr not in self.tenant:
return super(self, Tenant).__getattr__(attr)
return self.tenant["attr"]
return object.__getattribute__(self, attr)
# return super(Tenant, self).__getattr__(attr)
return self.tenant[attr]
def invoice(self):
@ -137,24 +189,32 @@ class Tenant(object):
invoice = self.invoice_type(self, config)
return invoice
@property
def resources(self):
def resources(self, start, end):
if not self._resources:
r = requests.get(
os.path.join(self.config["ceilometer"]["host"], "v2/resources"),
headers={"X-Auth-Token": self.auth.auth_token, "Content-Type":"application/json"},
data=json.dumps( { "q": resourcing_fields } )
)
if not r.ok:
return None
self._resources = json.loads(r.text)
date_fields = [{
"field": "timestamp",
"op": "ge",
"value": start.strftime(date_format)
},
{
"field": "timestamp",
"op": "lt",
"value": end.strftime(date_format)
},
{ "field": "project_id",
"op": "eq",
"value": self.tenant["id"]
},
]
self._resources = self.ceilometer.resources.list(date_fields)
return self._resources
# def usage(self, start, end, section=None):
def contents(self, start, end):
def usage(self, start, end):
"""
Contents is the meat of Artifice, returning a dict of location to
sub-information
"""
# Returns a usage dict, based on regions.
vms = {}
vm_to_region = {}
@ -162,69 +222,63 @@ class Tenant(object):
usage_by_dc = {}
date_fields = [{
"field": "timestamp",
"op": "ge",
"value": start.strftime(date_format)
},
{
"field": "timestamp",
"op": "lt",
"value": end.strftime(date_format)
},
]
writing_to = None
vms = []
networks = []
storage = []
images = []
volumes = []
for resource in self.resources:
for resource in self.resources(start, end):
rels = [link["rel"] for link in resource["links"] if link["rel"] != 'self' ]
if "image" in rels:
# Images don't have location data - we don't know where they are.
# It may not matter where they are.
resource["_type"] = "image"
images.append(resource)
images.append(Resource(resource, self.conn))
pass
elif "storage" in rels:
elif "storage.objects" in rels:
# Unknown how this data layout happens yet.
resource["_type"] = "storage"
storage.append(resource)
resource["_type"] = "object"
storage.append(Resource(resource, self.conn))
pass
elif "network" in rels:
# Have we seen the VM that owns this yet?
resource["_type"] = "network"
networks.append(resource)
else:
networks.append(Resource(resource , self.conn))
elif "volumne" in rels:
volumes.append( Resource(resource, self.conn) )
elif 'instance' in rels:
resource["_type"] = "vm"
vms.append(resource)
vms.append(Resource(resource, self.conn ))
datacenters = {}
region_tmpl = { "vms": [],
"network": [],
"storage": []
region_tmpl = { "vms": vms,
"network": networks,
"objects": storage,
"volumes": volumes
}
vm_to_region = {}
for vm in vms:
id_ = vm["resource_id"]
# vm_to_region = {}
# for vm in vms:
# id_ = vm["resource_id"]
datacenter = self.host_to_dc( vm["metadata"]["host"] )
# datacenter = self.conn.host_to_dc( vm["metadata"]["host"] )
if datacenter not in datacenters:
dict_ = copy(region_tmpl)
datacenters[datacenter] = dict_
# if datacenter not in datacenters:
# dict_ = copy(region_tmpl)
# datacenters[datacenter] = dict_
datacenters[datacenter]["vms"].append(vm)
# datacenters[datacenter]["vms"].append(vm)
vm_to_region[id_] = datacenter
# vm_to_region[id_] = datacenter
for network in networks:
vm_id = network["metadata"]["instance_id"]
datacenter = vm_to_region[ vm_id ]
# for network in networks:
# vm_id = network["metadata"]["instance_id"]
# datacenter = self.host_to_dc( network["metadata"]["host"] )
datacenters[datacenter]["network"].append(network)
# datacenters[datacenter]["network"].append(network)
# for resource in storage:
# pass
@ -240,42 +294,62 @@ class Tenant(object):
# So we can now start to collect stats and construct what we consider to be
# usage information for this tenant for this timerange
return Contents(datacenters, start, end)
@property
def meters(self):
if not self.meters:
resourcing_fields = [{"field": "project_id", "op": "eq", "value": self.tenant.id }]
r = requests.get(
os.path.join(self.config["ceilometer"]["host"], "v2/resources"),
headers={"X-Auth-Token": self.auth.auth_token, "Content-Type":"application/json"},
data=json.dumps( { "q": resourcing_fields } )
)
# meters = set()
resources = json.loads(r.text)
for resource in resources:
for link in resource["links"]:
if link["rel"] == "self":
continue
self._meters.add(link["rel"])
# sections.append(Section(self, link))
return self._meters()
return Usage(region_tmpl, start, end, self.conn)
class Contents(object):
class Usage(object):
"""
This is a dict-like object containing all the datacenters and
meters available in those datacenters.
"""
def __init__(self, contents, start, end):
def __init__(self, contents, start, end, conn):
self.contents = contents
self.start = start
self.end = end
self.conn = conn
self._vms = []
self._objects = []
self._volumes = []
# Replaces all the internal references with better references to
# actual metered values.
self._replace()
# self._replace()
def __getitem__(self, item):
@property
def vms(self):
return self.contents[item]
if not self._vms:
vms = []
for vm in self.contents["vms"]:
VM = resources.VM(vm)
VM.location = self.conn.host_to_dc( vm["metadata"]["host"] )
vms.append(VM)
self._vms = vms
return self._vms
@property
def objects(self):
if not self._objects:
vms = []
for object_ in self.contents["objects"]:
obj = resources.Object(object_)
obj.location = self.conn.host_to_dc( vm["metadata"]["host"] )
vms.append(VM)
self._vms = vms
return self._vms
@property
def volumes(self):
return []
# def __getitem__(self, item):
# return self.contents[item]
def __iter__(self):
return self
@ -290,19 +364,21 @@ class Contents(object):
def _replace(self):
# Turns individual metering objects into
# Usage objects that this expects.
for dc in contents.iterkeys():
for section in contents[dc].iterkeys():
for dc in self.contents.iterkeys():
for section in self.contents[dc].iterkeys():
meters = []
for meter in contents[dc][section]:
for resource in self.contents[dc][section]:
meters.extend(resource.meters)
usages = []
for meter in meters:
usage = meter.usage(self.start, self.end)
usage.db = self.db # catch the DB context?
usage.db = self.conn # catch the DB context?
meters.append(usage)
usages.append(usage)
# Overwrite the original metering objects
# with the core usage objects.
# This is because we're not storing metering.
contents[dc][section] = meters
self.contents[dc][section] = usages
def save(self):
@ -310,78 +386,79 @@ class Contents(object):
Iterate the list of things; save them to DB.
"""
# self.db.begin()
for dc in contents.iterkeys():
for section in contents[dc].iterkeys():
for meter in contents[dc][section]:
meter.save()
self.db.commit()
# for
# for dc in self.contents.iterkeys():
# for section in self.contents[dc].iterkeys():
# for meter in self.contents[dc][section]:
# meter.save()
# self.conn.session.commit()
raise NotImplementedError("Not implemented")
class Resource(object):
def __init__(self, resource):
def __init__(self, resource, conn):
self.resource = resource
self.conn = conn
self._meters = {}
def meter(self, name):
pass # Return a named meter
def __getitem__(self, name):
return self.resource[name]
@property
def meters(self):
meters = []
for link in self.resource["links"]:
if link["rel"] == "self":
continue
meter = Meter(self.resource, link)
meters.append(meter)
return meters
if not self._meters:
meters = []
for link in self.resource["links"]:
if link["rel"] == "self":
continue
meter = Meter(self, link, self.conn)
meters.append(meter)
self._meters = meters
return self._meters
class Meter(object):
def __init__(self, resource, link):
def __init__(self, resource, link, conn):
self.resource = resource
self.link = link
self.conn = conn
# self.meter = meter
def __getitem__(self, x):
if isintance(x, slice):
if isinstance(x, slice):
# Woo
pass
pass
@property
def usage(self, start, end):
"""
Usage condenses the entirety of a meter into a single datapoint:
A volume value that we can plot as a single number against previous
usage for a given range.
"""
date_fields = [{
"field": "timestamp",
"op": "ge",
"value": start.strftime(date_format)
},
{
"field": "timestamp",
"op": "lt",
"value": end.strftime(date_format)
}
]
r = requests.get(
self.link,
headers={
"X-Auth-Token": self.auth.auth_token,
"Content-Type":"application/json"},
data=json.dumps({"q": date_fields})
)
measurements = json.loads(r)
measurements = get_meter(self, start, end, self.conn.auth.auth_token)
self.measurements = defaultdict(list)
self.type = set([a["counter_type"] for a in measurements])
if len(self.type) > 1:
# That's a big problem
raise RuntimeError("Too many types for measurement!")
elif len(self.type) == 0:
raise RuntimeError("No types!")
else:
self.type = self.type.pop()
type_ = None
if self.type == "cumulative":
# The usage is the last one, which is the highest value.
#
# Base it just on the resource ID.
# Is this a reasonable thing to do?
# Composition style: resource.meter("cpu_util").usage(start, end) == artifact
type_ = Cumulative
elif self.type == "gauge":
type_ = Gauge
# return Gauge(self.Resource, )
@ -392,6 +469,11 @@ class Meter(object):
class Artifact(object):
"""
Provides base artifact controls; generic typing information
for the artifact structures.
"""
def __init__(self, resource, usage, start, end):
self.resource = resource
@ -409,13 +491,41 @@ class Artifact(object):
Persists to our database backend. Opinionatedly this is a sql datastore.
"""
value = self.volume()
session = self.resource.conn.session
# self.artifice.
tenant_id = self.resource["tenant"]
try:
tenant_id = self.resource["tenant_id"]
except KeyError:
tenant_id = self.resource["project_id"]
resource_id = self.resource["resource_id"]
usage = models.Usage(
resource_id,
tenant_id,
tenant = session.query(tenants.Tenant).get(tenant_id)
if tenant is None:
res = resources.Resource()
tenant = tenants.Tenant()
tenant.id = tenant_id
res.id = resource_id
res.tenant = tenant
session.add(res)
session.add(tenant)
else:
try:
res = session.query(resources.Resource).filter(resources.Resource.id == resource_id)[0]
tenant = res.tenant
except IndexError:
res = resources.Resource()
tenant = tenants.Tenant()
tenant.id = tenant_id
res.id = resource_id
res.tenant = tenant
session.add(res)
session.add(tenant)
usage = Usage(
res,
tenant,
value,
self.start,
self.end,
@ -424,7 +534,6 @@ class Artifact(object):
session.commit() # Persist to our backend
def volume(self):
"""
Default billable number for this volume

View File

@ -10,4 +10,53 @@ class Resource(Base):
id = Column(String, primary_key=True)
type_ = Column(String)
tenant_id = Column(String, ForeignKey("tenants.id"), primary_key=True)
tenant = relationship("Tenant", backref=backref("tenants"))
tenant = relationship("Tenant", backref=backref("tenants"))
class VM(object):
def __init__(self, raw):
# raw is the raw data for this
self._raw = raw
self._location = None
@property
def type(self):
return self._raw["metadata"]["instance_type"]
@property
def size(self):
return self.type
@property
def memory(self):
return self._raw["metadata"]["memory"]
@property
def cpus(self):
return self._raw["metadata"]["memory"]
@property
def state(self):
return self._raw["metadata"]["state"]
@property
def bandwidth(self):
# This is a metered value
return 0
@property
def ips(self):
"""floating IPs; this is a metered value"""
return 0
class Object(object):
pass
class Volume(object):
@property
def location(self):
pass

1
tests/data/index.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/port?q.field=resource_id&q.value=07771c9c-f10e-4b08-9702-78c8618f5a48": [{"counter_name": "port", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "07771c9c-f10e-4b08-9702-78c8618f5a48", "timestamp": "2013-08-05T05:17:40.283000", "message_id": "5981ec68-fd8e-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:nova", "mac_address": "fa:16:3e:b3:f6:db", "event_type": "port.create.end", "id": "07771c9c-f10e-4b08-9702-78c8618f5a48", "device_id": "c01c63fe-7a29-4426-bab1-34d088e92415"}, "counter_type": "gauge"}, {"counter_name": "port", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "07771c9c-f10e-4b08-9702-78c8618f5a48", "timestamp": "2013-08-05T05:17:40.283000", "message_id": "5981ec68-fd8e-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:nova", "mac_address": "fa:16:3e:b3:f6:db", "event_type": "port.create.end", "id": "07771c9c-f10e-4b08-9702-78c8618f5a48", "device_id": "c01c63fe-7a29-4426-bab1-34d088e92415"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/port.create?q.field=resource_id&q.value=07771c9c-f10e-4b08-9702-78c8618f5a48": [{"counter_name": "port.create", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "07771c9c-f10e-4b08-9702-78c8618f5a48", "timestamp": "2013-08-05T05:17:40.283000", "message_id": "5988a0e4-fd8e-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:nova", "mac_address": "fa:16:3e:b3:f6:db", "event_type": "port.create.end", "id": "07771c9c-f10e-4b08-9702-78c8618f5a48", "device_id": "c01c63fe-7a29-4426-bab1-34d088e92415"}, "counter_type": "delta"}]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/port.create?q.field=resource_id&q.value=5561589b-c1fa-46eb-b9d4-5998daceaee4": [{"counter_name": "port.create", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "5561589b-c1fa-46eb-b9d4-5998daceaee4", "timestamp": "2013-08-05T21:56:25.971000", "message_id": "dff93784-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "name": "", "admin_state_up": "True", "network_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "device_owner": "", "mac_address": "fa:16:3e:4a:5d:9a", "event_type": "port.create.end", "id": "5561589b-c1fa-46eb-b9d4-5998daceaee4", "device_id": ""}, "counter_type": "delta"}], "http://localhost:8777/v2/meters/port?q.field=resource_id&q.value=5561589b-c1fa-46eb-b9d4-5998daceaee4": [{"counter_name": "port", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "5561589b-c1fa-46eb-b9d4-5998daceaee4", "timestamp": "2013-08-05T21:56:25.971000", "message_id": "dff84b8a-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "name": "", "admin_state_up": "True", "network_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "device_owner": "", "mac_address": "fa:16:3e:4a:5d:9a", "event_type": "port.create.end", "id": "5561589b-c1fa-46eb-b9d4-5998daceaee4", "device_id": ""}, "counter_type": "gauge"}]}

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/volume?q.field=resource_id&q.value=5d658ace-2535-48c5-a05f-85d651c0d0ac": [{"counter_name": "volume", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "5d658ace-2535-48c5-a05f-85d651c0d0ac", "timestamp": "2013-08-05T03:52:24.800000", "message_id": "7069d14a-fd82-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "volume", "counter_volume": 1.0, "project_id": "7ec5ec9750414a4c8e767e16e1b6c9cf", "resource_metadata": {"status": "available", "display_name": "asdf", "event_type": "volume.create.end", "availability_zone": "nova", "tenant_id": "7ec5ec9750414a4c8e767e16e1b6c9cf", "created_at": "2013-08-05 03:52:23", "volume_id": "5d658ace-2535-48c5-a05f-85d651c0d0ac", "volume_type": "None", "host": "volume.openstack", "snapshot_id": "None", "user_id": "2b8fbe55863d4dfab5310796202b2019", "launched_at": "", "size": "2"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/volume.size?q.field=resource_id&q.value=5d658ace-2535-48c5-a05f-85d651c0d0ac": [{"counter_name": "volume.size", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "5d658ace-2535-48c5-a05f-85d651c0d0ac", "timestamp": "2013-08-05T03:52:24.800000", "message_id": "706bcc3e-fd82-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "GB", "counter_volume": 2.0, "project_id": "7ec5ec9750414a4c8e767e16e1b6c9cf", "resource_metadata": {"status": "available", "display_name": "asdf", "event_type": "volume.create.end", "availability_zone": "nova", "tenant_id": "7ec5ec9750414a4c8e767e16e1b6c9cf", "created_at": "2013-08-05 03:52:23", "volume_id": "5d658ace-2535-48c5-a05f-85d651c0d0ac", "volume_type": "None", "host": "volume.openstack", "snapshot_id": "None", "user_id": "2b8fbe55863d4dfab5310796202b2019", "launched_at": "", "size": "2"}, "counter_type": "gauge"}]}

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/port.create?q.field=resource_id&q.value=615a397c-fca2-4233-9f42-b769a8d6ea59": [{"counter_name": "port.create", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "615a397c-fca2-4233-9f42-b769a8d6ea59", "timestamp": "2013-08-04T23:58:49.296000", "message_id": "ce871c4a-fd61-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:None", "mac_address": "fa:16:3e:4d:0f:e5", "event_type": "port.create.end", "id": "615a397c-fca2-4233-9f42-b769a8d6ea59", "device_id": "03bd0d4c-99da-4ccf-b308-2721f33e84ef"}, "counter_type": "delta"}], "http://localhost:8777/v2/meters/port?q.field=resource_id&q.value=615a397c-fca2-4233-9f42-b769a8d6ea59": [{"counter_name": "port", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "615a397c-fca2-4233-9f42-b769a8d6ea59", "timestamp": "2013-08-04T23:58:49.296000", "message_id": "ce863870-fd61-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:None", "mac_address": "fa:16:3e:4d:0f:e5", "event_type": "port.create.end", "id": "615a397c-fca2-4233-9f42-b769a8d6ea59", "device_id": "03bd0d4c-99da-4ccf-b308-2721f33e84ef"}, "counter_type": "gauge"}]}

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/port?q.field=resource_id&q.value=6b9765e2-9138-4468-a60c-4e9f63044d9d": [{"counter_name": "port", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "6b9765e2-9138-4468-a60c-4e9f63044d9d", "timestamp": "2013-08-05T05:17:40.286000", "message_id": "5982294e-fd8e-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:nova", "mac_address": "fa:16:3e:19:28:00", "event_type": "port.create.end", "id": "6b9765e2-9138-4468-a60c-4e9f63044d9d", "device_id": "1bd5a017-2777-4601-8c48-fd7569a4e485"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/port.create?q.field=resource_id&q.value=6b9765e2-9138-4468-a60c-4e9f63044d9d": [{"counter_name": "port.create", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "6b9765e2-9138-4468-a60c-4e9f63044d9d", "timestamp": "2013-08-05T05:17:40.286000", "message_id": "5983bcb4-fd8e-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:nova", "mac_address": "fa:16:3e:19:28:00", "event_type": "port.create.end", "id": "6b9765e2-9138-4468-a60c-4e9f63044d9d", "device_id": "1bd5a017-2777-4601-8c48-fd7569a4e485"}, "counter_type": "delta"}]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/network?q.field=resource_id&q.value=79b5e163-496c-4ab6-9c21-3b597ed632dc": [{"counter_name": "network", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "timestamp": "2013-08-05T21:56:01.044000", "message_id": "d11cd69e-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "network", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "name": "two", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "shared": "False", "id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "event_type": "network.create.end"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/network.create?q.field=resource_id&q.value=79b5e163-496c-4ab6-9c21-3b597ed632dc": [{"counter_name": "network.create", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "timestamp": "2013-08-05T21:56:01.044000", "message_id": "d11dd012-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "network", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "name": "two", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "shared": "False", "id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "event_type": "network.create.end"}, "counter_type": "delta"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/subnet.create?q.field=resource_id&q.value=a2777ed6-7442-42b8-bb86-5b554aad7f08": [{"counter_name": "subnet.create", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "a2777ed6-7442-42b8-bb86-5b554aad7f08", "timestamp": "2013-08-05T21:56:01.107000", "message_id": "d126f9da-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "subnet", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"name": "two", "enable_dhcp": "True", "network_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "gateway_ip": "192.168.0.1", "ip_version": "4", "cidr": "192.168.0.0/24", "id": "a2777ed6-7442-42b8-bb86-5b554aad7f08", "event_type": "subnet.create.end"}, "counter_type": "delta"}], "http://localhost:8777/v2/meters/subnet?q.field=resource_id&q.value=a2777ed6-7442-42b8-bb86-5b554aad7f08": [{"counter_name": "subnet", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "a2777ed6-7442-42b8-bb86-5b554aad7f08", "timestamp": "2013-08-05T21:56:01.107000", "message_id": "d1264c1a-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "subnet", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"name": "two", "enable_dhcp": "True", "network_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "gateway_ip": "192.168.0.1", "ip_version": "4", "cidr": "192.168.0.0/24", "id": "a2777ed6-7442-42b8-bb86-5b554aad7f08", "event_type": "subnet.create.end"}, "counter_type": "gauge"}]}

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/volume?q.field=resource_id&q.value=a5696083-56d2-4e25-a0cb-2b51bd2ca90d": [{"counter_name": "volume", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "a5696083-56d2-4e25-a0cb-2b51bd2ca90d", "timestamp": "2013-08-05T03:50:05.084000", "message_id": "1d224d8c-fd82-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "volume", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "available", "display_name": "tests", "event_type": "volume.create.end", "availability_zone": "nova", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "created_at": "2013-08-05 03:50:03", "volume_id": "a5696083-56d2-4e25-a0cb-2b51bd2ca90d", "volume_type": "2fbf14d8-e544-487e-aba7-98c1ebba8fcd", "host": "volume.openstack", "snapshot_id": "None", "user_id": "2b8fbe55863d4dfab5310796202b2019", "launched_at": "", "size": "1"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/volume.size?q.field=resource_id&q.value=a5696083-56d2-4e25-a0cb-2b51bd2ca90d": [{"counter_name": "volume.size", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "a5696083-56d2-4e25-a0cb-2b51bd2ca90d", "timestamp": "2013-08-05T03:50:05.084000", "message_id": "1d244fd8-fd82-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "GB", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "available", "display_name": "tests", "event_type": "volume.create.end", "availability_zone": "nova", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "created_at": "2013-08-05 03:50:03", "volume_id": "a5696083-56d2-4e25-a0cb-2b51bd2ca90d", "volume_type": "2fbf14d8-e544-487e-aba7-98c1ebba8fcd", "host": "volume.openstack", "snapshot_id": "None", "user_id": "2b8fbe55863d4dfab5310796202b2019", "launched_at": "", "size": "1"}, "counter_type": "gauge"}]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/port?q.field=resource_id&q.value=b35709ef-6b3a-42d7-bd57-ef4cd673ec9b": [{"counter_name": "port", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "b35709ef-6b3a-42d7-bd57-ef4cd673ec9b", "timestamp": "2013-08-05T22:04:18.505000", "message_id": "f99f7c38-fe1a-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:None", "mac_address": "fa:16:3e:a6:d8:57", "event_type": "port.create.end", "id": "b35709ef-6b3a-42d7-bd57-ef4cd673ec9b", "device_id": "0a16330b-d457-425d-8249-a275fab9adea"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/port.create?q.field=resource_id&q.value=b35709ef-6b3a-42d7-bd57-ef4cd673ec9b": [{"counter_name": "port.create", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "b35709ef-6b3a-42d7-bd57-ef4cd673ec9b", "timestamp": "2013-08-05T22:04:18.505000", "message_id": "f9a15652-fe1a-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "79b5e163-496c-4ab6-9c21-3b597ed632dc", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:None", "mac_address": "fa:16:3e:a6:d8:57", "event_type": "port.create.end", "id": "b35709ef-6b3a-42d7-bd57-ef4cd673ec9b", "device_id": "0a16330b-d457-425d-8249-a275fab9adea"}, "counter_type": "delta"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/port?q.field=resource_id&q.value=24916b40-f8d8-49ad-be4a-4edbf6c9a370": [{"counter_name": "port", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "24916b40-f8d8-49ad-be4a-4edbf6c9a370", "timestamp": "2013-08-05T22:04:19.030000", "message_id": "f9f4e18c-fe1a-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:None", "mac_address": "fa:16:3e:de:89:e7", "event_type": "port.create.end", "id": "24916b40-f8d8-49ad-be4a-4edbf6c9a370", "device_id": "0a16330b-d457-425d-8249-a275fab9adea"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/port.create?q.field=resource_id&q.value=24916b40-f8d8-49ad-be4a-4edbf6c9a370": [{"counter_name": "port.create", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "24916b40-f8d8-49ad-be4a-4edbf6c9a370", "timestamp": "2013-08-05T22:04:19.030000", "message_id": "f9f5a234-fe1a-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:None", "mac_address": "fa:16:3e:de:89:e7", "event_type": "port.create.end", "id": "24916b40-f8d8-49ad-be4a-4edbf6c9a370", "device_id": "0a16330b-d457-425d-8249-a275fab9adea"}, "counter_type": "delta"}]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/port?q.field=resource_id&q.value=375d1c5d-e732-4082-857b-ca133f08006b": [{"counter_name": "port", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "375d1c5d-e732-4082-857b-ca133f08006b", "timestamp": "2013-08-05T05:17:39.866000", "message_id": "593b506e-fd8e-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:nova", "mac_address": "fa:16:3e:20:df:2e", "event_type": "port.create.end", "id": "375d1c5d-e732-4082-857b-ca133f08006b", "device_id": "6d254988-1431-49d7-9491-e7c27d0bd6cb"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/port.create?q.field=resource_id&q.value=375d1c5d-e732-4082-857b-ca133f08006b": [{"counter_name": "port.create", "user_id": "9692a61b44f246259af534f0b1d95942", "resource_id": "375d1c5d-e732-4082-857b-ca133f08006b", "timestamp": "2013-08-05T05:17:39.866000", "message_id": "593d6552-fd8e-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "port", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "DOWN", "binding:host_id": "openstack", "name": "", "admin_state_up": "True", "network_id": "0e5f3762-a8a9-4051-ad35-49ad879ebfc3", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "binding:vif_type": "ovs", "device_owner": "compute:nova", "mac_address": "fa:16:3e:20:df:2e", "event_type": "port.create.end", "id": "375d1c5d-e732-4082-857b-ca133f08006b", "device_id": "6d254988-1431-49d7-9491-e7c27d0bd6cb"}, "counter_type": "delta"}]}

View File

@ -0,0 +1 @@
{"http://localhost:8777/v2/meters/router.create?q.field=resource_id&q.value=4392aff1-d6a8-4106-9e75-818eb91a3d45": [{"counter_name": "router.create", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "timestamp": "2013-08-05T21:55:26.292000", "message_id": "bc685070-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "router", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "external_gateway_info": "None", "name": "router2", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "event_type": "router.create.end"}, "counter_type": "delta"}], "http://localhost:8777/v2/meters/router?q.field=resource_id&q.value=4392aff1-d6a8-4106-9e75-818eb91a3d45": [{"counter_name": "router", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "timestamp": "2013-08-05T22:07:15.284000", "message_id": "62fe6586-fe1b-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "router", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "external_gateway_info": "None", "name": "router2", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "event_type": "router.update.end"}, "counter_type": "gauge"}, {"counter_name": "router", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "timestamp": "2013-08-05T21:55:33.751000", "message_id": "c0d80f10-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "router", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "name": "router2", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "event_type": "router.update.end"}, "counter_type": "gauge"}, {"counter_name": "router", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "timestamp": "2013-08-05T21:55:26.292000", "message_id": "bc6726c8-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "router", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "external_gateway_info": "None", "name": "router2", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "event_type": "router.create.end"}, "counter_type": "gauge"}], "http://localhost:8777/v2/meters/router.update?q.field=resource_id&q.value=4392aff1-d6a8-4106-9e75-818eb91a3d45": [{"counter_name": "router.update", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "timestamp": "2013-08-05T22:07:15.284000", "message_id": "62ff4c80-fe1b-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "router", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "external_gateway_info": "None", "name": "router2", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "event_type": "router.update.end"}, "counter_type": "delta"}, {"counter_name": "router.update", "user_id": "2b8fbe55863d4dfab5310796202b2019", "resource_id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "timestamp": "2013-08-05T21:55:33.751000", "message_id": "c0d8dc74-fe19-11e2-a072-080027880ca6", "source": "openstack", "counter_unit": "router", "counter_volume": 1.0, "project_id": "931dc699f9934021bb4a2b1088ba4d3b", "resource_metadata": {"status": "ACTIVE", "name": "router2", "admin_state_up": "True", "tenant_id": "931dc699f9934021bb4a2b1088ba4d3b", "host": "network.openstack", "id": "4392aff1-d6a8-4106-9e75-818eb91a3d45", "event_type": "router.update.end"}, "counter_type": "delta"}]}

File diff suppressed because one or more lines are too long

3
tests/test_import.py Normal file
View File

@ -0,0 +1,3 @@
print __file__
import os
print os.path.abspath(__file__)

320
tests/test_interface.py Normal file
View File

@ -0,0 +1,320 @@
import unittest
from artifice import interface
from artifice.interface import Artifice
import mock
import random
import json
import copy
from sqlalchemy import create_engine
from artifice.models import Session
from artifice.models import usage, tenants
from artifice.models.resources import Resource
from datetime import datetime, timedelta
"""
This module tests interacting with the Ceilometer web service.
This will require an active Ceilometer with data in it, or,
"""
# @mock.patch("artifice.models.Session")
# @mock.patch("keystoneclient.v2_0.client.Client")
# @mock.patch("sqlalchemy.create_engine")
# def test_instance_artifice(self, sqlmock, keystone, session):
# """Tests that artifice correctly instances."""
# from artifice.interface import Artifice
# # from artifice.models import usage
config = {
"main": {},
"database": {
"username": "aurynn",
"password": "aurynn",
"host": "localhost",
"port": "5433",
"database": "artifice"
},
"openstack": {
"username": "foo",
"password": "bar",
"default_tenant":"asdf",
"authentication_url": "http://foo"
},
"ceilometer": {
"host": 'http://whee'
},
"invoices": {
"plugin": "json"
}
}
# Enter in a whoooooole bunch of mock data.
TENANTS = [
{u'enabled': True,
u'description': None,
u'name': u'demo',
u'id': u'931dc699f9934021bb4a2b1088ba4d3b'}
]
DATACENTRE = "testcenter"
# I think three is good
import os
try:
fn = os.path.abspath(__file__)
path, f = os.path.split(fn)
except NameError:
path = os.getcwd()
fh = open( os.path.join( path, "data/resources.json") )
resources = json.loads(fh.read () )
fh.close()
i = 0
mappings = {}
hosts = set([resource["metadata"]["host"] for resource in resources if resource["metadata"].get("host")])
while True:
try:
fh = open( os.path.join( path, "data/map_fixture_%s.json" % i ) )
d = json.loads(fh.read())
fh.close()
mappings.update(d)
i += 1
except IOError:
break
# Mappings should now be a set of resources.
AUTH_TOKEN = "ASDFTOKEN"
storages = []
vms = []
networks = []
# res = {"vms": [], "net": [], 'storage': [], "ports":[], "ips": []}
# res = {"vms": [], "network": [], 'storage': [], "ports":[]}
res = {"vms": [], "volumes": [], 'objects': [], "network": []}
for resource in resources:
rels = [link["rel"] for link in resource["links"] if link["rel"] != 'self' ]
if "image" in rels:
continue
elif "storage.objects" in rels:
# Unknown how this data layout happens yet.
# resource["_type"] = "storage"
res["objects"].append(resource)
elif "volume" in rels:
res["volumes"].append(resource)
elif "network" in rels:
res["network"].append(resource)
elif "instance" in rels:
res["vms"].append(resource)
# elif "port" in rels:
# res["ports"].append(resource)
# elif "ip.floating" in rels:
# res["ips"].append(resource)
def resources_replacement(tester):
#
def repl(self, start, end):
tester.called_replacement_resources = True
return resources
class TestInterface(unittest.TestCase):
def setUp(self):
engine = create_engine(os.environ["DATABASE_URL"])
Session.configure(bind=engine)
self.session = Session()
self.objects = []
self.session.rollback()
self.called_replacement_resources = False
num = random.randrange(len(res["vms"]))
# Only one vm for this
self.resources = [ res["vms"][ num ] ]
self.start = datetime.now() - timedelta(days=30)
self.end = datetime.now()
def tearDown(self):
self.session.query(usage.Usage).delete()
self.session.query(Resource).delete()
self.session.query(tenants.Tenant).delete()
self.session.commit()
self.contents = None
self.resources = []
@mock.patch("artifice.models.Session")
# @mock.patch("artifice.interface.get_meter") # I don't think this will work
@mock.patch("artifice.interface.keystone")
@mock.patch("sqlalchemy.create_engine")
def test_get_usage(self, sqlmock, keystone, session):
# At this point, we prime the ceilometer/requests response
# system, so that what we return to usage is what we expect
# to get in the usage system.
keystone.auth_token = AUTH_TOKEN
# keystone.
self.assertTrue(TENANTS is not None)
def get_meter(self, start, end, auth):
# Returns meter data from our data up above
global mappings
# print self.link
data = mappings[self.link["href"]]
return data
interface.get_meter = get_meter
artifice = Artifice(config)
# Needed because this
artifice.auth.tenants.list.return_value = TENANTS
# this_config = copy.deepcopy(config["openstack"])
# this_config["tenant_name"] = this_config["default_tenant"]
# del this_config["default_tenant"]
keystone.assert_called_with(
username= config["openstack"]["username"],
password= config["openstack"]["password"],
tenant_name= config["openstack"]["default_tenant"],
auth_url= config["openstack"]["authentication_url"]
)
tenants = None
try:
tenants = artifice.tenants
except Exception as e:
self.fail(e)
# self.assertEqual ( len(tenants.vms), 1 )
self.assertEqual( len(tenants), 1 )
k = tenants.keys()[0]
t = tenants[k]
self.assertTrue( isinstance( t, interface.Tenant ) )
contents = None
# t.resources = resources_replacement(self)
t.resources = mock.Mock()
t.resources.return_value = self.resources
artifice.host_to_dc = mock.Mock()
artifice.host_to_dc.return_value = DATACENTRE
usage = t.usage(self.start, self.end)
# try:
# except Exception as e:
# self.fail(e)
# self.assertTrue( self.called_replacement_resources )
t.resources.assert_called_with(self.start, self.end)
# So, we should be able to assert a couple of things here
# What got called, when, and how.
for call in artifice.host_to_dc.call_args_list:
self.assertTrue ( len(call[0]) == 1 )
self.assertTrue ( call[0][0] in hosts )
self.assertTrue ( isinstance(usage, interface.Usage) )
# self.assertEqual( len(usage.vms), 1 )
# self.assertEqual( len(usage.objects), 0)
# self.assertEqual( len(usage.volumes), 0)
self.usage = usage
# @mock.patch("artifice.models.Session")
# @mock.patch("artifice.interface.get_meter") # I don't think this will work
# @mock.patch("artifice.interface.keystone")
# @mock.patch("sqlalchemy.create_engine")
# def test_save_smaller_range_no_overlap(self, sqlmock, keystone, meters, session):
# self.test_get_usage()
# first_contents = self.usage
# # self.resources = [
# # res["vms"][random.randrange(len[res["vms"]])],
# # ]
def add_element(self, from_):
self.resources.append( res[from_][random.randrange(len(res[from_]))] )
print len(self.resources)
self.test_get_usage()
usage = self.usage
# key = contents.keys()[0] # Key is the datacenter
print from_
self.assertTrue( hasattr(usage, from_) )
self.assertTrue( hasattr(usage, "vms") )
lens = { "vms": 1}
if from_ == "vms":
lens["vms"] = 2
else:
lens[from_] = 1
self.assertEqual(len(usage.vms), lens["vms"])
self.assertEqual(len( getattr(usage, from_) ), lens[from_])
self.assertEqual( usage.vms[0].location, DATACENTRE )
def test_add_instance(self):
"""
Adds a new instance, tests that the returned data has both
Tests that if we create a new instance in the data,
we should get another instance in the returned contents.
"""
self.add_element("vms")
def test_add_storage(self):
self.add_element("objects")
# def test_add_ip(self):
# self.add_element("ips")
def test_save_contents(self):
self.test_get_usage()
usage = self.usage
# try:
usage.save()
# except Exception as e:
self.fail(e)
# Now examine the database
def test_correct_usage_values(self):
pass
def test_use_a_bunch_of_data(self):
pass

View File

@ -11,12 +11,10 @@ class TestInvoicing(unittest.TestCase):
def tearDown(self):
pass
def test_replace_invoice_object(self):
pass
def test_add_usage_to_invoice(self):
pass
def test_save_invoice(self):