summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmrith Kumar <amrith@tesora.com>2016-06-07 08:15:24 -0400
committerAmrith Kumar <amrith.kumar@gmail.com>2017-06-09 16:22:11 +0000
commit109ff949511c0185ab1dd0beffeb03a8a3332005 (patch)
tree75adba86b2c07ee9da54c7ea6e8896358f6f2952
parentfb870b306ec044e8100fe67886d20be84f5807ef (diff)
downloadtrove-109ff949511c0185ab1dd0beffeb03a8a3332005.tar.gz
Handle isotime deprecation in oslo_utils.timeutils
oslo_utils.timeutils is deprecating isotime(). In reality they are deprecating some other things as well but Trove doesn't (currently) use any of those things. Much has been written on the subject of this deprecation. I think the proposal to merely replace isotime with datetime.datetime.isoformat() is a little simplistic. Well intentioned, but nonetheless I believe that it is simplistic. The primary issue I could find with oslo_utils.timeutils.isotime() was the fact that it was naive. I think it could well have been fixed in oslo_utils but for whatever reason(s) oslo decided not to want to go that route. The primary challenge from Trove's perspective is that I want to respect the existing API contract while at the same time get an implementation of time handling that is not identical in its flaws with oslo_utils.timeutils.isotime(). This change set attempts to address that by making trove.common.timeutils.isotime() that is aware. It also implements a utcnow_aware() function that is aware. ISO 8601 allows for four representations of timezone and those are <time>Z <time>[+-]hh:mm <time>[+-]hhmm <time>[+-]hh Trove conventionally used the first one, even if the time wasn't really a UTC time. That's one of the things being fixed here. In review cp16net asked whether this change removes the 'Z' at the end of time strings generated by the isotime() function. The answer is NO. The new isotime() function performs identical to the old and now deprecated function in oslo_utils.timeutils for UTC (Z) times. There was a utcnow() function in trove.common.utils which just wrapped datetime.datetime.utcnow(). That has been moved now to trove.common.timeutils with the other new time related functions. There were a couple of places in Trove where code was using datetime.now() which was not ideal. Those have been corrected now as well. Unit tests have been proposed for the new routines. Closes-Bug: #1532120 Change-Id: Ic5abf6669edd4f1a9fd62e61f437565aa887aebe
-rw-r--r--trove/common/notification.py2
-rw-r--r--trove/common/timeutils.py84
-rw-r--r--trove/common/utils.py7
-rw-r--r--trove/configuration/models.py6
-rw-r--r--trove/configuration/service.py5
-rw-r--r--trove/datastore/models.py3
-rw-r--r--trove/db/models.py11
-rw-r--r--trove/extensions/common/models.py4
-rw-r--r--trove/extensions/mgmt/instances/models.py13
-rw-r--r--trove/guestagent/backup/backupagent.py7
-rw-r--r--trove/guestagent/common/timeutils.py19
-rw-r--r--trove/guestagent/datastore/service.py6
-rw-r--r--trove/guestagent/guest_log.py4
-rw-r--r--trove/guestagent/models.py3
-rw-r--r--trove/instance/models.py7
-rw-r--r--trove/limits/views.py5
-rw-r--r--trove/module/models.py10
-rw-r--r--trove/quota/models.py5
-rwxr-xr-xtrove/taskmanager/models.py4
-rw-r--r--trove/tests/examples/snippets.py3
-rw-r--r--trove/tests/scenario/runners/test_runners.py3
-rw-r--r--trove/tests/unittests/backup/test_backup_models.py11
-rw-r--r--trove/tests/unittests/common/test_timeutils.py129
-rw-r--r--trove/tests/unittests/conductor/test_methods.py10
-rw-r--r--trove/tests/unittests/guestagent/test_models.py9
-rw-r--r--trove/tests/unittests/taskmanager/test_models.py7
26 files changed, 287 insertions, 90 deletions
diff --git a/trove/common/notification.py b/trove/common/notification.py
index 73ec7990..49a3aad5 100644
--- a/trove/common/notification.py
+++ b/trove/common/notification.py
@@ -18,11 +18,11 @@ import copy
import traceback
from oslo_log import log as logging
-from oslo_utils import timeutils
from trove.common import cfg
from trove.common.exception import TroveError
from trove.common.i18n import _
+from trove.common import timeutils
from trove.conductor import api as conductor_api
from trove import rpc
diff --git a/trove/common/timeutils.py b/trove/common/timeutils.py
new file mode 100644
index 00000000..44a0b9ff
--- /dev/null
+++ b/trove/common/timeutils.py
@@ -0,0 +1,84 @@
+# Copyright 2016 Tesora Inc.
+# All Rights Reserved.
+#
+# 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 datetime import datetime
+from datetime import timedelta
+from datetime import tzinfo
+
+
+class zulutime(tzinfo):
+ """A tzinfo class for zulu time"""
+
+ def utcoffset(self, dt):
+ return timedelta(0)
+
+ def tzname(self, dt):
+ return "Z"
+
+ def dst(self, dt):
+ return timedelta(0)
+
+
+def utcnow_aware():
+ """An aware utcnow() that uses zulutime for the tzinfo."""
+ return datetime.now(zulutime())
+
+
+def utcnow():
+ """A wrapper around datetime.datetime.utcnow(). We're doing this
+ because it is mock'ed in some places.
+ """
+ return datetime.utcnow()
+
+
+def isotime(tm=None, subsecond=False):
+ """Stringify a time and return it in an ISO 8601 format. Subsecond
+ information is only provided if the subsecond parameter is set
+ to True (default: False).
+
+ If a time (tm) is provided, it will be stringified. If tm is
+ not provided, the current UTC time is used instead.
+
+ The timezone for UTC time will be provided as 'Z' and not
+ [+-]00:00. Time zone differential for non UTC times will be
+ provided as the full six character string format provided by
+ datetime.datetime.isoformat() namely [+-]NN:NN.
+
+ If an invalid time is provided such that tm.utcoffset() causes
+ a ValueError, that exception will be propagated.
+ """
+
+ _dt = tm if tm else utcnow_aware()
+
+ if not subsecond:
+ _dt = _dt.replace(microsecond=0)
+
+ # might cause an exception if _dt has a bad utcoffset.
+ delta = _dt.utcoffset() if _dt.utcoffset() else timedelta(0)
+
+ ts = None
+
+ if delta == timedelta(0):
+ # either we are provided a naive time (tm) or no tm, or an
+ # aware UTC time. In any event, we want to use 'Z' for the
+ # timezone rather than the full 6 character offset.
+ _dt = _dt.replace(tzinfo=None)
+ ts = _dt.isoformat()
+ ts += 'Z'
+ else:
+ # an aware non-UTC time was provided
+ ts = _dt.isoformat()
+
+ return ts
diff --git a/trove/common/utils.py b/trove/common/utils.py
index 9a81be45..774cfc66 100644
--- a/trove/common/utils.py
+++ b/trove/common/utils.py
@@ -15,7 +15,6 @@
"""I totally stole most of this from melange, thx guys!!!"""
import collections
-import datetime
import inspect
import os
import shutil
@@ -29,7 +28,6 @@ from oslo_log import log as logging
from oslo_service import loopingcall
from oslo_utils import importutils
from oslo_utils import strutils
-from oslo_utils import timeutils
from passlib import pwd
import six
import six.moves.urllib.parse as urlparse
@@ -46,7 +44,6 @@ import_object = importutils.import_object
import_module = importutils.import_module
bool_from_string = strutils.bool_from_string
execute = processutils.execute
-isotime = timeutils.isotime
def build_jinja_environment():
@@ -99,10 +96,6 @@ def generate_uuid():
return str(uuid.uuid4())
-def utcnow():
- return datetime.datetime.utcnow()
-
-
def raise_if_process_errored(process, exception):
try:
err = process.stderr.read()
diff --git a/trove/configuration/models.py b/trove/configuration/models.py
index 3ca3c2bd..599a1501 100644
--- a/trove/configuration/models.py
+++ b/trove/configuration/models.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from datetime import datetime
import json
from oslo_log import log as logging
@@ -22,6 +21,7 @@ from trove.common import cfg
from trove.common import exception
from trove.common.exception import ModelNotFoundError
from trove.common.i18n import _
+from trove.common import timeutils
from trove.common import utils
from trove.datastore import models as dstore_models
from trove.db import get_db_api
@@ -103,7 +103,7 @@ class Configuration(object):
@staticmethod
def delete(context, group):
- deleted_at = datetime.utcnow()
+ deleted_at = timeutils.utcnow()
Configuration.remove_all_items(context, group.id, deleted_at)
group.deleted = True
group.deleted_at = deleted_at
@@ -313,7 +313,7 @@ class DatastoreConfigurationParameters(object):
config_param = DatastoreConfigurationParameters.load_parameter_by_name(
version_id, config_param_name)
config_param.deleted = True
- config_param.deleted_at = datetime.utcnow()
+ config_param.deleted_at = timeutils.utcnow()
config_param.save()
@classmethod
diff --git a/trove/configuration/service.py b/trove/configuration/service.py
index 5cf9ff8b..463cae93 100644
--- a/trove/configuration/service.py
+++ b/trove/configuration/service.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from datetime import datetime
-
from oslo_log import log as logging
import six
@@ -27,6 +25,7 @@ from trove.common import notification
from trove.common.notification import StartNotification, EndNotification
from trove.common import pagination
from trove.common import policy
+from trove.common import timeutils
from trove.common import wsgi
from trove.configuration import models
from trove.configuration.models import DBConfigurationParameter
@@ -194,7 +193,7 @@ class ConfigurationsController(wsgi.Controller):
name=group.name, description=group.description):
items = self._configuration_items_list(group,
body['configuration'])
- deleted_at = datetime.utcnow()
+ deleted_at = timeutils.utcnow()
models.Configuration.remove_all_items(context, group.id,
deleted_at)
models.Configuration.save(group, items)
diff --git a/trove/datastore/models.py b/trove/datastore/models.py
index 981489ed..e7a7a200 100644
--- a/trove/datastore/models.py
+++ b/trove/datastore/models.py
@@ -21,6 +21,7 @@ from trove.common import cfg
from trove.common import exception
from trove.common.i18n import _
from trove.common.remote import create_nova_client
+from trove.common import timeutils
from trove.common import utils
from trove.db import get_db_api
from trove.db import models as dbmodels
@@ -631,7 +632,7 @@ class DatastoreVersionMetadata(object):
key=key, value=value)
if db_record.deleted == 1:
db_record.deleted = 0
- db_record.updated_at = utils.utcnow()
+ db_record.updated_at = timeutils.utcnow()
db_record.save()
return
else:
diff --git a/trove/db/models.py b/trove/db/models.py
index 0553bcfd..8d8c7ad5 100644
--- a/trove/db/models.py
+++ b/trove/db/models.py
@@ -19,6 +19,7 @@ from trove.common import exception
from trove.common.i18n import _
from trove.common import models
from trove.common import pagination
+from trove.common import timeutils
from trove.common import utils
from trove.db import db_query
from trove.db import get_db_api
@@ -33,7 +34,7 @@ class DatabaseModelBase(models.ModelBase):
def create(cls, **values):
init_vals = {
'id': utils.generate_uuid(),
- 'created': utils.utcnow(),
+ 'created': timeutils.utcnow(),
}
if hasattr(cls, 'deleted'):
init_vals['deleted'] = False
@@ -58,20 +59,20 @@ class DatabaseModelBase(models.ModelBase):
def save(self):
if not self.is_valid():
raise exception.InvalidModelError(errors=self.errors)
- self['updated'] = utils.utcnow()
+ self['updated'] = timeutils.utcnow()
LOG.debug("Saving %(name)s: %(dict)s",
{'name': self.__class__.__name__,
'dict': strutils.mask_dict_password(self.__dict__)})
return self.db_api.save(self)
def delete(self):
- self['updated'] = utils.utcnow()
+ self['updated'] = timeutils.utcnow()
LOG.debug("Deleting %(name)s: %(dict)s",
{'name': self.__class__.__name__,
'dict': strutils.mask_dict_password(self.__dict__)})
if self.preserve_on_delete:
- self['deleted_at'] = utils.utcnow()
+ self['deleted_at'] = timeutils.utcnow()
self['deleted'] = True
return self.db_api.save(self)
else:
@@ -81,7 +82,7 @@ class DatabaseModelBase(models.ModelBase):
for key in values:
if hasattr(self, key):
setattr(self, key, values[key])
- self['updated'] = utils.utcnow()
+ self['updated'] = timeutils.utcnow()
return self.db_api.save(self)
def __init__(self, **kwargs):
diff --git a/trove/extensions/common/models.py b/trove/extensions/common/models.py
index 4102a425..c67438d0 100644
--- a/trove/extensions/common/models.py
+++ b/trove/extensions/common/models.py
@@ -18,7 +18,7 @@ from oslo_log import log as logging
from trove.common.db import models as guest_models
from trove.common import exception
from trove.common.remote import create_guest_client
-from trove.common import utils
+from trove.common import timeutils
from trove.db import get_db_api
from trove.instance import models as base_models
@@ -106,7 +106,7 @@ class RootHistory(object):
def __init__(self, instance_id, user):
self.id = instance_id
self.user = user
- self.created = utils.utcnow()
+ self.created = timeutils.utcnow()
def save(self):
LOG.debug("Saving %(name)s: %(dict)s",
diff --git a/trove/extensions/mgmt/instances/models.py b/trove/extensions/mgmt/instances/models.py
index b2b6fb0d..64da1a1e 100644
--- a/trove/extensions/mgmt/instances/models.py
+++ b/trove/extensions/mgmt/instances/models.py
@@ -19,7 +19,7 @@ from trove.common import cfg
from trove.common import exception
from trove.common.i18n import _
from trove.common import remote
-from trove.common import utils
+from trove.common import timeutils
from trove.extensions.mysql import models as mysql_models
from trove.instance import models as instance_models
from trove import rpc
@@ -186,12 +186,11 @@ class NotificationTransformer(object):
@staticmethod
def _get_audit_period():
- now = datetime.datetime.now()
- audit_start = utils.isotime(
- now - datetime.timedelta(
- seconds=CONF.exists_notification_interval),
- subsecond=True)
- audit_end = utils.isotime(now, subsecond=True)
+ now = timeutils.utcnow()
+ start_time = now - datetime.timedelta(
+ seconds=CONF.exists_notification_interval)
+ audit_start = timeutils.isotime(start_time)
+ audit_end = timeutils.isotime(now)
return audit_start, audit_end
def _get_service_id(self, datastore_manager, id_map):
diff --git a/trove/guestagent/backup/backupagent.py b/trove/guestagent/backup/backupagent.py
index 71ccaa52..9d13e746 100644
--- a/trove/guestagent/backup/backupagent.py
+++ b/trove/guestagent/backup/backupagent.py
@@ -15,13 +15,13 @@
#
from oslo_log import log as logging
+from oslo_utils import timeutils
from trove.backup.state import BackupState
from trove.common import cfg
from trove.common.i18n import _
from trove.common.strategies.storage import get_storage_strategy
from trove.conductor import api as conductor_api
-from trove.guestagent.common import timeutils
from trove.guestagent.dbaas import get_filesystem_volume_stats
from trove.guestagent.strategies.backup.base import BackupError
from trove.guestagent.strategies.backup.base import UnknownBackupType
@@ -74,7 +74,7 @@ class BackupAgent(object):
'state': BackupState.BUILDING,
}
conductor.update_backup(CONF.guest_id,
- sent=timeutils.float_utcnow(),
+ sent=timeutils.utcnow_ts(microsecond=True),
**backup_state)
LOG.debug("Updated state for %s to %s.", backup_id, backup_state)
@@ -120,7 +120,8 @@ class BackupAgent(object):
finally:
LOG.info(_("Completed backup %(backup_id)s."), backup_state)
conductor.update_backup(CONF.guest_id,
- sent=timeutils.float_utcnow(),
+ sent=timeutils.utcnow_ts(
+ microsecond=True),
**backup_state)
LOG.debug("Updated state for %s to %s.",
backup_id, backup_state)
diff --git a/trove/guestagent/common/timeutils.py b/trove/guestagent/common/timeutils.py
deleted file mode 100644
index e5895bd8..00000000
--- a/trove/guestagent/common/timeutils.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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 datetime import datetime
-
-from oslo_utils import timeutils
-
-
-def float_utcnow():
- return float(datetime.strftime(timeutils.utcnow(), "%s.%f"))
diff --git a/trove/guestagent/datastore/service.py b/trove/guestagent/datastore/service.py
index d03d87f8..bedd63c3 100644
--- a/trove/guestagent/datastore/service.py
+++ b/trove/guestagent/datastore/service.py
@@ -18,6 +18,7 @@ import os
import time
from oslo_log import log as logging
+from oslo_utils import timeutils
from trove.common import cfg
from trove.common import context as trove_context
@@ -26,7 +27,7 @@ from trove.common import instance
from trove.conductor import api as conductor_api
from trove.guestagent.common import guestagent_utils
from trove.guestagent.common import operating_system
-from trove.guestagent.common import timeutils
+
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
@@ -167,7 +168,8 @@ class BaseDbStatus(object):
heartbeat = {'service_status': status.description}
conductor_api.API(context).heartbeat(
- CONF.guest_id, heartbeat, sent=timeutils.float_utcnow())
+ CONF.guest_id, heartbeat,
+ sent=timeutils.utcnow_ts(microsecond=True))
LOG.debug("Successfully cast set_status.")
self.status = status
else:
diff --git a/trove/guestagent/guest_log.py b/trove/guestagent/guest_log.py
index 8e93e4a1..bc1cc000 100644
--- a/trove/guestagent/guest_log.py
+++ b/trove/guestagent/guest_log.py
@@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from datetime import datetime
import enum
import hashlib
import os
@@ -26,6 +25,7 @@ from trove.common import exception
from trove.common.i18n import _
from trove.common.remote import create_swift_client
from trove.common import stream_codecs
+from trove.common import timeutils
from trove.guestagent.common import operating_system
from trove.guestagent.common.operating_system import FileMode
@@ -404,7 +404,7 @@ class GuestLog(object):
'log': self._name}
def _object_name(self):
- return 'log-%s' % str(datetime.utcnow()).replace(' ', 'T')
+ return 'log-%s' % str(timeutils.utcnow()).replace(' ', 'T')
def _get_meta_details(self):
LOG.debug("Getting meta details for '%s'", self._name)
diff --git a/trove/guestagent/models.py b/trove/guestagent/models.py
index 4cdc4279..cdf64651 100644
--- a/trove/guestagent/models.py
+++ b/trove/guestagent/models.py
@@ -20,6 +20,7 @@ from oslo_log import log as logging
from trove.common import cfg
from trove.common import exception
from trove.common.i18n import _
+from trove.common import timeutils
from trove.common import utils
from trove.db import get_db_api
from trove.db import models as dbmodels
@@ -56,7 +57,7 @@ class AgentHeartBeat(dbmodels.DatabaseModelBase):
def save(self):
if not self.is_valid():
raise exception.InvalidModelError(errors=self.errors)
- self['updated_at'] = utils.utcnow()
+ self['updated_at'] = timeutils.utcnow()
LOG.debug("Saving %(name)s: %(dict)s",
{'name': self.__class__.__name__, 'dict': self.__dict__})
return get_db_api().save(self)
diff --git a/trove/instance/models.py b/trove/instance/models.py
index d1825ef5..922d3b06 100644
--- a/trove/instance/models.py
+++ b/trove/instance/models.py
@@ -39,6 +39,7 @@ from trove.common.remote import create_guest_client
from trove.common.remote import create_nova_client
from trove.common import server_group as srv_grp
from trove.common import template
+from trove.common import timeutils
from trove.common.trove_remote import create_trove_client
from trove.common import utils
from trove.configuration.models import Configuration
@@ -651,7 +652,7 @@ class BaseInstance(SimpleInstance):
pass
def delete_async(self):
- deleted_at = datetime.utcnow()
+ deleted_at = timeutils.utcnow()
self._delete_resources(deleted_at)
LOG.debug("Setting instance %s to be deleted.", self.id)
self.update_db(deleted=True, deleted_at=deleted_at,
@@ -1217,7 +1218,7 @@ class Instance(BuiltInstance):
raise exception.BadRequest(_("Instance %s is not a replica"
" source.") % self.id)
service = InstanceServiceStatus.find_by(instance_id=self.id)
- last_heartbeat_delta = datetime.utcnow() - service.updated_at
+ last_heartbeat_delta = timeutils.utcnow() - service.updated_at
agent_expiry_interval = timedelta(seconds=CONF.agent_heartbeat_expiry)
if last_heartbeat_delta < agent_expiry_interval:
raise exception.BadRequest(_("Replica Source %s cannot be ejected"
@@ -1788,7 +1789,7 @@ class InstanceServiceStatus(dbmodels.DatabaseModelBase):
self.status_description = value.description
def save(self):
- self['updated_at'] = utils.utcnow()
+ self['updated_at'] = timeutils.utcnow()
return get_db_api().save(self)
status = property(get_status, set_status)
diff --git a/trove/limits/views.py b/trove/limits/views.py
index 6c483c08..f381d1ca 100644
--- a/trove/limits/views.py
+++ b/trove/limits/views.py
@@ -14,7 +14,8 @@
# under the License.
import datetime
-from oslo_utils import timeutils
+
+from trove.common import timeutils
class LimitView(object):
@@ -27,7 +28,7 @@ class LimitView(object):
next_avail = get_utc(self.rate_limit.get("resetTime", 0))
return {"limit": {
- "nextAvailable": timeutils.isotime(at=next_avail),
+ "nextAvailable": timeutils.isotime(next_avail),
"remaining": self.rate_limit.get("remaining", 0),
"unit": self.rate_limit.get("unit", ""),
"value": self.rate_limit.get("value", ""),
diff --git a/trove/module/models.py b/trove/module/models.py
index dc9138bb..80df8cb7 100644
--- a/trove/module/models.py
+++ b/trove/module/models.py
@@ -16,7 +16,6 @@
"""Model classes that form the core of Module functionality."""
-from datetime import datetime
import hashlib
import six
from sqlalchemy.sql.expression import or_
@@ -27,6 +26,7 @@ from trove.common import cfg
from trove.common import crypto_utils
from trove.common import exception
from trove.common.i18n import _
+from trove.common import timeutils
from trove.common import utils
from trove.datastore import models as datastore_models
from trove.db import models
@@ -266,7 +266,7 @@ class Module(object):
module.priority_apply, None)
Module.enforce_live_update(module.id, module.live_update, module.md5)
module.deleted = True
- module.deleted_at = datetime.utcnow()
+ module.deleted_at = timeutils.utcnow()
module.save()
@staticmethod
@@ -342,7 +342,7 @@ class Module(object):
if module.datastore_version_id:
module.datastore_version_id = ds_ver_id
- module.updated = datetime.utcnow()
+ module.updated = timeutils.utcnow()
DBModule.save(module)
@staticmethod
@@ -424,7 +424,7 @@ class InstanceModule(object):
@staticmethod
def delete(context, instance_module):
instance_module.deleted = True
- instance_module.deleted_at = datetime.utcnow()
+ instance_module.deleted_at = timeutils.utcnow()
instance_module.save()
@staticmethod
@@ -440,7 +440,7 @@ class InstanceModule(object):
@staticmethod
def update(context, instance_module):
- instance_module.updated = datetime.utcnow()
+ instance_module.updated = timeutils.utcnow()
DBInstanceModule.save(instance_module)
diff --git a/trove/quota/models.py b/trove/quota/models.py
index 64e225c0..31b7542d 100644
--- a/trove/quota/models.py
+++ b/trove/quota/models.py
@@ -14,6 +14,7 @@
from trove.common import cfg
+from trove.common import timeutils
from trove.common import utils
from trove.db import models as dbmodels
@@ -31,8 +32,8 @@ class Quota(dbmodels.DatabaseModelBase):
'hard_limit', 'id']
def __init__(self, tenant_id, resource, hard_limit,
- id=utils.generate_uuid(), created=utils.utcnow(),
- update=utils.utcnow()):
+ id=utils.generate_uuid(), created=timeutils.utcnow(),
+ update=timeutils.utcnow()):
self.tenant_id = tenant_id
self.resource = resource
self.hard_limit = hard_limit
diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py
index e104df11..6e9272a3 100755
--- a/trove/taskmanager/models.py
+++ b/trove/taskmanager/models.py
@@ -21,7 +21,6 @@ from eventlet import greenthread
from eventlet.timeout import Timeout
from novaclient import exceptions as nova_exceptions
from oslo_log import log as logging
-from oslo_utils import timeutils
from swiftclient.client import ClientException
from trove.backup import models as bkup_models
@@ -61,6 +60,7 @@ from trove.common.remote import create_guest_client
from trove.common import server_group as srv_grp
from trove.common.strategies.cluster import strategy
from trove.common import template
+from trove.common import timeutils
from trove.common import utils
from trove.common.utils import try_recover
from trove.extensions.mysql import models as mysql_models
@@ -315,7 +315,7 @@ class ClusterTasks(Cluster):
LOG.debug("setting cluster %s as deleted.", cluster_id)
cluster = DBCluster.find_by(id=cluster_id)
cluster.deleted = True
- cluster.deleted_at = utils.utcnow()
+ cluster.deleted_at = timeutils.utcnow()
cluster.task_status = tasks.ClusterTasks.NONE
cluster.save()
LOG.debug("end delete_cluster for id: %s", cluster_id)
diff --git a/trove/tests/examples/snippets.py b/trove/tests/examples/snippets.py
index 8e539ebf..65d90198 100644
--- a/trove/tests/examples/snippets.py
+++ b/trove/tests/examples/snippets.py
@@ -71,8 +71,9 @@ def set_fake_stuff(uuid=None, minute=None, unique_id=None):
def monkey_patch_uuid_and_date():
import uuid
uuid.uuid4 = get_uuid
+ from trove.common import timeutils
from trove.common import utils
- utils.utcnow = get_now
+ timeutils.utcnow = get_now
utils.generate_uuid = get_uuid
diff --git a/trove/tests/scenario/runners/test_runners.py b/trove/tests/scenario/runners/test_runners.py
index a1f2fa1b..d858893d 100644
--- a/trove/tests/scenario/runners/test_runners.py
+++ b/trove/tests/scenario/runners/test_runners.py
@@ -31,6 +31,7 @@ from troveclient.compat import exceptions
from trove.common import cfg
from trove.common import exception
from trove.common.strategies.strategy import Strategy
+from trove.common import timeutils
from trove.common import utils
from trove.common.utils import poll_until, build_polling_task
from trove.tests.config import CONFIG
@@ -323,7 +324,7 @@ class TestRunner(object):
self.def_timeout = timeout
self.instance_info.name = "TEST_" + datetime.datetime.strftime(
- datetime.datetime.now(), '%Y_%m_%d__%H_%M_%S')
+ timeutils.utcnow(), '%Y_%m_%d__%H_%M_%S')
self.instance_info.dbaas_datastore = CONFIG.dbaas_datastore
self.instance_info.dbaas_datastore_version = (
CONFIG.dbaas_datastore_version)
diff --git a/trove/tests/unittests/backup/test_backup_models.py b/trove/tests/unittests/backup/test_backup_models.py
index 315a508e..a4964ae6 100644
--- a/trove/tests/unittests/backup/test_backup_models.py
+++ b/trove/tests/unittests/backup/test_backup_models.py
@@ -23,6 +23,7 @@ from trove.backup import state
from trove.common import context
from trove.common import exception
from trove.common import remote
+from trove.common import timeutils
from trove.common import utils
from trove.db.models import DatabaseModelBase
from trove.instance import models as instance_models
@@ -54,7 +55,7 @@ class BackupCreateTest(trove_testtools.TestCase):
def setUp(self):
super(BackupCreateTest, self).setUp()
util.init_db()
- self.context, self.instance_id = _prep_conf(utils.utcnow())
+ self.context, self.instance_id = _prep_conf(timeutils.utcnow())
self.created = False
def tearDown(self):
@@ -241,7 +242,7 @@ class BackupDeleteTest(trove_testtools.TestCase):
def setUp(self):
super(BackupDeleteTest, self).setUp()
util.init_db()
- self.context, self.instance_id = _prep_conf(utils.utcnow())
+ self.context, self.instance_id = _prep_conf(timeutils.utcnow())
def tearDown(self):
super(BackupDeleteTest, self).tearDown()
@@ -272,7 +273,7 @@ class BackupORMTest(trove_testtools.TestCase):
def setUp(self):
super(BackupORMTest, self).setUp()
util.init_db()
- self.context, self.instance_id = _prep_conf(utils.utcnow())
+ self.context, self.instance_id = _prep_conf(timeutils.utcnow())
self.backup = models.DBBackup.create(tenant_id=self.context.tenant,
name=BACKUP_NAME,
state=BACKUP_STATE,
@@ -449,7 +450,7 @@ class PaginationTests(trove_testtools.TestCase):
def setUp(self):
super(PaginationTests, self).setUp()
util.init_db()
- self.context, self.instance_id = _prep_conf(utils.utcnow())
+ self.context, self.instance_id = _prep_conf(timeutils.utcnow())
# Create a bunch of backups
bkup_info = {
'tenant_id': self.context.tenant,
@@ -507,7 +508,7 @@ class OrderingTests(trove_testtools.TestCase):
def setUp(self):
super(OrderingTests, self).setUp()
util.init_db()
- now = utils.utcnow()
+ now = timeutils.utcnow()
self.context, self.instance_id = _prep_conf(now)
info = {
'tenant_id': self.context.tenant,
diff --git a/trove/tests/unittests/common/test_timeutils.py b/trove/tests/unittests/common/test_timeutils.py
new file mode 100644
index 00000000..6027b7c1
--- /dev/null
+++ b/trove/tests/unittests/common/test_timeutils.py
@@ -0,0 +1,129 @@
+# Copyright 2016 Tesora Inc.
+# All Rights Reserved.
+#
+# 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 datetime import datetime
+from datetime import timedelta
+from datetime import tzinfo
+
+from trove.common import timeutils
+from trove.tests.unittests import trove_testtools
+
+
+class bogus_tzinfo(tzinfo):
+ """A bogus tzinfo class"""
+ def utcoffset(self, dt):
+ return timedelta(hours=2)
+
+ def tzname(self, dt):
+ return "BOGUS"
+
+ def dst(self, dt):
+ return timedelta(hours=1)
+
+
+class invalid_tzinfo(tzinfo):
+ """A bogus tzinfo class"""
+ def utcoffset(self, dt):
+ return timedelta(hours=25)
+
+ def tzname(self, dt):
+ return "INVALID"
+
+ def dst(self, dt):
+ return timedelta(hours=25)
+
+
+class TestTroveTimeutils(trove_testtools.TestCase):
+
+ def setUp(self):
+ super(TestTroveTimeutils, self).setUp()
+
+ def tearDown(self):
+ super(TestTroveTimeutils, self).tearDown()
+
+ def test_utcnow_tz(self):
+ dt = timeutils.utcnow()
+
+ self.assertIsNone(dt.tzinfo)
+
+ def test_utcnow_aware_tz(self):
+ dt = timeutils.utcnow_aware()
+
+ self.assertEqual(timedelta(0), dt.utcoffset())
+ self.assertEqual('Z', dt.tzname())
+
+ def test_isotime(self):
+ dt = timeutils.utcnow_aware()
+
+ expected = "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
+ dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
+
+ self.assertEqual(expected, timeutils.isotime(dt))
+
+ def test_isotime_subsecond(self):
+ dt = timeutils.utcnow_aware()
+
+ expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" % (
+ dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second,
+ dt.microsecond)
+
+ self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
+
+ def test_isotime_unaware(self):
+ dt = timeutils.utcnow()
+
+ expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" % (
+ dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second,
+ dt.microsecond)
+
+ self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
+
+ def test_isotime_unaware_subsecond(self):
+ dt = timeutils.utcnow()
+
+ expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" % (
+ dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second,
+ dt.microsecond)
+
+ self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
+
+ def test_bogus_unaware(self):
+ dt = datetime.now(bogus_tzinfo())
+
+ expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06d+02:00" % (
+ dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second,
+ dt.microsecond)
+
+ self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
+
+ def test_bogus_unaware_subsecond(self):
+ dt = datetime.now(bogus_tzinfo())
+
+ expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06d+02:00" % (
+ dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second,
+ dt.microsecond)
+
+ self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
+
+ def test_throws_exception(self):
+ dt = datetime.now()
+ dt = dt.replace(tzinfo=invalid_tzinfo())
+
+ self.assertRaises(ValueError, timeutils.isotime, dt)
diff --git a/trove/tests/unittests/conductor/test_methods.py b/trove/tests/unittests/conductor/test_methods.py
index 86e6a832..56278adc 100644
--- a/trove/tests/unittests/conductor/test_methods.py
+++ b/trove/tests/unittests/conductor/test_methods.py
@@ -13,6 +13,7 @@
# under the License.
from mock import patch
+from oslo_utils import timeutils
from trove.backup import models as bkup_models
from trove.backup import state
@@ -20,7 +21,6 @@ from trove.common import exception as t_exception
from trove.common.instance import ServiceStatuses
from trove.common import utils
from trove.conductor import manager as conductor_manager
-from trove.guestagent.common import timeutils
from trove.instance import models as t_models
from trove.tests.unittests import trove_testtools
from trove.tests.unittests.util import util
@@ -151,7 +151,7 @@ class ConductorMethodTests(trove_testtools.TestCase):
build_p = {'service_status': ServiceStatuses.BUILDING.description}
iss_id = self._create_iss()
iss = self._get_iss(iss_id)
- now = timeutils.float_utcnow()
+ now = timeutils.utcnow_ts(microsecond=True)
future = now + 60
self.cond_mgr.heartbeat(None, self.instance_id, new_p, sent=now)
self.cond_mgr.heartbeat(None, self.instance_id, build_p, sent=future)
@@ -164,7 +164,7 @@ class ConductorMethodTests(trove_testtools.TestCase):
build_p = {'service_status': ServiceStatuses.BUILDING.description}
iss_id = self._create_iss()
iss = self._get_iss(iss_id)
- now = timeutils.float_utcnow()
+ now = timeutils.utcnow_ts(microsecond=True)
past = now - 60
self.cond_mgr.heartbeat(None, self.instance_id, new_p, sent=past)
self.cond_mgr.heartbeat(None, self.instance_id, build_p, sent=past)
@@ -176,7 +176,7 @@ class ConductorMethodTests(trove_testtools.TestCase):
new_name = "renamed"
bkup_id = self._create_backup(old_name)
bkup = self._get_backup(bkup_id)
- now = timeutils.float_utcnow()
+ now = timeutils.utcnow_ts(microsecond=True)
future = now + 60
self.cond_mgr.update_backup(None, self.instance_id, bkup_id,
sent=now, name=old_name)
@@ -190,7 +190,7 @@ class ConductorMethodTests(trove_testtools.TestCase):
new_name = "renamed"
bkup_id = self._create_backup(old_name)
bkup = self._get_backup(bkup_id)
- now = timeutils.float_utcnow()
+ now = timeutils.utcnow_ts(microsecond=True)
past = now - 60
self.cond_mgr.update_backup(None, self.instance_id, bkup_id,
sent=now, name=old_name)
diff --git a/trove/tests/unittests/guestagent/test_models.py b/trove/tests/unittests/guestagent/test_models.py
index 783f8e20..6e45834b 100644
--- a/trove/tests/unittests/guestagent/test_models.py
+++ b/trove/tests/unittests/guestagent/test_models.py
@@ -16,6 +16,7 @@ from datetime import datetime
from mock import Mock, MagicMock, patch
+from trove.common import timeutils
from trove.common import utils
from trove.db import models as dbmodels
from trove.db.sqlalchemy import api as dbapi
@@ -27,7 +28,7 @@ class AgentHeartBeatTest(trove_testtools.TestCase):
def setUp(self):
super(AgentHeartBeatTest, self).setUp()
self.origin_get_db_api = dbmodels.get_db_api
- self.origin_utcnow = utils.utcnow
+ self.origin_utcnow = timeutils.utcnow
self.origin_db_api_save = dbapi.save
self.origin_is_valid = dbmodels.DatabaseModelBase.is_valid
self.origin_generate_uuid = utils.generate_uuid
@@ -35,7 +36,7 @@ class AgentHeartBeatTest(trove_testtools.TestCase):
def tearDown(self):
super(AgentHeartBeatTest, self).tearDown()
dbmodels.get_db_api = self.origin_get_db_api
- utils.utcnow = self.origin_utcnow
+ timeutils.utcnow = self.origin_utcnow
dbapi.save = self.origin_db_api_save
dbmodels.DatabaseModelBase.is_valid = self.origin_is_valid
utils.generate_uuid = self.origin_generate_uuid
@@ -52,14 +53,14 @@ class AgentHeartBeatTest(trove_testtools.TestCase):
@patch('trove.db.models.DatabaseModelBase')
def test_save(self, dmb_mock):
- utils.utcnow = Mock()
+ timeutils.utcnow = Mock()
dbmodels.get_db_api = MagicMock(
return_value=dbmodels.DatabaseModelBase)
dbapi.save = Mock()
dbmodels.DatabaseModelBase.is_valid = Mock(return_value=True)
self.heartBeat = models.AgentHeartBeat()
self.heartBeat.save()
- self.assertEqual(1, utils.utcnow.call_count)
+ self.assertEqual(1, timeutils.utcnow.call_count)
def test_is_active(self):
models.AGENT_HEARTBEAT = 10000000000
diff --git a/trove/tests/unittests/taskmanager/test_models.py b/trove/tests/unittests/taskmanager/test_models.py
index 7fbba5dd..53bed50d 100644
--- a/trove/tests/unittests/taskmanager/test_models.py
+++ b/trove/tests/unittests/taskmanager/test_models.py
@@ -11,7 +11,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-import datetime
import os
from tempfile import NamedTemporaryFile
import uuid
@@ -23,7 +22,6 @@ from mock import Mock, MagicMock, patch, PropertyMock, call
from novaclient import exceptions as nova_exceptions
import novaclient.v2.flavors
import novaclient.v2.servers
-from oslo_utils import timeutils
from swiftclient.client import ClientException
from testtools.matchers import Equals, Is
@@ -39,6 +37,7 @@ from trove.common.instance import ServiceStatuses
from trove.common.notification import TroveInstanceModifyVolume
from trove.common import remote
import trove.common.template as template
+from trove.common import timeutils
from trove.common import utils
from trove.datastore import models as datastore_models
import trove.db.models
@@ -651,8 +650,8 @@ class BuiltInstanceTasksTest(trove_testtools.TestCase):
datastore_id='id-1',
flavor_id='6',
manager='mysql',
- created=datetime.datetime.utcnow(),
- updated=datetime.datetime.utcnow(),
+ created=timeutils.utcnow(),
+ updated=timeutils.utcnow(),
compute_instance_id='computeinst-id-1',
tenant_id='testresize-tenant-id',
volume_size='1',