cd80ae6e42
During the development of patch [1], it became apparent that the DB was rounding the microseconds part of a datetime object. This caused inconsistencies between what would be stored in the DB and what was returned to the user via the API because the API uses strftime. For an example, see the INSERT statement on L1-L2 and then the subsequent retrieval of the same record on L43 of [2]. The microseconds were rounded up even though the mysql docs say they will be truncated. In order to ensure consistency across SQL drivers, backends, and our API, this patch adds a TruncatedDateTime object which truncates off the microseconds before they go into the database. 1. I78b00516e31ce83376d37f57299b2229b6fb8fcf 2. http://paste.openstack.org/show/565116/ Closes-Bug: #1619299 Change-Id: I47d5ad5a5cdc0cb0d61f0642616cccc8f341ed62
84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
# 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.
|
|
|
|
"""Custom SQLAlchemy types."""
|
|
|
|
import netaddr
|
|
from sqlalchemy import types
|
|
|
|
from neutron._i18n import _
|
|
|
|
|
|
class IPAddress(types.TypeDecorator):
|
|
|
|
impl = types.String(64)
|
|
|
|
def process_result_value(self, value, dialect):
|
|
return netaddr.IPAddress(value)
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
if not isinstance(value, netaddr.IPAddress):
|
|
raise AttributeError(_("Received type '%(type)s' and value "
|
|
"'%(value)s'. Expecting netaddr.IPAddress "
|
|
"type.") % {'type': type(value),
|
|
'value': value})
|
|
|
|
return str(value)
|
|
|
|
|
|
class CIDR(types.TypeDecorator):
|
|
|
|
impl = types.String(64)
|
|
|
|
def process_result_value(self, value, dialect):
|
|
return netaddr.IPNetwork(value)
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
if not isinstance(value, netaddr.IPNetwork):
|
|
raise AttributeError(_("Received type '%(type)s' and value "
|
|
"'%(value)s'. Expecting netaddr.IPNetwork "
|
|
"type.") % {'type': type(value),
|
|
'value': value})
|
|
return str(value)
|
|
|
|
|
|
class MACAddress(types.TypeDecorator):
|
|
|
|
impl = types.String(64)
|
|
|
|
def process_result_value(self, value, dialect):
|
|
return netaddr.EUI(value)
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
if not isinstance(value, netaddr.EUI):
|
|
raise AttributeError(_("Received type '%(type)s' and value "
|
|
"'%(value)s'. Expecting netaddr.EUI "
|
|
"type.") % {'type': type(value),
|
|
'value': value})
|
|
return str(value)
|
|
|
|
|
|
class TruncatedDateTime(types.TypeDecorator):
|
|
"""Truncates microseconds.
|
|
|
|
Use this for datetime fields so we don't have to worry about DB-specifc
|
|
behavior when it comes to rounding/truncating microseconds off of
|
|
timestamps.
|
|
"""
|
|
|
|
impl = types.DateTime
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
return value.replace(microsecond=0) if value else value
|
|
|
|
process_result_value = process_bind_param
|