OpenStack Networking (Neutron)
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

329 行
12KB

  1. #
  2. # Copyright 2012 New Dream Network, LLC (DreamHost)
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. import logging
  16. from logging import handlers
  17. import os
  18. import sys
  19. import mock
  20. from neutron_lib import exceptions
  21. from neutron_lib import fixture as lib_fixtures
  22. import testtools
  23. from neutron.agent.linux import daemon
  24. from neutron.tests import base
  25. FAKE_FD = 8
  26. class FakeEntry(object):
  27. def __init__(self, name, value):
  28. setattr(self, name, value)
  29. class TestUnwatchLog(base.BaseTestCase):
  30. def setUp(self):
  31. super(TestUnwatchLog, self).setUp()
  32. self.temp_file = self.get_temp_file_path('unwatch_log_temp_file')
  33. def test_unwatch_log(self):
  34. stream_handler = logging.StreamHandler()
  35. logger = logging.Logger('fake')
  36. logger.addHandler(stream_handler)
  37. logger.addHandler(handlers.WatchedFileHandler(self.temp_file))
  38. with mock.patch('logging.getLogger', return_value=logger):
  39. daemon.unwatch_log()
  40. self.assertEqual(2, len(logger.handlers))
  41. logger.handlers.remove(stream_handler)
  42. observed = logger.handlers[0]
  43. self.assertEqual(logging.FileHandler, type(observed))
  44. self.assertEqual(self.temp_file, observed.baseFilename)
  45. class TestPrivileges(base.BaseTestCase):
  46. def test_setuid_with_name(self):
  47. with mock.patch('pwd.getpwnam', return_value=FakeEntry('pw_uid', 123)):
  48. with mock.patch('os.setuid') as setuid_mock:
  49. daemon.setuid('user')
  50. setuid_mock.assert_called_once_with(123)
  51. def test_setuid_with_id(self):
  52. with mock.patch('os.setuid') as setuid_mock:
  53. daemon.setuid('321')
  54. setuid_mock.assert_called_once_with(321)
  55. def test_setuid_fails(self):
  56. with mock.patch('os.setuid', side_effect=OSError()):
  57. with mock.patch.object(daemon.LOG, 'critical') as log_critical:
  58. self.assertRaises(exceptions.FailToDropPrivilegesExit,
  59. daemon.setuid, '321')
  60. log_critical.assert_called_once_with(mock.ANY)
  61. def test_setgid_with_name(self):
  62. with mock.patch('grp.getgrnam', return_value=FakeEntry('gr_gid', 123)):
  63. with mock.patch('os.setgid') as setgid_mock:
  64. daemon.setgid('group')
  65. setgid_mock.assert_called_once_with(123)
  66. def test_setgid_with_id(self):
  67. with mock.patch('os.setgid') as setgid_mock:
  68. daemon.setgid('321')
  69. setgid_mock.assert_called_once_with(321)
  70. def test_setgid_fails(self):
  71. with mock.patch('os.setgid', side_effect=OSError()):
  72. with mock.patch.object(daemon.LOG, 'critical') as log_critical:
  73. self.assertRaises(exceptions.FailToDropPrivilegesExit,
  74. daemon.setgid, '321')
  75. log_critical.assert_called_once_with(mock.ANY)
  76. @mock.patch.object(os, 'setgroups')
  77. @mock.patch.object(daemon, 'setgid')
  78. @mock.patch.object(daemon, 'setuid')
  79. def test_drop_no_privileges(self, mock_setuid, mock_setgid,
  80. mock_setgroups):
  81. daemon.drop_privileges()
  82. for cursor in (mock_setuid, mock_setgid, mock_setgroups):
  83. self.assertFalse(cursor.called)
  84. @mock.patch.object(os, 'geteuid', return_value=0)
  85. @mock.patch.object(os, 'setgroups')
  86. @mock.patch.object(daemon, 'setgid')
  87. @mock.patch.object(daemon, 'setuid')
  88. def _test_drop_privileges(self, setuid, setgid, setgroups,
  89. geteuid, user=None, group=None):
  90. daemon.drop_privileges(user=user, group=group)
  91. if user:
  92. setuid.assert_called_once_with(user)
  93. else:
  94. self.assertFalse(setuid.called)
  95. if group:
  96. setgroups.assert_called_once_with([])
  97. setgid.assert_called_once_with(group)
  98. else:
  99. self.assertFalse(setgroups.called)
  100. self.assertFalse(setgid.called)
  101. def test_drop_user_privileges(self):
  102. self._test_drop_privileges(user='user')
  103. def test_drop_uid_privileges(self):
  104. self._test_drop_privileges(user='321')
  105. def test_drop_group_privileges(self):
  106. self._test_drop_privileges(group='group')
  107. def test_drop_gid_privileges(self):
  108. self._test_drop_privileges(group='654')
  109. def test_drop_privileges_without_root_permissions(self):
  110. with mock.patch('os.geteuid', return_value=1):
  111. with mock.patch.object(daemon.LOG, 'critical') as log_critical:
  112. self.assertRaises(exceptions.FailToDropPrivilegesExit,
  113. daemon.drop_privileges, 'user')
  114. log_critical.assert_called_once_with(mock.ANY)
  115. class TestPidfile(base.BaseTestCase):
  116. def setUp(self):
  117. super(TestPidfile, self).setUp()
  118. self.os_p = mock.patch.object(daemon, 'os')
  119. self.os = self.os_p.start()
  120. self.os.open.return_value = FAKE_FD
  121. self.fcntl_p = mock.patch.object(daemon, 'fcntl')
  122. self.fcntl = self.fcntl_p.start()
  123. self.fcntl.flock.return_value = 0
  124. def test_init(self):
  125. self.os.O_CREAT = os.O_CREAT
  126. self.os.O_RDWR = os.O_RDWR
  127. daemon.Pidfile('thefile', sys.executable)
  128. self.os.open.assert_called_once_with('thefile', os.O_CREAT | os.O_RDWR)
  129. self.fcntl.flock.assert_called_once_with(FAKE_FD, self.fcntl.LOCK_EX |
  130. self.fcntl.LOCK_NB)
  131. def test_init_open_fail(self):
  132. self.os.open.side_effect = IOError
  133. with mock.patch.object(daemon.sys, 'stderr'):
  134. with testtools.ExpectedException(SystemExit):
  135. daemon.Pidfile('thefile', sys.executable)
  136. sys.assert_has_calls([
  137. mock.call.stderr.write(mock.ANY),
  138. mock.call.exit(1)]
  139. )
  140. def test_unlock(self):
  141. p = daemon.Pidfile('thefile', sys.executable)
  142. p.unlock()
  143. self.fcntl.flock.assert_has_calls([
  144. mock.call(FAKE_FD, self.fcntl.LOCK_EX | self.fcntl.LOCK_NB),
  145. mock.call(FAKE_FD, self.fcntl.LOCK_UN)]
  146. )
  147. def test_write(self):
  148. p = daemon.Pidfile('thefile', sys.executable)
  149. p.write(34)
  150. self.os.assert_has_calls([
  151. mock.call.ftruncate(FAKE_FD, 0),
  152. mock.call.write(FAKE_FD, b'34'),
  153. mock.call.fsync(FAKE_FD)]
  154. )
  155. def test_read(self):
  156. self.os.read.return_value = '34'
  157. p = daemon.Pidfile('thefile', sys.executable)
  158. self.assertEqual(34, p.read())
  159. def test_is_running(self):
  160. mock_open = self.useFixture(
  161. lib_fixtures.OpenFixture('/proc/34/cmdline',
  162. sys.executable)).mock_open
  163. p = daemon.Pidfile('thefile', sys.executable)
  164. with mock.patch.object(p, 'read') as read:
  165. read.return_value = 34
  166. self.assertTrue(p.is_running())
  167. mock_open.assert_called_once_with('/proc/34/cmdline', 'r')
  168. def test_is_running_uuid_true(self):
  169. mock_open = self.useFixture(
  170. lib_fixtures.OpenFixture(
  171. '/proc/34/cmdline', '{} 1234'.format(
  172. sys.executable))).mock_open
  173. p = daemon.Pidfile('thefile', sys.executable, uuid='1234')
  174. with mock.patch.object(p, 'read') as read:
  175. read.return_value = 34
  176. self.assertTrue(p.is_running())
  177. mock_open.assert_called_once_with('/proc/34/cmdline', 'r')
  178. def test_is_running_uuid_false(self):
  179. mock_open = self.useFixture(
  180. lib_fixtures.OpenFixture(
  181. '/proc/34/cmdline', '{} 1234'.format(
  182. sys.executable))).mock_open
  183. p = daemon.Pidfile('thefile', sys.executable, uuid='6789')
  184. with mock.patch.object(p, 'read') as read:
  185. read.return_value = 34
  186. self.assertFalse(p.is_running())
  187. mock_open.assert_called_once_with('/proc/34/cmdline', 'r')
  188. class TestDaemon(base.BaseTestCase):
  189. def setUp(self):
  190. super(TestDaemon, self).setUp()
  191. self.os_p = mock.patch.object(daemon, 'os')
  192. self.os = self.os_p.start()
  193. self.pidfile_p = mock.patch.object(daemon, 'Pidfile')
  194. self.pidfile = self.pidfile_p.start()
  195. def test_init(self):
  196. d = daemon.Daemon('pidfile')
  197. self.assertEqual(d.procname, sys.executable)
  198. def test_init_nopidfile(self):
  199. d = daemon.Daemon(pidfile=None)
  200. self.assertEqual(d.procname, sys.executable)
  201. self.assertFalse(self.pidfile.called)
  202. def test_fork_parent(self):
  203. self.os.fork.return_value = 1
  204. d = daemon.Daemon('pidfile')
  205. d._fork()
  206. self.os._exit.assert_called_once_with(mock.ANY)
  207. def test_fork_child(self):
  208. self.os.fork.return_value = 0
  209. d = daemon.Daemon('pidfile')
  210. self.assertIsNone(d._fork())
  211. def test_fork_error(self):
  212. self.os.fork.side_effect = OSError(1)
  213. with mock.patch.object(daemon.sys, 'stderr'):
  214. with testtools.ExpectedException(SystemExit):
  215. d = daemon.Daemon('pidfile', 'stdin')
  216. d._fork()
  217. def test_daemonize(self):
  218. self.os.devnull = '/dev/null'
  219. d = daemon.Daemon('pidfile')
  220. with mock.patch.object(d, '_fork') as fork:
  221. with mock.patch.object(daemon, 'atexit') as atexit:
  222. with mock.patch.object(daemon, 'signal') as signal:
  223. signal.SIGTERM = 15
  224. with mock.patch.object(daemon, 'sys') as sys:
  225. sys.stdin.fileno.return_value = 0
  226. sys.stdout.fileno.return_value = 1
  227. sys.stderr.fileno.return_value = 2
  228. d.daemonize()
  229. signal.signal.assert_called_once_with(15, d.handle_sigterm)
  230. atexit.register.assert_called_once_with(d.delete_pid)
  231. fork.assert_has_calls([mock.call(), mock.call()])
  232. self.os.assert_has_calls([
  233. mock.call.chdir('/'),
  234. mock.call.setsid(),
  235. mock.call.umask(0),
  236. mock.call.dup2(mock.ANY, 0),
  237. mock.call.dup2(mock.ANY, 1),
  238. mock.call.dup2(mock.ANY, 2),
  239. mock.call.getpid()]
  240. )
  241. def test_delete_pid(self):
  242. self.pidfile.return_value.__str__.return_value = 'pidfile'
  243. d = daemon.Daemon('pidfile')
  244. d.delete_pid()
  245. self.os.remove.assert_called_once_with('pidfile')
  246. def test_handle_sigterm(self):
  247. d = daemon.Daemon('pidfile')
  248. with mock.patch.object(daemon, 'sys') as sys:
  249. d.handle_sigterm(15, 1234)
  250. sys.exit.assert_called_once_with(0)
  251. def test_start(self):
  252. self.pidfile.return_value.is_running.return_value = False
  253. d = daemon.Daemon('pidfile')
  254. with mock.patch.object(d, 'daemonize') as daemonize:
  255. with mock.patch.object(d, 'run') as run:
  256. d.start()
  257. run.assert_called_once_with()
  258. daemonize.assert_called_once_with()
  259. def test_start_running(self):
  260. self.pidfile.return_value.is_running.return_value = True
  261. d = daemon.Daemon('pidfile')
  262. with mock.patch.object(daemon.sys, 'stderr'):
  263. with mock.patch.object(d, 'daemonize') as daemonize:
  264. with testtools.ExpectedException(SystemExit):
  265. d.start()
  266. self.assertFalse(daemonize.called)