2016-11-01 11:21:41 +00:00
|
|
|
|
|
|
|
import unittest
|
2018-11-14 14:10:56 +00:00
|
|
|
from unittest import case
|
2016-11-01 11:21:41 +00:00
|
|
|
import time
|
|
|
|
import logging
|
|
|
|
|
|
|
|
from teuthology.orchestra.run import CommandFailedError
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class CephTestCase(unittest.TestCase):
|
|
|
|
"""
|
|
|
|
For test tasks that want to define a structured set of
|
|
|
|
tests implemented in python. Subclass this with appropriate
|
|
|
|
helpers for the subsystem you're testing.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Environment references
|
|
|
|
mounts = None
|
|
|
|
fs = None
|
2017-07-12 15:41:11 +00:00
|
|
|
recovery_fs = None
|
2016-11-06 10:59:08 +00:00
|
|
|
ceph_cluster = None
|
2016-11-01 11:21:41 +00:00
|
|
|
mds_cluster = None
|
|
|
|
mgr_cluster = None
|
|
|
|
ctx = None
|
|
|
|
|
|
|
|
mon_manager = None
|
|
|
|
|
2018-11-14 14:10:56 +00:00
|
|
|
# Declarative test requirements: subclasses should override these to indicate
|
|
|
|
# their special needs. If not met, tests will be skipped.
|
|
|
|
REQUIRE_MEMSTORE = False
|
|
|
|
|
2016-11-17 11:03:49 +00:00
|
|
|
def setUp(self):
|
|
|
|
self.ceph_cluster.mon_manager.raw_cluster_cmd("log",
|
|
|
|
"Starting test {0}".format(self.id()))
|
|
|
|
|
2018-11-14 14:10:56 +00:00
|
|
|
if self.REQUIRE_MEMSTORE:
|
|
|
|
objectstore = self.ceph_cluster.get_config("osd_objectstore", "osd")
|
|
|
|
if objectstore != "memstore":
|
|
|
|
# You certainly *could* run this on a real OSD, but you don't want to sit
|
|
|
|
# here for hours waiting for the test to fill up a 1TB drive!
|
|
|
|
raise case.SkipTest("Require `memstore` OSD backend (test " \
|
|
|
|
"would take too long on full sized OSDs")
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-11-17 11:03:49 +00:00
|
|
|
def tearDown(self):
|
|
|
|
self.ceph_cluster.mon_manager.raw_cluster_cmd("log",
|
|
|
|
"Ended test {0}".format(self.id()))
|
|
|
|
|
2018-09-24 10:18:56 +00:00
|
|
|
def assert_cluster_log(self, expected_pattern, invert_match=False,
|
|
|
|
timeout=10, watch_channel=None):
|
2016-11-01 11:21:41 +00:00
|
|
|
"""
|
|
|
|
Context manager. Assert that during execution, or up to 5 seconds later,
|
|
|
|
the Ceph cluster log emits a message matching the expected pattern.
|
|
|
|
|
2018-09-24 10:18:56 +00:00
|
|
|
:param expected_pattern: A string that you expect to see in the log output
|
|
|
|
:type expected_pattern: str
|
|
|
|
:param watch_channel: Specifies the channel to be watched. This can be
|
|
|
|
'cluster', 'audit', ...
|
|
|
|
:type watch_channel: str
|
2016-11-01 11:21:41 +00:00
|
|
|
"""
|
|
|
|
|
2016-11-06 10:59:08 +00:00
|
|
|
ceph_manager = self.ceph_cluster.mon_manager
|
2016-11-01 11:21:41 +00:00
|
|
|
|
|
|
|
class ContextManager(object):
|
|
|
|
def match(self):
|
|
|
|
found = expected_pattern in self.watcher_process.stdout.getvalue()
|
|
|
|
if invert_match:
|
|
|
|
return not found
|
|
|
|
|
|
|
|
return found
|
|
|
|
|
|
|
|
def __enter__(self):
|
2018-09-24 10:18:56 +00:00
|
|
|
self.watcher_process = ceph_manager.run_ceph_w(watch_channel)
|
2016-11-01 11:21:41 +00:00
|
|
|
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
|
|
if not self.watcher_process.finished:
|
|
|
|
# Check if we got an early match, wait a bit if we didn't
|
|
|
|
if self.match():
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
log.debug("No log hits yet, waiting...")
|
|
|
|
# Default monc tick interval is 10s, so wait that long and
|
|
|
|
# then some grace
|
|
|
|
time.sleep(5 + timeout)
|
|
|
|
|
|
|
|
self.watcher_process.stdin.close()
|
|
|
|
try:
|
|
|
|
self.watcher_process.wait()
|
|
|
|
except CommandFailedError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if not self.match():
|
|
|
|
log.error("Log output: \n{0}\n".format(self.watcher_process.stdout.getvalue()))
|
|
|
|
raise AssertionError("Expected log message not found: '{0}'".format(expected_pattern))
|
|
|
|
|
|
|
|
return ContextManager()
|
|
|
|
|
|
|
|
def wait_for_health(self, pattern, timeout):
|
|
|
|
"""
|
|
|
|
Wait until 'ceph health' contains messages matching the pattern
|
|
|
|
"""
|
|
|
|
def seen_health_warning():
|
2016-11-06 10:59:08 +00:00
|
|
|
health = self.ceph_cluster.mon_manager.get_mon_health()
|
2017-07-11 03:39:31 +00:00
|
|
|
codes = [s for s in health['checks']]
|
2017-08-03 22:45:11 +00:00
|
|
|
summary_strings = [s[1]['summary']['message'] for s in health['checks'].iteritems()]
|
2016-11-01 11:21:41 +00:00
|
|
|
if len(summary_strings) == 0:
|
|
|
|
log.debug("Not expected number of summary strings ({0})".format(summary_strings))
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
for ss in summary_strings:
|
|
|
|
if pattern in ss:
|
|
|
|
return True
|
2017-07-11 03:39:31 +00:00
|
|
|
if pattern in codes:
|
|
|
|
return True
|
2016-11-01 11:21:41 +00:00
|
|
|
|
|
|
|
log.debug("Not found expected summary strings yet ({0})".format(summary_strings))
|
|
|
|
return False
|
|
|
|
|
|
|
|
self.wait_until_true(seen_health_warning, timeout)
|
|
|
|
|
|
|
|
def wait_for_health_clear(self, timeout):
|
|
|
|
"""
|
|
|
|
Wait until `ceph health` returns no messages
|
|
|
|
"""
|
|
|
|
def is_clear():
|
2016-11-06 10:59:08 +00:00
|
|
|
health = self.ceph_cluster.mon_manager.get_mon_health()
|
2017-07-11 03:39:31 +00:00
|
|
|
return len(health['checks']) == 0
|
2016-11-01 11:21:41 +00:00
|
|
|
|
|
|
|
self.wait_until_true(is_clear, timeout)
|
|
|
|
|
|
|
|
def wait_until_equal(self, get_fn, expect_val, timeout, reject_fn=None):
|
|
|
|
period = 5
|
|
|
|
elapsed = 0
|
|
|
|
while True:
|
|
|
|
val = get_fn()
|
|
|
|
if val == expect_val:
|
|
|
|
return
|
|
|
|
elif reject_fn and reject_fn(val):
|
|
|
|
raise RuntimeError("wait_until_equal: forbidden value {0} seen".format(val))
|
|
|
|
else:
|
|
|
|
if elapsed >= timeout:
|
|
|
|
raise RuntimeError("Timed out after {0} seconds waiting for {1} (currently {2})".format(
|
|
|
|
elapsed, expect_val, val
|
|
|
|
))
|
|
|
|
else:
|
|
|
|
log.debug("wait_until_equal: {0} != {1}, waiting...".format(val, expect_val))
|
|
|
|
time.sleep(period)
|
|
|
|
elapsed += period
|
|
|
|
|
|
|
|
log.debug("wait_until_equal: success")
|
|
|
|
|
2018-02-28 22:11:34 +00:00
|
|
|
@classmethod
|
|
|
|
def wait_until_true(cls, condition, timeout):
|
2016-11-01 11:21:41 +00:00
|
|
|
period = 5
|
|
|
|
elapsed = 0
|
|
|
|
while True:
|
|
|
|
if condition():
|
2016-11-10 22:45:19 +00:00
|
|
|
log.debug("wait_until_true: success in {0}s".format(elapsed))
|
2016-11-01 11:21:41 +00:00
|
|
|
return
|
|
|
|
else:
|
|
|
|
if elapsed >= timeout:
|
2016-11-10 22:45:19 +00:00
|
|
|
raise RuntimeError("Timed out after {0}s".format(elapsed))
|
2016-11-01 11:21:41 +00:00
|
|
|
else:
|
|
|
|
log.debug("wait_until_true: waiting...")
|
|
|
|
time.sleep(period)
|
|
|
|
elapsed += period
|
|
|
|
|
2016-11-10 22:45:19 +00:00
|
|
|
|