karbor/karbor/tests/unit/operationengine/engine/triggers/timetrigger/test_time_trigger_multi_nod...

283 lines
9.6 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.
from collections import namedtuple
from datetime import datetime
from datetime import timedelta
import eventlet
import functools
import heapq
from unittest import mock
from oslo_config import cfg
from oslo_utils import uuidutils
from karbor import context as karbor_context
from karbor import exception
from karbor.services.operationengine.engine.triggers.timetrigger import \
time_trigger_multi_node as tt
from karbor.services.operationengine.engine.triggers.timetrigger import utils
from karbor.tests import base
TriggerExecution = namedtuple('TriggerExecution',
['execution_time', 'id', 'trigger_id'])
class FakeTimeFormat(object):
def __init__(self, start_time, pattern):
super(FakeTimeFormat, self).__init__()
@classmethod
def check_time_format(cls, pattern):
pass
def compute_next_time(self, current_time):
return current_time + timedelta(seconds=0.5)
def get_min_interval(self):
return cfg.CONF.min_interval
class FakeExecutor(object):
def __init__(self):
super(FakeExecutor, self).__init__()
self._ops = {}
def execute_operation(self, operation_id, triggered_time,
expect_start_time, window):
if operation_id not in self._ops:
self._ops[operation_id] = 0
self._ops[operation_id] += 1
eventlet.sleep(0.5)
class FakeTimeTrigger(object):
@classmethod
def get_time_format(cls, *args, **kwargs):
return FakeTimeFormat
class FakeDb(object):
def __init__(self):
self._db = []
def trigger_execution_get_next(self, context):
if len(self._db) == 0:
return None
return self._db[0]
def trigger_execution_create(self, context, trigger_id, time):
element = TriggerExecution(time, uuidutils.generate_uuid(), trigger_id)
heapq.heappush(self._db, element)
def trigger_execution_update(self, context, id, current_time, new_time):
for idx, element in enumerate(self._db):
if element.id == id:
if element.execution_time != current_time:
return False
self._db[idx] = TriggerExecution(new_time, element.id,
element.trigger_id)
break
heapq.heapify(self._db)
return True
def trigger_execution_delete(self, context, id, trigger_id):
removed_ids = []
for idx, element in enumerate(self._db):
if (id and element.id == id) or (trigger_id and
element.trigger_id == trigger_id):
removed_ids.append(idx)
for idx in reversed(removed_ids):
self._db.pop(idx)
heapq.heapify(self._db)
return len(removed_ids)
def time_trigger_test(func):
@functools.wraps(func)
@mock.patch.object(tt, 'db', FakeDb())
@mock.patch.object(karbor_context, 'get_admin_context', lambda: None)
@mock.patch.object(utils, 'get_time_format_class',
FakeTimeTrigger.get_time_format)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
class TimeTriggerTestCase(base.TestCase):
_tid = 0
_default_executor = FakeExecutor()
def setUp(self):
super(TimeTriggerTestCase, self).setUp()
self._set_configuration()
def test_check_configuration(self):
self._set_configuration(10, 20, 30)
self.assertRaisesRegex(exception.InvalidInput,
"Configurations of time trigger are invalid",
tt.TimeTrigger.check_configuration)
self._set_configuration()
@time_trigger_test
def test_check_trigger_property_start_time(self):
trigger_property = {
"pattern": "",
"start_time": ""
}
self.assertRaisesRegex(exception.InvalidInput,
"The trigger\'s start time is unknown",
tt.TimeTrigger.check_trigger_definition,
trigger_property)
trigger_property['start_time'] = 'abc'
self.assertRaisesRegex(exception.InvalidInput,
"The format of trigger .* is not correct",
tt.TimeTrigger.check_trigger_definition,
trigger_property)
trigger_property['start_time'] = 123
self.assertRaisesRegex(exception.InvalidInput,
"The trigger .* is not an instance of string",
tt.TimeTrigger.check_trigger_definition,
trigger_property)
@mock.patch.object(FakeTimeFormat, 'get_min_interval')
@time_trigger_test
def test_check_trigger_property_interval(self, get_min_interval):
get_min_interval.return_value = 0
trigger_property = {
"start_time": '2016-8-18 01:03:04'
}
self.assertRaisesRegex(exception.InvalidInput,
"The interval of two adjacent time points .*",
tt.TimeTrigger.check_trigger_definition,
trigger_property)
@time_trigger_test
def test_check_trigger_property_window(self):
trigger_property = {
"window": "abc",
"start_time": '2016-8-18 01:03:04'
}
self.assertRaisesRegex(exception.InvalidInput,
"The trigger window.* is not integer",
tt.TimeTrigger.check_trigger_definition,
trigger_property)
trigger_property['window'] = 1000
self.assertRaisesRegex(exception.InvalidInput,
"The trigger windows .* must be between .*",
tt.TimeTrigger.check_trigger_definition,
trigger_property)
@time_trigger_test
def test_check_trigger_property_end_time(self):
trigger_property = {
"window": 15,
"start_time": '2016-8-18 01:03:04',
"end_time": "abc"
}
self.assertRaisesRegex(exception.InvalidInput,
"The format of trigger .* is not correct",
tt.TimeTrigger.check_trigger_definition,
trigger_property)
@time_trigger_test
def test_register_operation(self):
trigger = self._generate_trigger()
operation_id = "1"
trigger.register_operation(operation_id)
eventlet.sleep(2)
self.assertGreaterEqual(self._default_executor._ops[operation_id], 1)
self.assertRaisesRegex(exception.ScheduledOperationExist,
"The operation_id.* is exist",
trigger.register_operation,
operation_id)
@time_trigger_test
def test_unregister_operation(self):
trigger = self._generate_trigger()
operation_id = "2"
trigger.register_operation(operation_id)
self.assertIn(operation_id, trigger._operation_ids)
trigger.unregister_operation(operation_id)
self.assertNotIn(trigger._id, trigger._operation_ids)
@time_trigger_test
def test_update_trigger_property(self):
trigger = self._generate_trigger()
trigger_property = {
"pattern": "",
"window": 15,
"start_time": '2016-8-18 01:03:04',
"end_time": datetime.utcnow(),
}
self.assertRaisesRegex(exception.InvalidInput,
".*Can not find the first run time",
trigger.update_trigger_property,
trigger_property)
@time_trigger_test
def test_update_trigger_property_success(self):
trigger = self._generate_trigger()
trigger.register_operation('7')
eventlet.sleep(0.2)
trigger_property = {
"pattern": "",
"window": 15,
"start_time": datetime.utcnow(),
"end_time": ''
}
with mock.patch.object(FakeTimeFormat, 'compute_next_time') as c:
c.return_value = datetime.utcnow() + timedelta(seconds=20)
trigger.update_trigger_property(trigger_property)
def _generate_trigger(self, end_time=None):
if not end_time:
end_time = datetime.utcnow() + timedelta(seconds=1)
trigger_property = {
"pattern": "",
"window": 15,
"start_time": datetime.utcnow(),
"end_time": end_time
}
return tt.TimeTrigger(
uuidutils.generate_uuid(),
trigger_property,
self._default_executor,
)
def _set_configuration(self, min_window=15,
max_window=30, min_interval=60, poll_interval=1):
self.override_config('min_interval', min_interval)
self.override_config('min_window_time', min_window)
self.override_config('max_window_time', max_window)
self.override_config('trigger_poll_interval', poll_interval)