Removes os-simple-tenant-usage from the V3 API

The extension pre-dates ceilometer and equivalent
functionality is now much better managed using
ceilometer. So we're removing this before the V3 API
is officially released.

Partially implements blueprint v3-api-remove-extensions

Change-Id: Ia8fd475e6f0c4dbc67f3c47279cbaa2280b3af0b
This commit is contained in:
Christopher Yeoh
2014-02-07 20:33:05 -08:00
parent c73f8eb6e8
commit d090d3d8c6
14 changed files with 0 additions and 814 deletions

View File

@@ -1,10 +0,0 @@
{
"server" : {
"name" : "new-server-test",
"image_ref" : "http://glance.openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b",
"flavor_ref" : "http://openstack.example.com/flavors/1",
"metadata" : {
"My Server Name" : "Apache1"
}
}
}

View File

@@ -1,16 +0,0 @@
{
"server": {
"admin_password": "andqf3WBEYDj",
"id": "69eaa1f4-7be0-42d5-934f-320cdc80981d",
"links": [
{
"href": "http://openstack.example.com/v3/servers/69eaa1f4-7be0-42d5-934f-320cdc80981d",
"rel": "self"
},
{
"href": "http://openstack.example.com/servers/69eaa1f4-7be0-42d5-934f-320cdc80981d",
"rel": "bookmark"
}
]
}
}

View File

@@ -1,27 +0,0 @@
{
"tenant_usage": {
"server_usages": [
{
"ended_at": null,
"flavor": "m1.tiny",
"hours": 1.0,
"instance_id": "bf2fbe78-ae1c-4f6b-a68b-390f12b1b983",
"local_gb": 1,
"memory_mb": 512,
"name": "new-server-test",
"started_at": "2013-09-09T13:18:28.101337",
"state": "active",
"tenant_id": "openstack",
"uptime": 3600,
"vcpus": 1
}
],
"start": "2013-09-09T13:18:28.101337",
"stop": "2013-09-09T14:18:28.101337",
"tenant_id": "openstack",
"total_hours": 1.0,
"total_local_gb_usage": 1.0,
"total_memory_mb_usage": 512.0,
"total_vcpus_usage": 1.0
}
}

View File

@@ -1,13 +0,0 @@
{
"tenant_usages": [
{
"start": "2013-09-09T13:18:27.898411",
"stop": "2013-09-09T14:18:27.898411",
"tenant_id": "openstack",
"total_hours": 1.0,
"total_local_gb_usage": 1.0,
"total_memory_mb_usage": 512.0,
"total_vcpus_usage": 1.0
}
]
}

View File

@@ -227,10 +227,7 @@
"compute_extension:v3:os-suspend-server:discoverable": "",
"compute_extension:v3:os-suspend-server:suspend": "rule:admin_or_owner",
"compute_extension:v3:os-suspend-server:resume": "rule:admin_or_owner",
"compute_extension:v3:os-simple-tenant-usage:show": "rule:admin_or_owner",
"compute_extension:v3:os-simple-tenant-usage:discoverable": "",
"compute_extension:simple_tenant_usage:list": "rule:admin_api",
"compute_extension:v3:os-simple-tenant-usage:list": "rule:admin_api",
"compute_extension:unshelve": "",
"compute_extension:v3:os-shelve:unshelve": "",
"compute_extension:users": "rule:admin_api",

View File

