1
0
mirror of https://gerrit.ovirt.org/vdsm synced 2026-02-05 12:46:23 +01:00
Files
vdsm/tests/jobs_test.py
Nir Soffer db675aad6c tests: Remove useless wait_for_job() helper
Maybe it was useful for some test in the past, but there is no test that
needs it, since job is always done after calling job.run().

Change-Id: I9e302e6c2042ddf4bdcca9c3b4c3cfd2a48c7461
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
2019-01-03 18:50:27 -05:00

374 lines
11 KiB
Python

# Copyright 2015-2016 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Refer to the README and COPYING files for full details of the license
#
from __future__ import absolute_import
from __future__ import division
import threading
import uuid
from vdsm.common import exception, response
from vdsm import jobs
from fakelib import FakeNotifier
from fakelib import FakeScheduler
from monkeypatch import MonkeyPatchScope
from testlib import VdsmTestCase, expandPermutations, permutations
from testlib import make_config
from testlib import start_thread
class TestingJob(jobs.Job):
_JOB_TYPE = 'testing'
def __init__(self, status=jobs.STATUS.PENDING, exception=None):
jobs.Job.__init__(self, str(uuid.uuid4()))
self._aborted = False
self._status = status
self._exception = exception
def _abort(self):
self._aborted = True
def _run(self):
assert(self.status == jobs.STATUS.RUNNING)
if self._exception:
raise self._exception
class FooJob(TestingJob):
_JOB_TYPE = 'foo'
class BarJob(TestingJob):
_JOB_TYPE = 'bar'
class AutodeleteJob(TestingJob):
autodelete = True
class ProgressingJob(jobs.Job):
def __init__(self):
jobs.Job.__init__(self, str(uuid.uuid4()))
self._progress = None
@property
def progress(self):
return self._progress
@progress.setter
def progress(self, value):
self._progress = value
class StuckJob(TestingJob):
def __init__(self):
TestingJob.__init__(self)
self.event_running = threading.Event()
self.event_aborted = threading.Event()
def _run(self):
self.event_running.set()
self.event_aborted.wait(1)
raise exception.ActionStopped()
def _abort(self):
self.event_aborted.set()
@expandPermutations
class JobsTests(VdsmTestCase):
TIMEOUT = 1
def setUp(self):
self.scheduler = FakeScheduler()
self.notifier = FakeNotifier()
jobs.start(self.scheduler, self.notifier)
def tearDown(self):
jobs._clear()
def run_job(self, job):
self.assertEqual(jobs.STATUS.PENDING, job.status)
job.run()
def test_job_initial_state(self):
job = TestingJob()
self.assertEqual(jobs.STATUS.PENDING, job.status)
self.assertEqual('', job.description)
self.assertEqual('testing', job.job_type)
def test_job_info(self):
job = TestingJob()
self.assertEqual({'id': job.id,
'status': jobs.STATUS.PENDING,
'job_type': 'testing',
'description': ''},
job.info())
def test_add_job(self):
job = TestingJob()
jobs.add(job)
self.assertEqual(1, len(jobs._jobs))
def test_add_existing_job(self):
job = TestingJob()
jobs.add(job)
self.assertRaises(jobs.JobExistsError, jobs.add, job)
def test_get_job(self):
job = TestingJob()
jobs.add(job)
self.assertEqual(job.id, jobs.get(job.id).id)
def test_get_unknown_job(self):
self.assertRaises(jobs.NoSuchJob, jobs.get, 'foo')
def test_get_jobs_info_empty(self):
self.assertEqual({}, jobs.info())
def test_get_jobs_info_any(self):
foo = FooJob()
jobs.add(foo)
bar = BarJob()
jobs.add(bar)
self.assertEqual({foo.id: foo.info(), bar.id: bar.info()},
jobs.info())
def test_get_jobs_info_by_type(self):
foo = FooJob()
jobs.add(foo)
bar = BarJob()
jobs.add(bar)
self.assertEqual({bar.id: bar.info()},
jobs.info(job_type=bar.job_type))
def test_get_jobs_info_by_uuid_single(self):
foo = FooJob()
jobs.add(foo)
bar = BarJob()
jobs.add(bar)
self.assertEqual({foo.id: foo.info()},
jobs.info(job_ids=[foo.id]))
def test_get_jobs_info_by_uuid_multi(self):
foo = FooJob()
jobs.add(foo)
bar = BarJob()
jobs.add(bar)
self.assertEqual({foo.id: foo.info(), bar.id: bar.info()},
jobs.info(job_ids=[foo.id, bar.id]))
def test_get_jobs_info_by_type_and_uuid(self):
foo = FooJob()
jobs.add(foo)
bar = BarJob()
jobs.add(bar)
self.assertEqual({}, jobs.info(job_type=bar.job_type,
job_ids=[foo.id]))
@permutations([
[jobs.STATUS.PENDING, jobs.STATUS.ABORTED, False],
[jobs.STATUS.RUNNING, jobs.STATUS.ABORTING, True],
])
def test_abort_job(self, status, aborted_status, did_abort):
job = TestingJob(status)
jobs.add(job)
jobs.abort(job.id)
self.assertEqual(aborted_status, job.status)
self.assertEqual(did_abort, job._aborted)
def test_abort_running_job(self):
job = StuckJob()
jobs.add(job)
t = start_thread(job.run)
job.event_running.wait(1)
self.assertEqual(jobs.STATUS.RUNNING, job.status)
jobs.abort(job.id)
t.join()
self.assertEqual(jobs.STATUS.ABORTED, job.status)
self.validate_event_sent(job)
@permutations([
[jobs.STATUS.ABORTED, jobs.JobNotActive.name],
[jobs.STATUS.DONE, jobs.JobNotActive.name],
[jobs.STATUS.FAILED, jobs.JobNotActive.name]
])
def test_abort_from_invalid_state(self, status, err):
job = TestingJob(status)
jobs.add(job)
res = jobs.abort(job.id)
self.assertEqual(response.error(err), res)
def test_abort_unknown_job(self):
self.assertEqual(response.error(jobs.NoSuchJob.name),
jobs.abort('foo'))
def test_abort_not_supported(self):
job = jobs.Job(str(uuid.uuid4()))
job._status = jobs.STATUS.RUNNING
jobs.add(job)
self.assertEqual(response.error(jobs.AbortNotSupported.name),
jobs.abort(job.id))
@permutations([
[jobs.STATUS.PENDING, True],
[jobs.STATUS.RUNNING, True],
[jobs.STATUS.ABORTED, False],
[jobs.STATUS.DONE, False],
[jobs.STATUS.FAILED, False]
])
def test_job_active(self, status, active):
job = TestingJob(status)
self.assertEqual(active, job.active)
@permutations([
[jobs.STATUS.ABORTED],
[jobs.STATUS.DONE],
[jobs.STATUS.FAILED]
])
def test_delete_inactive_job(self, status):
job = TestingJob(status)
jobs.add(job)
self.assertEqual(response.success(), jobs.delete(job.id))
@permutations([
[jobs.STATUS.PENDING],
[jobs.STATUS.RUNNING],
])
def test_delete_active_job(self, status):
job = TestingJob(status)
jobs.add(job)
self.assertEqual(response.error(jobs.JobNotDone.name),
jobs.delete(job.id))
def test_delete_unknown_job(self):
self.assertEqual(response.error(jobs.NoSuchJob.name),
jobs.delete('foo'))
def test_job_get_progress(self):
job = ProgressingJob()
# Job queued or initializing, no progress yet
self.assertNotIn('progress', job.info())
# Job running
for i in [0, 42, 100]:
job.progress = i
self.assertEqual(i, job.info()['progress'])
def test_job_get_error(self):
job = TestingJob()
self.assertIsNone(job.error)
self.assertNotIn('error', job.info())
error = exception.GeneralException()
job._error = error
self.assertEqual(job.error, error)
self.assertEqual(error.info(), job.info()['error'])
def test_job_repr(self):
job = TestingJob()
rep = repr(job)
self.assertIn("TestingJob", rep)
self.assertIn("id=%s" % job.id, rep)
self.assertIn("status=pending", rep)
self.assertNotIn("progress=", rep)
def test_job_repr_with_progress(self):
job = ProgressingJob()
job.progress = 32
rep = repr(job)
self.assertIn("progress=32%", rep)
def test_running_states(self):
job = TestingJob()
self.run_job(job)
self.assertEqual(jobs.STATUS.DONE, job.status)
self.validate_event_sent(job)
@permutations([
[jobs.STATUS.RUNNING],
[jobs.STATUS.DONE],
[jobs.STATUS.FAILED]
])
def test_run_from_invalid_state(self, state):
job = TestingJob(state)
self.assertRaises(RuntimeError, job.run)
self.validate_event_not_sent()
def test_default_exception(self):
message = "testing failure"
job = TestingJob(exception=Exception(message))
self.run_job(job)
self.assertEqual(jobs.STATUS.FAILED, job.status)
self.assertIsInstance(job.error, exception.GeneralException)
self.assertIn(message, str(job.error))
self.validate_event_sent(job)
def test_vdsm_exception(self):
job = TestingJob(exception=exception.VdsmException())
self.run_job(job)
self.assertEqual(jobs.STATUS.FAILED, job.status)
self.assertIsInstance(job.error, exception.VdsmException)
self.validate_event_sent(job)
def _verify_autodelete(self, job, expected_delay):
self.assertEqual(1, len(self.scheduler.calls))
delay, callable = self.scheduler.calls[0]
self.assertEqual(expected_delay, delay)
self.assertIn(job.id, jobs.info())
callable()
self.assertNotIn(job.id, jobs.info())
@permutations(((None, ), (Exception(),)))
def test_autodelete_when_finished(self, error):
cfg = make_config([('jobs', 'autodelete_delay', '10')])
job = AutodeleteJob(exception=error)
jobs.add(job)
with MonkeyPatchScope([(jobs, 'config', cfg)]):
self.run_job(job)
self._verify_autodelete(job, 10)
def test_autodelete_when_aborted(self):
cfg = make_config([('jobs', 'autodelete_delay', '10')])
job = AutodeleteJob()
jobs.add(job)
with MonkeyPatchScope([(jobs, 'config', cfg)]):
job.abort()
self._verify_autodelete(job, 10)
def test_autodelete_disabled(self):
cfg = make_config([('jobs', 'autodelete_delay', '-1')])
job = AutodeleteJob()
jobs.add(job)
with MonkeyPatchScope([(jobs, 'config', cfg)]):
self.run_job(job)
self.assertEqual(0, len(self.scheduler.calls))
def validate_event_sent(self, job):
expected_calls = [('|jobs|status|%s' % job.id, job.info())]
self.assertEqual(self.notifier.calls, expected_calls)
def validate_event_not_sent(self):
self.assertEqual(self.notifier.calls, [])