From 072001a6289bee6594a97fe30dd5ab53bb1d9e99 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 23 May 2017 09:04:25 -0500 Subject: [PATCH] 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 --- ...mpute-usage-defaults-5f5b2936f17ff400.yaml | 9 ++++ requirements.txt | 1 + shade/operatorcloud.py | 49 +++++++++++++++++-- 3 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/compute-usage-defaults-5f5b2936f17ff400.yaml diff --git a/releasenotes/notes/compute-usage-defaults-5f5b2936f17ff400.yaml b/releasenotes/notes/compute-usage-defaults-5f5b2936f17ff400.yaml new file mode 100644 index 000000000..7ca6b37f5 --- /dev/null +++ b/releasenotes/notes/compute-usage-defaults-5f5b2936f17ff400.yaml @@ -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. diff --git a/requirements.txt b/requirements.txt index 6bc05760b..8472bc6d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,6 +19,7 @@ requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0 requestsexceptions>=1.2.0 # Apache-2.0 six>=1.9.0 # MIT 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 netifaces>=0.10.4 # MIT diff --git a/shade/operatorcloud.py b/shade/operatorcloud.py index ded7bc909..0a109533c 100644 --- a/shade/operatorcloud.py +++ b/shade/operatorcloud.py @@ -11,6 +11,7 @@ # limitations under the License. import datetime +import iso8601 import jsonpatch from ironicclient import exceptions as ironic_exceptions @@ -2103,22 +2104,60 @@ class OperatorCloud(openstackcloud.OpenStackCloud): except nova_exceptions.BadRequest: 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 :param name_or_id: project name or id - :param start: :class:`datetime.datetime` Start date in UTC - :param end: :class:`datetime.datetime` End date in UTC. Defaults to now + :param start: :class:`datetime.datetime` or string. Start date in UTC + 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 :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) if not proj: raise OpenStackCloudException("project does not exist: {}".format( name=proj.id)) - if not end: - end = datetime.datetime.now() with _utils.shade_exceptions( "Unable to get resources usage for project: {name}".format(