@@ -1,277 +0,0 @@
# Copyright 2011 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import urlparse
import iso8601
from webob import exc
from nova.api.openstack import extensions
from nova import exception
from nova.objects import flavor as flavor_obj
from nova.objects import instance as instance_obj
from nova.openstack.common.gettextutils import _
from nova.openstack.common import timeutils
ALIAS = 'os-simple-tenant-usage'
authorize_show = extensions.extension_authorizer('compute',
'v3:' + ALIAS + ':show')
authorize_list = extensions.extension_authorizer('compute',
'v3:' + ALIAS + ':list')
VALID_DATETIME_FORMAT = ["%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M:%S.%f",
"%Y-%m-%d %H:%M:%S.%f"]
class SimpleTenantUsageController(object):
def _hours_for(self, instance, period_start, period_stop):
launched_at = instance.launched_at
terminated_at = instance.terminated_at
if terminated_at is not None:
if not isinstance(terminated_at, datetime.datetime):
# NOTE(mriedem): Instance object DateTime fields are
# timezone-aware so convert using isotime.
terminated_at = timeutils.parse_isotime(terminated_at)
if launched_at is not None:
if not isinstance(launched_at, datetime.datetime):
launched_at = timeutils.parse_isotime(launched_at)
if terminated_at and terminated_at < period_start:
return 0
# nothing if it started after the usage report ended
if launched_at and launched_at > period_stop:
return 0
if launched_at:
# if instance launched after period_started, don't charge for first
start = max(launched_at, period_start)
if terminated_at:
# if instance stopped before period_stop, don't charge after
stop = min(period_stop, terminated_at)
else:
# instance is still running, so charge them up to current time
stop = period_stop
dt = stop - start
seconds = (dt.days * 3600 * 24 + dt.seconds +
dt.microseconds / 100000.0)
return seconds / 3600.0
else:
# instance hasn't launched, so no charge
return 0
def _get_flavor(self, context, instance, flavors_cache):
"""Get flavor information from the instance's system_metadata,
allowing a fallback to lookup by-id for deleted instances only.
"""
try:
return instance.get_flavor()
except KeyError:
if not instance.deleted:
# Only support the fallback mechanism for deleted instances
# that would have been skipped by migration #153
raise
flavor_type = instance.instance_type_id
if flavor_type in flavors_cache:
return flavors_cache[flavor_type]
try:
flavor_ref = flavor_obj.Flavor.get_by_id(context, flavor_type)
flavors_cache[flavor_type] = flavor_ref
except exception.FlavorNotFound:
# can't bill if there is no flavor
flavor_ref = None
return flavor_ref
def _tenant_usages_for_period(self, context, period_start,
period_stop, tenant_id=None, detailed=True):
instances = instance_obj.InstanceList.get_active_by_window_joined(
context, period_start, period_stop, tenant_id,
expected_attrs=instance_obj.INSTANCE_DEFAULT_FIELDS)
rval = {}
flavors = {}
for instance in instances:
info = {}
info['hours'] = self._hours_for(instance,
period_start,
period_stop)
flavor = self._get_flavor(context, instance, flavors)
if not flavor:
continue
info['instance_id'] = instance.uuid
info['name'] = instance.display_name
info['memory_mb'] = flavor.memory_mb
info['local_gb'] = flavor.root_gb + flavor.ephemeral_gb
info['vcpus'] = flavor.vcpus
info['tenant_id'] = instance.project_id
info['flavor'] = flavor.name
# NOTE(mriedem): We need to normalize the start/end times back
# to timezone-naive so the response doesn't change after the
# conversion to objects.
info['started_at'] = timeutils.normalize_time(instance.launched_at)
info['ended_at'] = (
timeutils.normalize_time(instance.terminated_at) if
instance.terminated_at else None)
if info['ended_at']:
info['state'] = 'terminated'
else:
info['state'] = instance.vm_state
now = timeutils.utcnow()
if info['state'] == 'terminated':
delta = info['ended_at'] - info['started_at']
else:
delta = now - info['started_at']
info['uptime'] = delta.days * 24 * 3600 + delta.seconds
if info['tenant_id'] not in rval:
summary = {}
summary['tenant_id'] = info['tenant_id']
if detailed:
summary['server_usages'] = []
summary['total_local_gb_usage'] = 0
summary['total_vcpus_usage'] = 0
summary['total_memory_mb_usage'] = 0
summary['total_hours'] = 0
summary['start'] = timeutils.normalize_time(period_start)
summary['stop'] = timeutils.normalize_time(period_stop)
rval[info['tenant_id']] = summary
summary = rval[info['tenant_id']]
summary['total_local_gb_usage'] += info['local_gb'] * info['hours']
summary['total_vcpus_usage'] += info['vcpus'] * info['hours']
summary['total_memory_mb_usage'] += (info['memory_mb'] *
info['hours'])
summary['total_hours'] += info['hours']
if detailed:
summary['server_usages'].append(info)
return rval.values()
def _parse_datetime(self, dtstr):
value = None
if not dtstr:
value = timeutils.utcnow()
elif isinstance(dtstr, datetime.datetime):
value = dtstr
for format in VALID_DATETIME_FORMAT:
try:
value = timeutils.parse_strtime(dtstr, format)
break
except ValueError:
continue
# NOTE(mriedem): Instance object DateTime fields are timezone-aware
# so we have to force UTC timezone for comparing this datetime against
# instance object fields and still maintain backwards compatibility
# in the API.
if value and value.utcoffset() is None:
value = value.replace(tzinfo=iso8601.iso8601.Utc())
return value
def _get_datetime_range(self, req):
qs = req.environ.get('QUERY_STRING', '')
env = urlparse.parse_qs(qs)
# NOTE(lzyeval): env.get() always returns a list
period_start = self._parse_datetime(env.get('start', [None])[0])
if not period_start:
msg = _("Start time is invalid format, valid "
"formats are %s") % VALID_DATETIME_FORMAT
raise exc.HTTPBadRequest(explanation=msg)
period_stop = self._parse_datetime(env.get('end', [None])[0])
if not period_stop:
msg = _("Stop time is invalid format, valid "
"formats are %s") % VALID_DATETIME_FORMAT
raise exc.HTTPBadRequest(explanation=msg)
if not period_start < period_stop:
msg = _("Invalid start time. The start time cannot occur after "
"the end time.")
raise exc.HTTPBadRequest(explanation=msg)
detailed = env.get('detailed', ['0'])[0] == '1'
return (period_start, period_stop, detailed)
@extensions.expected_errors(400)
def index(self, req):
"""Retrieve tenant_usage for all tenants."""
context = req.environ['nova.context']
authorize_list(context)
(period_start, period_stop, detailed) = self._get_datetime_range(req)
now = timeutils.parse_isotime(timeutils.strtime())
if period_stop > now:
period_stop = now
usages = self._tenant_usages_for_period(context,
period_start,
period_stop,
detailed=detailed)
return {'tenant_usages': usages}
@extensions.expected_errors(400)
def show(self, req, id):
"""Retrieve tenant_usage for a specified tenant."""
tenant_id = id
context = req.environ['nova.context']
authorize_show(context, {'project_id': tenant_id})
(period_start, period_stop, ignore) = self._get_datetime_range(req)
now = timeutils.parse_isotime(timeutils.strtime())
if period_stop > now:
period_stop = now
usage = self._tenant_usages_for_period(context,
period_start,
period_stop,
tenant_id=tenant_id,
detailed=True)
if len(usage):
usage = usage[0]
else:
usage = {}
return {'tenant_usage': usage}
class SimpleTenantUsage(extensions.V3APIExtensionBase):
"""Simple tenant usage extension."""
name = "SimpleTenantUsage"
alias = ALIAS
version = 1
def get_resources(self):
res = [extensions.ResourceExtension('os-simple-tenant-usage',
SimpleTenantUsageController())]
return res
def get_controller_extensions(self):
"""It's an abstract function V3APIExtensionBase and the extension
will not be loaded without it.
"""
return []

