Skip to content
This repository was archived by the owner on Nov 29, 2023. It is now read-only.

Commit 66ea8f9

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Fix usage-list date range to use UTC time"
2 parents 46a87dc + f2d2e4c commit 66ea8f9

5 files changed

Lines changed: 146 additions & 5 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2+
3+
# Copyright 2011 OpenStack LLC.
4+
# All Rights Reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7+
# not use this file except in compliance with the License. You may obtain
8+
# a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15+
# License for the specific language governing permissions and limitations
16+
# under the License.
17+
18+
"""
19+
Time related utilities and helper functions.
20+
"""
21+
22+
import calendar
23+
import datetime
24+
25+
import iso8601
26+
27+
28+
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
29+
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
30+
31+
32+
def isotime(at=None):
33+
"""Stringify time in ISO 8601 format"""
34+
if not at:
35+
at = utcnow()
36+
str = at.strftime(TIME_FORMAT)
37+
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
38+
str += ('Z' if tz == 'UTC' else tz)
39+
return str
40+
41+
42+
def parse_isotime(timestr):
43+
"""Parse time from ISO 8601 format"""
44+
try:
45+
return iso8601.parse_date(timestr)
46+
except iso8601.ParseError as e:
47+
raise ValueError(e.message)
48+
except TypeError as e:
49+
raise ValueError(e.message)
50+
51+
52+
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
53+
"""Returns formatted utcnow."""
54+
if not at:
55+
at = utcnow()
56+
return at.strftime(fmt)
57+
58+
59+
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
60+
"""Turn a formatted time back into a datetime."""
61+
return datetime.datetime.strptime(timestr, fmt)
62+
63+
64+
def normalize_time(timestamp):
65+
"""Normalize time in arbitrary timezone to UTC"""
66+
offset = timestamp.utcoffset()
67+
return timestamp.replace(tzinfo=None) - offset if offset else timestamp
68+
69+
70+
def is_older_than(before, seconds):
71+
"""Return True if before is older than seconds."""
72+
return utcnow() - before > datetime.timedelta(seconds=seconds)
73+
74+
75+
def utcnow_ts():
76+
"""Timestamp version of our utcnow function."""
77+
return calendar.timegm(utcnow().timetuple())
78+
79+
80+
def utcnow():
81+
"""Overridable version of utils.utcnow."""
82+
if utcnow.override_time:
83+
return utcnow.override_time
84+
return datetime.datetime.utcnow()
85+
86+
87+
utcnow.override_time = None
88+
89+
90+
def set_time_override(override_time=datetime.datetime.utcnow()):
91+
"""Override utils.utcnow to return a constant time."""
92+
utcnow.override_time = override_time
93+
94+
95+
def advance_time_delta(timedelta):
96+
"""Advance overridden time using a datetime.timedelta."""
97+
assert(not utcnow.override_time is None)
98+
utcnow.override_time += timedelta
99+
100+
101+
def advance_time_seconds(seconds):
102+
"""Advance overridden time by seconds."""
103+
advance_time_delta(datetime.timedelta(0, seconds))
104+
105+
106+
def clear_time_override():
107+
"""Remove the overridden time."""
108+
utcnow.override_time = None
109+
110+
111+
def marshall_now(now=None):
112+
"""Make an rpc-safe datetime with microseconds.
113+
114+
Note: tzinfo is stripped, but not required for relative times."""
115+
if not now:
116+
now = utcnow()
117+
return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
118+
minute=now.minute, second=now.second,
119+
microsecond=now.microsecond)
120+
121+
122+
def unmarshall_time(tyme):
123+
"""Unmarshall a datetime dict."""
124+
return datetime.datetime(day=tyme['day'], month=tyme['month'],
125+
year=tyme['year'], hour=tyme['hour'], minute=tyme['minute'],
126+
second=tyme['second'], microsecond=tyme['microsecond'])

novaclient/v1_1/shell.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import time
2424

2525
from novaclient import exceptions
26+
from novaclient.openstack.common import timeutils
2627
from novaclient import utils
2728
from novaclient.v1_1 import servers
2829

@@ -1615,17 +1616,17 @@ def do_usage_list(cs, args):
16151616
rows = ["Tenant ID", "Instances", "RAM MB-Hours", "CPU Hours",
16161617
"Disk GB-Hours"]
16171618

1619+
now = timeutils.utcnow()
1620+
16181621
if args.start:
16191622
start = datetime.datetime.strptime(args.start, dateformat)
16201623
else:
1621-
start = (datetime.datetime.today() -
1622-
datetime.timedelta(weeks=4))
1624+
start = now - datetime.timedelta(weeks=4)
16231625

16241626
if args.end:
16251627
end = datetime.datetime.strptime(args.end, dateformat)
16261628
else:
1627-
end = (datetime.datetime.today() +
1628-
datetime.timedelta(days=1))
1629+
end = now + datetime.timedelta(days=1)
16291630

16301631
def simplify_usage(u):
16311632
simplerows = map(lambda x: x.lower().replace(" ", "_"), rows)

openstack-common.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[DEFAULT]
22

33
# The list of modules to copy from openstack-common
4-
modules=setup
4+
modules=setup,timeutils
55

66
# The base module to hold the copy of openstack.common
77
base=novaclient

tests/v1_1/test_shell.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# License for the specific language governing permissions and limitations
1616
# under the License.
1717

18+
import datetime
1819
import os
1920
import mock
2021
import sys
@@ -23,6 +24,7 @@
2324
import novaclient.shell
2425
import novaclient.client
2526
from novaclient import exceptions
27+
from novaclient.openstack.common import timeutils
2628
from tests.v1_1 import fakes
2729
from tests import utils
2830

@@ -59,6 +61,8 @@ def tearDown(self):
5961
#HACK(bcwaldon): replace this when we start using stubs
6062
novaclient.client.get_client_class = self.old_get_client_class
6163

64+
timeutils.clear_time_override()
65+
6266
def run_command(self, cmd):
6367
self.shell.main(cmd.split())
6468

@@ -353,6 +357,15 @@ def test_usage_list(self):
353357
'end=2005-02-01T00:00:00&' +
354358
'detailed=1')
355359

360+
def test_usage_list_no_args(self):
361+
timeutils.set_time_override(datetime.datetime(2005, 2, 1, 0, 0))
362+
self.run_command('usage-list')
363+
self.assert_called('GET',
364+
'/os-simple-tenant-usage?' +
365+
'start=2005-01-04T00:00:00&' +
366+
'end=2005-02-02T00:00:00&' +
367+
'detailed=1')
368+
356369
def test_flavor_delete(self):
357370
self.run_command("flavor-delete flavordelete")
358371
self.assert_called('DELETE', '/flavors/flavordelete')

tools/pip-requires

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
argparse
22
httplib2
3+
iso8601>=0.1.4
34
prettytable>=0.6,<0.7
45
simplejson

0 commit comments

Comments
 (0)