Fix swiftdir option and usage of storage policy aliases

If swift-recon/swift-get-nodes/swift-object-info is used with the
swiftdir option they will read rings from the given directory; however
they are still using /etc/swift/swift.conf to find the policies on the
current node.

This makes it impossible to maintain a local swift.conf copy (if you
don't have write access to /etc/swift) or check multiple clusters from
the same node.

Until now swift-recon was also not usable with storage policy aliases,
this patch fixes this as well.

Closes-Bug: 1577582
Closes-Bug: 1604707
Closes-Bug: 1617951
Co-Authored-By: Alistair Coles <alistairncoles@gmail.com>
Co-Authored-By: Thiago da Silva <thiago@redhat.com>
Change-Id: I13188d42ec19e32e4420739eacd1e5b454af2ae3
This commit is contained in:
Christian Schwede 2016-07-20 09:51:24 +00:00 committed by Thiago da Silva
parent 4cb76a41ce
commit 2410b616bb
9 changed files with 123 additions and 29 deletions

View File

@ -19,6 +19,8 @@ from optparse import OptionParser
from os.path import basename from os.path import basename
from swift.common.ring import Ring from swift.common.ring import Ring
from swift.common.storage_policy import reload_storage_policies
from swift.common.utils import set_swift_dir
from swift.cli.info import (parse_get_node_args, print_item_locations, from swift.cli.info import (parse_get_node_args, print_item_locations,
InfoSystemExit) InfoSystemExit)
@ -51,6 +53,10 @@ if __name__ == '__main__':
parser.add_option('-d', '--swift-dir', default='/etc/swift', parser.add_option('-d', '--swift-dir', default='/etc/swift',
dest='swift_dir', help='Path to swift directory') dest='swift_dir', help='Path to swift directory')
options, args = parser.parse_args() options, args = parser.parse_args()
if set_swift_dir(options.swift_dir):
reload_storage_policies()
try: try:
ring_path, args = parse_get_node_args(options, args) ring_path, args = parse_get_node_args(options, args)
except InfoSystemExit as e: except InfoSystemExit as e:

View File

@ -17,6 +17,8 @@
import sys import sys
from optparse import OptionParser from optparse import OptionParser
from swift.common.storage_policy import reload_storage_policies
from swift.common.utils import set_swift_dir
from swift.cli.info import print_obj, InfoSystemExit from swift.cli.info import print_obj, InfoSystemExit
@ -38,6 +40,9 @@ if __name__ == '__main__':
if len(args) != 1: if len(args) != 1:
sys.exit(parser.print_help()) sys.exit(parser.print_help())
if set_swift_dir(options.swift_dir):
reload_storage_policies()
try: try:
print_obj(*args, **vars(options)) print_obj(*args, **vars(options))
except InfoSystemExit: except InfoSystemExit:

View File

@ -18,11 +18,13 @@
from __future__ import print_function from __future__ import print_function
from eventlet.green import socket from eventlet.green import socket
from six import string_types
from six.moves.urllib.parse import urlparse from six.moves.urllib.parse import urlparse
from swift.common.utils import SWIFT_CONF_FILE, md5_hash_for_file from swift.common.utils import (
SWIFT_CONF_FILE, md5_hash_for_file, set_swift_dir)
from swift.common.ring import Ring from swift.common.ring import Ring
from swift.common.storage_policy import POLICIES from swift.common.storage_policy import POLICIES, reload_storage_policies
import eventlet import eventlet
import json import json
import optparse import optparse
@ -916,7 +918,9 @@ class SwiftRecon(object):
if self.server_type == 'object': if self.server_type == 'object':
ring_names = [p.ring_name for p in POLICIES if ( ring_names = [p.ring_name for p in POLICIES if (
p.name == policy or not policy or ( p.name == policy or not policy or (
policy.isdigit() and int(policy) == int(p)))] policy.isdigit() and int(policy) == int(p) or
(isinstance(policy, string_types)
and policy in p.aliases)))]
else: else:
ring_names = [self.server_type] ring_names = [self.server_type]
@ -1013,6 +1017,9 @@ class SwiftRecon(object):
server_types = ['object'] server_types = ['object']
swift_dir = options.swiftdir swift_dir = options.swiftdir
if set_swift_dir(swift_dir):
reload_storage_policies()
self.verbose = options.verbose self.verbose = options.verbose
self.suppress_errors = options.suppress self.suppress_errors = options.suppress
self.timeout = options.timeout self.timeout = options.timeout

View File

@ -20,10 +20,10 @@ import textwrap
import six import six
from six.moves.configparser import ConfigParser from six.moves.configparser import ConfigParser
from swift.common.utils import ( from swift.common.utils import (
config_true_value, SWIFT_CONF_FILE, whataremyips, list_from_csv, config_true_value, quorum_size, whataremyips, list_from_csv,
config_positive_int_value) config_positive_int_value)
from swift.common.ring import Ring, RingData from swift.common.ring import Ring, RingData
from swift.common.utils import quorum_size from swift.common import utils
from swift.common.exceptions import RingLoadError from swift.common.exceptions import RingLoadError
from pyeclib.ec_iface import ECDriver, ECDriverError, VALID_EC_TYPES from pyeclib.ec_iface import ECDriver, ECDriverError, VALID_EC_TYPES
@ -925,15 +925,19 @@ class StoragePolicySingleton(object):
def reload_storage_policies(): def reload_storage_policies():
""" """
Reload POLICIES from ``swift.conf``. Reload POLICIES from ``swift.conf``.
:param swift_conf_dir: non-default directory to read swift.conf from
This is by default /etc/swift/swift.conf. If given,
it will also trigger a re-validation of swift.conf
""" """
global _POLICIES global _POLICIES
policy_conf = ConfigParser() policy_conf = ConfigParser()
policy_conf.read(SWIFT_CONF_FILE) policy_conf.read(utils.SWIFT_CONF_FILE)
try: try:
_POLICIES = parse_storage_policies(policy_conf) _POLICIES = parse_storage_policies(policy_conf)
except PolicyError as e: except PolicyError as e:
raise SystemExit('ERROR: Invalid Storage Policy Configuration ' raise SystemExit('ERROR: Invalid Storage Policy Configuration '
'in %s (%s)' % (SWIFT_CONF_FILE, e)) 'in %s (%s)' % (utils.SWIFT_CONF_FILE, e))
# parse configuration and setup singleton # parse configuration and setup singleton

View File

@ -187,6 +187,29 @@ class InvalidHashPathConfigError(ValueError):
"swift_hash_path_prefix are missing from %s" % SWIFT_CONF_FILE "swift_hash_path_prefix are missing from %s" % SWIFT_CONF_FILE
def set_swift_dir(swift_dir):
"""
Sets the directory from which swift config files will be read. If the given
directory differs from that already set then the swift.conf file in the new
directory will be validated and storage policies will be reloaded from the
new swift.conf file.
:param swift_dir: non-default directory to read swift.conf from
"""
global HASH_PATH_SUFFIX
global HASH_PATH_PREFIX
global SWIFT_CONF_FILE
if (swift_dir is not None and
swift_dir != os.path.dirname(SWIFT_CONF_FILE)):
SWIFT_CONF_FILE = os.path.join(
swift_dir, os.path.basename(SWIFT_CONF_FILE))
HASH_PATH_PREFIX = ''
HASH_PATH_SUFFIX = ''
validate_configuration()
return True
return False
def validate_hash_conf(): def validate_hash_conf():
global HASH_PATH_SUFFIX global HASH_PATH_SUFFIX
global HASH_PATH_PREFIX global HASH_PATH_PREFIX

View File

@ -39,6 +39,7 @@ from six.moves.http_client import HTTPException
from swift.common.middleware.memcache import MemcacheMiddleware from swift.common.middleware.memcache import MemcacheMiddleware
from swift.common.storage_policy import parse_storage_policies, PolicyError from swift.common.storage_policy import parse_storage_policies, PolicyError
from swift.common.utils import set_swift_dir
from test import get_config, listen_zero from test import get_config, listen_zero
from test.functional.swift_test_client import Account, Connection, Container, \ from test.functional.swift_test_client import Account, Connection, Container, \
@ -106,9 +107,6 @@ skip, skip2, skip3, skip_service_tokens, skip_if_no_reseller_admin = \
orig_collate = '' orig_collate = ''
insecure = False insecure = False
orig_hash_path_suff_pref = ('', '')
orig_swift_conf_name = None
in_process = False in_process = False
_testdir = _test_servers = _test_coros = _test_socks = None _testdir = _test_servers = _test_coros = _test_socks = None
policy_specified = None policy_specified = None
@ -413,6 +411,7 @@ def in_process_setup(the_object_server=object_server):
utils.mkdirs(os.path.join(_testdir, 'sdc1', 'tmp')) utils.mkdirs(os.path.join(_testdir, 'sdc1', 'tmp'))
swift_conf = _in_process_setup_swift_conf(swift_conf_src, _testdir) swift_conf = _in_process_setup_swift_conf(swift_conf_src, _testdir)
_info('prepared swift.conf: %s' % swift_conf)
# Call the associated method for the value of # Call the associated method for the value of
# 'SWIFT_TEST_IN_PROCESS_CONF_LOADER', if one exists # 'SWIFT_TEST_IN_PROCESS_CONF_LOADER', if one exists
@ -437,12 +436,11 @@ def in_process_setup(the_object_server=object_server):
obj_sockets = _in_process_setup_ring(swift_conf, conf_src_dir, _testdir) obj_sockets = _in_process_setup_ring(swift_conf, conf_src_dir, _testdir)
global orig_swift_conf_name # load new swift.conf file
orig_swift_conf_name = utils.SWIFT_CONF_FILE if set_swift_dir(os.path.dirname(swift_conf)):
utils.SWIFT_CONF_FILE = swift_conf constraints.reload_constraints()
constraints.reload_constraints() storage_policy.reload_storage_policies()
storage_policy.SWIFT_CONF_FILE = swift_conf
storage_policy.reload_storage_policies()
global config global config
if constraints.SWIFT_CONSTRAINTS_LOADED: if constraints.SWIFT_CONSTRAINTS_LOADED:
# Use the swift constraints that are loaded for the test framework # Use the swift constraints that are loaded for the test framework
@ -453,9 +451,6 @@ def in_process_setup(the_object_server=object_server):
else: else:
# In-process swift constraints were not loaded, somethings wrong # In-process swift constraints were not loaded, somethings wrong
raise SkipTest raise SkipTest
global orig_hash_path_suff_pref
orig_hash_path_suff_pref = utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX
utils.validate_hash_conf()
global _test_socks global _test_socks
_test_socks = [] _test_socks = []
@ -918,10 +913,7 @@ def teardown_package():
rmtree(os.path.dirname(_testdir)) rmtree(os.path.dirname(_testdir))
except Exception: except Exception:
pass pass
utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX = \
orig_hash_path_suff_pref
utils.SWIFT_CONF_FILE = orig_swift_conf_name
constraints.reload_constraints()
reset_globals() reset_globals()

View File

@ -16,11 +16,13 @@
import json import json
import mock import mock
import os import os
import random
import re import re
import tempfile import tempfile
import time import time
import unittest import unittest
import shutil import shutil
import string
import sys import sys
import six import six
@ -147,6 +149,7 @@ class TestScout(unittest.TestCase):
@patch_policies @patch_policies
class TestRecon(unittest.TestCase): class TestRecon(unittest.TestCase):
def setUp(self, *_args, **_kwargs): def setUp(self, *_args, **_kwargs):
self.swift_conf_file = utils.SWIFT_CONF_FILE
self.recon_instance = recon.SwiftRecon() self.recon_instance = recon.SwiftRecon()
self.swift_dir = tempfile.mkdtemp() self.swift_dir = tempfile.mkdtemp()
self.ring_name = POLICIES.legacy.ring_name self.ring_name = POLICIES.legacy.ring_name
@ -156,10 +159,24 @@ class TestRecon(unittest.TestCase):
self.tmpfile_name2 = os.path.join( self.tmpfile_name2 = os.path.join(
self.swift_dir, self.ring_name2 + '.ring.gz') self.swift_dir, self.ring_name2 + '.ring.gz')
utils.HASH_PATH_SUFFIX = 'endcap' swift_conf = os.path.join(self.swift_dir, 'swift.conf')
utils.HASH_PATH_PREFIX = 'startcap' self.policy_name = ''.join(random.sample(string.letters, 20))
with open(swift_conf, "wb") as sc:
sc.write('''
[swift-hash]
swift_hash_path_suffix = changeme
[storage-policy:0]
name = default
default = yes
[storage-policy:1]
name = unu
aliases = %s
''' % self.policy_name)
def tearDown(self, *_args, **_kwargs): def tearDown(self, *_args, **_kwargs):
utils.SWIFT_CONF_FILE = self.swift_conf_file
shutil.rmtree(self.swift_dir, ignore_errors=True) shutil.rmtree(self.swift_dir, ignore_errors=True)
def _make_object_rings(self): def _make_object_rings(self):
@ -590,7 +607,7 @@ class TestRecon(unittest.TestCase):
self.assertEqual(expected, discovered_hosts) self.assertEqual(expected, discovered_hosts)
def test_main_object_hosts_default_unu(self): def _test_main_object_hosts_policy_name(self, policy_name='unu'):
self._make_object_rings() self._make_object_rings()
discovered_hosts = set() discovered_hosts = set()
@ -602,7 +619,7 @@ class TestRecon(unittest.TestCase):
with mock.patch.object(sys, 'argv', [ with mock.patch.object(sys, 'argv', [
"prog", "object", "--swiftdir=%s" % self.swift_dir, "prog", "object", "--swiftdir=%s" % self.swift_dir,
"--validate-servers", '--policy=unu']): "--validate-servers", '--policy', policy_name]):
self.recon_instance.main() self.recon_instance.main()
@ -612,6 +629,12 @@ class TestRecon(unittest.TestCase):
]) ])
self.assertEqual(expected, discovered_hosts) self.assertEqual(expected, discovered_hosts)
def test_main_object_hosts_default_unu(self):
self._test_main_object_hosts_policy_name()
def test_main_object_hosts_default_alias(self):
self._test_main_object_hosts_policy_name(self.policy_name)
def test_main_object_hosts_default_invalid(self): def test_main_object_hosts_default_invalid(self):
self._make_object_rings() self._make_object_rings()
stdout = StringIO() stdout = StringIO()