View File

@@ -1,338 +0,0 @@
# Copyright 2011 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import mock
import webob
from nova.api.openstack.compute.plugins.v3 import simple_tenant_usage
from nova.compute import flavors
from nova.compute import vm_states
from nova import context
from nova import db
from nova.objects import flavor as flavor_obj
from nova.objects import instance as instance_obj
from nova.openstack.common import jsonutils
from nova.openstack.common import policy as common_policy
from nova.openstack.common import timeutils
from nova import policy
from nova import test
from nova.tests.api.openstack import fakes
from nova import utils
SERVERS = 5
TENANTS = 2
HOURS = 24
ROOT_GB = 10
EPHEMERAL_GB = 20
MEMORY_MB = 1024
VCPUS = 2
NOW = timeutils.utcnow()
START = NOW - datetime.timedelta(hours=HOURS)
STOP = NOW
FAKE_INST_TYPE = {'id': 1,
'vcpus': VCPUS,
'root_gb': ROOT_GB,
'ephemeral_gb': EPHEMERAL_GB,
'memory_mb': MEMORY_MB,
'name': 'fakeflavor',
'flavorid': 'foo',
'rxtx_factor': 1.0,
'vcpu_weight': 1,
'swap': 0,
'created_at': None,
'updated_at': None,
'deleted_at': None,
'deleted': 0,
'disabled': False,
'is_public': True,
'extra_specs': {'foo': 'bar'}}
def get_fake_db_instance(start, end, instance_id, tenant_id,
vm_state=vm_states.ACTIVE):
sys_meta = utils.dict_to_metadata(
flavors.save_flavor_info({}, FAKE_INST_TYPE))
# NOTE(mriedem): We use fakes.stub_instance since it sets the fields
# needed on the db instance for converting it to an object, but we still
# need to override system_metadata to use our fake flavor.
inst = fakes.stub_instance(
id=instance_id,
uuid='00000000-0000-0000-0000-00000000000000%02d' % instance_id,
image_ref='1',
project_id=tenant_id,
user_id='fakeuser',
display_name='name',
flavor_id=FAKE_INST_TYPE['id'],
launched_at=start,
terminated_at=end,
vm_state=vm_state)
inst['system_metadata'] = sys_meta
return inst
def fake_instance_get_active_by_window_joined(context, begin, end,
project_id, host):
return [get_fake_db_instance(START,
STOP,
x,
"faketenant_%s" % (x / SERVERS))
for x in xrange(TENANTS * SERVERS)]
@mock.patch.object(db, 'instance_get_active_by_window_joined',
fake_instance_get_active_by_window_joined)
class SimpleTenantUsageTest(test.TestCase):
def setUp(self):
super(SimpleTenantUsageTest, self).setUp()
self.admin_context = context.RequestContext('fakeadmin_0',
'faketenant_0',
is_admin=True)
self.user_context = context.RequestContext('fakeadmin_0',
'faketenant_0',
is_admin=False)
self.alt_user_context = context.RequestContext('fakeadmin_0',
'faketenant_1',
is_admin=False)
self.flags(
osapi_compute_extension=[
'nova.api.openstack.compute.contrib.select_extensions'],
osapi_compute_ext_list=['Simple_tenant_usage'])
def _test_verify_index(self, start, stop):
req = webob.Request.blank(
'/v3/os-simple-tenant-usage?start=%s&end=%s' %
(start.isoformat(), stop.isoformat()))
req.method = "GET"
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app_v3(
fake_auth_context=self.admin_context,
init_only=('os-simple-tenant-usage',
'servers')))
self.assertEqual(res.status_int, 200)
res_dict = jsonutils.loads(res.body)
usages = res_dict['tenant_usages']
for i in xrange(TENANTS):
self.assertEqual(int(usages[i]['total_hours']),
SERVERS * HOURS)
self.assertEqual(int(usages[i]['total_local_gb_usage']),
SERVERS * (ROOT_GB + EPHEMERAL_GB) * HOURS)
self.assertEqual(int(usages[i]['total_memory_mb_usage']),
SERVERS * MEMORY_MB * HOURS)
self.assertEqual(int(usages[i]['total_vcpus_usage']),
SERVERS * VCPUS * HOURS)
self.assertFalse(usages[i].get('server_usages'))
def test_verify_index(self):
self._test_verify_index(START, STOP)
def test_verify_index_future_end_time(self):
future = NOW + datetime.timedelta(hours=HOURS)
self._test_verify_index(START, future)
def test_verify_index_with_invalid_time_format(self):
req = webob.Request.blank(
'/v3/os-simple-tenant-usage?start=%s&end=%s' %
('aa', 'bb'))
req.method = "GET"
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app_v3(
fake_auth_context=self.admin_context,
init_only=('os-simple-tenant-usage',
'servers')))
self.assertEqual(res.status_int, 400)
def test_verify_show(self):
self._test_verify_show(START, STOP)
def test_verify_show_future_end_time(self):
future = NOW + datetime.timedelta(hours=HOURS)
self._test_verify_show(START, future)
def test_verify_show_with_invalid_time_format(self):
tenant_id = 0
req = webob.Request.blank(
'/v3/os-simple-tenant-usage/'
'faketenant_%s?start=%s&end=%s' %
(tenant_id, 'aa', 'bb'))
req.method = "GET"
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app_v3(
fake_auth_context=self.user_context,
init_only=('os-simple-tenant-usage',
'servers')))
self.assertEqual(res.status_int, 400)
def _get_tenant_usages(self, detailed=''):
req = webob.Request.blank(
'/v3/os-simple-tenant-usage?'
'detailed=%s&start=%s&end=%s' %
(detailed, START.isoformat(), STOP.isoformat()))
req.method = "GET"
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app_v3(
fake_auth_context=self.admin_context,
init_only=('os-simple-tenant-usage',
'servers')))
self.assertEqual(res.status_int, 200)
res_dict = jsonutils.loads(res.body)
return res_dict['tenant_usages']
def test_verify_detailed_index(self):
usages = self._get_tenant_usages('1')
for i in xrange(TENANTS):
servers = usages[i]['server_usages']
for j in xrange(SERVERS):
self.assertEqual(int(servers[j]['hours']), HOURS)
def test_verify_simple_index(self):
usages = self._get_tenant_usages(detailed='0')
for i in xrange(TENANTS):
self.assertIsNone(usages[i].get('server_usages'))
def test_verify_simple_index_empty_param(self):
# NOTE(lzyeval): 'detailed=&start=..&end=..'
usages = self._get_tenant_usages()
for i in xrange(TENANTS):
self.assertIsNone(usages[i].get('server_usages'))
def _test_verify_show(self, start, stop):
tenant_id = 0
req = webob.Request.blank(
'/v3/os-simple-tenant-usage/'
'faketenant_%s?start=%s&end=%s' %
(tenant_id, start.isoformat(), stop.isoformat()))
req.method = "GET"
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app_v3(
fake_auth_context=self.user_context,
init_only=('os-simple-tenant-usage',
'servers')))
self.assertEqual(res.status_int, 200)
res_dict = jsonutils.loads(res.body)
usage = res_dict['tenant_usage']
servers = usage['server_usages']
self.assertEqual(len(usage['server_usages']), SERVERS)
uuids = ['00000000-0000-0000-0000-00000000000000%02d' %
(x + (tenant_id * SERVERS)) for x in xrange(SERVERS)]
for j in xrange(SERVERS):
delta = STOP - START
uptime = delta.days * 24 * 3600 + delta.seconds
self.assertEqual(int(servers[j]['uptime']), uptime)
self.assertEqual(int(servers[j]['hours']), HOURS)
self.assertIn(servers[j]['instance_id'], uuids)
def test_verify_show_cant_view_other_tenant(self):
req = webob.Request.blank(
'/v3/os-simple-tenant-usage/'
'faketenant_0?start=%s&end=%s' %
(START.isoformat(), STOP.isoformat()))
req.method = "GET"
req.headers["content-type"] = "application/json"
rules = {
"compute_extension:simple_tenant_usage:show":
common_policy.parse_rule([
["role:admin"], ["project_id:%(project_id)s"]
])
}
common_policy.set_rules(common_policy.Rules(rules))
try:
res = req.get_response(fakes.wsgi_app_v3(
fake_auth_context=self.alt_user_context,
init_only=('os-simple-tenant-usage',
'servers')))
self.assertEqual(res.status_int, 403)
finally:
policy.reset()
def test_get_tenants_usage_with_bad_start_date(self):
future = NOW + datetime.timedelta(hours=HOURS)
tenant_id = 0
req = webob.Request.blank(
'/v3/os-simple-tenant-usage/'
'faketenant_%s?start=%s&end=%s' %
(tenant_id, future.isoformat(), NOW.isoformat()))
req.method = "GET"
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app_v3(
fake_auth_context=self.user_context,
init_only=('os-simple-tenant-usage',
'servers')))
self.assertEqual(res.status_int, 400)
class SimpleTenantUsageControllerTest(test.TestCase):
def setUp(self):
super(SimpleTenantUsageControllerTest, self).setUp()
self.controller = simple_tenant_usage.SimpleTenantUsageController()
self.context = context.RequestContext('fakeuser', 'fake-project')
self.baseinst = get_fake_db_instance(START, STOP, instance_id=1,
tenant_id=self.context.project_id,
vm_state=vm_states.DELETED)
# convert the fake instance dict to an object
self.inst_obj = instance_obj.Instance._from_db_object(
self.context, instance_obj.Instance(), self.baseinst)
def test_get_flavor_from_sys_meta(self):
# Non-deleted instances get their type information from their
# system_metadata
with mock.patch.object(db, 'instance_get_by_uuid',
return_value=self.baseinst):
flavor = self.controller._get_flavor(self.context,
self.inst_obj, {})
self.assertEqual(flavor_obj.Flavor, type(flavor))
self.assertEqual(FAKE_INST_TYPE['id'], flavor.id)
def test_get_flavor_from_non_deleted_with_id_fails(self):
# If an instance is not deleted and missing type information from
# system_metadata, then that's a bug
self.inst_obj.system_metadata = {}
self.assertRaises(KeyError,
self.controller._get_flavor, self.context,
self.inst_obj, {})
def test_get_flavor_from_deleted_with_id(self):
# Deleted instances may not have type info in system_metadata,
# so verify that they get their type from a lookup of their
# instance_type_id
self.inst_obj.system_metadata = {}
self.inst_obj.deleted = 1
flavor = self.controller._get_flavor(self.context, self.inst_obj, {})
self.assertEqual(flavor_obj.Flavor, type(flavor))
self.assertEqual(FAKE_INST_TYPE['id'], flavor.id)
def test_get_flavor_from_deleted_with_id_of_deleted(self):
# Verify the legacy behavior of instance_type_id pointing to a
# missing type being non-fatal
self.inst_obj.system_metadata = {}
self.inst_obj.deleted = 1
self.inst_obj.instance_type_id = 99
flavor = self.controller._get_flavor(self.context, self.inst_obj, {})
self.assertIsNone(flavor)

