Move get_compute_usage to use proxy

Address todo in compute cloud layer and move get_compute_usage to rely
to the proxy layer. With this change method returns Usage object instead
of munch. This also allows us to stop importing and inheriting from
normalize class here. "attached" filter of the search_floating_ips is
dopped since it is being pretty awkward.

Change-Id: I3de1654fc35b2985e4a0658f7117bbe9db554dd3
This commit is contained in:
Artem Goncharov 2022-11-18 13:25:35 +01:00
parent 1288359436
commit dc6be6e906
7 changed files with 26 additions and 52 deletions

View File

@ -14,27 +14,23 @@
# We can't just use list, because sphinx gets confused by # We can't just use list, because sphinx gets confused by
# openstack.resource.Resource.list and openstack.resource2.Resource.list # openstack.resource.Resource.list and openstack.resource2.Resource.list
import base64 import base64
import datetime
import functools import functools
import operator import operator
import threading import threading
import time import time
import types # noqa
import iso8601 import iso8601
from openstack.cloud import _normalize
from openstack.cloud import _utils from openstack.cloud import _utils
from openstack.cloud import exc from openstack.cloud import exc
from openstack.cloud import meta from openstack.cloud import meta
from openstack.compute.v2 import quota_set as _qs from openstack.compute.v2 import quota_set as _qs
from openstack.compute.v2 import server as _server from openstack.compute.v2 import server as _server
from openstack import exceptions from openstack import exceptions
from openstack import proxy
from openstack import utils from openstack import utils
class ComputeCloudMixin(_normalize.Normalizer): class ComputeCloudMixin:
def __init__(self): def __init__(self):
self._servers = None self._servers = None
@ -1688,7 +1684,6 @@ class ComputeCloudMixin(_normalize.Normalizer):
name_or_id, ignore_missing=False) name_or_id, ignore_missing=False)
self.compute.revert_quota_set(proj) self.compute.revert_quota_set(proj)
# TODO(stephenfin): Convert to proxy methods
def get_compute_usage(self, name_or_id, start=None, end=None): def get_compute_usage(self, name_or_id, start=None, end=None):
""" Get usage for a specific project """ Get usage for a specific project
@ -1700,9 +1695,8 @@ class ComputeCloudMixin(_normalize.Normalizer):
Defaults to now Defaults to now
:raises: OpenStackCloudException if it's not a valid project :raises: OpenStackCloudException if it's not a valid project
:returns: Munch object with the usage :returns: A :class:`~openstack.compute.v2.usage.Usage` object
""" """
def parse_date(date): def parse_date(date):
try: try:
return iso8601.parse_date(date) return iso8601.parse_date(date)
@ -1716,43 +1710,17 @@ class ComputeCloudMixin(_normalize.Normalizer):
" YYYY-MM-DDTHH:MM:SS".format( " YYYY-MM-DDTHH:MM:SS".format(
date=date)) date=date))
def parse_datetime_for_nova(date): if isinstance(start, str):
# Must strip tzinfo from the date- it breaks Nova. Also,
# Nova is expecting this in UTC. If someone passes in an
# ISO8601 date string or a datetime with timzeone data attached,
# strip the timezone data but apply offset math first so that
# the user's well formed perfectly valid date will be used
# correctly.
offset = date.utcoffset()
if offset:
date = date - datetime.timedelta(hours=offset)
return date.replace(tzinfo=None)
if not start:
start = parse_date('2010-07-06')
elif not isinstance(start, datetime.datetime):
start = parse_date(start) start = parse_date(start)
if not end: if isinstance(end, str):
end = datetime.datetime.utcnow()
elif not isinstance(start, datetime.datetime):
end = parse_date(end) end = parse_date(end)
start = parse_datetime_for_nova(start)
end = parse_datetime_for_nova(end)
proj = self.get_project(name_or_id) proj = self.get_project(name_or_id)
if not proj: if not proj:
raise exc.OpenStackCloudException( raise exc.OpenStackCloudException(
"project does not exist: {name}".format(name=proj.id)) "project does not exist: {name}".format(name=proj.id))
data = proxy._json_response( return self.compute.get_usage(proj, start, end)
self.compute.get(
'/os-simple-tenant-usage/{project}'.format(project=proj.id),
params=dict(start=start.isoformat(), end=end.isoformat())),
error_message="Unable to get usage for project: {name}".format(
name=proj.id))
return self._normalize_compute_usage(
self._get_and_munchify('tenant_usage', data))
def _encode_server_userdata(self, userdata): def _encode_server_userdata(self, userdata):
if hasattr(userdata, 'read'): if hasattr(userdata, 'read'):

