# 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. """Tests for swift.cli.info""" import os import unittest import cPickle as pickle import mock from cStringIO import StringIO from contextlib import closing from gzip import GzipFile from shutil import rmtree from tempfile import mkdtemp from test.unit import patch_policies from swift.common import ring, utils from swift.common.swob import Request from swift.cli.info import print_db_info_metadata, print_ring_locations, \ print_info, InfoSystemExit from swift.account.server import AccountController from swift.container.server import ContainerController @patch_policies class TestCliInfo(unittest.TestCase): def setUp(self): self.orig_hp = utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX utils.HASH_PATH_PREFIX = 'info' utils.HASH_PATH_SUFFIX = 'info' self.testdir = os.path.join(mkdtemp(), 'tmp_test_cli_info') utils.mkdirs(self.testdir) rmtree(self.testdir) utils.mkdirs(os.path.join(self.testdir, 'sda1')) utils.mkdirs(os.path.join(self.testdir, 'sda1', 'tmp')) utils.mkdirs(os.path.join(self.testdir, 'sdb1')) utils.mkdirs(os.path.join(self.testdir, 'sdb1', 'tmp')) self.account_ring_path = os.path.join(self.testdir, 'account.ring.gz') with closing(GzipFile(self.account_ring_path, 'wb')) as f: pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]], [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.1', 'port': 42}, {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.2', 'port': 43}], 30), f) self.container_ring_path = os.path.join(self.testdir, 'container.ring.gz') with closing(GzipFile(self.container_ring_path, 'wb')) as f: pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]], [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.3', 'port': 42}, {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.4', 'port': 43}], 30), f) def tearDown(self): utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX = self.orig_hp rmtree(os.path.dirname(self.testdir)) def assertRaisesMessage(self, exc, msg, func, *args, **kwargs): try: func(*args, **kwargs) except Exception, e: self.assertEqual(msg, str(e)) self.assertTrue(isinstance(e, exc), "Expected %s, got %s" % (exc, type(e))) def test_print_db_info_metadata(self): self.assertRaisesMessage(ValueError, 'Wrong DB type', print_db_info_metadata, 't', {}, {}) self.assertRaisesMessage(ValueError, 'DB info is None', print_db_info_metadata, 'container', None, {}) self.assertRaisesMessage(ValueError, 'Info is incomplete', print_db_info_metadata, 'container', {}, {}) info = dict( account='acct', created_at=100.1, put_timestamp=106.3, delete_timestamp=107.9, container_count='3', object_count='20', bytes_used='42') info['hash'] = 'abaddeadbeefcafe' info['id'] = 'abadf100d0ddba11' md = {'x-account-meta-mydata': ('swift', '0000000000.00000'), 'x-other-something': ('boo', '0000000000.00000')} out = StringIO() with mock.patch('sys.stdout', out): print_db_info_metadata('account', info, md) exp_out = '''Path: /acct Account: acct Account Hash: dc5be2aa4347a22a0fee6bc7de505b47 Metadata: Created at: 1970-01-01 00:01:40.100000 (100.1) Put Timestamp: 1970-01-01 00:01:46.300000 (106.3) Delete Timestamp: 1970-01-01 00:01:47.900000 (107.9) Container Count: 3 Object Count: 20 Bytes Used: 42 Chexor: abaddeadbeefcafe UUID: abadf100d0ddba11 X-Other-Something: boo No system metadata found in db file User Metadata: {'mydata': 'swift'}''' self.assertEquals(sorted(out.getvalue().strip().split('\n')), sorted(exp_out.split('\n'))) info = dict( account='acct', container='cont', created_at='0000000100.10000', put_timestamp='0000000106.30000', delete_timestamp='0000000107.90000', object_count='20', bytes_used='42', reported_put_timestamp='0000010106.30000', reported_delete_timestamp='0000010107.90000', reported_object_count='20', reported_bytes_used='42', x_container_foo='bar', x_container_bar='goo', storage_policy_index=1) info['hash'] = 'abaddeadbeefcafe' info['id'] = 'abadf100d0ddba11' md = {'x-container-sysmeta-mydata': ('swift', '0000000000.00000')} out = StringIO() with mock.patch('sys.stdout', out): print_db_info_metadata('container', info, md) exp_out = '''Path: /acct/cont Account: acct Container: cont Container Hash: d49d0ecbb53be1fcc49624f2f7c7ccae Metadata: Created at: 1970-01-01 00:01:40.100000 (0000000100.10000) Put Timestamp: 1970-01-01 00:01:46.300000 (0000000106.30000) Delete Timestamp: 1970-01-01 00:01:47.900000 (0000000107.90000) Object Count: 20 Bytes Used: 42 Storage Policy: unu (1) Reported Put Timestamp: 1970-01-01 02:48:26.300000 (0000010106.30000) Reported Delete Timestamp: 1970-01-01 02:48:27.900000 (0000010107.90000) Reported Object Count: 20 Reported Bytes Used: 42 Chexor: abaddeadbeefcafe UUID: abadf100d0ddba11 X-Container-Bar: goo X-Container-Foo: bar System Metadata: {'mydata': 'swift'} No user metadata found in db file''' self.assertEquals(sorted(out.getvalue().strip().split('\n')), sorted(exp_out.split('\n'))) def test_print_ring_locations(self): self.assertRaisesMessage(ValueError, 'None type', print_ring_locations, None, 'dir', 'acct') self.assertRaisesMessage(ValueError, 'None type', print_ring_locations, [], None, 'acct') self.assertRaisesMessage(ValueError, 'None type', print_ring_locations, [], 'dir', None) self.assertRaisesMessage(ValueError, 'Ring error', print_ring_locations, [], 'dir', 'acct', 'con') out = StringIO() with mock.patch('sys.stdout', out): acctring = ring.Ring(self.testdir, ring_name='account') print_ring_locations(acctring, 'dir', 'acct') exp_db2 = os.path.join('/srv', 'node', 'sdb1', 'dir', '3', 'b47', 'dc5be2aa4347a22a0fee6bc7de505b47', 'dc5be2aa4347a22a0fee6bc7de505b47.db') exp_db1 = os.path.join('/srv', 'node', 'sda1', 'dir', '3', 'b47', 'dc5be2aa4347a22a0fee6bc7de505b47', 'dc5be2aa4347a22a0fee6bc7de505b47.db') exp_out = ('Ring locations:\n 127.0.0.2:43 - %s\n' ' 127.0.0.1:42 - %s\n' '\nnote: /srv/node is used as default value of `devices`,' ' the real value is set in the account config file on' ' each storage node.' % (exp_db2, exp_db1)) self.assertEquals(out.getvalue().strip(), exp_out) out = StringIO() with mock.patch('sys.stdout', out): contring = ring.Ring(self.testdir, ring_name='container') print_ring_locations(contring, 'dir', 'acct', 'con') exp_db4 = os.path.join('/srv', 'node', 'sdb1', 'dir', '1', 'fe6', '63e70955d78dfc62821edc07d6ec1fe6', '63e70955d78dfc62821edc07d6ec1fe6.db') exp_db3 = os.path.join('/srv', 'node', 'sda1', 'dir', '1', 'fe6', '63e70955d78dfc62821edc07d6ec1fe6', '63e70955d78dfc62821edc07d6ec1fe6.db') exp_out = ('Ring locations:\n 127.0.0.4:43 - %s\n' ' 127.0.0.3:42 - %s\n' '\nnote: /srv/node is used as default value of `devices`,' ' the real value is set in the container config file on' ' each storage node.' % (exp_db4, exp_db3)) self.assertEquals(out.getvalue().strip(), exp_out) def test_print_info(self): db_file = 'foo' self.assertRaises(InfoSystemExit, print_info, 'object', db_file) db_file = os.path.join(self.testdir, './acct.db') self.assertRaises(InfoSystemExit, print_info, 'account', db_file) controller = AccountController( {'devices': self.testdir, 'mount_check': 'false'}) req = Request.blank('/sda1/1/acct', environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '0'}) resp = req.get_response(controller) self.assertEqual(resp.status_int, 201) out = StringIO() exp_raised = False with mock.patch('sys.stdout', out): db_file = os.path.join(self.testdir, 'sda1', 'accounts', '1', 'b47', 'dc5be2aa4347a22a0fee6bc7de505b47', 'dc5be2aa4347a22a0fee6bc7de505b47.db') try: print_info('account', db_file, swift_dir=self.testdir) except Exception: exp_raised = True if exp_raised: self.fail("Unexpected exception raised") else: self.assertTrue(len(out.getvalue().strip()) > 800) controller = ContainerController( {'devices': self.testdir, 'mount_check': 'false'}) req = Request.blank('/sda1/1/acct/cont', environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '0'}) resp = req.get_response(controller) self.assertEqual(resp.status_int, 201) out = StringIO() exp_raised = False with mock.patch('sys.stdout', out): db_file = os.path.join(self.testdir, 'sda1', 'containers', '1', 'cae', 'd49d0ecbb53be1fcc49624f2f7c7ccae', 'd49d0ecbb53be1fcc49624f2f7c7ccae.db') orig_cwd = os.getcwd() try: os.chdir(os.path.dirname(db_file)) print_info('container', os.path.basename(db_file), swift_dir='/dev/null') except Exception: exp_raised = True finally: os.chdir(orig_cwd) if exp_raised: self.fail("Unexpected exception raised") else: self.assertTrue(len(out.getvalue().strip()) > 600) out = StringIO() exp_raised = False with mock.patch('sys.stdout', out): db_file = os.path.join(self.testdir, 'sda1', 'containers', '1', 'cae', 'd49d0ecbb53be1fcc49624f2f7c7ccae', 'd49d0ecbb53be1fcc49624f2f7c7ccae.db') orig_cwd = os.getcwd() try: os.chdir(os.path.dirname(db_file)) print_info('account', os.path.basename(db_file), swift_dir='/dev/null') except InfoSystemExit: exp_raised = True finally: os.chdir(orig_cwd) if exp_raised: exp_out = 'Does not appear to be a DB of type "account":' \ ' ./d49d0ecbb53be1fcc49624f2f7c7ccae.db' self.assertEquals(out.getvalue().strip(), exp_out) else: self.fail("Expected an InfoSystemExit exception to be raised")