View File

@ -1051,7 +1051,7 @@ class TestStoragePolicies(unittest.TestCase):
with NamedTemporaryFile() as f: with NamedTemporaryFile() as f:
conf.write(f) conf.write(f)
f.flush() f.flush()
with mock.patch('swift.common.storage_policy.SWIFT_CONF_FILE', with mock.patch('swift.common.utils.SWIFT_CONF_FILE',
new=f.name): new=f.name):
try: try:
reload_storage_policies() reload_storage_policies()

View File

@ -31,6 +31,7 @@ import mock
import random import random
import re import re
import socket import socket
import string
import sys import sys
import json import json
import math import math
@ -61,9 +62,11 @@ from swift.common.exceptions import Timeout, MessageTimeout, \
ConnectionTimeout, LockTimeout, ReplicationLockTimeout, \ ConnectionTimeout, LockTimeout, ReplicationLockTimeout, \
MimeInvalid MimeInvalid
from swift.common import utils from swift.common import utils
from swift.common.utils import is_valid_ip, is_valid_ipv4, is_valid_ipv6 from swift.common.utils import is_valid_ip, is_valid_ipv4, is_valid_ipv6, \
set_swift_dir
from swift.common.container_sync_realms import ContainerSyncRealms from swift.common.container_sync_realms import ContainerSyncRealms
from swift.common.header_key_dict import HeaderKeyDict from swift.common.header_key_dict import HeaderKeyDict
from swift.common.storage_policy import POLICIES, reload_storage_policies
from swift.common.swob import Request, Response from swift.common.swob import Request, Response
from test.unit import FakeLogger, requires_o_tmpfile_support from test.unit import FakeLogger, requires_o_tmpfile_support
@ -6186,5 +6189,36 @@ class TestHashForFileFunction(unittest.TestCase):
self.fail('Some data did not compute expected hash:\n' + self.fail('Some data did not compute expected hash:\n' +
'\n'.join(failures)) '\n'.join(failures))
class TestSetSwiftDir(unittest.TestCase):
def setUp(self):
self.swift_dir = tempfile.mkdtemp()
self.swift_conf = os.path.join(self.swift_dir, 'swift.conf')
self.policy_name = ''.join(random.sample(string.letters, 20))
with open(self.swift_conf, "wb") as sc:
sc.write('''
[swift-hash]
swift_hash_path_suffix = changeme
[storage-policy:0]
name = default
default = yes
[storage-policy:1]
name = %s
''' % self.policy_name)
def tearDown(self):
shutil.rmtree(self.swift_dir, ignore_errors=True)
def test_set_swift_dir(self):
set_swift_dir(None)
reload_storage_policies()
self.assertIsNone(POLICIES.get_by_name(self.policy_name))
set_swift_dir(self.swift_dir)
reload_storage_policies()
self.assertIsNotNone(POLICIES.get_by_name(self.policy_name))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()