View File

@ -1977,10 +1977,10 @@ class Proxy(proxy.Proxy):
:returns: A list of compute ``Usage`` objects. :returns: A list of compute ``Usage`` objects.
""" """
if start is not None: if start is not None:
query['start'] = start query['start'] = start.isoformat()
if end is not None: if end is not None:
query['end'] = end query['end'] = end.isoformat()
return self._list(_usage.Usage, **query) return self._list(_usage.Usage, **query)
@ -1998,10 +1998,10 @@ class Proxy(proxy.Proxy):
project = self._get_resource(_project.Project, project) project = self._get_resource(_project.Project, project)
if start is not None: if start is not None:
query['start'] = start query['start'] = start.isoformat()
if end is not None: if end is not None:
query['end'] = end query['end'] = end.isoformat()
res = self._get_resource(_usage.Usage, project.id) res = self._get_resource(_usage.Usage, project.id)
return res.fetch(self, **query) return res.fetch(self, **query)

View File

@ -485,6 +485,6 @@ class TestCompute(base.BaseFunctionalTest):
self.add_info_on_exception('usage', usage) self.add_info_on_exception('usage', usage)
self.assertIsNotNone(usage) self.assertIsNotNone(usage)
self.assertIn('total_hours', usage) self.assertIn('total_hours', usage)
self.assertIn('started_at', usage) self.assertIn('start', usage)
self.assertEqual(start.isoformat(), usage['started_at']) self.assertEqual(start.isoformat(), usage['start'])
self.assertIn('location', usage) self.assertIn('location', usage)

View File

@ -274,13 +274,7 @@ class TestFloatingIP(base.BaseFunctionalTest):
self.assertIn( self.assertIn(
fip_user['id'], fip_user['id'],
[fip.id for fip in self.user_cloud.search_floating_ips( [fip.id for fip in self.user_cloud.search_floating_ips()]
filters={"attached": False})]
)
self.assertNotIn(
fip_user['id'],
[fip.id for fip in self.user_cloud.search_floating_ips(
filters={"attached": True})]
) )
def test_get_floating_ip_by_id(self): def test_get_floating_ip_by_id(self):

View File

@ -25,6 +25,7 @@ class TestUsage(base.TestCase):
start = end = datetime.datetime.now() start = end = datetime.datetime.now()
self.register_uris([ self.register_uris([
self.get_nova_discovery_mock_dict(),
dict(method='GET', dict(method='GET',
uri=self.get_mock_url( uri=self.get_mock_url(
'compute', 'public', 'compute', 'public',

View File

@ -1292,7 +1292,10 @@ class TestCompute(TestComputeProxy):
self.proxy.usages, self.proxy.usages,
usage.Usage, usage.Usage,
method_kwargs={'start': start, 'end': end}, method_kwargs={'start': start, 'end': end},
expected_kwargs={'start': start, 'end': end}, expected_kwargs={
'start': start.isoformat(),
'end': end.isoformat()
},
) )
def test_get_usage(self): def test_get_usage(self):
@ -1315,7 +1318,10 @@ class TestCompute(TestComputeProxy):
method_args=['value'], method_args=['value'],
method_kwargs={'start': start, 'end': end}, method_kwargs={'start': start, 'end': end},
expected_args=[self.proxy], expected_args=[self.proxy],
expected_kwargs={'start': start, 'end': end}, expected_kwargs={
'start': start.isoformat(),
'end': end.isoformat()
},
) )
def test_create_server_remote_console(self): def test_create_server_remote_console(self):

View File

@ -0,0 +1,5 @@
---
upgrade:
- |
cloud.get_compute_usage method return instance of compute.usage.Usage class
instead of munch.