c20d611016
While innocently running probe tests that usued to work, they started dropping to [sudo] prompt and hang. Apparently, someone added an harmless-looking sudo, but this does not work if the testing user does not have sudo priviletes. The code checks very explicitly above that these operations would be allowed, and thus the sudo is unnecessary. We can just omit it, and get probe tests working again. Change-Id: I654c3d954711d4892ff43d7a8df7c3248cdf7aff
202 lines
7.4 KiB
Python
Executable File
202 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# 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.
|
|
|
|
import os
|
|
|
|
from errno import EEXIST
|
|
from shutil import copyfile, move
|
|
from tempfile import mkstemp
|
|
from time import time
|
|
from unittest import main
|
|
from uuid import uuid4
|
|
|
|
from swiftclient import client
|
|
|
|
from swift.cli.relinker import main as relinker_main
|
|
from swift.common.manager import Manager, Server
|
|
from swift.common.ring import RingBuilder
|
|
from swift.common.utils import replace_partition_in_path, readconf
|
|
from swift.obj.diskfile import get_data_dir
|
|
from test.probe.common import ECProbeTest, ProbeTest, ReplProbeTest
|
|
|
|
|
|
class TestPartPowerIncrease(ProbeTest):
|
|
def setUp(self):
|
|
super(TestPartPowerIncrease, self).setUp()
|
|
_, self.ring_file_backup = mkstemp()
|
|
_, self.builder_file_backup = mkstemp()
|
|
self.ring_file = self.object_ring.serialized_path
|
|
self.builder_file = self.ring_file.replace('ring.gz', 'builder')
|
|
copyfile(self.ring_file, self.ring_file_backup)
|
|
copyfile(self.builder_file, self.builder_file_backup)
|
|
# In case the test user is not allowed to write rings
|
|
self.assertTrue(os.access('/etc/swift', os.W_OK))
|
|
self.assertTrue(os.access('/etc/swift/backups', os.W_OK))
|
|
self.assertTrue(os.access('/etc/swift/object.builder', os.W_OK))
|
|
self.assertTrue(os.access('/etc/swift/object.ring.gz', os.W_OK))
|
|
# Ensure the test object will be erasure coded
|
|
self.data = ' ' * getattr(self.policy, 'ec_segment_size', 1)
|
|
|
|
self.conf_files = Server('object').conf_files()
|
|
self.devices = [readconf(conf_file)['app:object-server']['devices']
|
|
for conf_file in self.conf_files]
|
|
|
|
def tearDown(self):
|
|
# Keep a backup copy of the modified .builder file
|
|
backup_dir = os.path.join(
|
|
os.path.dirname(self.builder_file), 'backups')
|
|
try:
|
|
os.mkdir(backup_dir)
|
|
except OSError as err:
|
|
if err.errno != EEXIST:
|
|
raise
|
|
backup_name = (os.path.join(
|
|
backup_dir,
|
|
'%d.probe.' % time() + os.path.basename(self.builder_file)))
|
|
copyfile(self.builder_file, backup_name)
|
|
|
|
# Restore original ring
|
|
move(self.ring_file_backup, self.ring_file)
|
|
move(self.builder_file_backup, self.builder_file)
|
|
|
|
def _find_objs_ondisk(self, container, obj):
|
|
locations = []
|
|
opart, onodes = self.object_ring.get_nodes(
|
|
self.account, container, obj)
|
|
for node in onodes:
|
|
start_dir = os.path.join(
|
|
self.device_dir(node),
|
|
get_data_dir(self.policy),
|
|
str(opart))
|
|
for root, dirs, files in os.walk(start_dir):
|
|
for filename in files:
|
|
if filename.endswith('.data'):
|
|
locations.append(os.path.join(root, filename))
|
|
return locations
|
|
|
|
def _test_main(self, cancel=False):
|
|
container = 'container-%s' % uuid4()
|
|
obj = 'object-%s' % uuid4()
|
|
obj2 = 'object-%s' % uuid4()
|
|
|
|
# Create container
|
|
headers = {'X-Storage-Policy': self.policy.name}
|
|
client.put_container(self.url, self.token, container, headers=headers)
|
|
|
|
# Create a new object
|
|
client.put_object(self.url, self.token, container, obj, self.data)
|
|
client.head_object(self.url, self.token, container, obj)
|
|
|
|
# Prepare partition power increase
|
|
builder = RingBuilder.load(self.builder_file)
|
|
builder.prepare_increase_partition_power()
|
|
builder.save(self.builder_file)
|
|
ring_data = builder.get_ring()
|
|
ring_data.save(self.ring_file)
|
|
|
|
# Ensure the proxy uses the changed ring
|
|
Manager(['proxy']).restart()
|
|
|
|
# Ensure object is still accessible
|
|
client.head_object(self.url, self.token, container, obj)
|
|
|
|
# Relink existing objects
|
|
for conf in self.conf_files:
|
|
self.assertEqual(0, relinker_main(['relink', conf]))
|
|
|
|
# Create second object after relinking and ensure it is accessible
|
|
client.put_object(self.url, self.token, container, obj2, self.data)
|
|
client.head_object(self.url, self.token, container, obj2)
|
|
|
|
# Remember the original object locations
|
|
org_locations = self._find_objs_ondisk(container, obj)
|
|
org_locations += self._find_objs_ondisk(container, obj2)
|
|
|
|
# Remember the new object locations
|
|
new_locations = []
|
|
for loc in org_locations:
|
|
for dev_root in self.devices:
|
|
if loc.startswith(dev_root):
|
|
break
|
|
else:
|
|
self.fail('Unable to find device for %s' % loc)
|
|
new_locations.append(replace_partition_in_path(
|
|
dev_root, str(loc), self.object_ring.part_power + 1))
|
|
|
|
# Overwrite existing object - to ensure that older timestamp files
|
|
# will be cleaned up properly later
|
|
client.put_object(self.url, self.token, container, obj, self.data)
|
|
|
|
# Ensure objects are still accessible
|
|
client.head_object(self.url, self.token, container, obj)
|
|
client.head_object(self.url, self.token, container, obj2)
|
|
|
|
# Increase partition power
|
|
builder = RingBuilder.load(self.builder_file)
|
|
if not cancel:
|
|
builder.increase_partition_power()
|
|
else:
|
|
builder.cancel_increase_partition_power()
|
|
builder.save(self.builder_file)
|
|
ring_data = builder.get_ring()
|
|
ring_data.save(self.ring_file)
|
|
|
|
# Ensure the proxy uses the changed ring
|
|
Manager(['proxy']).restart()
|
|
|
|
# Ensure objects are still accessible
|
|
client.head_object(self.url, self.token, container, obj)
|
|
client.head_object(self.url, self.token, container, obj2)
|
|
|
|
# Overwrite existing object - to ensure that older timestamp files
|
|
# will be cleaned up properly later
|
|
client.put_object(self.url, self.token, container, obj, self.data)
|
|
|
|
# Cleanup old objects in the wrong location
|
|
for conf in self.conf_files:
|
|
self.assertEqual(0, relinker_main(['cleanup', conf]))
|
|
|
|
# Ensure objects are still accessible
|
|
client.head_object(self.url, self.token, container, obj)
|
|
client.head_object(self.url, self.token, container, obj2)
|
|
|
|
# Ensure data in old or relinked object locations is removed
|
|
if not cancel:
|
|
for fn in org_locations:
|
|
self.assertFalse(os.path.exists(fn))
|
|
else:
|
|
for fn in new_locations:
|
|
self.assertFalse(os.path.exists(fn))
|
|
|
|
|
|
class TestReplPartPowerIncrease(TestPartPowerIncrease, ReplProbeTest):
|
|
def test_main(self):
|
|
self._test_main()
|
|
|
|
def test_canceled(self):
|
|
self._test_main(cancel=True)
|
|
|
|
|
|
class TestECPartPowerIncrease(TestPartPowerIncrease, ECProbeTest):
|
|
def test_main(self):
|
|
self._test_main()
|
|
|
|
def test_canceled(self):
|
|
self._test_main(cancel=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|