Add a 'usage' module and 'usage-list' cli command

This module talks to the nova extenstion os-simple-tenant-usage,
replacing the openstackx module currently used by horizon.

v2:
    Fix some pep8 and style violations

v4:
    Have usage-list default to including todays usage

v5:
    Fix a HACKING violation
    Fix rebase conflicts

Change-Id: Ica0b128c7b807b839abf23b4026e48bbee08b1be
This commit is contained in:
Cole Robinson 2012-01-22 18:56:39 -05:00
parent de9813c6c5
commit c3b043be0e
8 changed files with 178 additions and 0 deletions

View File

@ -8,6 +8,7 @@ Brian Waldon <brian.waldon@rackspace.com>
Chmouel Boudjnah <chmouel.boudjnah@rackspace.co.uk>
Chris Behrens <cbehrens+github@codestud.com>
Christopher MacGown <ignoti+github@gmail.com>
Cole Robinson <crobinso@redhat.com>
Dan Wendlandt <dan@nicira.com>
Dean Troyer <dtroyer@gmail.com>
Ed Leafe <ed@leafe.com>

View File

@ -135,6 +135,7 @@ You'll find complete documentation on the shell by running
suspend Suspend a server.
unpause Unpause a server.
unrescue Unrescue a server.
usage-list List usage data for all tenants
volume-attach Attach a volume to a server.
volume-create Add a new volume.
volume-delete Remove a volume.

View File

@ -10,6 +10,7 @@ from novaclient.v1_1 import quotas
from novaclient.v1_1 import security_group_rules
from novaclient.v1_1 import security_groups
from novaclient.v1_1 import servers
from novaclient.v1_1 import usage
from novaclient.v1_1 import virtual_interfaces
from novaclient.v1_1 import volumes
from novaclient.v1_1 import volume_snapshots
@ -57,6 +58,7 @@ class Client(object):
self.security_groups = security_groups.SecurityGroupManager(self)
self.security_group_rules = \
security_group_rules.SecurityGroupRuleManager(self)
self.usage = usage.UsageManager(self)
self.virtual_interfaces = \
virtual_interfaces.VirtualInterfaceManager(self)

View File

@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import getpass
import os
@ -1275,3 +1276,46 @@ def do_rate_limits(cs, args):
limits = cs.limits.get().rate
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
utils.print_list(limits, columns)
@utils.arg('--start', metavar='<start>',
help='Usage range start date ex 2012-01-20 (default: 4 weeks ago)',
default=None)
@utils.arg('--end', metavar='<end>',
help='Usage range end date, ex 2012-01-20 (default: tomorrow) ',
default=None)
def do_usage_list(cs, args):
"""List usage data for all tenants"""
dateformat = "%Y-%m-%d"
rows = ["Tenant ID", "Instances", "RAM MB-Hours", "CPU Hours",
"Disk GB-Hours"]
if args.start:
start = datetime.datetime.strptime(args.start, dateformat)
else:
start = (datetime.datetime.today() -
datetime.timedelta(weeks=4))
if args.end:
end = datetime.datetime.strptime(args.end, dateformat)
else:
end = datetime.datetime.tomorrow()
def simplify_usage(u):
simplerows = map(lambda x: x.lower().replace(" ", "_"), rows)
setattr(u, simplerows[0], u.tenant_id)
setattr(u, simplerows[1], "%d" % len(u.server_usages))
setattr(u, simplerows[2], "%.2f" % u.total_memory_mb_usage)
setattr(u, simplerows[3], "%.2f" % u.total_vcpus_usage)
setattr(u, simplerows[4], "%.2f" % u.total_local_gb_usage)
usage_list = cs.usage.list(start, end, detailed=True)
print "Usage from %s to %s:" % (start.strftime(dateformat),
end.strftime(dateformat))
for usage in usage_list:
simplify_usage(usage)
utils.print_list(usage_list, rows)

48
novaclient/v1_1/usage.py Normal file
View File

