diff --git a/swift/common/manager.py b/swift/common/manager.py index 47b275e6eb..28e9283376 100644 --- a/swift/common/manager.py +++ b/swift/common/manager.py @@ -163,6 +163,9 @@ class Manager(object): for name in server_names: self.servers.add(Server(name, run_dir)) + def __iter__(self): + return iter(self.servers) + @command def status(self, **kwargs): """display status of tracked pids for server @@ -250,6 +253,17 @@ class Manager(object): kill_wait, server) return 1 + @command + def kill(self, **kwargs): + """stop a server (no error if not running) + """ + status = self.stop(**kwargs) + kwargs['quiet'] = True + if status and not self.status(**kwargs): + # only exit error if the server is still running + return status + return 0 + @command def shutdown(self, **kwargs): """allow current requests to finish on supporting servers @@ -523,7 +537,7 @@ class Server(object): :param conf_file: path to conf_file to use as first arg :param once: boolean, add once argument to command :param wait: boolean, if true capture stdout with a pipe - :param daemon: boolean, if true ask server to log to console + :param daemon: boolean, if false ask server to log to console :returns : the pid of the spawned process """ @@ -560,6 +574,11 @@ class Server(object): for proc in self.procs: # wait for process to close its stdout output = proc.stdout.read() + if kwargs.get('once', False): + # if you don't want once to wait you can send it to the + # background on the command line, I generally just run with + # no-daemon anyway, but this is quieter + proc.wait() if output: print output start = time.time() diff --git a/test/probe/common.py b/test/probe/common.py index d49a4e8f1b..3c9192f615 100644 --- a/test/probe/common.py +++ b/test/probe/common.py @@ -15,11 +15,10 @@ from httplib import HTTPConnection import os -from os import kill, path -from signal import SIGTERM from subprocess import Popen, PIPE import sys from time import sleep, time +from collections import defaultdict from swiftclient import get_auth, head_account @@ -30,22 +29,25 @@ from swift.common.manager import Manager from test.probe import CHECK_SERVER_TIMEOUT, VALIDATE_RSYNC +def get_server_number(port, port2server): + server_number = port2server[port] + server, number = server_number[:-1], server_number[-1:] + try: + number = int(number) + except ValueError: + # probably the proxy + return server_number, None + return server, number + + def start_server(port, port2server, pids, check=True): - server = port2server[port] - if server[:-1] in ('account', 'container', 'object'): - if not path.exists('/etc/swift/%s-server/%s.conf' % - (server[:-1], server[-1])): - return None - pids[server] = Popen([ - 'swift-%s-server' % server[:-1], - '/etc/swift/%s-server/%s.conf' % (server[:-1], server[-1])]).pid - if check: - return check_server(port, port2server, pids) - else: - pids[server] = Popen(['swift-%s-server' % server, - '/etc/swift/%s-server.conf' % server]).pid - if check: - return check_server(port, port2server, pids) + server, number = get_server_number(port, port2server) + err = Manager([server]).start(number=number, wait=False) + if err: + raise Exception('unable to start %s' % ( + server if not number else '%s%s' % (server, number))) + if check: + return check_server(port, port2server, pids) return None @@ -97,10 +99,11 @@ def check_server(port, port2server, pids, timeout=CHECK_SERVER_TIMEOUT): def kill_server(port, port2server, pids): - try: - kill(pids[port2server[port]], SIGTERM) - except Exception as err: - print err + server, number = get_server_number(port, port2server) + err = Manager([server]).kill(number=number) + if err: + raise Exception('unable to kill %s' % (server if not number else + '%s%s' % (server, number))) try_until = time() + 30 while True: try: @@ -116,8 +119,7 @@ def kill_server(port, port2server, pids): def kill_servers(port2server, pids): - for port in port2server: - kill_server(port, port2server, pids) + Manager(['all']).kill() def kill_nonprimary_server(primary_nodes, port2server, pids): @@ -145,18 +147,19 @@ def get_ring(server, force_validate=None): ring.serialized_path, len(ring.devs)) # map server to config by port port_to_config = {} - for node_id in range(1, 5): - conf = readconf('/etc/swift/%s-server/%d.conf' % (server, node_id), - section_name='%s-replicator' % server) - port_to_config[int(conf['bind_port'])] = conf + for server_ in Manager([server]): + for config_path in server_.conf_files(): + conf = readconf(config_path, + section_name='%s-replicator' % server_.type) + port_to_config[int(conf['bind_port'])] = conf for dev in ring.devs: # verify server is exposing mounted device conf = port_to_config[dev['port']] for device in os.listdir(conf['devices']): if device == dev['device']: - dev_path = path.join(conf['devices'], device) - full_path = path.realpath(dev_path) - assert path.exists(full_path), \ + dev_path = os.path.join(conf['devices'], device) + full_path = os.path.realpath(dev_path) + assert os.path.exists(full_path), \ 'device %s in %s was not found (%s)' % ( device, conf['devices'], full_path) break @@ -195,21 +198,22 @@ def reset_environment(): account_ring = get_ring('account') container_ring = get_ring('container') object_ring = get_ring('object') + Manager(['main']).start(wait=False) port2server = {} - config_dict = {} for server, port in [('account', 6002), ('container', 6001), ('object', 6000)]: for number in xrange(1, 9): port2server[port + (number * 10)] = '%s%d' % (server, number) - for port in port2server: - start_server(port, port2server, pids, check=False) for port in port2server: check_server(port, port2server, pids) port2server[8080] = 'proxy' - url, token, account = start_server(8080, port2server, pids) + url, token, account = check_server(8080, port2server, pids) + config_dict = defaultdict(dict) for name in ('account', 'container', 'object'): - for server in (name, '%s-replicator' % name): - config_dict[server] = '/etc/swift/%s-server/%%d.conf' % name + for server_name in (name, '%s-replicator' % name): + for server in Manager([server_name]): + for i, conf in enumerate(server.conf_files(), 1): + config_dict[server.server][i] = conf except BaseException: try: raise @@ -226,41 +230,15 @@ def reset_environment(): def get_to_final_state(): - processes = [] - for job in ('account-replicator', 'container-replicator', - 'object-replicator'): - for number in xrange(1, 9): - if not path.exists('/etc/swift/%s-server/%d.conf' % - (job.split('-')[0], number)): - continue - processes.append(Popen([ - 'swift-%s' % job, - '/etc/swift/%s-server/%d.conf' % (job.split('-')[0], number), - 'once'])) - for process in processes: - process.wait() - processes = [] - for job in ('container-updater', 'object-updater'): - for number in xrange(1, 5): - processes.append(Popen([ - 'swift-%s' % job, - '/etc/swift/%s-server/%d.conf' % (job.split('-')[0], number), - 'once'])) - for process in processes: - process.wait() - processes = [] - for job in ('account-replicator', 'container-replicator', - 'object-replicator'): - for number in xrange(1, 9): - if not path.exists('/etc/swift/%s-server/%d.conf' % - (job.split('-')[0], number)): - continue - processes.append(Popen([ - 'swift-%s' % job, - '/etc/swift/%s-server/%d.conf' % (job.split('-')[0], number), - 'once'])) - for process in processes: - process.wait() + replicators = Manager(['account-replicator', 'container-replicator', + 'object-replicator']) + replicators.stop() + updaters = Manager(['container-updater', 'object-updater']) + updaters.stop() + + replicators.once() + updaters.once() + replicators.once() if __name__ == "__main__": diff --git a/test/probe/test_account_failures.py b/test/probe/test_account_failures.py index facbacc923..aa7713f14e 100755 --- a/test/probe/test_account_failures.py +++ b/test/probe/test_account_failures.py @@ -14,12 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from subprocess import Popen from unittest import main, TestCase from swiftclient import client from swift.common import direct_client +from swift.common.manager import Manager from test.probe.common import get_to_final_state, kill_nonprimary_server, \ kill_server, kill_servers, reset_environment, start_server @@ -136,14 +136,7 @@ class TestAccountFailures(TestCase): self.assert_(not found1) self.assert_(found2) - processes = [] - for node in xrange(1, 5): - processes.append(Popen([ - 'swift-container-updater', - self.configs['container'] % node, - 'once'])) - for process in processes: - process.wait() + Manager(['container-updater']).once() headers, containers = client.get_account(self.url, self.token) self.assertEquals(headers['x-account-container-count'], '1') self.assertEquals(headers['x-account-object-count'], '2') diff --git a/test/probe/test_container_failures.py b/test/probe/test_container_failures.py index fb1b68b693..f42dd3cee8 100755 --- a/test/probe/test_container_failures.py +++ b/test/probe/test_container_failures.py @@ -124,7 +124,7 @@ class TestContainerFailures(TestCase): node_id = (onode['port'] - 6000) / 10 device = onode['device'] hash_str = hash_path(self.account, container) - server_conf = readconf(self.configs['container'] % node_id) + server_conf = readconf(self.configs['container-server'][node_id]) devices = server_conf['app:container-server']['devices'] obj_dir = '%s/%s/containers/%s/%s/%s/' % (devices, device, opart, diff --git a/test/probe/test_empty_device_handoff.py b/test/probe/test_empty_device_handoff.py index e42a0fddb8..2841bcd425 100755 --- a/test/probe/test_empty_device_handoff.py +++ b/test/probe/test_empty_device_handoff.py @@ -18,7 +18,6 @@ import os import shutil import time -from subprocess import call from unittest import main, TestCase from uuid import uuid4 @@ -29,6 +28,7 @@ from swift.common.exceptions import ClientException from test.probe.common import kill_server, kill_servers, reset_environment,\ start_server from swift.common.utils import readconf +from swift.common.manager import Manager class TestEmptyDevice(TestCase): @@ -44,7 +44,7 @@ class TestEmptyDevice(TestCase): def _get_objects_dir(self, onode): device = onode['device'] node_id = (onode['port'] - 6000) / 10 - obj_server_conf = readconf(self.configs['object'] % node_id) + obj_server_conf = readconf(self.configs['object-server'][node_id]) devices = obj_server_conf['app:object-server']['devices'] obj_dir = '%s/%s' % (devices, device) return obj_dir @@ -143,12 +143,12 @@ class TestEmptyDevice(TestCase): another_port_num = another_onode['replication_port'] except KeyError: another_port_num = another_onode['port'] - call(['swift-object-replicator', - self.configs['object-replicator'] % - ((port_num - 6000) / 10), 'once']) - call(['swift-object-replicator', - self.configs['object-replicator'] % - ((another_port_num - 6000) / 10), 'once']) + + num = (port_num - 6000) / 10 + Manager(['object-replicator']).once(number=num) + + another_num = (another_port_num - 6000) / 10 + Manager(['object-replicator']).once(number=another_num) odata = direct_client.direct_get_object(onode, opart, self.account, container, obj)[-1] diff --git a/test/probe/test_object_async_update.py b/test/probe/test_object_async_update.py index 4b6c91b650..e48dd91ad5 100755 --- a/test/probe/test_object_async_update.py +++ b/test/probe/test_object_async_update.py @@ -14,13 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from subprocess import Popen from unittest import main, TestCase from uuid import uuid4 from swiftclient import client from swift.common import direct_client +from swift.common.manager import Manager from test.probe.common import kill_nonprimary_server, kill_server, \ kill_servers, reset_environment, start_server @@ -54,13 +54,7 @@ class TestObjectAsyncUpdate(TestCase): start_server(cnode['port'], self.port2server, self.pids) self.assert_(not direct_client.direct_get_container( cnode, cpart, self.account, container)[1]) - processes = [] - for node in xrange(1, 5): - processes.append(Popen(['swift-object-updater', - self.configs['object'] % node, - 'once'])) - for process in processes: - process.wait() + Manager(['object-updater']).once() objs = [o['name'] for o in direct_client.direct_get_container( cnode, cpart, self.account, container)[1]] self.assert_(obj in objs) diff --git a/test/probe/test_object_failures.py b/test/probe/test_object_failures.py index 339648ea4f..f8fc119fa4 100755 --- a/test/probe/test_object_failures.py +++ b/test/probe/test_object_failures.py @@ -70,7 +70,7 @@ class TestObjectFailures(TestCase): node_id = (onode['port'] - 6000) / 10 device = onode['device'] hash_str = hash_path(self.account, container, obj) - obj_server_conf = readconf(self.configs['object'] % node_id) + obj_server_conf = readconf(self.configs['object-server'][node_id]) devices = obj_server_conf['app:object-server']['devices'] obj_dir = '%s/%s/objects/%s/%s/%s/' % (devices, device, opart, diff --git a/test/probe/test_object_handoff.py b/test/probe/test_object_handoff.py index dd7b91cdae..565b2ff7cc 100755 --- a/test/probe/test_object_handoff.py +++ b/test/probe/test_object_handoff.py @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from subprocess import call, Popen from unittest import main, TestCase from uuid import uuid4 @@ -22,6 +21,7 @@ from swiftclient import client from swift.common import direct_client from swift.common.exceptions import ClientException +from swift.common.manager import Manager from test.probe.common import kill_server, kill_servers, reset_environment, \ start_server @@ -115,25 +115,19 @@ class TestObjectHandoff(TestCase): exc = err self.assertEquals(exc.http_status, 404) # Run the extra server last so it'll remove its extra partition - processes = [] for node in onodes: try: port_num = node['replication_port'] except KeyError: port_num = node['port'] - processes.append(Popen(['swift-object-replicator', - self.configs['object-replicator'] % - ((port_num - 6000) / 10), - 'once'])) - for process in processes: - process.wait() + node_id = (port_num - 6000) / 10 + Manager(['object-replicator']).once(number=node_id) try: another_port_num = another_onode['replication_port'] except KeyError: another_port_num = another_onode['port'] - call(['swift-object-replicator', - self.configs['object-replicator'] % - ((another_port_num - 6000) / 10), 'once']) + another_num = (another_port_num - 6000) / 10 + Manager(['object-replicator']).once(number=another_num) odata = direct_client.direct_get_object(onode, opart, self.account, container, obj)[-1] if odata != 'VERIFY': @@ -171,21 +165,15 @@ class TestObjectHandoff(TestCase): direct_client.direct_get_object(onode, opart, self.account, container, obj) # Run the extra server last so it'll remove its extra partition - processes = [] for node in onodes: try: port_num = node['replication_port'] except KeyError: port_num = node['port'] - processes.append(Popen(['swift-object-replicator', - self.configs['object-replicator'] % - ((port_num - 6000) / 10), - 'once'])) - for process in processes: - process.wait() - call(['swift-object-replicator', - self.configs['object-replicator'] % - ((another_port_num - 6000) / 10), 'once']) + node_id = (port_num - 6000) / 10 + Manager(['object-replicator']).once(number=node_id) + another_node_id = (another_port_num - 6000) / 10 + Manager(['object-replicator']).once(number=another_node_id) exc = None try: direct_client.direct_get_object(another_onode, opart, self.account, diff --git a/test/probe/test_replication_servers_working.py b/test/probe/test_replication_servers_working.py index 96f0de1c9c..dae315ac26 100644 --- a/test/probe/test_replication_servers_working.py +++ b/test/probe/test_replication_servers_working.py @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from subprocess import Popen from unittest import main, TestCase from uuid import uuid4 import os @@ -25,6 +24,7 @@ from swiftclient import client from test.probe.common import kill_servers, reset_environment from swift.common.utils import readconf +from swift.common.manager import Manager def collect_info(path_list): @@ -102,7 +102,7 @@ class TestReplicatorFunctions(TestCase): path_list = [] # Figure out where the devices are for node_id in range(1, 5): - conf = readconf(self.configs['object'] % node_id) + conf = readconf(self.configs['object-server'][node_id]) device_path = conf['app:object-server']['devices'] for dev in self.object_ring.devs: if dev['port'] == int(conf['app:object-server']['bind_port']): @@ -126,18 +126,9 @@ class TestReplicatorFunctions(TestCase): test_node_files_list.append(files) test_node_dir_list = dir_list[num] # Run all replicators - processes = [] - try: - for num in xrange(1, 9): - for server in ['object-replicator', - 'container-replicator', - 'account-replicator']: - if not os.path.exists(self.configs[server] % (num)): - continue - processes.append(Popen(['swift-%s' % (server), - self.configs[server] % (num), - 'forever'])) + Manager(['object-replicator', 'container-replicator', + 'account-replicator']).start() # Delete some files for directory in os.listdir(test_node): @@ -211,8 +202,8 @@ class TestReplicatorFunctions(TestCase): raise time.sleep(1) finally: - for process in processes: - process.kill() + Manager(['object-replicator', 'container-replicator', + 'account-replicator']).stop() if __name__ == '__main__': diff --git a/test/unit/common/test_manager.py b/test/unit/common/test_manager.py index 5867497442..987782d303 100644 --- a/test/unit/common/test_manager.py +++ b/test/unit/common/test_manager.py @@ -1331,6 +1331,12 @@ class TestManager(unittest.TestCase): for s in m.servers: self.assert_(str(s) in replicators) + def test_iter(self): + m = manager.Manager(['all']) + self.assertEquals(len(list(m)), len(manager.ALL_SERVERS)) + for server in m: + self.assert_(server.server in manager.ALL_SERVERS) + def test_status(self): class MockServer(object): @@ -1560,6 +1566,9 @@ class TestManager(unittest.TestCase): def stop(self, **kwargs): return self.pids + def status(self, **kwargs): + return not self.pids + def __init__(self, server_pids, run_dir=manager.RUN_DIR): self.server_pids = server_pids @@ -1593,6 +1602,14 @@ class TestManager(unittest.TestCase): m = manager.Manager(['test']) status = m.stop() self.assertEquals(status, 1) + # test kill not running + server_pids = { + 'test': [] + } + manager.Server = MockServerFactory(server_pids) + m = manager.Manager(['test']) + status = m.kill() + self.assertEquals(status, 0) # test won't die server_pids = { 'test': [None]