Restore timezone information in API response
When oslo_utils isotime was deprecated, we switch to isoformat and lost the timezone information. This switches back to a function which put the Z suffix indicating UTC. It's a potential backward incompatible change, but presumably better than sending incomplete information. Change-Id: I78ab2dd025f1d00d4afaf72070e481aa8e09c5e8 Closes-Bug: #1607562
This commit is contained in:
parent
49226daee0
commit
f655726560
@ -85,3 +85,13 @@ def round_to_seconds(dt):
|
||||
rounding = 1
|
||||
return dt + datetime.timedelta(0, rounding,
|
||||
-dt.microsecond)
|
||||
|
||||
|
||||
def isotime(at):
|
||||
"""Stringify UTC time in ISO 8601 format.
|
||||
|
||||
:param at: Timestamp in UTC to format.
|
||||
"""
|
||||
if at is None:
|
||||
return None
|
||||
return at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
|
@ -21,6 +21,7 @@ from heat.common.i18n import _
|
||||
from heat.common.i18n import _LE
|
||||
from heat.common import param_utils
|
||||
from heat.common import template_format
|
||||
from heat.common import timeutils as heat_timeutils
|
||||
from heat.engine import constraints as constr
|
||||
from heat.rpc import api as rpc_api
|
||||
|
||||
@ -210,13 +211,14 @@ def format_stack(stack, preview=False, resolve_outputs=True):
|
||||
Return a representation of the given stack that matches the API output
|
||||
expectations.
|
||||
"""
|
||||
updated_time = stack.updated_time and stack.updated_time.isoformat()
|
||||
created_time = stack.created_time or timeutils.utcnow()
|
||||
deleted_time = stack.deleted_time and stack.deleted_time.isoformat()
|
||||
updated_time = heat_timeutils.isotime(stack.updated_time)
|
||||
created_time = heat_timeutils.isotime(stack.created_time or
|
||||
timeutils.utcnow())
|
||||
deleted_time = heat_timeutils.isotime(stack.deleted_time)
|
||||
info = {
|
||||
rpc_api.STACK_NAME: stack.name,
|
||||
rpc_api.STACK_ID: dict(stack.identifier()),
|
||||
rpc_api.STACK_CREATION_TIME: created_time.isoformat(),
|
||||
rpc_api.STACK_CREATION_TIME: created_time,
|
||||
rpc_api.STACK_UPDATED_TIME: updated_time,
|
||||
rpc_api.STACK_DELETION_TIME: deleted_time,
|
||||
rpc_api.STACK_NOTIFICATION_TOPICS: [], # TODO(therve) Not implemented
|
||||
@ -255,9 +257,9 @@ def format_stack_db_object(stack):
|
||||
Given a stack versioned db object, return a representation of the given
|
||||
stack for a stack listing.
|
||||
"""
|
||||
updated_time = stack.updated_at and stack.updated_at.isoformat()
|
||||
created_time = stack.created_at
|
||||
deleted_time = stack.deleted_at and stack.deleted_at.isoformat()
|
||||
updated_time = heat_timeutils.isotime(stack.updated_at)
|
||||
created_time = heat_timeutils.isotime(stack.created_at)
|
||||
deleted_time = heat_timeutils.isotime(stack.deleted_at)
|
||||
|
||||
tags = None
|
||||
if stack.tags:
|
||||
@ -269,7 +271,7 @@ def format_stack_db_object(stack):
|
||||
rpc_api.STACK_ACTION: stack.action,
|
||||
rpc_api.STACK_STATUS: stack.status,
|
||||
rpc_api.STACK_STATUS_DATA: stack.status_reason,
|
||||
rpc_api.STACK_CREATION_TIME: created_time.isoformat(),
|
||||
rpc_api.STACK_CREATION_TIME: created_time,
|
||||
rpc_api.STACK_UPDATED_TIME: updated_time,
|
||||
rpc_api.STACK_DELETION_TIME: deleted_time,
|
||||
rpc_api.STACK_OWNER: stack.username,
|
||||
@ -330,9 +332,9 @@ def format_stack_resource(resource, detail=True, with_props=False,
|
||||
Return a representation of the given resource that matches the API output
|
||||
expectations.
|
||||
"""
|
||||
created_time = resource.created_time and resource.created_time.isoformat()
|
||||
last_updated_time = (resource.updated_time and
|
||||
resource.updated_time.isoformat()) or created_time
|
||||
created_time = heat_timeutils.isotime(resource.created_time)
|
||||
last_updated_time = heat_timeutils.isotime(
|
||||
resource.updated_time or resource.created_time)
|
||||
res = {
|
||||
rpc_api.RES_UPDATED_TIME: last_updated_time,
|
||||
rpc_api.RES_CREATION_TIME: created_time,
|
||||
@ -386,7 +388,7 @@ def format_event(event, stack_identifier, root_stack_identifier=None):
|
||||
rpc_api.EVENT_ID: dict(event.identifier(stack_identifier)),
|
||||
rpc_api.EVENT_STACK_ID: dict(stack_identifier),
|
||||
rpc_api.EVENT_STACK_NAME: stack_identifier.stack_name,
|
||||
rpc_api.EVENT_TIMESTAMP: event.created_at.isoformat(),
|
||||
rpc_api.EVENT_TIMESTAMP: heat_timeutils.isotime(event.created_at),
|
||||
rpc_api.EVENT_RES_NAME: event.resource_name,
|
||||
rpc_api.EVENT_RES_PHYSICAL_ID: event.physical_resource_id,
|
||||
rpc_api.EVENT_RES_ACTION: event.resource_action,
|
||||
@ -411,7 +413,7 @@ def format_notification_body(stack):
|
||||
else:
|
||||
state = 'Unknown'
|
||||
|
||||
updated_at = stack.updated_time and stack.updated_time.isoformat()
|
||||
updated_at = heat_timeutils.isotime(stack.updated_time)
|
||||
result = {
|
||||
rpc_api.NOTIFY_TENANT_ID: stack.context.tenant_id,
|
||||
rpc_api.NOTIFY_USER_ID: stack.context.username,
|
||||
@ -423,7 +425,7 @@ def format_notification_body(stack):
|
||||
rpc_api.NOTIFY_STACK_NAME: stack.name,
|
||||
rpc_api.NOTIFY_STATE: state,
|
||||
rpc_api.NOTIFY_STATE_REASON: stack.status_reason,
|
||||
rpc_api.NOTIFY_CREATE_AT: stack.created_time.isoformat(),
|
||||
rpc_api.NOTIFY_CREATE_AT: heat_timeutils.isotime(stack.created_time),
|
||||
rpc_api.NOTIFY_DESCRIPTION: stack.t[stack.t.DESCRIPTION],
|
||||
rpc_api.NOTIFY_TAGS: stack.tags,
|
||||
rpc_api.NOTIFY_UPDATE_AT: updated_at
|
||||
@ -433,14 +435,15 @@ def format_notification_body(stack):
|
||||
|
||||
def format_watch(watch):
|
||||
|
||||
updated_at = watch.updated_at or timeutils.utcnow()
|
||||
updated_time = heat_timeutils.isotime(watch.updated_at or
|
||||
timeutils.utcnow())
|
||||
result = {
|
||||
rpc_api.WATCH_ACTIONS_ENABLED: watch.rule.get(
|
||||
rpc_api.RULE_ACTIONS_ENABLED),
|
||||
rpc_api.WATCH_ALARM_ACTIONS: watch.rule.get(
|
||||
rpc_api.RULE_ALARM_ACTIONS),
|
||||
rpc_api.WATCH_TOPIC: watch.rule.get(rpc_api.RULE_TOPIC),
|
||||
rpc_api.WATCH_UPDATED_TIME: updated_at.isoformat(),
|
||||
rpc_api.WATCH_UPDATED_TIME: updated_time,
|
||||
rpc_api.WATCH_DESCRIPTION: watch.rule.get(rpc_api.RULE_DESCRIPTION),
|
||||
rpc_api.WATCH_NAME: watch.name,
|
||||
rpc_api.WATCH_COMPARISON: watch.rule.get(rpc_api.RULE_COMPARISON),
|
||||
@ -456,8 +459,9 @@ def format_watch(watch):
|
||||
rpc_api.WATCH_STATE_REASON: watch.rule.get(rpc_api.RULE_STATE_REASON),
|
||||
rpc_api.WATCH_STATE_REASON_DATA:
|
||||
watch.rule.get(rpc_api.RULE_STATE_REASON_DATA),
|
||||
rpc_api.WATCH_STATE_UPDATED_TIME: watch.rule.get(
|
||||
rpc_api.RULE_STATE_UPDATED_TIME, timeutils.utcnow()).isoformat(),
|
||||
rpc_api.WATCH_STATE_UPDATED_TIME: heat_timeutils.isotime(
|
||||
watch.rule.get(rpc_api.RULE_STATE_UPDATED_TIME,
|
||||
timeutils.utcnow())),
|
||||
rpc_api.WATCH_STATE_VALUE: watch.state,
|
||||
rpc_api.WATCH_STATISTIC: watch.rule.get(rpc_api.RULE_STATISTIC),
|
||||
rpc_api.WATCH_THRESHOLD: watch.rule.get(rpc_api.RULE_THRESHOLD),
|
||||
@ -484,7 +488,7 @@ def format_watch_data(wd, rule_names):
|
||||
result = {
|
||||
rpc_api.WATCH_DATA_ALARM: rule_names.get(wd.watch_rule_id),
|
||||
rpc_api.WATCH_DATA_METRIC: metric_name,
|
||||
rpc_api.WATCH_DATA_TIME: wd.created_at.isoformat(),
|
||||
rpc_api.WATCH_DATA_TIME: heat_timeutils.isotime(wd.created_at),
|
||||
rpc_api.WATCH_DATA_NAMESPACE: namespace,
|
||||
rpc_api.WATCH_DATA: metric_data
|
||||
}
|
||||
@ -568,7 +572,8 @@ def format_software_config(sc, detail=True):
|
||||
rpc_api.SOFTWARE_CONFIG_ID: sc.id,
|
||||
rpc_api.SOFTWARE_CONFIG_NAME: sc.name,
|
||||
rpc_api.SOFTWARE_CONFIG_GROUP: sc.group,
|
||||
rpc_api.SOFTWARE_CONFIG_CREATION_TIME: sc.created_at.isoformat()
|
||||
rpc_api.SOFTWARE_CONFIG_CREATION_TIME:
|
||||
heat_timeutils.isotime(sc.created_at)
|
||||
}
|
||||
if detail:
|
||||
result[rpc_api.SOFTWARE_CONFIG_CONFIG] = sc.config['config']
|
||||
@ -590,11 +595,12 @@ def format_software_deployment(sd):
|
||||
rpc_api.SOFTWARE_DEPLOYMENT_STATUS: sd.status,
|
||||
rpc_api.SOFTWARE_DEPLOYMENT_STATUS_REASON: sd.status_reason,
|
||||
rpc_api.SOFTWARE_DEPLOYMENT_CONFIG_ID: sd.config.id,
|
||||
rpc_api.SOFTWARE_DEPLOYMENT_CREATION_TIME: sd.created_at.isoformat(),
|
||||
rpc_api.SOFTWARE_DEPLOYMENT_CREATION_TIME:
|
||||
heat_timeutils.isotime(sd.created_at),
|
||||
}
|
||||
if sd.updated_at:
|
||||
result[rpc_api.SOFTWARE_DEPLOYMENT_UPDATED_TIME] = (
|
||||
sd.updated_at.isoformat())
|
||||
heat_timeutils.isotime(sd.updated_at))
|
||||
return result
|
||||
|
||||
|
||||
@ -607,7 +613,8 @@ def format_snapshot(snapshot):
|
||||
rpc_api.SNAPSHOT_STATUS: snapshot.status,
|
||||
rpc_api.SNAPSHOT_STATUS_REASON: snapshot.status_reason,
|
||||
rpc_api.SNAPSHOT_DATA: snapshot.data,
|
||||
rpc_api.SNAPSHOT_CREATION_TIME: snapshot.created_at.isoformat(),
|
||||
rpc_api.SNAPSHOT_CREATION_TIME:
|
||||
heat_timeutils.isotime(snapshot.created_at),
|
||||
}
|
||||
return result
|
||||
|
||||
|
@ -21,6 +21,7 @@ import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.common import timeutils as heat_timeutils
|
||||
from heat.engine import api
|
||||
from heat.engine import event
|
||||
from heat.engine import parameters
|
||||
@ -94,9 +95,9 @@ class FormatTest(common.HeatTestCase):
|
||||
|
||||
formatted = api.format_stack_resource(res, False)
|
||||
self.assertEqual(resource_keys, set(formatted.keys()))
|
||||
self.assertEqual(self.stack.created_time.isoformat(),
|
||||
self.assertEqual(heat_timeutils.isotime(self.stack.created_time),
|
||||
formatted[rpc_api.RES_CREATION_TIME])
|
||||
self.assertEqual(self.stack.updated_time.isoformat(),
|
||||
self.assertEqual(heat_timeutils.isotime(self.stack.updated_time),
|
||||
formatted[rpc_api.RES_UPDATED_TIME])
|
||||
self.assertEqual(res.INIT, formatted[rpc_api.RES_ACTION])
|
||||
|
||||
@ -347,7 +348,7 @@ class FormatTest(common.HeatTestCase):
|
||||
'stacks/test_stack/' + self.stack.id)
|
||||
expected_stack_info = {
|
||||
'capabilities': [],
|
||||
'creation_time': '1970-01-01T00:00:00',
|
||||
'creation_time': '1970-01-01T00:00:00Z',
|
||||
'deletion_time': None,
|
||||
'description': 'No description',
|
||||
'disable_rollback': True,
|
||||
@ -387,7 +388,7 @@ class FormatTest(common.HeatTestCase):
|
||||
|
||||
self.stack.updated_time = datetime(1970, 1, 1)
|
||||
info = api.format_stack(self.stack)
|
||||
self.assertEqual('1970-01-01T00:00:00', info['updated_time'])
|
||||
self.assertEqual('1970-01-01T00:00:00Z', info['updated_time'])
|
||||
|
||||
@mock.patch.object(api, 'format_stack_outputs')
|
||||
def test_format_stack_adds_outputs(self, mock_fmt_outputs):
|
||||
@ -1053,7 +1054,8 @@ class FormatSoftwareConfigDeploymentTest(common.HeatTestCase):
|
||||
self.assertEqual([{'name': 'result'}], result['outputs'])
|
||||
self.assertEqual([{'name': 'result'}], result['outputs'])
|
||||
self.assertEqual({}, result['options'])
|
||||
self.assertEqual(self.now.isoformat(), result['creation_time'])
|
||||
self.assertEqual(heat_timeutils.isotime(self.now),
|
||||
result['creation_time'])
|
||||
|
||||
def test_format_software_config_none(self):
|
||||
self.assertIsNone(api.format_software_config(None))
|
||||
@ -1070,8 +1072,10 @@ class FormatSoftwareConfigDeploymentTest(common.HeatTestCase):
|
||||
self.assertEqual(deployment.action, result['action'])
|
||||
self.assertEqual(deployment.status, result['status'])
|
||||
self.assertEqual(deployment.status_reason, result['status_reason'])
|
||||
self.assertEqual(self.now.isoformat(), result['creation_time'])
|
||||
self.assertEqual(self.now.isoformat(), result['updated_time'])
|
||||
self.assertEqual(heat_timeutils.isotime(self.now),
|
||||
result['creation_time'])
|
||||
self.assertEqual(heat_timeutils.isotime(self.now),
|
||||
result['updated_time'])
|
||||
|
||||
def test_format_software_deployment_none(self):
|
||||
self.assertIsNone(api.format_software_deployment(None))
|
||||
|
@ -14,6 +14,7 @@
|
||||
import mock
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from heat.common import timeutils as heat_timeutils
|
||||
from heat.engine import notification
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
@ -54,11 +55,11 @@ class StackTest(common.HeatTestCase):
|
||||
'stack_identity': 'hay-are-en',
|
||||
'stack_name': 'fred',
|
||||
'tenant_id': 'test_tenant_id',
|
||||
'create_at': created_time.isoformat(),
|
||||
'create_at': heat_timeutils.isotime(created_time),
|
||||
'state': 'x_f',
|
||||
'description': 'for test',
|
||||
'tags': ['tag1', 'tag2'],
|
||||
'updated_at': updated_time.isoformat()})
|
||||
'updated_at': heat_timeutils.isotime(updated_time)})
|
||||
|
||||
|
||||
class AutoScaleTest(common.HeatTestCase):
|
||||
@ -106,10 +107,10 @@ class AutoScaleTest(common.HeatTestCase):
|
||||
'stack_identity': 'hay-are-en',
|
||||
'stack_name': 'fred',
|
||||
'tenant_id': 'test_tenant_id',
|
||||
'create_at': stack.created_time.isoformat(),
|
||||
'create_at': heat_timeutils.isotime(stack.created_time),
|
||||
'description': 'for test',
|
||||
'tags': ['tag1', 'tag2'],
|
||||
'updated_at': stack.updated_time.isoformat(),
|
||||
'updated_at': heat_timeutils.isotime(stack.updated_time),
|
||||
'state': 'x_f', 'adjustment_type': 'y',
|
||||
'groupname': 'c', 'capacity': '5',
|
||||
'message': 'fred', 'adjustment': 'x'})
|
||||
@ -132,10 +133,10 @@ class AutoScaleTest(common.HeatTestCase):
|
||||
'stack_identity': 'hay-are-en',
|
||||
'stack_name': 'fred',
|
||||
'tenant_id': 'test_tenant_id',
|
||||
'create_at': stack.created_time.isoformat(),
|
||||
'create_at': heat_timeutils.isotime(stack.created_time),
|
||||
'description': 'for test',
|
||||
'tags': ['tag1', 'tag2'],
|
||||
'updated_at': stack.updated_time.isoformat(),
|
||||
'updated_at': heat_timeutils.isotime(stack.updated_time),
|
||||
'state': 'x_f', 'adjustment_type': 'y',
|
||||
'groupname': 'c', 'capacity': '5',
|
||||
'message': 'error', 'adjustment': 'x'})
|
||||
|
Loading…
Reference in New Issue
Block a user