@ -0,0 +1,48 @@
"""
Usage interface.
"""
from novaclient import base
class Usage(base.Resource):
"""
Usage contains infomartion about a tenants physical resource usage
"""
def __repr__(self):
return "<ComputeUsage>"
class UsageManager(base.ManagerWithFind):
"""
Manage :class:`Usage` resources.
"""
resource_class = Usage
def list(self, start, end, detailed=False):
"""
Get usage for all tenants
:param start: :class:`datetime.datetime` Start date
:param end: :class:`datetime.datetime` End date
:param detailed: Whether to include information about each
instance whose usage is part of the report
:rtype: list of :class:`Usage`.
"""
return self._list(
"/os-simple-tenant-usage?start=%s&end=%s&detailed=%s" %
(start.isoformat(), end.isoformat(), int(bool(detailed))),
"tenant_usages")
def get(self, tenant_id, start, end):
"""
Get usage for a specific tenant.
:param tenant_id: Tenant ID to fetch usage for
:param start: :class:`datetime.datetime` Start date
:param end: :class:`datetime.datetime` End date
:rtype: :class:`Usage`
"""
return self._get("/os-simple-tenant-usage/%s?start=%s&end=%s" %
(tenant_id, start.isoformat(), end.isoformat()),
"tenant_usage")

View File

@ -643,3 +643,42 @@ class FakeHTTPClient(base_client.HTTPClient):
r = {'security_group_rule':
self.get_os_security_group_rules()[1]['security_group_rules'][0]}
return (202, r)
#
# Tenant Usage
#
def get_os_simple_tenant_usage(self, **kw):
return (200, {u'tenant_usages': [{
u'total_memory_mb_usage': 25451.762807466665,
u'total_vcpus_usage': 49.71047423333333,
u'total_hours': 49.71047423333333,
u'tenant_id': u'7b0a1d73f8fb41718f3343c207597869',
u'stop': u'2012-01-22 19:48:41.750722',
u'server_usages': [{
u'hours': 49.71047423333333,
u'uptime': 27035, u'local_gb': 0, u'ended_at': None,
u'name': u'f15image1',
u'tenant_id': u'7b0a1d73f8fb41718f3343c207597869',
u'vcpus': 1, u'memory_mb': 512, u'state': u'active',
u'flavor': u'm1.tiny',
u'started_at': u'2012-01-20 18:06:06.479998'}],
u'start': u'2011-12-25 19:48:41.750687',
u'total_local_gb_usage': 0.0}]})
def get_os_simple_tenant_usage_tenantfoo(self, **kw):
return (200, {u'tenant_usage': {
u'total_memory_mb_usage': 25451.762807466665,
u'total_vcpus_usage': 49.71047423333333,
u'total_hours': 49.71047423333333,
u'tenant_id': u'7b0a1d73f8fb41718f3343c207597869',
u'stop': u'2012-01-22 19:48:41.750722',
u'server_usages': [{
u'hours': 49.71047423333333,
u'uptime': 27035, u'local_gb': 0, u'ended_at': None,
u'name': u'f15image1',
u'tenant_id': u'7b0a1d73f8fb41718f3343c207597869',
u'vcpus': 1, u'memory_mb': 512, u'state': u'active',
u'flavor': u'm1.tiny',
u'started_at': u'2012-01-20 18:06:06.479998'}],
u'start': u'2011-12-25 19:48:41.750687',
u'total_local_gb_usage': 0.0}})

View File

@ -320,3 +320,11 @@ class ShellTest(utils.TestCase):
def test_dns_zones(self):
self.run_command('dns-zones')
self.assert_called('GET', '/os-floating-ip-dns')
def test_usage_list(self):
self.run_command('usage-list --start 2000-01-20 --end 2005-02-01')
self.assert_called('GET',
'/os-simple-tenant-usage?' +
'start=2000-01-20T00:00:00&' +
'end=2005-02-01T00:00:00&' +
'detailed=1')

35
tests/v1_1/test_usage.py Normal file
View File

@ -0,0 +1,35 @@
import datetime
from novaclient.v1_1 import usage
from tests import utils
from tests.v1_1 import fakes
cs = fakes.FakeClient()
class UsageTest(utils.TestCase):
def test_usage_list(self, detailed=False):
now = datetime.datetime.now()
usages = cs.usage.list(now, now, detailed)
cs.assert_called('GET',
"/os-simple-tenant-usage?" +
("start=%s&" % now.isoformat()) +
("end=%s&" % now.isoformat()) +
("detailed=%s" % int(bool(detailed))))
[self.assertTrue(isinstance(u, usage.Usage)) for u in usages]
def test_usage_list_detailed(self):
self.test_usage_list(True)
def test_usage_get(self):
now = datetime.datetime.now()
u = cs.usage.get("tenantfoo", now, now)
cs.assert_called('GET',
"/os-simple-tenant-usage/tenantfoo?" +
("start=%s&" % now.isoformat()) +
("end=%s" % now.isoformat()))
self.assertTrue(isinstance(u, usage.Usage))