Add simple unittest for utils.CommaJoin
[ganeti-local] / test / ganeti.jqueue_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2010 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Script for testing ganeti.jqueue"""
23
24 import os
25 import sys
26 import unittest
27 import tempfile
28 import shutil
29 import errno
30
31 from ganeti import constants
32 from ganeti import utils
33 from ganeti import errors
34 from ganeti import jqueue
35
36 import testutils
37
38
39 class _FakeJob:
40   def __init__(self, job_id, status):
41     self.id = job_id
42     self._status = status
43     self._log = []
44
45   def SetStatus(self, status):
46     self._status = status
47
48   def AddLogEntry(self, msg):
49     self._log.append((len(self._log), msg))
50
51   def CalcStatus(self):
52     return self._status
53
54   def GetInfo(self, fields):
55     result = []
56
57     for name in fields:
58       if name == "status":
59         result.append(self._status)
60       else:
61         raise Exception("Unknown field")
62
63     return result
64
65   def GetLogEntries(self, newer_than):
66     assert newer_than is None or newer_than >= 0
67
68     if newer_than is None:
69       return self._log
70
71     return self._log[newer_than:]
72
73
74 class TestJobChangesChecker(unittest.TestCase):
75   def testStatus(self):
76     job = _FakeJob(9094, constants.JOB_STATUS_QUEUED)
77     checker = jqueue._JobChangesChecker(["status"], None, None)
78     self.assertEqual(checker(job), ([constants.JOB_STATUS_QUEUED], []))
79
80     job.SetStatus(constants.JOB_STATUS_RUNNING)
81     self.assertEqual(checker(job), ([constants.JOB_STATUS_RUNNING], []))
82
83     job.SetStatus(constants.JOB_STATUS_SUCCESS)
84     self.assertEqual(checker(job), ([constants.JOB_STATUS_SUCCESS], []))
85
86     # job.id is used by checker
87     self.assertEqual(job.id, 9094)
88
89   def testStatusWithPrev(self):
90     job = _FakeJob(12807, constants.JOB_STATUS_QUEUED)
91     checker = jqueue._JobChangesChecker(["status"],
92                                         [constants.JOB_STATUS_QUEUED], None)
93     self.assert_(checker(job) is None)
94
95     job.SetStatus(constants.JOB_STATUS_RUNNING)
96     self.assertEqual(checker(job), ([constants.JOB_STATUS_RUNNING], []))
97
98   def testFinalStatus(self):
99     for status in constants.JOBS_FINALIZED:
100       job = _FakeJob(2178711, status)
101       checker = jqueue._JobChangesChecker(["status"], [status], None)
102       # There won't be any changes in this status, hence it should signal
103       # a change immediately
104       self.assertEqual(checker(job), ([status], []))
105
106   def testLog(self):
107     job = _FakeJob(9094, constants.JOB_STATUS_RUNNING)
108     checker = jqueue._JobChangesChecker(["status"], None, None)
109     self.assertEqual(checker(job), ([constants.JOB_STATUS_RUNNING], []))
110
111     job.AddLogEntry("Hello World")
112     (job_info, log_entries) = checker(job)
113     self.assertEqual(job_info, [constants.JOB_STATUS_RUNNING])
114     self.assertEqual(log_entries, [[0, "Hello World"]])
115
116     checker2 = jqueue._JobChangesChecker(["status"], job_info, len(log_entries))
117     self.assert_(checker2(job) is None)
118
119     job.AddLogEntry("Foo Bar")
120     job.SetStatus(constants.JOB_STATUS_ERROR)
121
122     (job_info, log_entries) = checker2(job)
123     self.assertEqual(job_info, [constants.JOB_STATUS_ERROR])
124     self.assertEqual(log_entries, [[1, "Foo Bar"]])
125
126     checker3 = jqueue._JobChangesChecker(["status"], None, None)
127     (job_info, log_entries) = checker3(job)
128     self.assertEqual(job_info, [constants.JOB_STATUS_ERROR])
129     self.assertEqual(log_entries, [[0, "Hello World"], [1, "Foo Bar"]])
130
131
132 class TestJobChangesWaiter(unittest.TestCase):
133   def setUp(self):
134     self.tmpdir = tempfile.mkdtemp()
135     self.filename = utils.PathJoin(self.tmpdir, "job-1")
136     utils.WriteFile(self.filename, data="")
137
138   def tearDown(self):
139     shutil.rmtree(self.tmpdir)
140
141   def _EnsureNotifierClosed(self, notifier):
142     try:
143       os.fstat(notifier._fd)
144     except EnvironmentError, err:
145       self.assertEqual(err.errno, errno.EBADF)
146     else:
147       self.fail("File descriptor wasn't closed")
148
149   def testClose(self):
150     for wait in [False, True]:
151       waiter = jqueue._JobFileChangesWaiter(self.filename)
152       try:
153         if wait:
154           waiter.Wait(0.001)
155       finally:
156         waiter.Close()
157
158       # Ensure file descriptor was closed
159       self._EnsureNotifierClosed(waiter._notifier)
160
161   def testChangingFile(self):
162     waiter = jqueue._JobFileChangesWaiter(self.filename)
163     try:
164       self.assertFalse(waiter.Wait(0.1))
165       utils.WriteFile(self.filename, data="changed")
166       self.assert_(waiter.Wait(60))
167     finally:
168       waiter.Close()
169
170     self._EnsureNotifierClosed(waiter._notifier)
171
172   def testChangingFile2(self):
173     waiter = jqueue._JobChangesWaiter(self.filename)
174     try:
175       self.assertFalse(waiter._filewaiter)
176       self.assert_(waiter.Wait(0.1))
177       self.assert_(waiter._filewaiter)
178
179       # File waiter is now used, but there have been no changes
180       self.assertFalse(waiter.Wait(0.1))
181       utils.WriteFile(self.filename, data="changed")
182       self.assert_(waiter.Wait(60))
183     finally:
184       waiter.Close()
185
186     self._EnsureNotifierClosed(waiter._filewaiter._notifier)
187
188
189 class TestWaitForJobChangesHelper(unittest.TestCase):
190   def setUp(self):
191     self.tmpdir = tempfile.mkdtemp()
192     self.filename = utils.PathJoin(self.tmpdir, "job-2614226563")
193     utils.WriteFile(self.filename, data="")
194
195   def tearDown(self):
196     shutil.rmtree(self.tmpdir)
197
198   def _LoadWaitingJob(self):
199     return _FakeJob(2614226563, constants.JOB_STATUS_WAITLOCK)
200
201   def _LoadLostJob(self):
202     return None
203
204   def testNoChanges(self):
205     wfjc = jqueue._WaitForJobChangesHelper()
206
207     # No change
208     self.assertEqual(wfjc(self.filename, self._LoadWaitingJob, ["status"],
209                           [constants.JOB_STATUS_WAITLOCK], None, 0.1),
210                      constants.JOB_NOTCHANGED)
211
212     # No previous information
213     self.assertEqual(wfjc(self.filename, self._LoadWaitingJob,
214                           ["status"], None, None, 1.0),
215                      ([constants.JOB_STATUS_WAITLOCK], []))
216
217   def testLostJob(self):
218     wfjc = jqueue._WaitForJobChangesHelper()
219     self.assert_(wfjc(self.filename, self._LoadLostJob,
220                       ["status"], None, None, 1.0) is None)
221
222
223 class TestEncodeOpError(unittest.TestCase):
224   def test(self):
225     encerr = jqueue._EncodeOpError(errors.LockError("Test 1"))
226     self.assert_(isinstance(encerr, tuple))
227     self.assertRaises(errors.LockError, errors.MaybeRaise, encerr)
228
229     encerr = jqueue._EncodeOpError(errors.GenericError("Test 2"))
230     self.assert_(isinstance(encerr, tuple))
231     self.assertRaises(errors.GenericError, errors.MaybeRaise, encerr)
232
233     encerr = jqueue._EncodeOpError(NotImplementedError("Foo"))
234     self.assert_(isinstance(encerr, tuple))
235     self.assertRaises(errors.OpExecError, errors.MaybeRaise, encerr)
236
237     encerr = jqueue._EncodeOpError("Hello World")
238     self.assert_(isinstance(encerr, tuple))
239     self.assertRaises(errors.OpExecError, errors.MaybeRaise, encerr)
240
241
242 if __name__ == "__main__":
243   testutils.GanetiTestProgram()