Merge "Add support for storage policies to have more than one name"
This commit is contained in:
commit
d446035ec4
@ -57,7 +57,7 @@ deployers. Each container has a new special immutable metadata element called
|
||||
the storage policy index. Note that internally, Swift relies on policy
|
||||
indexes and not policy names. Policy names exist for human readability and
|
||||
translation is managed in the proxy. When a container is created, one new
|
||||
optional header is supported to specify the policy name. If nothing is
|
||||
optional header is supported to specify the policy name. If no name is
|
||||
specified, the default policy is used (and if no other policies defined,
|
||||
Policy-0 is considered the default). We will be covering the difference
|
||||
between default and Policy-0 in the next section.
|
||||
@ -170,12 +170,13 @@ Storage Policies is a versatile feature intended to support both new and
|
||||
pre-existing clusters with the same level of flexibility. For that reason, we
|
||||
introduce the ``Policy-0`` concept which is not the same as the "default"
|
||||
policy. As you will see when we begin to configure policies, each policy has
|
||||
both a name (human friendly, configurable) as well as an index (or simply
|
||||
policy number). Swift reserves index 0 to map to the object ring that's
|
||||
present in all installations (e.g., ``/etc/swift/object.ring.gz``). You can
|
||||
name this policy anything you like, and if no policies are defined it will
|
||||
report itself as ``Policy-0``, however you cannot change the index as there must
|
||||
always be a policy with index 0.
|
||||
a single name and an arbitrary number of aliases (human friendly,
|
||||
configurable) as well as an index (or simply policy number). Swift reserves
|
||||
index 0 to map to the object ring that's present in all installations
|
||||
(e.g., ``/etc/swift/object.ring.gz``). You can name this policy anything you
|
||||
like, and if no policies are defined it will report itself as ``Policy-0``,
|
||||
however you cannot change the index as there must always be a policy with
|
||||
index 0.
|
||||
|
||||
Another important concept is the default policy which can be any policy
|
||||
in the cluster. The default policy is the policy that is automatically
|
||||
@ -273,6 +274,8 @@ file:
|
||||
* Policy names must contain only letters, digits or a dash
|
||||
* Policy names must be unique
|
||||
* The policy name 'Policy-0' can only be used for the policy with index 0
|
||||
* Multiple names can be assigned to one policy using aliases. All names
|
||||
must follow the Swift naming rules.
|
||||
* If any policies are defined, exactly one policy must be declared default
|
||||
* Deprecated policies cannot be declared the default
|
||||
* If no ``policy_type`` is provided, ``replication`` is the default value.
|
||||
@ -288,6 +291,7 @@ example configuration.::
|
||||
|
||||
[storage-policy:0]
|
||||
name = gold
|
||||
aliases = yellow, orange
|
||||
policy_type = replication
|
||||
default = yes
|
||||
|
||||
@ -301,8 +305,10 @@ information about the ``default`` and ``deprecated`` options.
|
||||
|
||||
There are some other considerations when managing policies:
|
||||
|
||||
* Policy names can be changed (but be sure that users are aware, aliases are
|
||||
not currently supported but could be implemented in custom middleware!)
|
||||
* Policy names can be changed.
|
||||
* Aliases are supported and can be added and removed. If the primary name
|
||||
of a policy is removed the next available alias will be adopted as the
|
||||
primary name. A policy must always have at least one name.
|
||||
* You cannot change the index of a policy once it has been created
|
||||
* The default policy can be changed at any time, by adding the
|
||||
default directive to the desired policy section
|
||||
@ -399,7 +405,7 @@ The module, :ref:`storage_policy`, is responsible for parsing the
|
||||
configured policies via class :class:`.StoragePolicyCollection`. This
|
||||
collection is made up of policies of class :class:`.StoragePolicy`. The
|
||||
collection class includes handy functions for getting to a policy either by
|
||||
name or by index , getting info about the policies, etc. There's also one
|
||||
name or by index , getting info about the policies, etc. There's also one
|
||||
very important function, :meth:`~.StoragePolicyCollection.get_object_ring`.
|
||||
Object rings are members of the :class:`.StoragePolicy` class and are
|
||||
actually not instantiated until the :meth:`~.StoragePolicy.load_ring`
|
||||
|
4
doc/source/policies_saio.rst
Normal file → Executable file
4
doc/source/policies_saio.rst
Normal file → Executable file
@ -26,6 +26,7 @@ to implement a usable set of policies.
|
||||
|
||||
[storage-policy:0]
|
||||
name = gold
|
||||
aliases = yellow, orange
|
||||
default = yes
|
||||
|
||||
[storage-policy:1]
|
||||
@ -82,7 +83,8 @@ Storage Policies effect placement of data in Swift.
|
||||
|
||||
You should see this: (only showing the policy output here)::
|
||||
|
||||
policies: [{'default': True, 'name': 'gold'}, {'name': 'silver'}]
|
||||
policies: [{'aliases': 'gold, yellow, orange', 'default': True,
|
||||
'name': 'gold'}, {'aliases': 'silver', 'name': 'silver'}]
|
||||
|
||||
3. Now create a container without specifying a policy, it will use the
|
||||
default, 'gold' and then put a test object in it (create the file ``file0.txt``
|
||||
|
15
etc/swift.conf-sample
Normal file → Executable file
15
etc/swift.conf-sample
Normal file → Executable file
@ -21,7 +21,7 @@ swift_hash_path_prefix = changeme
|
||||
# policy with index 0 will be declared the default. If multiple policies are
|
||||
# defined you must define a policy with index 0 and you must specify a
|
||||
# default. It is recommended you always define a section for
|
||||
# storage-policy:0.
|
||||
# storage-policy:0. Aliases are not required when defining a storage policy.
|
||||
#
|
||||
# A 'policy_type' argument is also supported but is not mandatory. Default
|
||||
# policy type 'replication' is used when 'policy_type' is unspecified.
|
||||
@ -29,6 +29,7 @@ swift_hash_path_prefix = changeme
|
||||
name = Policy-0
|
||||
default = yes
|
||||
#policy_type = replication
|
||||
aliases = yellow, orange
|
||||
|
||||
# the following section would declare a policy called 'silver', the number of
|
||||
# replicas will be determined by how the ring is built. In this example the
|
||||
@ -40,7 +41,10 @@ default = yes
|
||||
# this config has specified it as the default. However if a legacy container
|
||||
# (one created with a pre-policy version of swift) is accessed, it is known
|
||||
# implicitly to be assigned to the policy with index 0 as opposed to the
|
||||
# current default.
|
||||
# current default. Note that even without specifying any aliases, a policy
|
||||
# always has at least the default name stored in aliases because this field is
|
||||
# used to contain all human readable names for a storage policy.
|
||||
#
|
||||
#[storage-policy:1]
|
||||
#name = silver
|
||||
#policy_type = replication
|
||||
@ -67,12 +71,13 @@ default = yes
|
||||
# refer to Swift documentation for details on how to configure EC policies.
|
||||
#
|
||||
# The example 'deepfreeze10-4' policy defined below is a _sample_
|
||||
# configuration with 10 'data' and 4 'parity' fragments. 'ec_type'
|
||||
# defines the Erasure Coding scheme. 'jerasure_rs_vand' (Reed-Solomon
|
||||
# Vandermonde) is used as an example below.
|
||||
# configuration with an alias of 'df10-4' as well as 10 'data' and 4 'parity'
|
||||
# fragments. 'ec_type' defines the Erasure Coding scheme.
|
||||
# 'jerasure_rs_vand' (Reed-Solomon Vandermonde) is used as an example below.
|
||||
#
|
||||
#[storage-policy:2]
|
||||
#name = deepfreeze10-4
|
||||
#aliases = df10-4
|
||||
#policy_type = erasure_coding
|
||||
#ec_type = jerasure_rs_vand
|
||||
#ec_num_data_fragments = 10
|
||||
|
199
swift/common/storage_policy.py
Normal file → Executable file
199
swift/common/storage_policy.py
Normal file → Executable file
@ -16,11 +16,9 @@ import os
|
||||
import string
|
||||
import textwrap
|
||||
import six
|
||||
|
||||
from six.moves.configparser import ConfigParser
|
||||
|
||||
from swift.common.utils import (
|
||||
config_true_value, SWIFT_CONF_FILE, whataremyips)
|
||||
config_true_value, SWIFT_CONF_FILE, whataremyips, list_from_csv)
|
||||
from swift.common.ring import Ring, RingData
|
||||
from swift.common.utils import quorum_size
|
||||
from swift.common.exceptions import RingValidationError
|
||||
@ -84,7 +82,6 @@ class BindPortsCache(object):
|
||||
|
||||
|
||||
class PolicyError(ValueError):
|
||||
|
||||
def __init__(self, msg, index=None):
|
||||
if index is not None:
|
||||
msg += ', for index %r' % index
|
||||
@ -161,7 +158,7 @@ class BaseStoragePolicy(object):
|
||||
policy_type_to_policy_cls = {}
|
||||
|
||||
def __init__(self, idx, name='', is_default=False, is_deprecated=False,
|
||||
object_ring=None):
|
||||
object_ring=None, aliases=''):
|
||||
# do not allow BaseStoragePolicy class to be instantiated directly
|
||||
if type(self) == BaseStoragePolicy:
|
||||
raise TypeError("Can't instantiate BaseStoragePolicy directly")
|
||||
@ -172,18 +169,17 @@ class BaseStoragePolicy(object):
|
||||
raise PolicyError('Invalid index', idx)
|
||||
if self.idx < 0:
|
||||
raise PolicyError('Invalid index', idx)
|
||||
if not name:
|
||||
self.alias_list = []
|
||||
if not name or not self._validate_policy_name(name):
|
||||
raise PolicyError('Invalid name %r' % name, idx)
|
||||
# this is defensively restrictive, but could be expanded in the future
|
||||
if not all(c in VALID_CHARS for c in name):
|
||||
raise PolicyError('Names are used as HTTP headers, and can not '
|
||||
'reliably contain any characters not in %r. '
|
||||
'Invalid name %r' % (VALID_CHARS, name))
|
||||
if name.upper() == LEGACY_POLICY_NAME.upper() and self.idx != 0:
|
||||
msg = 'The name %s is reserved for policy index 0. ' \
|
||||
'Invalid name %r' % (LEGACY_POLICY_NAME, name)
|
||||
raise PolicyError(msg, idx)
|
||||
self.name = name
|
||||
self.alias_list.append(name)
|
||||
if aliases:
|
||||
names_list = list_from_csv(aliases)
|
||||
for alias in names_list:
|
||||
if alias == name:
|
||||
continue
|
||||
self._validate_policy_name(alias)
|
||||
self.alias_list.append(alias)
|
||||
self.is_deprecated = config_true_value(is_deprecated)
|
||||
self.is_default = config_true_value(is_default)
|
||||
if self.policy_type not in BaseStoragePolicy.policy_type_to_policy_cls:
|
||||
@ -191,9 +187,23 @@ class BaseStoragePolicy(object):
|
||||
if self.is_deprecated and self.is_default:
|
||||
raise PolicyError('Deprecated policy can not be default. '
|
||||
'Invalid config', self.idx)
|
||||
|
||||
self.ring_name = _get_policy_string('object', self.idx)
|
||||
self.object_ring = object_ring
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.alias_list[0]
|
||||
|
||||
@name.setter
|
||||
def name_setter(self, name):
|
||||
self._validate_policy_name(name)
|
||||
self.alias_list[0] = name
|
||||
|
||||
@property
|
||||
def aliases(self):
|
||||
return ", ".join(self.alias_list)
|
||||
|
||||
def __int__(self):
|
||||
return self.idx
|
||||
|
||||
@ -203,8 +213,8 @@ class BaseStoragePolicy(object):
|
||||
def __repr__(self):
|
||||
return ("%s(%d, %r, is_default=%s, "
|
||||
"is_deprecated=%s, policy_type=%r)") % \
|
||||
(self.__class__.__name__, self.idx, self.name,
|
||||
self.is_default, self.is_deprecated, self.policy_type)
|
||||
(self.__class__.__name__, self.idx, self.alias_list,
|
||||
self.is_default, self.is_deprecated, self.policy_type)
|
||||
|
||||
@classmethod
|
||||
def register(cls, policy_type):
|
||||
@ -213,6 +223,7 @@ class BaseStoragePolicy(object):
|
||||
their StoragePolicy class. This will also set the policy_type
|
||||
attribute on the registered implementation.
|
||||
"""
|
||||
|
||||
def register_wrapper(policy_cls):
|
||||
if policy_type in cls.policy_type_to_policy_cls:
|
||||
raise PolicyError(
|
||||
@ -222,6 +233,7 @@ class BaseStoragePolicy(object):
|
||||
cls.policy_type_to_policy_cls[policy_type] = policy_cls
|
||||
policy_cls.policy_type = policy_type
|
||||
return policy_cls
|
||||
|
||||
return register_wrapper
|
||||
|
||||
@classmethod
|
||||
@ -231,6 +243,7 @@ class BaseStoragePolicy(object):
|
||||
"""
|
||||
return {
|
||||
'name': 'name',
|
||||
'aliases': 'aliases',
|
||||
'policy_type': 'policy_type',
|
||||
'default': 'is_default',
|
||||
'deprecated': 'is_deprecated',
|
||||
@ -269,6 +282,77 @@ class BaseStoragePolicy(object):
|
||||
info.pop('policy_type')
|
||||
return info
|
||||
|
||||
def _validate_policy_name(self, name):
|
||||
"""
|
||||
Helper function to determine the validity of a policy name. Used
|
||||
to check policy names before setting them.
|
||||
|
||||
:param name: a name string for a single policy name.
|
||||
:returns: true if the name is valid.
|
||||
:raises: PolicyError if the policy name is invalid.
|
||||
"""
|
||||
# this is defensively restrictive, but could be expanded in the future
|
||||
if not all(c in VALID_CHARS for c in name):
|
||||
raise PolicyError('Names are used as HTTP headers, and can not '
|
||||
'reliably contain any characters not in %r. '
|
||||
'Invalid name %r' % (VALID_CHARS, name))
|
||||
if name.upper() == LEGACY_POLICY_NAME.upper() and self.idx != 0:
|
||||
msg = 'The name %s is reserved for policy index 0. ' \
|
||||
'Invalid name %r' % (LEGACY_POLICY_NAME, name)
|
||||
raise PolicyError(msg, self.idx)
|
||||
if name.upper() in (existing_name.upper() for existing_name
|
||||
in self.alias_list):
|
||||
msg = 'The name %s is already assigned to this policy.' % name
|
||||
raise PolicyError(msg, self.idx)
|
||||
|
||||
return True
|
||||
|
||||
def add_name(self, name):
|
||||
"""
|
||||
Adds an alias name to the storage policy. Shouldn't be called
|
||||
directly from the storage policy but instead through the
|
||||
storage policy collection class, so lookups by name resolve
|
||||
correctly.
|
||||
|
||||
:param name: a new alias for the storage policy
|
||||
"""
|
||||
if self._validate_policy_name(name):
|
||||
self.alias_list.append(name)
|
||||
|
||||
def remove_name(self, name):
|
||||
"""
|
||||
Removes an alias name from the storage policy. Shouldn't be called
|
||||
directly from the storage policy but instead through the storage
|
||||
policy collection class, so lookups by name resolve correctly. If
|
||||
the name removed is the primary name then the next availiable alias
|
||||
will be adopted as the new primary name.
|
||||
|
||||
:param name: a name assigned to the storage policy
|
||||
"""
|
||||
if name not in self.alias_list:
|
||||
raise PolicyError("%s is not a name assigned to policy %s"
|
||||
% (name, self.idx))
|
||||
if len(self.alias_list) == 1:
|
||||
raise PolicyError("Cannot remove only name %s from policy %s. "
|
||||
"Policies must have at least one name."
|
||||
% (name, self.idx))
|
||||
else:
|
||||
self.alias_list.remove(name)
|
||||
|
||||
def change_primary_name(self, name):
|
||||
"""
|
||||
Changes the primary/default name of the policy to a specified name.
|
||||
|
||||
:param name: a string name to replace the current primary name.
|
||||
"""
|
||||
if name == self.name:
|
||||
return
|
||||
elif name in self.alias_list:
|
||||
self.remove_name(name)
|
||||
else:
|
||||
self._validate_policy_name(name)
|
||||
self.alias_list.insert(0, name)
|
||||
|
||||
def _validate_ring(self):
|
||||
"""
|
||||
Hook, called when the ring is loaded. Can be used to
|
||||
@ -329,13 +413,15 @@ class ECStoragePolicy(BaseStoragePolicy):
|
||||
:func:`~swift.common.storage_policy.reload_storage_policies` to load
|
||||
POLICIES from ``swift.conf``.
|
||||
"""
|
||||
def __init__(self, idx, name='', is_default=False,
|
||||
|
||||
def __init__(self, idx, name='', aliases='', is_default=False,
|
||||
is_deprecated=False, object_ring=None,
|
||||
ec_segment_size=DEFAULT_EC_OBJECT_SEGMENT_SIZE,
|
||||
ec_type=None, ec_ndata=None, ec_nparity=None):
|
||||
|
||||
super(ECStoragePolicy, self).__init__(
|
||||
idx, name, is_default, is_deprecated, object_ring)
|
||||
idx=idx, name=name, aliases=aliases, is_default=is_default,
|
||||
is_deprecated=is_deprecated, object_ring=object_ring)
|
||||
|
||||
# Validate erasure_coding policy specific members
|
||||
# ec_type is one of the EC implementations supported by PyEClib
|
||||
@ -441,9 +527,9 @@ class ECStoragePolicy(BaseStoragePolicy):
|
||||
|
||||
def __repr__(self):
|
||||
return ("%s, EC config(ec_type=%s, ec_segment_size=%d, "
|
||||
"ec_ndata=%d, ec_nparity=%d)") % (
|
||||
super(ECStoragePolicy, self).__repr__(), self.ec_type,
|
||||
self.ec_segment_size, self.ec_ndata, self.ec_nparity)
|
||||
"ec_ndata=%d, ec_nparity=%d)") % \
|
||||
(super(ECStoragePolicy, self).__repr__(), self.ec_type,
|
||||
self.ec_segment_size, self.ec_ndata, self.ec_nparity)
|
||||
|
||||
@classmethod
|
||||
def _config_options_map(cls):
|
||||
@ -532,6 +618,7 @@ class StoragePolicyCollection(object):
|
||||
* Deprecated policies can not be declared the default
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, pols):
|
||||
self.default = []
|
||||
self.by_name = {}
|
||||
@ -542,7 +629,8 @@ class StoragePolicyCollection(object):
|
||||
"""
|
||||
Add pre-validated policies to internal indexes.
|
||||
"""
|
||||
self.by_name[policy.name.upper()] = policy
|
||||
for name in policy.alias_list:
|
||||
self.by_name[name.upper()] = policy
|
||||
self.by_index[int(policy)] = policy
|
||||
|
||||
def __repr__(self):
|
||||
@ -570,9 +658,10 @@ class StoragePolicyCollection(object):
|
||||
if int(policy) in self.by_index:
|
||||
raise PolicyError('Duplicate index %s conflicts with %s' % (
|
||||
policy, self.get_by_index(int(policy))))
|
||||
if policy.name.upper() in self.by_name:
|
||||
raise PolicyError('Duplicate name %s conflicts with %s' % (
|
||||
policy, self.get_by_name(policy.name)))
|
||||
for name in policy.alias_list:
|
||||
if name.upper() in self.by_name:
|
||||
raise PolicyError('Duplicate name %s conflicts with %s' % (
|
||||
policy, self.get_by_name(name)))
|
||||
if policy.is_default:
|
||||
if not self.default:
|
||||
self.default = policy
|
||||
@ -667,6 +756,62 @@ class StoragePolicyCollection(object):
|
||||
policy_info.append(policy_entry)
|
||||
return policy_info
|
||||
|
||||
def add_policy_alias(self, policy_index, *aliases):
|
||||
"""
|
||||
Adds a new name or names to a policy
|
||||
|
||||
:param policy_index: index of a policy in this policy collection.
|
||||
:param *aliases: arbitrary number of string policy names to add.
|
||||
"""
|
||||
policy = self.get_by_index(policy_index)
|
||||
for alias in aliases:
|
||||
if alias.upper() in self.by_name:
|
||||
raise PolicyError('Duplicate name %s in use '
|
||||
'by policy %s' % (alias,
|
||||
self.get_by_name(alias)))
|
||||
else:
|
||||
policy.add_name(alias)
|
||||
self.by_name[alias.upper()] = policy
|
||||
|
||||
def remove_policy_alias(self, *aliases):
|
||||
"""
|
||||
Removes a name or names from a policy. If the name removed is the
|
||||
primary name then the next availiable alias will be adopted
|
||||
as the new primary name.
|
||||
|
||||
:param *aliases: arbitrary number of existing policy names to remove.
|
||||
"""
|
||||
for alias in aliases:
|
||||
policy = self.get_by_name(alias)
|
||||
if not policy:
|
||||
raise PolicyError('No policy with name %s exists.' % alias)
|
||||
if len(policy.alias_list) == 1:
|
||||
raise PolicyError('Policy %s with name %s has only one name. '
|
||||
'Policies must have at least one name.' % (
|
||||
policy, alias))
|
||||
else:
|
||||
policy.remove_name(alias)
|
||||
del self.by_name[alias.upper()]
|
||||
|
||||
def change_policy_primary_name(self, policy_index, new_name):
|
||||
"""
|
||||
Changes the primary or default name of a policy. The new primary
|
||||
name can be an alias that already belongs to the policy or a
|
||||
completely new name.
|
||||
|
||||
:param policy_index: index of a policy in this policy collection.
|
||||
:param new_name: a string name to set as the new default name.
|
||||
"""
|
||||
policy = self.get_by_index(policy_index)
|
||||
name_taken = self.get_by_name(new_name)
|
||||
# if the name belongs to some other policy in the collection
|
||||
if name_taken and name_taken != policy:
|
||||
raise PolicyError('Other policy %s with name %s exists.' %
|
||||
(self.get_by_name(new_name).idx, new_name))
|
||||
else:
|
||||
policy.change_primary_name(new_name)
|
||||
self.by_name[new_name.upper()] = policy
|
||||
|
||||
|
||||
def parse_storage_policies(conf):
|
||||
"""
|
||||
|
245
test/unit/common/test_storage_policy.py
Normal file → Executable file
245
test/unit/common/test_storage_policy.py
Normal file → Executable file
@ -17,7 +17,6 @@ import unittest
|
||||
import os
|
||||
import mock
|
||||
from functools import partial
|
||||
|
||||
from six.moves.configparser import ConfigParser
|
||||
from tempfile import NamedTemporaryFile
|
||||
from test.unit import patch_policies, FakeRing, temptree, DEFAULT_TEST_EC_TYPE
|
||||
@ -36,6 +35,7 @@ class FakeStoragePolicy(BaseStoragePolicy):
|
||||
Test StoragePolicy class - the only user at the moment is
|
||||
test_validate_policies_type_invalid()
|
||||
"""
|
||||
|
||||
def __init__(self, idx, name='', is_default=False, is_deprecated=False,
|
||||
object_ring=None):
|
||||
super(FakeStoragePolicy, self).__init__(
|
||||
@ -43,7 +43,6 @@ class FakeStoragePolicy(BaseStoragePolicy):
|
||||
|
||||
|
||||
class TestStoragePolicies(unittest.TestCase):
|
||||
|
||||
def _conf(self, conf_str):
|
||||
conf_str = "\n".join(line.strip() for line in conf_str.split("\n"))
|
||||
conf = ConfigParser()
|
||||
@ -75,10 +74,10 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
])
|
||||
def test_swift_info(self):
|
||||
# the deprecated 'three' should not exist in expect
|
||||
expect = [{'default': True, 'name': 'zero'},
|
||||
{'name': 'two'},
|
||||
{'name': 'one'},
|
||||
{'name': 'ten'}]
|
||||
expect = [{'aliases': 'zero', 'default': True, 'name': 'zero', },
|
||||
{'aliases': 'two', 'name': 'two'},
|
||||
{'aliases': 'one', 'name': 'one'},
|
||||
{'aliases': 'ten', 'name': 'ten'}]
|
||||
swift_info = POLICIES.get_policy_info()
|
||||
self.assertEqual(sorted(expect, key=lambda k: k['name']),
|
||||
sorted(swift_info, key=lambda k: k['name']))
|
||||
@ -286,6 +285,7 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
def test_validate_policies_type_invalid(self):
|
||||
class BogusStoragePolicy(FakeStoragePolicy):
|
||||
policy_type = 'bogus'
|
||||
|
||||
# unsupported policy type - initialization with FakeStoragePolicy
|
||||
self.assertRaisesWithMessage(PolicyError, 'Invalid type',
|
||||
BogusStoragePolicy, 1, 'one')
|
||||
@ -330,6 +330,221 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
self.assertEqual(pol1, policies.get_by_name(name))
|
||||
self.assertEqual(policies.get_by_name(name).name, 'One')
|
||||
|
||||
def test_multiple_names(self):
|
||||
# checking duplicate on insert
|
||||
test_policies = [StoragePolicy(0, 'zero', True),
|
||||
StoragePolicy(1, 'one', False, aliases='zero')]
|
||||
self.assertRaises(PolicyError, StoragePolicyCollection,
|
||||
test_policies)
|
||||
|
||||
# checking correct retrival using other names
|
||||
test_policies = [StoragePolicy(0, 'zero', True, aliases='cero, kore'),
|
||||
StoragePolicy(1, 'one', False, aliases='uno, tahi'),
|
||||
StoragePolicy(2, 'two', False, aliases='dos, rua')]
|
||||
|
||||
policies = StoragePolicyCollection(test_policies)
|
||||
|
||||
for name in ('zero', 'cero', 'kore'):
|
||||
self.assertEqual(policies.get_by_name(name), test_policies[0])
|
||||
for name in ('two', 'dos', 'rua'):
|
||||
self.assertEqual(policies.get_by_name(name), test_policies[2])
|
||||
|
||||
# Testing parsing of conf files/text
|
||||
good_conf = self._conf("""
|
||||
[storage-policy:0]
|
||||
name = one
|
||||
aliases = uno, tahi
|
||||
default = yes
|
||||
""")
|
||||
|
||||
policies = parse_storage_policies(good_conf)
|
||||
self.assertEqual(policies.get_by_name('one'),
|
||||
policies[0])
|
||||
self.assertEqual(policies.get_by_name('one'),
|
||||
policies.get_by_name('tahi'))
|
||||
|
||||
name_repeat_conf = self._conf("""
|
||||
[storage-policy:0]
|
||||
name = one
|
||||
aliases = one
|
||||
default = yes
|
||||
""")
|
||||
# Test on line below should not generate errors. Repeat of main
|
||||
# name under aliases is permitted during construction
|
||||
# but only because automated testing requires it.
|
||||
policies = parse_storage_policies(name_repeat_conf)
|
||||
|
||||
bad_conf = self._conf("""
|
||||
[storage-policy:0]
|
||||
name = one
|
||||
aliases = uno, uno
|
||||
default = yes
|
||||
""")
|
||||
|
||||
self.assertRaisesWithMessage(PolicyError,
|
||||
'is already assigned to this policy',
|
||||
parse_storage_policies, bad_conf)
|
||||
|
||||
def test_multiple_names_EC(self):
|
||||
# checking duplicate names on insert
|
||||
test_policies_ec = [
|
||||
ECStoragePolicy(
|
||||
0, 'ec8-2',
|
||||
aliases='zeus, jupiter',
|
||||
ec_type=DEFAULT_TEST_EC_TYPE,
|
||||
ec_ndata=8, ec_nparity=2,
|
||||
object_ring=FakeRing(replicas=8),
|
||||
is_default=True),
|
||||
ECStoragePolicy(
|
||||
1, 'ec10-4',
|
||||
aliases='ec8-2',
|
||||
ec_type=DEFAULT_TEST_EC_TYPE,
|
||||
ec_ndata=10, ec_nparity=4,
|
||||
object_ring=FakeRing(replicas=10))]
|
||||
|
||||
self.assertRaises(PolicyError, StoragePolicyCollection,
|
||||
test_policies_ec)
|
||||
|
||||
# checking correct retrival using other names
|
||||
good_test_policies_EC = [
|
||||
ECStoragePolicy(0, 'ec8-2', aliases='zeus, jupiter',
|
||||
ec_type=DEFAULT_TEST_EC_TYPE,
|
||||
ec_ndata=8, ec_nparity=2,
|
||||
object_ring=FakeRing(replicas=8),
|
||||
is_default=True),
|
||||
ECStoragePolicy(1, 'ec10-4', aliases='athena, minerva',
|
||||
ec_type=DEFAULT_TEST_EC_TYPE,
|
||||
ec_ndata=10, ec_nparity=4,
|
||||
object_ring=FakeRing(replicas=10)),
|
||||
ECStoragePolicy(2, 'ec4-2', aliases='poseidon, neptune',
|
||||
ec_type=DEFAULT_TEST_EC_TYPE,
|
||||
ec_ndata=4, ec_nparity=2,
|
||||
object_ring=FakeRing(replicas=7)),
|
||||
]
|
||||
ec_policies = StoragePolicyCollection(good_test_policies_EC)
|
||||
|
||||
for name in ('ec8-2', 'zeus', 'jupiter'):
|
||||
self.assertEqual(ec_policies.get_by_name(name), ec_policies[0])
|
||||
for name in ('ec10-4', 'athena', 'minerva'):
|
||||
self.assertEqual(ec_policies.get_by_name(name), ec_policies[1])
|
||||
|
||||
# Testing parsing of conf files/text
|
||||
good_ec_conf = self._conf("""
|
||||
[storage-policy:0]
|
||||
name = ec8-2
|
||||
aliases = zeus, jupiter
|
||||
policy_type = erasure_coding
|
||||
ec_type = %(ec_type)s
|
||||
default = yes
|
||||
ec_num_data_fragments = 8
|
||||
ec_num_parity_fragments = 2
|
||||
[storage-policy:1]
|
||||
name = ec10-4
|
||||
aliases = poseidon, neptune
|
||||
policy_type = erasure_coding
|
||||
ec_type = %(ec_type)s
|
||||
ec_num_data_fragments = 10
|
||||
ec_num_parity_fragments = 4
|
||||
""" % {'ec_type': DEFAULT_TEST_EC_TYPE})
|
||||
|
||||
ec_policies = parse_storage_policies(good_ec_conf)
|
||||
self.assertEqual(ec_policies.get_by_name('ec8-2'),
|
||||
ec_policies[0])
|
||||
self.assertEqual(ec_policies.get_by_name('ec10-4'),
|
||||
ec_policies.get_by_name('poseidon'))
|
||||
|
||||
name_repeat_ec_conf = self._conf("""
|
||||
[storage-policy:0]
|
||||
name = ec8-2
|
||||
aliases = ec8-2
|
||||
policy_type = erasure_coding
|
||||
ec_type = %(ec_type)s
|
||||
default = yes
|
||||
ec_num_data_fragments = 8
|
||||
ec_num_parity_fragments = 2
|
||||
""" % {'ec_type': DEFAULT_TEST_EC_TYPE})
|
||||
# Test on line below should not generate errors. Repeat of main
|
||||
# name under aliases is permitted during construction
|
||||
# but only because automated testing requires it.
|
||||
ec_policies = parse_storage_policies(name_repeat_ec_conf)
|
||||
|
||||
bad_ec_conf = self._conf("""
|
||||
[storage-policy:0]
|
||||
name = ec8-2
|
||||
aliases = zeus, zeus
|
||||
policy_type = erasure_coding
|
||||
ec_type = %(ec_type)s
|
||||
default = yes
|
||||
ec_num_data_fragments = 8
|
||||
ec_num_parity_fragments = 2
|
||||
""" % {'ec_type': DEFAULT_TEST_EC_TYPE})
|
||||
self.assertRaisesWithMessage(PolicyError,
|
||||
'is already assigned to this policy',
|
||||
parse_storage_policies, bad_ec_conf)
|
||||
|
||||
def test_add_remove_names(self):
|
||||
test_policies = [StoragePolicy(0, 'zero', True),
|
||||
StoragePolicy(1, 'one', False),
|
||||
StoragePolicy(2, 'two', False)]
|
||||
policies = StoragePolicyCollection(test_policies)
|
||||
|
||||
# add names
|
||||
policies.add_policy_alias(1, 'tahi')
|
||||
self.assertEqual(policies.get_by_name('tahi'), test_policies[1])
|
||||
|
||||
policies.add_policy_alias(2, 'rua', 'dos')
|
||||
self.assertEqual(policies.get_by_name('rua'), test_policies[2])
|
||||
self.assertEqual(policies.get_by_name('dos'), test_policies[2])
|
||||
|
||||
self.assertRaisesWithMessage(PolicyError, 'Invalid name',
|
||||
policies.add_policy_alias, 2, 'double\n')
|
||||
|
||||
# try to add existing name
|
||||
self.assertRaisesWithMessage(PolicyError, 'Duplicate name',
|
||||
policies.add_policy_alias, 2, 'two')
|
||||
|
||||
self.assertRaisesWithMessage(PolicyError, 'Duplicate name',
|
||||
policies.add_policy_alias, 1, 'two')
|
||||
|
||||
# remove name
|
||||
policies.remove_policy_alias('tahi')
|
||||
self.assertEqual(policies.get_by_name('tahi'), None)
|
||||
|
||||
# remove only name
|
||||
self.assertRaisesWithMessage(PolicyError,
|
||||
'Policies must have at least one name.',
|
||||
policies.remove_policy_alias, 'zero')
|
||||
|
||||
# remove non-existent name
|
||||
self.assertRaisesWithMessage(PolicyError,
|
||||
'No policy with name',
|
||||
policies.remove_policy_alias, 'three')
|
||||
|
||||
# remove default name
|
||||
policies.remove_policy_alias('two')
|
||||
self.assertEqual(policies.get_by_name('two'), None)
|
||||
self.assertEqual(policies.get_by_index(2).name, 'rua')
|
||||
|
||||
# change default name to a new name
|
||||
policies.change_policy_primary_name(2, 'two')
|
||||
self.assertEqual(policies.get_by_name('two'), test_policies[2])
|
||||
self.assertEqual(policies.get_by_index(2).name, 'two')
|
||||
|
||||
# change default name to an existing alias
|
||||
policies.change_policy_primary_name(2, 'dos')
|
||||
self.assertEqual(policies.get_by_index(2).name, 'dos')
|
||||
|
||||
# change default name to a bad new name
|
||||
self.assertRaisesWithMessage(PolicyError, 'Invalid name',
|
||||
policies.change_policy_primary_name,
|
||||
2, 'bad\nname')
|
||||
|
||||
# change default name to a name belonging to another policy
|
||||
self.assertRaisesWithMessage(PolicyError,
|
||||
'Other policy',
|
||||
policies.change_policy_primary_name,
|
||||
1, 'dos')
|
||||
|
||||
def test_deprecated_default(self):
|
||||
bad_conf = self._conf("""
|
||||
[storage-policy:1]
|
||||
@ -815,7 +1030,7 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
part_shift=24)
|
||||
|
||||
with mock.patch(
|
||||
'swift.common.storage_policy.RingData.load'
|
||||
'swift.common.storage_policy.RingData.load'
|
||||
) as mock_ld, \
|
||||
patch_policies(test_policies), \
|
||||
mock.patch('swift.common.storage_policy.whataremyips') \
|
||||
@ -933,14 +1148,14 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
msg = 'EC ring for policy %s needs to be configured with ' \
|
||||
'exactly %d nodes.' % \
|
||||
(policy.name, policy.ec_ndata + policy.ec_nparity)
|
||||
self.assertRaisesWithMessage(
|
||||
RingValidationError, msg,
|
||||
policy._validate_ring)
|
||||
self.assertRaisesWithMessage(RingValidationError, msg,
|
||||
policy._validate_ring)
|
||||
|
||||
def test_storage_policy_get_info(self):
|
||||
test_policies = [
|
||||
StoragePolicy(0, 'zero', is_default=True),
|
||||
StoragePolicy(1, 'one', is_deprecated=True),
|
||||
StoragePolicy(1, 'one', is_deprecated=True,
|
||||
aliases='tahi, uno'),
|
||||
ECStoragePolicy(10, 'ten',
|
||||
ec_type=DEFAULT_TEST_EC_TYPE,
|
||||
ec_ndata=10, ec_nparity=3),
|
||||
@ -953,28 +1168,33 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
# default replication
|
||||
(0, True): {
|
||||
'name': 'zero',
|
||||
'aliases': 'zero',
|
||||
'default': True,
|
||||
'deprecated': False,
|
||||
'policy_type': REPL_POLICY
|
||||
},
|
||||
(0, False): {
|
||||
'name': 'zero',
|
||||
'aliases': 'zero',
|
||||
'default': True,
|
||||
},
|
||||
# deprecated replication
|
||||
(1, True): {
|
||||
'name': 'one',
|
||||
'aliases': 'one, tahi, uno',
|
||||
'default': False,
|
||||
'deprecated': True,
|
||||
'policy_type': REPL_POLICY
|
||||
},
|
||||
(1, False): {
|
||||
'name': 'one',
|
||||
'aliases': 'one, tahi, uno',
|
||||
'deprecated': True,
|
||||
},
|
||||
# enabled ec
|
||||
(10, True): {
|
||||
'name': 'ten',
|
||||
'aliases': 'ten',
|
||||
'default': False,
|
||||
'deprecated': False,
|
||||
'policy_type': EC_POLICY,
|
||||
@ -985,10 +1205,12 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
},
|
||||
(10, False): {
|
||||
'name': 'ten',
|
||||
'aliases': 'ten',
|
||||
},
|
||||
# deprecated ec
|
||||
(11, True): {
|
||||
'name': 'done',
|
||||
'aliases': 'done',
|
||||
'default': False,
|
||||
'deprecated': True,
|
||||
'policy_type': EC_POLICY,
|
||||
@ -999,6 +1221,7 @@ class TestStoragePolicies(unittest.TestCase):
|
||||
},
|
||||
(11, False): {
|
||||
'name': 'done',
|
||||
'aliases': 'done',
|
||||
'deprecated': True,
|
||||
},
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user