# Copyright (c) 2015-2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from contextlib import contextmanager import errno import unittest from unittest import mock from storlets.sbus import command as sbus_cmd from storlets.sbus.client import SBusResponse from storlets.sbus.client.exceptions import SBusClientSendError from storlets.agent.daemon_factory.server import SDaemonError, \ StorletDaemonFactory from tests.unit import FakeLogger from tests.unit.agent.common import test_server class DummyDatagram(object): def __init__(self, prms=None): self.params = prms or {} class TestStorletDaemonFactory(unittest.TestCase): # Class paths used in mock.patch base_path = 'storlets.agent.daemon_factory.server' os_environ_path = base_path + '.os.environ' os_path_isfile_path = base_path + '.os.path.isfile' os_access_path = base_path + '.os.access' os_kill_path = base_path + '.os.kill' os_waitpid_path = base_path + '.os.waitpid' subprocess_popen_path = base_path + '.subprocess.Popen' time_sleep_path = base_path + '.time.sleep' @contextmanager def _mock_sbus_client(self, method): sbusclient_path = self.base_path + '.SBusClient' with mock.patch('.'.join([sbusclient_path, method])) as _method: yield _method def setUp(self): self.logger = FakeLogger() self.pipe_path = 'path/to/pipe' self.container_id = 'contid' self.dfactory = StorletDaemonFactory(self.pipe_path, self.logger, self.container_id) def test_get_jvm_args(self): dummy_env = {'CLASSPATH': '/default/classpath', 'LD_LIBRARY_PATH': '/default/ld/library/path'} with mock.patch(self.os_environ_path, dummy_env): pargs, env = self.dfactory.get_jvm_args( 'java', 'path/to/storlet/a', 'Storlet-1.0.jar', 1, 'path/to/uds/a', 'DEBUG') self.assertEqual( ['/usr/bin/java', 'org.openstack.storlet.daemon.SDaemon', 'Storlet-1.0.jar', 'path/to/uds/a', 'DEBUG', '1', self.container_id], pargs) self.assertIn('CLASSPATH', env) self.assertEqual( ['/default/classpath', '/usr/local/lib/storlets/java/*', '/usr/local/lib/storlets/java/', 'path/to/storlet/a'], env['CLASSPATH'].split(':')) self.assertIn('LD_LIBRARY_PATH', env) self.assertEqual( ['/default/ld/library/path', '/usr/local/lib/storlets', '/usr/local/lib/storlets/java'], env['LD_LIBRARY_PATH'].split(':')) def test_get_python_args(self): self._test_get_python_args(None, '3') self._test_get_python_args('3.10', '3.10') self._test_get_python_args('3', '3') def _test_get_python_args(self, version, expected): dummy_env = {'PYTHONPATH': '/default/pythonpath'} with mock.patch(self.os_environ_path, dummy_env): pargs, env = self.dfactory.get_python_args( 'python', 'path/to/storlet', 'test_storlet.TestStorlet', 1, 'path/to/uds', 'DEBUG', version) self.assertEqual( ['/usr/bin/python%s' % expected, '/usr/local/libexec/storlets/storlets-daemon', 'test_storlet.TestStorlet', 'path/to/uds', 'DEBUG', '1', self.container_id], pargs) self.assertEqual( {'PYTHONPATH': '/default/pythonpath:' '/var/lib/storlets/test_storlet.TestStorlet'}, env) def test_spawn_subprocess(self): self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a'} class FakePopenObject(object): def __init__(self, pid): self.pid = pid self.stderr = mock.MagicMock() self.stdout = mock.MagicMock() with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.os_access_path) as access, \ mock.patch(self.subprocess_popen_path) as popen, \ mock.patch(self.time_sleep_path), \ mock.patch(self.os_waitpid_path) as waitpid, \ self._mock_sbus_client('ping') as ping: isfile.return_value = True access.return_velue = True popen.side_effect = [FakePopenObject(1000), FakePopenObject(1001)] waitpid.return_value = 0, 0 ping.return_value = SBusResponse(True, 'OK') self.dfactory.spawn_subprocess( ['arg0', 'argv1', 'argv2'], {'envk0': 'envv0'}, 'storleta') self.assertEqual(('arg0',), isfile.call_args[0]) self.assertEqual((1000, 1), waitpid.call_args[0]) self.assertEqual({'storleta': 1000}, self.dfactory.storlet_name_to_pid) with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.os_access_path) as access, \ mock.patch(self.subprocess_popen_path) as popen, \ mock.patch(self.time_sleep_path), \ mock.patch(self.os_waitpid_path) as waitpid, \ self._mock_sbus_client('ping') as ping: isfile.return_value = True access.return_velue = True popen.side_effect = [FakePopenObject(1000), FakePopenObject(1001)] waitpid.return_value = 0, 0 ping.return_value = SBusResponse(False, 'NG') with self.assertRaises(SDaemonError): self.dfactory.spawn_subprocess( ['arg0', 'argv1', 'argv2'], {'envk0': 'envv0'}, 'storleta') self.assertEqual(('arg0',), isfile.call_args[0]) self.assertEqual((1000, 1), waitpid.call_args[0]) self.assertEqual({'storleta': 1000}, self.dfactory.storlet_name_to_pid) with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.os_access_path) as access, \ mock.patch(self.subprocess_popen_path) as popen, \ mock.patch(self.time_sleep_path), \ mock.patch(self.os_waitpid_path) as waitpid: isfile.return_value = True access.return_velue = True popen.side_effect = [FakePopenObject(1000), FakePopenObject(1001)] waitpid.return_value = 1000, -1 with self.assertRaises(SDaemonError): self.dfactory.spawn_subprocess( ['arg0', 'argv1', 'argv2'], {'envk0': 'envv0'}, 'storleta') self.assertEqual(('arg0',), isfile.call_args[0]) self.assertEqual((1000, 1), waitpid.call_args[0]) with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.subprocess_popen_path) as popen: isfile.return_value = False with self.assertRaises(SDaemonError): self.dfactory.spawn_subprocess( ['arg0', 'argv1', 'argv2'], {'envk0': 'envv0'}, 'storleta') self.assertEqual(('arg0',), isfile.call_args[0]) popen.assert_not_called() with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.os_access_path) as access, \ mock.patch(self.subprocess_popen_path) as popen: isfile.return_value = True access.return_value = False with self.assertRaises(SDaemonError): self.dfactory.spawn_subprocess( ['arg0', 'argv1', 'argv2'], {'envk0': 'envv0'}, 'storleta') self.assertEqual(('arg0',), isfile.call_args[0]) popen.assert_not_called() with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.os_access_path) as access, \ mock.patch(self.subprocess_popen_path) as popen: isfile.return_value = True access.return_value = False popen.side_effect = OSError() with self.assertRaises(SDaemonError): self.dfactory.spawn_subprocess( ['arg0', 'argv1', 'argv2'], {'envk0': 'envv0'}, 'storleta') self.assertEqual(('arg0',), isfile.call_args[0]) with mock.patch(self.os_path_isfile_path) as isfile: with self.assertRaises(SDaemonError): self.dfactory.spawn_subprocess([], {}, 'storleta') self.assertEqual(0, isfile.call_count) def test_wait_for_daemon_to_initialize(self): self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a'} with self._mock_sbus_client('ping') as ping, \ mock.patch(self.time_sleep_path): ping.return_value = SBusResponse(True, 'OK') self.assertTrue( self.dfactory.wait_for_daemon_to_initialize('storleta')) self.assertEqual(1, ping.call_count) with self._mock_sbus_client('ping') as ping, \ mock.patch(self.time_sleep_path): ping.return_value = SBusResponse(False, 'NG') self.assertFalse( self.dfactory.wait_for_daemon_to_initialize('storleta')) self.assertEqual( self.dfactory.NUM_OF_TRIES_PINGING_STARTING_DAEMON, ping.call_count) self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a', 'storletb': 'path/to/uds/b'} with self._mock_sbus_client('ping') as ping, \ mock.patch(self.time_sleep_path): ping.side_effect = SBusClientSendError() self.assertFalse( self.dfactory.wait_for_daemon_to_initialize('storleta')) self.assertEqual( self.dfactory.NUM_OF_TRIES_PINGING_STARTING_DAEMON, ping.call_count) def test_process_start_daemon(self): # Not running self.dfactory.storlet_name_to_pid = {} self.dfactory.storlet_name_to_pipe_name = {} class FakePopenObject(object): def __init__(self, pid): self.pid = pid self.stderr = mock.MagicMock() self.stdout = mock.MagicMock() with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.os_access_path) as access, \ mock.patch(self.subprocess_popen_path) as popen, \ mock.patch(self.time_sleep_path), \ mock.patch(self.os_waitpid_path) as waitpid, \ self._mock_sbus_client('ping') as ping: isfile.return_value = True access.return_value = True popen.side_effect = [FakePopenObject(1000), FakePopenObject(1001)] waitpid.return_value = 0, 0 ping.return_value = SBusResponse(True, 'OK') self.assertTrue(self.dfactory.process_start_daemon( 'java', 'path/to/storlet/a', 'storleta', 1, 'path/to/uds/a', 'TRACE')) self.assertEqual({'storleta': 'path/to/uds/a'}, self.dfactory.storlet_name_to_pipe_name) # Already running self.dfactory.storlet_name_to_pid = {'storleta': 1000} self.dfactory.storlet_name_to_pipe_name = {'storleta': 'path/to/uds/a'} with mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 0, 0 self.assertFalse(self.dfactory.process_start_daemon( 'java', 'path/to/storlet/a', 'storleta', 1, 'path/to/uds/a', 'TRACE')) # Unsupported language with self.assertRaises(SDaemonError): self.dfactory.process_start_daemon( 'foo', 'path/to/storlet/a', 'storleta', 1, 'path/to/uds/a', 'TRACE') def test_get_process_status_by_name(self): self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 0, 0 self.assertTrue( self.dfactory.get_process_status_by_name('storleta')) self.assertEqual(1, waitpid.call_count) self.assertEqual((1000, 1), waitpid.call_args[0]) self.assertFalse( self.dfactory.get_process_status_by_name('storletc')) def test_get_process_status_by_pid(self): with mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 0, 0 self.assertTrue( self.dfactory.get_process_status_by_pid(1000, 'storleta')) self.assertEqual(1, waitpid.call_count) self.assertEqual((1000, 1), waitpid.call_args[0]) with mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 1000, 0 self.assertFalse( self.dfactory.get_process_status_by_pid(1000, 'storleta')) self.assertEqual(1, waitpid.call_count) self.assertEqual((1000, 1), waitpid.call_args[0]) with mock.patch(self.os_waitpid_path) as waitpid: waitpid.side_effect = OSError(errno.ESRCH, '') self.assertFalse( self.dfactory.get_process_status_by_pid(1000, 'storleta')) self.assertEqual(1, waitpid.call_count) self.assertEqual((1000, 1), waitpid.call_args[0]) with mock.patch(self.os_waitpid_path) as waitpid: waitpid.side_effect = OSError(errno.EPERM, '') exc_pattern = ('^No permission to access the storlet daemon' ' storleta$') with self.assertRaisesRegex(SDaemonError, exc_pattern): self.dfactory.get_process_status_by_pid(1000, 'storleta') self.assertEqual(1, waitpid.call_count) self.assertEqual((1000, 1), waitpid.call_args[0]) with mock.patch(self.os_waitpid_path) as waitpid: waitpid.side_effect = OSError() exc_pattern = '^Unknown error$' with self.assertRaisesRegex(SDaemonError, exc_pattern): self.dfactory.get_process_status_by_pid(1000, 'storleta') self.assertEqual(1, waitpid.call_count) self.assertEqual((1000, 1), waitpid.call_args[0]) def test_process_kill(self): # Success self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 1000, 0 self.assertEqual((1000, 0), self.dfactory.process_kill('storleta')) self.assertEqual(1, kill.call_count) self.assertEqual(1, waitpid.call_count) self.assertEqual({'storletb': 1001}, self.dfactory.storlet_name_to_pid) # When failed to send kill to the storlet daemon self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: kill.side_effect = OSError() with self.assertRaises(SDaemonError): self.dfactory.process_kill('storleta') self.assertEqual(1, kill.call_count) self.assertEqual(0, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) # When failed to wait self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: waitpid.side_effect = OSError() with self.assertRaises(SDaemonError): self.dfactory.process_kill('storleta') self.assertEqual(1, kill.call_count) self.assertEqual(1, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) # if the storlet daemon is not recognised self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: with self.assertRaises(SDaemonError): self.dfactory.process_kill('storletc') self.assertEqual(0, kill.call_count) self.assertEqual(0, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) def test_process_kill_all(self): # Success self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: waitpid.side_effect = [(1000, 0), (1001, 0)] self.dfactory.process_kill_all() self.assertEqual(2, kill.call_count) self.assertEqual(2, waitpid.call_count) self.assertEqual({}, self.dfactory.storlet_name_to_pid) # Success (no processes) self.dfactory.storlet_name_to_pid = {} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: self.dfactory.process_kill_all() self.assertEqual(0, kill.call_count) self.assertEqual(0, waitpid.call_count) self.assertEqual({}, self.dfactory.storlet_name_to_pid) # Failure (try_all = True) self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: kill.side_effect = OSError() exc_pattern = '^Failed to kill some storlet daemons: .*' with self.assertRaisesRegex(SDaemonError, exc_pattern) as e: self.dfactory.process_kill_all() self.assertIn('storleta', str(e.exception)) self.assertIn('storletb', str(e.exception)) self.assertEqual(2, kill.call_count) self.assertEqual(0, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) # Failure (try_all = False) self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path) as waitpid: kill.side_effect = OSError() exc_pattern = ('^Failed to send kill signal to the storlet daemon ' 'storlet[a-b]$') with self.assertRaisesRegex(SDaemonError, exc_pattern): self.dfactory.process_kill_all(False) self.assertEqual(1, kill.call_count) self.assertEqual(0, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) def test_shutdown_all_processes(self): # Success self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a', 'storletb': 'path/to/uds/b'} with self._mock_sbus_client('halt') as halt, \ mock.patch(self.os_waitpid_path): halt.return_value = SBusResponse(True, 'OK') terminated = self.dfactory.shutdown_all_processes() self.assertEqual(2, len(terminated)) self.assertIn('storleta', terminated) self.assertIn('storletb', terminated) self.assertEqual({}, self.dfactory.storlet_name_to_pid) # Failure (try_all = True) self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'patha', 'storletb': 'pathb'} with self._mock_sbus_client('halt') as halt, \ mock.patch(self.os_waitpid_path) as waitpid: halt.side_effect = SBusClientSendError() exc_pattern = '^Failed to shutdown some storlet daemons: .*' with self.assertRaisesRegex(SDaemonError, exc_pattern) as e: self.dfactory.shutdown_all_processes() self.assertIn('storleta', str(e.exception)) self.assertIn('storletb', str(e.exception)) self.assertEqual(2, halt.call_count) self.assertEqual(0, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) # Failure (try_all = False) self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'patha', 'storletb': 'pathb'} with self._mock_sbus_client('halt') as halt, \ mock.patch(self.os_waitpid_path) as waitpid: halt.side_effect = SBusClientSendError() exc_pattern = ('^Failed to send halt command to the storlet ' 'daemon storlet[a-b]$') with self.assertRaisesRegex(SDaemonError, exc_pattern): self.dfactory.shutdown_all_processes(False) self.assertEqual(1, halt.call_count) self.assertEqual(0, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) def test_shutdown_process(self): # Success self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a', 'storletb': 'path/to/uds/b'} with self._mock_sbus_client('halt') as halt, \ mock.patch(self.os_waitpid_path): halt.return_value = SBusResponse(True, 'OK') self.dfactory.shutdown_process('storleta') self.assertEqual({'storletb': 1001}, self.dfactory.storlet_name_to_pid) # Failed to send a command to the storlet daemon self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a', 'storletb': 'path/to/uds/b'} with self._mock_sbus_client('halt') as halt, \ mock.patch(self.os_waitpid_path) as waitpid: halt.side_effect = SBusClientSendError() with self.assertRaises(SDaemonError): self.dfactory.shutdown_process('storleta') self.assertEqual(0, waitpid.call_count) self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) # Failed to wait self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a', 'storletb': 'path/to/uds/b'} with self._mock_sbus_client('halt') as halt, \ mock.patch(self.os_waitpid_path) as waitpid: halt.return_value = SBusResponse(True, 'OK') waitpid.side_effect = OSError() with self.assertRaises(SDaemonError): self.dfactory.shutdown_process('storleta') self.assertEqual({'storleta': 1000, 'storletb': 1001}, self.dfactory.storlet_name_to_pid) # If the storlet is not found in pid mapping self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a', 'storletb': 'path/to/uds/b'} with self.assertRaises(SDaemonError): self.dfactory.shutdown_process('storletc') def test_start_daemon(self): prms = {'daemon_language': 'java', 'storlet_path': 'path/to/storlet/a', 'storlet_name': 'storleta', 'pool_size': 1, 'uds_path': 'path/to/uds/a', 'log_level': 'TRACE'} # Not running self.dfactory.storlet_name_to_pid = {} self.dfactory.storlet_name_to_pipe_name = {} class FakePopenObject(object): def __init__(self, pid): self.pid = pid self.stderr = mock.MagicMock() self.stdout = mock.MagicMock() with mock.patch(self.os_path_isfile_path) as isfile, \ mock.patch(self.os_access_path) as access, \ mock.patch(self.subprocess_popen_path) as popen, \ mock.patch(self.time_sleep_path), \ mock.patch(self.os_waitpid_path) as waitpid, \ self._mock_sbus_client('ping') as ping, \ self._mock_sbus_client('start_daemon') as start_daemon: isfile.return_value = True access.return_value = True popen.side_effect = [FakePopenObject(1000), FakePopenObject(1001)] waitpid.return_value = 0, 0 ping.return_value = SBusResponse(True, 'OK') start_daemon.return_value = SBusResponse(True, 'OK') ret = self.dfactory.start_daemon(DummyDatagram(prms)) self.assertTrue(ret.status) self.assertEqual('OK', ret.message) self.assertTrue(ret.iterable) # Already running self.dfactory.storlet_name_to_pid = {'storleta': 1000} self.dfactory.storlet_name_to_pipe_name = {'storleta': 'path/to/uds/a'} with mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 0, 0 ret = self.dfactory.start_daemon(DummyDatagram(prms)) self.assertTrue(ret.status) self.assertEqual( 'The storlet daemon storleta is already running', ret.message) self.assertTrue(ret.iterable) # Unsupported language prms['daemon_language'] = 'foo' ret = self.dfactory.start_daemon(DummyDatagram(prms)) self.assertFalse(ret.status) self.assertEqual('Got unsupported daemon language: foo', ret.message) self.assertTrue(ret.iterable) def test_stop_daemon(self): # Success self.dfactory.storlet_name_to_pid = \ {'storleta': 1000} with mock.patch(self.os_kill_path), \ mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 1000, 0 resp = self.dfactory.stop_daemon( DummyDatagram({'storlet_name': 'storleta'})) self.assertTrue(resp.status) self.assertEqual('The storlet daemon storleta is stopped', resp.message) self.assertTrue(resp.iterable) # Failure self.dfactory.storlet_name_to_pid = \ {'storleta': 1000} with mock.patch(self.os_kill_path) as kill, \ mock.patch(self.os_waitpid_path): kill.side_effect = OSError('ERROR') resp = self.dfactory.stop_daemon( DummyDatagram({'storlet_name': 'storleta'})) self.assertFalse(resp.status) self.assertEqual( 'Failed to send kill signal to the storlet daemon storleta', resp.message) self.assertTrue(resp.iterable) def test_daemon_status(self): self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 0, 0 resp = self.dfactory.daemon_status( DummyDatagram({'storlet_name': 'storleta'})) self.assertTrue(resp.status) self.assertEqual('The storlet daemon storleta seems to be OK', resp.message) self.assertTrue(resp.iterable) with mock.patch(self.os_waitpid_path) as waitpid: waitpid.return_value = 1000, 0 resp = self.dfactory.daemon_status( DummyDatagram({'storlet_name': 'storleta'})) self.assertFalse(resp.status) self.assertEqual('No running storlet daemons for storleta', resp.message) self.assertTrue(resp.iterable) with mock.patch(self.os_waitpid_path) as waitpid: waitpid.side_effect = OSError() resp = self.dfactory.daemon_status( DummyDatagram({'storlet_name': 'storleta'})) self.assertFalse(resp.status) self.assertEqual('Unknown error', resp.message) self.assertTrue(resp.iterable) def test_halt(self): self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} self.dfactory.storlet_name_to_pipe_name = \ {'storleta': 'path/to/uds/a', 'storletb': 'path/to/uds/b'} with self._mock_sbus_client('halt') as halt, \ mock.patch(self.os_waitpid_path): halt.return_value = SBusResponse(True, 'OK') resp = self.dfactory.halt(DummyDatagram()) self.assertTrue(resp.status) self.assertEqual( 'Stopped all storlet daemons. Terminating.', resp.message) self.assertFalse(resp.iterable) def test_stop_daemons(self): # Success self.dfactory.storlet_name_to_pid = \ {'storleta': 1000, 'storletb': 1001} with mock.patch(self.os_kill_path), \ mock.patch(self.os_waitpid_path) as waitpid: waitpid.side_effect = [(1000, 0), (1001, 0)] resp = self.dfactory.stop_daemons(DummyDatagram()) self.assertTrue(resp.status) self.assertEqual('OK', resp.message) self.assertFalse(resp.iterable) class TestSBusServerMain(test_server.TestSBusServerMain): def _get_test_server(self): return StorletDaemonFactory(self.sbus_path, self.logger, 'contid') def test_main_loop_successful_stop(self): # SBUS_CMD_HALT is for working to stop requested from # storlet_middleware self._test_main_loop_stop(sbus_cmd.SBUS_CMD_HALT) def test_main_loop_timeout(self): self._test_main_loop_timeout() if __name__ == '__main__': unittest.main()