Include timezone in timestamp fields
The Neutron 'created_at'/'updated_at' fields on API resources were inconsistent with other OpenStack projects because we did not include timezone information. This patch addressed that problem by adding the zulu time indicator onto the end of the fields. Because this could break clients expecting no timezone, this patch also eliminates the 'timestamp_core' and 'timestamp_ext' extensions and consolidates them into a new 'timestamp' extension. This makes the change discoverable via the API. This is assuming the current API development paradigm where extensions can come and go depending on the deployment and the client is expected to handle this by checking the loaded extensions. Once we decide extensions are permanent, this type of change will no longer be possible. Even though this is being proposed late in the cycle, it is better to get this change in before the release where we expose even more resources with incorrectly formatted timestamps. APIImpact Closes-Bug: #1561200 Change-Id: I2ee2ed4c713d88345adc55b022feb95653eec663
This commit is contained in:
parent
465d22180e
commit
424a633fd9
@ -29,7 +29,7 @@ TIMESTAMP_BODY = {
|
||||
}
|
||||
|
||||
|
||||
class Timestamp_core(extensions.ExtensionDescriptor):
|
||||
class Timestamp(extensions.ExtensionDescriptor):
|
||||
"""Extension class supporting timestamp.
|
||||
|
||||
This class is used by neutron's extension framework for adding timestamp
|
||||
@ -38,21 +38,20 @@ class Timestamp_core(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Time Stamp Fields addition for core resources"
|
||||
return "Resource timestamps"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "timestamp_core"
|
||||
return "standard-attr-timestamp"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return ("This extension can be used for recording "
|
||||
"create/update timestamps for core resources "
|
||||
"like port/subnet/network/subnetpools.")
|
||||
return ("Adds created_at and updated_at fields to all Neutron "
|
||||
"resources that have Neutron standard attributes.")
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-03-01T10:00:00-00:00"
|
||||
return "2016-09-12T10:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version != "2.0":
|
@ -1,47 +0,0 @@
|
||||
# Copyright 2016 HuaWei Technologies.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from neutron.api import extensions
|
||||
|
||||
|
||||
class Timestamp_ext(extensions.ExtensionDescriptor):
|
||||
"""Extension class supporting timestamp.
|
||||
|
||||
This class is used by neutron's extension framework for adding timestamp
|
||||
to neutron extension resources.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Standardattr Extension Timestamps"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "timestamp_ext"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return ("This extension adds create/update timestamps for all "
|
||||
"standard neutron resources not included by the "
|
||||
"'timestamp_core' extension.")
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-05-05T10:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
# NOTE(kevinbenton): this extension is basically a no-op because
|
||||
# the timestamp_core extension already defines all of the resources
|
||||
# now.
|
||||
return {}
|
@ -42,7 +42,7 @@ EXT_TO_SERVICE_MAPPING = {
|
||||
DEFAULT_SERVICE_PLUGINS = {
|
||||
'auto_allocate': 'auto-allocated-topology',
|
||||
'tag': 'tag',
|
||||
'timestamp_core': 'timestamp_core',
|
||||
'timestamp': 'timestamp',
|
||||
'network_ip_availability': 'network-ip-availability',
|
||||
'flavors': 'flavors',
|
||||
'revisions': 'revisions',
|
||||
|
@ -12,9 +12,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import time
|
||||
|
||||
from neutron_lib import exceptions as n_exc
|
||||
from oslo_log import log
|
||||
from oslo_utils import timeutils
|
||||
@ -46,15 +43,11 @@ class TimeStamp_db_mixin(object):
|
||||
return query
|
||||
data = filters[CHANGED_SINCE][0]
|
||||
try:
|
||||
# this block checks queried timestamp format.
|
||||
datetime.datetime.fromtimestamp(time.mktime(
|
||||
time.strptime(data,
|
||||
self.ISO8601_TIME_FORMAT)))
|
||||
changed_since_string = timeutils.parse_isotime(data)
|
||||
except Exception:
|
||||
msg = _LW("The input %s must be in the "
|
||||
"following format: YYYY-MM-DDTHH:MM:SS") % CHANGED_SINCE
|
||||
"following format: YYYY-MM-DDTHH:MM:SSZ") % CHANGED_SINCE
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
changed_since_string = timeutils.parse_isotime(data)
|
||||
changed_since = (timeutils.
|
||||
normalize_time(changed_since_string))
|
||||
target_model_class = list(query._mapper_adapter_map.keys())[0]
|
||||
@ -94,9 +87,9 @@ class TimeStamp_db_mixin(object):
|
||||
|
||||
def _format_timestamp(self, resource_db, result):
|
||||
result['created_at'] = (resource_db.created_at.
|
||||
strftime(self.ISO8601_TIME_FORMAT))
|
||||
strftime(self.ISO8601_TIME_FORMAT)) + 'Z'
|
||||
result['updated_at'] = (resource_db.updated_at.
|
||||
strftime(self.ISO8601_TIME_FORMAT))
|
||||
strftime(self.ISO8601_TIME_FORMAT)) + 'Z'
|
||||
|
||||
def extend_resource_dict_timestamp(self, plugin_obj,
|
||||
resource_res, resource_db):
|
||||
|
@ -24,7 +24,7 @@ class TimeStampPlugin(service_base.ServicePluginBase,
|
||||
ts_db.TimeStamp_db_mixin):
|
||||
"""Implements Neutron Timestamp Service plugin."""
|
||||
|
||||
supported_extension_aliases = ['timestamp_core', 'timestamp_ext']
|
||||
supported_extension_aliases = ['standard-attr-timestamp']
|
||||
|
||||
def __init__(self):
|
||||
super(TimeStampPlugin, self).__init__()
|
||||
@ -42,7 +42,7 @@ class TimeStampPlugin(service_base.ServicePluginBase,
|
||||
|
||||
@classmethod
|
||||
def get_plugin_type(cls):
|
||||
return 'timestamp_core'
|
||||
return 'timestamp'
|
||||
|
||||
def get_plugin_description(self):
|
||||
return "Neutron core resources timestamp addition support"
|
||||
return "Adds timestamps to Neutron resources with standard attributes"
|
||||
|
@ -34,10 +34,9 @@ NETWORK_API_EXTENSIONS="
|
||||
sorting, \
|
||||
standard-attr-description, \
|
||||
standard-attr-revisions, \
|
||||
standard-attr-timestamp, \
|
||||
subnet_allocation, \
|
||||
tag, \
|
||||
timestamp_core, \
|
||||
timestamp_ext, \
|
||||
trunk, \
|
||||
trunk-details"
|
||||
NETWORK_API_EXTENSIONS="$(echo $NETWORK_API_EXTENSIONS | tr -d ' ')"
|
||||
|
@ -34,7 +34,7 @@ class TestTimeStamp(base.BaseAdminNetworkTest):
|
||||
larger_prefix = '10.11.0.0/16'
|
||||
|
||||
@classmethod
|
||||
@test.requires_ext(extension="timestamp_core", service="network")
|
||||
@test.requires_ext(extension="standard-attr-timestamp", service="network")
|
||||
def skip_checks(cls):
|
||||
super(TestTimeStamp, cls).skip_checks()
|
||||
|
||||
@ -186,8 +186,9 @@ class TestTimeStampWithL3(base_routers.BaseRouterTest):
|
||||
def skip_checks(cls):
|
||||
super(TestTimeStampWithL3, cls).skip_checks()
|
||||
|
||||
if not test.is_extension_enabled('timestamp_ext', 'network'):
|
||||
raise cls.skipException("timestamp_ext extension not enabled")
|
||||
if not test.is_extension_enabled('standard-attr-timestamp', 'network'):
|
||||
raise cls.skipException("standard-attr-timestamp extension not "
|
||||
"enabled")
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
@ -260,8 +261,9 @@ class TestTimeStampWithSecurityGroup(base_security_groups.BaseSecGroupTest):
|
||||
def skip_checks(cls):
|
||||
super(TestTimeStampWithSecurityGroup, cls).skip_checks()
|
||||
|
||||
if not test.is_extension_enabled('timestamp_ext', 'network'):
|
||||
raise cls.skipException("timestamp_ext extension not enabled")
|
||||
if not test.is_extension_enabled('standard-attr-timestamp', 'network'):
|
||||
raise cls.skipException("standard-attr-timestamp extension not "
|
||||
"enabled")
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
|
@ -16,12 +16,13 @@ import datetime
|
||||
import six
|
||||
|
||||
import mock
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from neutron import context
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import tag_db as tag_module
|
||||
from neutron.extensions import timestamp_core as timestamp
|
||||
from neutron.extensions import timestamp
|
||||
from neutron import manager
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
||||
|
||||
@ -38,7 +39,7 @@ class TimeStampExtensionManager(object):
|
||||
return []
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
return timestamp.Timestamp_core().get_extended_resources(version)
|
||||
return timestamp.Timestamp().get_extended_resources(version)
|
||||
|
||||
|
||||
class TimeStampTestPlugin(db_base_plugin_v2.NeutronDbPluginV2):
|
||||
@ -47,7 +48,7 @@ class TimeStampTestPlugin(db_base_plugin_v2.NeutronDbPluginV2):
|
||||
|
||||
class TimeStampChangedsinceTestCase(test_db_base_plugin_v2.
|
||||
NeutronDbPluginV2TestCase):
|
||||
plugin = ('neutron.tests.unit.extensions.test_timestamp_core.' +
|
||||
plugin = ('neutron.tests.unit.extensions.test_timestamp.' +
|
||||
'TimeStampTestPlugin')
|
||||
|
||||
def setUp(self):
|
||||
@ -55,14 +56,14 @@ class TimeStampChangedsinceTestCase(test_db_base_plugin_v2.
|
||||
super(TimeStampChangedsinceTestCase, self).setUp(plugin=self.plugin,
|
||||
ext_mgr=ext_mgr)
|
||||
self.addCleanup(manager.NeutronManager.
|
||||
get_service_plugins()['timestamp_core'].
|
||||
get_service_plugins()['timestamp'].
|
||||
unregister_db_events)
|
||||
self.addCleanup(manager.NeutronManager.clear_instance)
|
||||
|
||||
def setup_coreplugin(self, core_plugin=None):
|
||||
super(TimeStampChangedsinceTestCase, self).setup_coreplugin(
|
||||
self.plugin)
|
||||
self.patched_default_svc_plugins.return_value = ['timestamp_core']
|
||||
self.patched_default_svc_plugins.return_value = ['timestamp']
|
||||
|
||||
def _get_resp_with_changed_since(self, resource_type, changed_since):
|
||||
query_params = 'changed_since=%s' % changed_since
|
||||
@ -73,18 +74,12 @@ class TimeStampChangedsinceTestCase(test_db_base_plugin_v2.
|
||||
|
||||
def _return_by_timedelay(self, resource, timedelay):
|
||||
resource_type = six.next(six.iterkeys(resource))
|
||||
try:
|
||||
time_create = datetime.datetime.strptime(
|
||||
resource[resource_type]['updated_at'],
|
||||
'%Y-%m-%dT%H:%M:%S')
|
||||
except Exception:
|
||||
time_create = datetime.datetime.strptime(
|
||||
resource[resource_type]['updated_at'],
|
||||
'%Y-%m-%d %H:%M:%S.%f')
|
||||
time_create = timeutils.parse_isotime(
|
||||
resource[resource_type]['updated_at'])
|
||||
time_before = datetime.timedelta(seconds=timedelay)
|
||||
addedtime_string = (datetime.datetime.
|
||||
strftime(time_create + time_before,
|
||||
'%Y-%m-%dT%H:%M:%S'))
|
||||
'%Y-%m-%dT%H:%M:%S')) + 'Z'
|
||||
return self._get_resp_with_changed_since(resource_type,
|
||||
addedtime_string)
|
||||
|
@ -0,0 +1,16 @@
|
||||
---
|
||||
prelude: >
|
||||
- The created_at and updated_at fields available on Neutron
|
||||
resources now include a timezone indicator at the end.
|
||||
Because this is a change in format, the old 'timestamp_core'
|
||||
extension has been removed and replaced with a 'timestamp'
|
||||
extension.
|
||||
upgrade:
|
||||
- The 'timestamp_core' extension has been removed and replaced
|
||||
with the 'standard-attr-timestamp' extension. Objects will still
|
||||
have timestamps in the 'created_at' and 'updated_at' fields, but
|
||||
they will have the timestamp appended to the end of them
|
||||
to be consistent with other OpenStack projects.
|
||||
fixes:
|
||||
- Bug 1561200 has been fixed by including the timezone with
|
||||
Neutron 'created_at' and 'updated_at' fields.
|
@ -77,7 +77,7 @@ neutron.service_plugins =
|
||||
segments = neutron.services.segments.plugin:Plugin
|
||||
network_ip_availability = neutron.services.network_ip_availability.plugin:NetworkIPAvailabilityPlugin
|
||||
revisions = neutron.services.revisions.revision_plugin:RevisionPlugin
|
||||
timestamp_core = neutron.services.timestamp.timestamp_plugin:TimeStampPlugin
|
||||
timestamp = neutron.services.timestamp.timestamp_plugin:TimeStampPlugin
|
||||
trunk = neutron.services.trunk.plugin:TrunkPlugin
|
||||
neutron.qos.notification_drivers =
|
||||
message_queue = neutron.services.qos.notification_drivers.message_queue:RpcQosServiceNotificationDriver
|
||||
|
Loading…
x
Reference in New Issue
Block a user