Merge "Refactor timeout calculation to utility"

This commit is contained in:
Jenkins 2015-07-21 11:46:42 +00:00 committed by Gerrit Code Review
commit 0ec488ee54
6 changed files with 86 additions and 54 deletions

View File

@ -17,11 +17,31 @@ Utilities for handling ISO 8601 duration format.
import random
import re
import time
from heat.common.i18n import _
iso_duration_re = re.compile('PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$')
wallclock = time.time
class Duration(object):
'''
Note that we don't attempt to handle leap seconds or large clock
jumps here. The latter are assumed to be rare and the former
negligible in the context of the timeout. Time zone adjustments,
Daylight Savings and the like *are* handled. PEP 418 adds a proper
monotonic clock, but only in Python 3.3.
'''
def __init__(self, timeout=0):
self._endtime = wallclock() + timeout
def expired(self):
return wallclock() > self._endtime
def endtime(self):
return self._endtime
def parse_isoduration(duration):

View File

@ -14,7 +14,6 @@
import functools
import itertools
import sys
import time
import types
import eventlet
@ -26,13 +25,13 @@ from six import reraise as raise_
from heat.common.i18n import _
from heat.common.i18n import _LI
from heat.common import timeutils
LOG = logging.getLogger(__name__)
# Whether TaskRunner._sleep actually does an eventlet sleep when called.
ENABLE_SLEEP = True
wallclock = time.time
def task_description(task):
@ -68,15 +67,10 @@ class Timeout(BaseException):
message = _('%s Timed out') % six.text_type(task_runner)
super(Timeout, self).__init__(message)
# Note that we don't attempt to handle leap seconds or large clock
# jumps here. The latter are assumed to be rare and the former
# negligible in the context of the timeout. Time zone adjustments,
# Daylight Savings and the like *are* handled. PEP 418 adds a proper
# monotonic clock, but only in Python 3.3.
self._endtime = wallclock() + timeout
self._duration = timeutils.Duration(timeout)
def expired(self):
return wallclock() > self._endtime
return self._duration.expired()
def trigger(self, generator):
"""Trigger the timeout on a given generator."""
@ -107,7 +101,7 @@ class Timeout(BaseException):
def __lt__(self, other):
if not isinstance(other, Timeout):
return NotImplemented
return self._endtime < other._endtime
return self._duration.endtime() < other._duration.endtime()
def __cmp__(self, other):
return self < other

View File

