neutron/neutron/db/sqlalchemytypes.py
Kevin Benton cd80ae6e42 Truncate microseconds before DB insertion
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
2016-09-01 13:25:24 +00:00

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