1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2012 New Dream Network, LLC (DreamHost)
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6
# not use this file except in compliance with the License. You may obtain
7
# a copy of the License at
9
# http://www.apache.org/licenses/LICENSE-2.0
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
# License for the specific language governing permissions and limitations
17
# @author: Mark McClain, DreamHost
22
import unittest2 as unittest
24
from quantum.agent.linux import daemon
29
class TestPidfile(unittest.TestCase):
31
self.os_p = mock.patch.object(daemon, 'os')
32
self.os = self.os_p.start()
33
self.os.open.return_value = FAKE_FD
35
self.fcntl_p = mock.patch.object(daemon, 'fcntl')
36
self.fcntl = self.fcntl_p.start()
37
self.fcntl.flock.return_value = 0
44
self.os.O_CREAT = os.O_CREAT
45
self.os.O_RDWR = os.O_RDWR
47
p = daemon.Pidfile('thefile', 'python')
48
self.os.open.assert_called_once_with('thefile', os.O_CREAT | os.O_RDWR)
49
self.fcntl.flock.assert_called_once_with(FAKE_FD, self.fcntl.LOCK_EX)
51
def test_init_open_fail(self):
52
self.os.open.side_effect = IOError
54
with mock.patch.object(daemon.sys, 'stderr') as stderr:
55
with self.assertRaises(SystemExit):
56
p = daemon.Pidfile('thefile', 'python')
57
sys.assert_has_calls([
58
mock.call.stderr.write(mock.ANY),
62
def test_unlock(self):
63
p = daemon.Pidfile('thefile', 'python')
65
self.fcntl.flock.assert_has_calls([
66
mock.call(FAKE_FD, self.fcntl.LOCK_EX),
67
mock.call(FAKE_FD, self.fcntl.LOCK_UN)]
71
p = daemon.Pidfile('thefile', 'python')
74
self.os.assert_has_calls([
75
mock.call.ftruncate(FAKE_FD, 0),
76
mock.call.write(FAKE_FD, '34'),
77
mock.call.fsync(FAKE_FD)]
81
self.os.read.return_value = '34'
82
p = daemon.Pidfile('thefile', 'python')
83
self.assertEqual(34, p.read())
85
def test_is_running(self):
86
with mock.patch('quantum.agent.linux.utils.execute') as execute:
87
execute.return_value = 'python'
88
p = daemon.Pidfile('thefile', 'python')
90
with mock.patch.object(p, 'read') as read:
91
read.return_value = 34
92
self.assertTrue(p.is_running())
94
execute.assert_called_once_with(
95
['cat', '/proc/34/cmdline'], 'sudo')
98
class TestDaemon(unittest.TestCase):
100
self.os_p = mock.patch.object(daemon, 'os')
101
self.os = self.os_p.start()
103
self.pidfile_p = mock.patch.object(daemon, 'Pidfile')
104
self.pidfile = self.pidfile_p.start()
107
self.pidfile_p.stop()
111
d = daemon.Daemon('pidfile')
112
self.assertEqual(d.procname, 'python')
114
def test_fork_parent(self):
115
self.os.fork.return_value = 1
116
with self.assertRaises(SystemExit):
117
d = daemon.Daemon('pidfile')
120
def test_fork_child(self):
121
self.os.fork.return_value = 0
122
d = daemon.Daemon('pidfile')
123
self.assertIsNone(d._fork())
125
def test_fork_error(self):
126
self.os.fork.side_effect = lambda: OSError(1)
127
with mock.patch.object(daemon.sys, 'stderr') as stderr:
128
with self.assertRaises(SystemExit):
129
d = daemon.Daemon('pidfile', 'stdin')
132
def test_daemonize(self):
133
d = daemon.Daemon('pidfile')
134
with mock.patch.object(d, '_fork') as fork:
135
with mock.patch.object(daemon, 'atexit') as atexit:
136
with mock.patch.object(daemon, 'sys') as sys:
137
sys.stdin.fileno.return_value = 0
138
sys.stdout.fileno.return_value = 1
139
sys.stderr.fileno.return_value = 2
141
atexit.register.assert_called_once_with(d.delete_pid)
143
fork.assert_has_calls([mock.call(), mock.call()])
145
self.os.assert_has_calls([
146
mock.call.chdir('/'),
149
mock.call.dup2(mock.ANY, 0),
150
mock.call.dup2(mock.ANY, 1),
151
mock.call.dup2(mock.ANY, 2),
155
def test_delete_pid(self):
156
self.pidfile.return_value.__str__.return_value = 'pidfile'
157
d = daemon.Daemon('pidfile')
159
self.os.remove.assert_called_once_with('pidfile')
161
def test_start(self):
162
self.pidfile.return_value.is_running.return_value = False
163
d = daemon.Daemon('pidfile')
165
with mock.patch.object(d, 'daemonize') as daemonize:
166
with mock.patch.object(d, 'run') as run:
168
run.assert_called_once_with()
169
daemonize.assert_called_once_with()
171
def test_start_running(self):
172
self.pidfile.return_value.is_running.return_value = True
173
d = daemon.Daemon('pidfile')
175
with mock.patch.object(daemon.sys, 'stderr') as stderr:
176
with mock.patch.object(d, 'daemonize') as daemonize:
177
with self.assertRaises(SystemExit):
179
self.assertFalse(daemonize.called)