@ -15,6 +15,7 @@ import contextlib
import eventlet
from heat.common import timeutils
from heat.engine import dependencies
from heat.engine import scheduler
from heat.tests import common
@ -647,16 +648,16 @@ class TaskTest(common.HeatTestCase):
self.assertTrue(runner.step())
def test_timeout(self):
st = scheduler.wallclock()
st = timeutils.wallclock()
def task():
while True:
yield
self.m.StubOutWithMock(scheduler, 'wallclock')
scheduler.wallclock().AndReturn(st)
scheduler.wallclock().AndReturn(st + 0.5)
scheduler.wallclock().AndReturn(st + 1.5)
self.m.StubOutWithMock(timeutils, 'wallclock')
timeutils.wallclock().AndReturn(st)
timeutils.wallclock().AndReturn(st + 0.5)
timeutils.wallclock().AndReturn(st + 1.5)
self.m.ReplayAll()
@ -667,7 +668,7 @@ class TaskTest(common.HeatTestCase):
self.assertRaises(scheduler.Timeout, runner.step)
def test_timeout_return(self):
st = scheduler.wallclock()
st = timeutils.wallclock()
def task():
while True:
@ -676,10 +677,10 @@ class TaskTest(common.HeatTestCase):
except scheduler.Timeout:
return
self.m.StubOutWithMock(scheduler, 'wallclock')
scheduler.wallclock().AndReturn(st)
scheduler.wallclock().AndReturn(st + 0.5)
scheduler.wallclock().AndReturn(st + 1.5)
self.m.StubOutWithMock(timeutils, 'wallclock')
timeutils.wallclock().AndReturn(st)
timeutils.wallclock().AndReturn(st + 0.5)
timeutils.wallclock().AndReturn(st + 1.5)
self.m.ReplayAll()
@ -691,7 +692,7 @@ class TaskTest(common.HeatTestCase):
self.assertFalse(runner)
def test_timeout_swallowed(self):
st = scheduler.wallclock()
st = timeutils.wallclock()
def task():
while True:
@ -701,10 +702,10 @@ class TaskTest(common.HeatTestCase):
yield
self.fail('Task still running')
self.m.StubOutWithMock(scheduler, 'wallclock')
scheduler.wallclock().AndReturn(st)
scheduler.wallclock().AndReturn(st + 0.5)
scheduler.wallclock().AndReturn(st + 1.5)
self.m.StubOutWithMock(timeutils, 'wallclock')
timeutils.wallclock().AndReturn(st)
timeutils.wallclock().AndReturn(st + 0.5)
timeutils.wallclock().AndReturn(st + 1.5)
self.m.ReplayAll()
@ -777,21 +778,21 @@ class TaskTest(common.HeatTestCase):
self.assertTrue(runner.step())
def test_cancel_grace_period(self):
st = scheduler.wallclock()
st = timeutils.wallclock()
task = DummyTask(5)
self.m.StubOutWithMock(task, 'do_step')
self.m.StubOutWithMock(scheduler.TaskRunner, '_sleep')
self.m.StubOutWithMock(scheduler, 'wallclock')
self.m.StubOutWithMock(timeutils, 'wallclock')
task.do_step(1).AndReturn(None)
task.do_step(2).AndReturn(None)
scheduler.wallclock().AndReturn(st)
scheduler.wallclock().AndReturn(st + 0.5)
timeutils.wallclock().AndReturn(st)
timeutils.wallclock().AndReturn(st + 0.5)
task.do_step(3).AndReturn(None)
scheduler.wallclock().AndReturn(st + 1.0)
timeutils.wallclock().AndReturn(st + 1.0)
task.do_step(4).AndReturn(None)
scheduler.wallclock().AndReturn(st + 1.5)
timeutils.wallclock().AndReturn(st + 1.5)
self.m.ReplayAll()
@ -808,24 +809,24 @@ class TaskTest(common.HeatTestCase):
self.assertTrue(runner.step())
def test_cancel_grace_period_before_timeout(self):
st = scheduler.wallclock()
st = timeutils.wallclock()
task = DummyTask(5)
self.m.StubOutWithMock(task, 'do_step')
self.m.StubOutWithMock(scheduler.TaskRunner, '_sleep')
self.m.StubOutWithMock(scheduler, 'wallclock')
self.m.StubOutWithMock(timeutils, 'wallclock')
scheduler.wallclock().AndReturn(st)
scheduler.wallclock().AndReturn(st + 0.1)
timeutils.wallclock().AndReturn(st)
timeutils.wallclock().AndReturn(st + 0.1)
task.do_step(1).AndReturn(None)
scheduler.wallclock().AndReturn(st + 0.2)
timeutils.wallclock().AndReturn(st + 0.2)
task.do_step(2).AndReturn(None)
scheduler.wallclock().AndReturn(st + 0.2)
scheduler.wallclock().AndReturn(st + 0.5)
timeutils.wallclock().AndReturn(st + 0.2)
timeutils.wallclock().AndReturn(st + 0.5)
task.do_step(3).AndReturn(None)
scheduler.wallclock().AndReturn(st + 1.0)
timeutils.wallclock().AndReturn(st + 1.0)
task.do_step(4).AndReturn(None)
scheduler.wallclock().AndReturn(st + 1.5)
timeutils.wallclock().AndReturn(st + 1.5)
self.m.ReplayAll()
@ -842,24 +843,24 @@ class TaskTest(common.HeatTestCase):
self.assertTrue(runner.step())
def test_cancel_grace_period_after_timeout(self):
st = scheduler.wallclock()
st = timeutils.wallclock()
task = DummyTask(5)
self.m.StubOutWithMock(task, 'do_step')
self.m.StubOutWithMock(scheduler.TaskRunner, '_sleep')
self.m.StubOutWithMock(scheduler, 'wallclock')
self.m.StubOutWithMock(timeutils, 'wallclock')
scheduler.wallclock().AndReturn(st)
scheduler.wallclock().AndReturn(st + 0.1)
timeutils.wallclock().AndReturn(st)
timeutils.wallclock().AndReturn(st + 0.1)
task.do_step(1).AndReturn(None)
scheduler.wallclock().AndReturn(st + 0.2)
timeutils.wallclock().AndReturn(st + 0.2)
task.do_step(2).AndReturn(None)
scheduler.wallclock().AndReturn(st + 0.2)
scheduler.wallclock().AndReturn(st + 0.5)
timeutils.wallclock().AndReturn(st + 0.2)
timeutils.wallclock().AndReturn(st + 0.5)
task.do_step(3).AndReturn(None)
scheduler.wallclock().AndReturn(st + 1.0)
timeutils.wallclock().AndReturn(st + 1.0)
task.do_step(4).AndReturn(None)
scheduler.wallclock().AndReturn(st + 1.5)
timeutils.wallclock().AndReturn(st + 1.5)
self.m.ReplayAll()

