Allow a user to submit start and end time as strings

get_compute_usage currently requires a user to provide a datetime
object. That's actually fairly unfriendly. Also, it turns out if you
happen to produce one of those with timezone offset information, Nova
will return a 400 error. It's pretty easy to convert a datetime with
timezone info to a datetime in UTC without it - so do that first.

Now a user can submit a fully compliant ISO 8601 datetime with whatever
info they want and we'll make it meet nova's standards. They can also
supply a datetime if they prefer.

Also - do date parsing before project get - to avoid an API call if the
validation that doesn't need an API call fails.

Finally, supply a default for 'start' of the first day OpenStack
existed.

Change-Id: I53989d47f0d24695c19c1023e35acb315bec9eea
This commit is contained in:
Monty Taylor 2017-05-23 09:04:25 -05:00
parent 12b07e12e2
commit 072001a628
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
3 changed files with 54 additions and 5 deletions

View File

@ -0,0 +1,9 @@
---
features:
- get_compute_usage now has a default value for the start
parameter of 2010-07-06. That was the date the OpenStack
project started. It's completely impossible for someone
to have Nova usage data that goes back further in time.
Also, both the start and end date parameters now also
accept strings which will be parsed and timezones will
be properly converted to UTC which is what Nova expects.

View File

@ -19,6 +19,7 @@ requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
requestsexceptions>=1.2.0 # Apache-2.0 requestsexceptions>=1.2.0 # Apache-2.0
six>=1.9.0 # MIT six>=1.9.0 # MIT
futures>=3.0;python_version=='2.7' or python_version=='2.6' # BSD futures>=3.0;python_version=='2.7' or python_version=='2.6' # BSD
iso8601>=0.1.11 # MIT
keystoneauth1>=2.20.0 # Apache-2.0 keystoneauth1>=2.20.0 # Apache-2.0
netifaces>=0.10.4 # MIT netifaces>=0.10.4 # MIT

View File

@ -11,6 +11,7 @@
# limitations under the License. # limitations under the License.
import datetime import datetime
import iso8601
import jsonpatch import jsonpatch
from ironicclient import exceptions as ironic_exceptions from ironicclient import exceptions as ironic_exceptions
@ -2103,22 +2104,60 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
except nova_exceptions.BadRequest: except nova_exceptions.BadRequest:
raise OpenStackCloudException("nova client call failed") raise OpenStackCloudException("nova client call failed")
def get_compute_usage(self, name_or_id, start, 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
:param name_or_id: project name or id :param name_or_id: project name or id
:param start: :class:`datetime.datetime` Start date in UTC :param start: :class:`datetime.datetime` or string. Start date in UTC
:param end: :class:`datetime.datetime` End date in UTC. Defaults to now Defaults to 2010-07-06T12:00:00Z (the date the OpenStack
project was started)
:param end: :class:`datetime.datetime` or string. End date in UTC.
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: Munch object with the usage
""" """
def parse_date(date):
try:
return iso8601.parse_date(date)
except iso8601.iso8601.ParseError:
# Yes. This is an exception mask. However,iso8601 is an
# implementation detail - and the error message is actually
# less informative.
raise OpenStackCloudException(
"Date given, {date}, is invalid. Please pass in a date"
" string in ISO 8601 format -"
" YYYY-MM-DDTHH:MM:SS".format(
date=date))
def parse_datetime_for_nova(date):
# 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)
if not end:
end = datetime.datetime.utcnow()
elif not isinstance(start, datetime.datetime):
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 OpenStackCloudException("project does not exist: {}".format( raise OpenStackCloudException("project does not exist: {}".format(
name=proj.id)) name=proj.id))
if not end:
end = datetime.datetime.now()
with _utils.shade_exceptions( with _utils.shade_exceptions(
"Unable to get resources usage for project: {name}".format( "Unable to get resources usage for project: {name}".format(