View File

@@ -265,9 +265,7 @@ policy_data = """
"compute_extension:v3:os-shelve:shelve": "",
"compute_extension:v3:os-shelve:shelve_offload": "",
"compute_extension:simple_tenant_usage:show": "",
"compute_extension:v3:os-simple-tenant-usage:show": "",
"compute_extension:simple_tenant_usage:list": "",
"compute_extension:v3:os-simple-tenant-usage:list": "",
"compute_extension:unshelve": "",
"compute_extension:v3:os-shelve:unshelve": "",
"compute_extension:v3:os-suspend-server:suspend": "",

View File

@@ -1,10 +0,0 @@
{
"server" : {
"name" : "new-server-test",
"image_ref" : "%(glance_host)s/images/%(image_id)s",
"flavor_ref" : "%(host)s/flavors/1",
"metadata" : {
"My Server Name" : "Apache1"
}
}
}

View File

@@ -1,16 +0,0 @@
{
"server": {
"admin_password": "%(password)s",
"id": "%(id)s",
"links": [
{
"href": "http://openstack.example.com/v3/servers/%(uuid)s",
"rel": "self"
},
{
"href": "http://openstack.example.com/servers/%(uuid)s",
"rel": "bookmark"
}
]
}
}

View File

@@ -1,27 +0,0 @@
{
"tenant_usage": {
"server_usages": [
{
"ended_at": null,
"flavor": "m1.tiny",
"hours": 1.0,
"instance_id": "%(uuid)s",
"local_gb": 1,
"memory_mb": 512,
"name": "new-server-test",
"started_at": "%(timestamp)s",
"state": "active",
"tenant_id": "openstack",
"uptime": 3600,
"vcpus": 1
}
],
"start": "%(timestamp)s",
"stop": "%(timestamp)s",
"tenant_id": "openstack",
"total_hours": 1.0,
"total_local_gb_usage": 1.0,
"total_memory_mb_usage": 512.0,
"total_vcpus_usage": 1.0
}
}

View File

@@ -1,13 +0,0 @@
{
"tenant_usages": [
{
"start": "%(timestamp)s",
"stop": "%(timestamp)s",
"tenant_id": "openstack",
"total_hours": 1.0,
"total_local_gb_usage": 1.0,
"total_memory_mb_usage": 512.0,
"total_vcpus_usage": 1.0
}
]
}

View File

@@ -1,61 +0,0 @@
# Copyright 2012 Nebula, Inc.
# Copyright 2013 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import urllib
from nova.openstack.common import timeutils
from nova.tests.integrated.v3 import test_servers
class SimpleTenantUsageSampleJsonTest(test_servers.ServersSampleBase):
extension_name = "os-simple-tenant-usage"
def setUp(self):
"""setUp method for simple tenant usage."""
super(SimpleTenantUsageSampleJsonTest, self).setUp()
started = timeutils.utcnow()
now = started + datetime.timedelta(hours=1)
timeutils.set_time_override(started)
self._post_server()
timeutils.set_time_override(now)
self.query = {
'start': str(started),
'end': str(now)
}
def tearDown(self):
"""tearDown method for simple tenant usage."""
super(SimpleTenantUsageSampleJsonTest, self).tearDown()
timeutils.clear_time_override()
def test_get_tenants_usage(self):
# Get api sample to get all tenants usage request.
response = self._do_get('os-simple-tenant-usage?%s' % (
urllib.urlencode(self.query)))
subs = self._get_regexes()
self._verify_response('simple-tenant-usage-get', subs, response, 200)
def test_get_tenant_usage_details(self):
# Get api sample to get specific tenant usage request.
tenant_id = 'openstack'
response = self._do_get('os-simple-tenant-usage/%s?%s' % (tenant_id,
urllib.urlencode(self.query)))
subs = self._get_regexes()
self._verify_response('simple-tenant-usage-get-specific', subs,
response, 200)

View File

@@ -107,7 +107,6 @@ nova.api.v3.extensions =
servers = nova.api.openstack.compute.plugins.v3.servers:Servers
services = nova.api.openstack.compute.plugins.v3.services:Services
shelve = nova.api.openstack.compute.plugins.v3.shelve:Shelve
simple_tenant_usage = nova.api.openstack.compute.plugins.v3.simple_tenant_usage:SimpleTenantUsage
suspend_server = nova.api.openstack.compute.plugins.v3.suspend_server:SuspendServer
versions = nova.api.openstack.compute.plugins.v3.versions:Versions