summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--novaclient/openstack/common/timeutils.py126
-rw-r--r--novaclient/v1_1/shell.py9
-rw-r--r--openstack-common.conf2
-rw-r--r--tests/v1_1/test_shell.py13
-rw-r--r--tools/pip-requires1
5 files changed, 146 insertions, 5 deletions
diff --git a/novaclient/openstack/common/timeutils.py b/novaclient/openstack/common/timeutils.py
new file mode 100644
index 00000000..c4f6cf04
--- /dev/null
+++ b/novaclient/openstack/common/timeutils.py
@@ -0,0 +1,126 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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.
+
+"""
+Time related utilities and helper functions.
+"""
+
+import calendar
+import datetime
+
+import iso8601
+
+
+TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
+PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
+
+
+def isotime(at=None):
+ """Stringify time in ISO 8601 format"""
+ if not at:
+ at = utcnow()
+ str = at.strftime(TIME_FORMAT)
+ tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
+ str += ('Z' if tz == 'UTC' else tz)
+ return str
+
+
+def parse_isotime(timestr):
+ """Parse time from ISO 8601 format"""
+ try:
+ return iso8601.parse_date(timestr)
+ except iso8601.ParseError as e:
+ raise ValueError(e.message)
+ except TypeError as e:
+ raise ValueError(e.message)
+
+
+def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
+ """Returns formatted utcnow."""
+ if not at:
+ at = utcnow()
+ return at.strftime(fmt)
+
+
+def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
+ """Turn a formatted time back into a datetime."""
+ return datetime.datetime.strptime(timestr, fmt)
+
+
+def normalize_time(timestamp):
+ """Normalize time in arbitrary timezone to UTC"""
+ offset = timestamp.utcoffset()
+ return timestamp.replace(tzinfo=None) - offset if offset else timestamp
+
+
+def is_older_than(before, seconds):
+ """Return True if before is older than seconds."""
+ return utcnow() - before > datetime.timedelta(seconds=seconds)
+
+
+def utcnow_ts():
+ """Timestamp version of our utcnow function."""
+ return calendar.timegm(utcnow().timetuple())
+
+
+def utcnow():
+ """Overridable version of utils.utcnow."""
+ if utcnow.override_time:
+ return utcnow.override_time
+ return datetime.datetime.utcnow()
+
+
+utcnow.override_time = None
+
+
+def set_time_override(override_time=datetime.datetime.utcnow()):
+ """Override utils.utcnow to return a constant time."""
+ utcnow.override_time = override_time
+
+
+def advance_time_delta(timedelta):
+ """Advance overridden time using a datetime.timedelta."""
+ assert(not utcnow.override_time is None)
+ utcnow.override_time += timedelta
+
+
+def advance_time_seconds(seconds):
+ """Advance overridden time by seconds."""
+ advance_time_delta(datetime.timedelta(0, seconds))
+
+
+def clear_time_override():
+ """Remove the overridden time."""
+ utcnow.override_time = None
+
+
+def marshall_now(now=None):
+ """Make an rpc-safe datetime with microseconds.
+
+ Note: tzinfo is stripped, but not required for relative times."""
+ if not now:
+ now = utcnow()
+ return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
+ minute=now.minute, second=now.second,
+ microsecond=now.microsecond)
+
+
+def unmarshall_time(tyme):
+ """Unmarshall a datetime dict."""
+ return datetime.datetime(day=tyme['day'], month=tyme['month'],
+ year=tyme['year'], hour=tyme['hour'], minute=tyme['minute'],
+ second=tyme['second'], microsecond=tyme['microsecond'])
diff --git a/novaclient/v1_1/shell.py b/novaclient/v1_1/shell.py
index 37f91d75..818faab3 100644
--- a/novaclient/v1_1/shell.py
+++ b/novaclient/v1_1/shell.py
@@ -23,6 +23,7 @@ import sys
import time
from novaclient import exceptions
+from novaclient.openstack.common import timeutils
from novaclient import utils
from novaclient.v1_1 import servers
@@ -1615,17 +1616,17 @@ def do_usage_list(cs, args):
rows = ["Tenant ID", "Instances", "RAM MB-Hours", "CPU Hours",
"Disk GB-Hours"]
+ now = timeutils.utcnow()
+
if args.start:
start = datetime.datetime.strptime(args.start, dateformat)
else:
- start = (datetime.datetime.today() -
- datetime.timedelta(weeks=4))
+ start = now - datetime.timedelta(weeks=4)
if args.end:
end = datetime.datetime.strptime(args.end, dateformat)
else:
- end = (datetime.datetime.today() +
- datetime.timedelta(days=1))
+ end = now + datetime.timedelta(days=1)
def simplify_usage(u):
simplerows = map(lambda x: x.lower().replace(" ", "_"), rows)
diff --git a/openstack-common.conf b/openstack-common.conf
index eaa80459..76f08056 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -1,7 +1,7 @@
[DEFAULT]
# The list of modules to copy from openstack-common
-modules=setup
+modules=setup,timeutils
# The base module to hold the copy of openstack.common
base=novaclient
diff --git a/tests/v1_1/test_shell.py b/tests/v1_1/test_shell.py
index 6ddb28aa..c1dfb6b1 100644
--- a/tests/v1_1/test_shell.py
+++ b/tests/v1_1/test_shell.py
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import datetime
import os
import mock
import sys
@@ -23,6 +24,7 @@ import tempfile
import novaclient.shell
import novaclient.client
from novaclient import exceptions
+from novaclient.openstack.common import timeutils
from tests.v1_1 import fakes
from tests import utils
@@ -59,6 +61,8 @@ class ShellTest(utils.TestCase):
#HACK(bcwaldon): replace this when we start using stubs
novaclient.client.get_client_class = self.old_get_client_class
+ timeutils.clear_time_override()
+
def run_command(self, cmd):
self.shell.main(cmd.split())
@@ -353,6 +357,15 @@ class ShellTest(utils.TestCase):
'end=2005-02-01T00:00:00&' +
'detailed=1')
+ def test_usage_list_no_args(self):
+ timeutils.set_time_override(datetime.datetime(2005, 2, 1, 0, 0))
+ self.run_command('usage-list')
+ self.assert_called('GET',
+ '/os-simple-tenant-usage?' +
+ 'start=2005-01-04T00:00:00&' +
+ 'end=2005-02-02T00:00:00&' +
+ 'detailed=1')
+
def test_flavor_delete(self):
self.run_command("flavor-delete flavordelete")
self.assert_called('DELETE', '/flavors/flavordelete')
diff --git a/tools/pip-requires b/tools/pip-requires
index 97b9a58c..2c409b3c 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -1,4 +1,5 @@
argparse
httplib2
+iso8601>=0.1.4
prettytable>=0.6,<0.7
simplejson