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:
parent
de9813c6c5
commit
c3b043be0e
1
AUTHORS
1
AUTHORS
@ -8,6 +8,7 @@ Brian Waldon <brian.waldon@rackspace.com>
|
|||||||
Chmouel Boudjnah <chmouel.boudjnah@rackspace.co.uk>
|
Chmouel Boudjnah <chmouel.boudjnah@rackspace.co.uk>
|
||||||
Chris Behrens <cbehrens+github@codestud.com>
|
Chris Behrens <cbehrens+github@codestud.com>
|
||||||
Christopher MacGown <ignoti+github@gmail.com>
|
Christopher MacGown <ignoti+github@gmail.com>
|
||||||
|
Cole Robinson <crobinso@redhat.com>
|
||||||
Dan Wendlandt <dan@nicira.com>
|
Dan Wendlandt <dan@nicira.com>
|
||||||
Dean Troyer <dtroyer@gmail.com>
|
Dean Troyer <dtroyer@gmail.com>
|
||||||
Ed Leafe <ed@leafe.com>
|
Ed Leafe <ed@leafe.com>
|
||||||
|
@ -135,6 +135,7 @@ You'll find complete documentation on the shell by running
|
|||||||
suspend Suspend a server.
|
suspend Suspend a server.
|
||||||
unpause Unpause a server.
|
unpause Unpause a server.
|
||||||
unrescue Unrescue a server.
|
unrescue Unrescue a server.
|
||||||
|
usage-list List usage data for all tenants
|
||||||
volume-attach Attach a volume to a server.
|
volume-attach Attach a volume to a server.
|
||||||
volume-create Add a new volume.
|
volume-create Add a new volume.
|
||||||
volume-delete Remove a volume.
|
volume-delete Remove a volume.
|
||||||
|
@ -10,6 +10,7 @@ from novaclient.v1_1 import quotas
|
|||||||
from novaclient.v1_1 import security_group_rules
|
from novaclient.v1_1 import security_group_rules
|
||||||
from novaclient.v1_1 import security_groups
|
from novaclient.v1_1 import security_groups
|
||||||
from novaclient.v1_1 import servers
|
from novaclient.v1_1 import servers
|
||||||
|
from novaclient.v1_1 import usage
|
||||||
from novaclient.v1_1 import virtual_interfaces
|
from novaclient.v1_1 import virtual_interfaces
|
||||||
from novaclient.v1_1 import volumes
|
from novaclient.v1_1 import volumes
|
||||||
from novaclient.v1_1 import volume_snapshots
|
from novaclient.v1_1 import volume_snapshots
|
||||||
@ -57,6 +58,7 @@ class Client(object):
|
|||||||
self.security_groups = security_groups.SecurityGroupManager(self)
|
self.security_groups = security_groups.SecurityGroupManager(self)
|
||||||
self.security_group_rules = \
|
self.security_group_rules = \
|
||||||
security_group_rules.SecurityGroupRuleManager(self)
|
security_group_rules.SecurityGroupRuleManager(self)
|
||||||
|
self.usage = usage.UsageManager(self)
|
||||||
self.virtual_interfaces = \
|
self.virtual_interfaces = \
|
||||||
virtual_interfaces.VirtualInterfaceManager(self)
|
virtual_interfaces.VirtualInterfaceManager(self)
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import datetime
|
||||||
import getpass
|
import getpass
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -1275,3 +1276,46 @@ def do_rate_limits(cs, args):
|
|||||||
limits = cs.limits.get().rate
|
limits = cs.limits.get().rate
|
||||||
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
|
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
|
||||||
utils.print_list(limits, columns)
|
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
48
novaclient/v1_1/usage.py
Normal 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")
|
@ -643,3 +643,42 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
r = {'security_group_rule':
|
r = {'security_group_rule':
|
||||||
self.get_os_security_group_rules()[1]['security_group_rules'][0]}
|
self.get_os_security_group_rules()[1]['security_group_rules'][0]}
|
||||||
return (202, r)
|
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}})
|
||||||
|
@ -320,3 +320,11 @@ class ShellTest(utils.TestCase):
|
|||||||
def test_dns_zones(self):
|
def test_dns_zones(self):
|
||||||
self.run_command('dns-zones')
|
self.run_command('dns-zones')
|
||||||
self.assert_called('GET', '/os-floating-ip-dns')
|
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
35
tests/v1_1/test_usage.py
Normal 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))
|
Loading…
x
Reference in New Issue
Block a user