View File

@ -24,6 +24,7 @@ import six
from heat.common import context
from heat.common import exception
from heat.common import template_format
from heat.common import timeutils
from heat.db import api as db_api
from heat.engine.clients.os import keystone
from heat.engine.clients.os import nova
@ -955,7 +956,7 @@ class StackTest(common.HeatTestCase):
def test_stack_create_timeout(self):
self.m.StubOutWithMock(scheduler.DependencyTaskGroup, '__call__')
self.m.StubOutWithMock(scheduler, 'wallclock')
self.m.StubOutWithMock(timeutils, 'wallclock')
stk = stack.Stack(self.ctx, 's', self.tmpl)
@ -964,10 +965,10 @@ class StackTest(common.HeatTestCase):
yield
start_time = time.time()
scheduler.wallclock().AndReturn(start_time)
scheduler.wallclock().AndReturn(start_time + 1)
timeutils.wallclock().AndReturn(start_time)
timeutils.wallclock().AndReturn(start_time + 1)
scheduler.DependencyTaskGroup.__call__().AndReturn(dummy_task())
scheduler.wallclock().AndReturn(start_time + stk.timeout_secs() + 1)
timeutils.wallclock().AndReturn(start_time + stk.timeout_secs() + 1)
self.m.ReplayAll()

View File

@ -22,6 +22,7 @@ import mock
from heat.common import exception
from heat.common import heat_keystoneclient as hkc
from heat.common import template_format
from heat.common import timeutils
from heat.engine.clients.os import keystone
from heat.engine import scheduler
from heat.engine import stack
@ -379,7 +380,7 @@ class StackTest(common.HeatTestCase):
start_time = time.time()
mock_tg = self.patchobject(scheduler.DependencyTaskGroup, '__call__',
return_value=dummy_task())
mock_wallclock = self.patchobject(scheduler, 'wallclock')
mock_wallclock = self.patchobject(timeutils, 'wallclock')
mock_wallclock.side_effect = [
start_time,
start_time + 1,

View File

@ -46,6 +46,21 @@ class ISO8601UtilityTest(common.HeatTestCase):
self.assertRaises(ValueError, util.parse_isoduration, 'ABCDEFGH')
class DurationTest(common.HeatTestCase):
def setUp(self):
super(DurationTest, self).setUp()
st = util.wallclock()
mock_clock = self.patchobject(util, 'wallclock')
mock_clock.side_effect = [st, st + 0.5]
def test_duration_not_expired(self):
self.assertFalse(util.Duration(1.0).expired())
def test_duration_expired(self):
self.assertTrue(util.Duration(0.1).expired())
class RetryBackoffExponentialTest(common.HeatTestCase):
scenarios = [(