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
|
the storage policy index. Note that internally, Swift relies on policy
|
||||||
indexes and not policy names. Policy names exist for human readability and
|
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
|
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,
|
specified, the default policy is used (and if no other policies defined,
|
||||||
Policy-0 is considered the default). We will be covering the difference
|
Policy-0 is considered the default). We will be covering the difference
|
||||||
between default and Policy-0 in the next section.
|
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
|
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"
|
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
|
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
|
a single name and an arbitrary number of aliases (human friendly,
|
||||||
policy number). Swift reserves index 0 to map to the object ring that's
|
configurable) as well as an index (or simply policy number). Swift reserves
|
||||||
present in all installations (e.g., ``/etc/swift/object.ring.gz``). You can
|
index 0 to map to the object ring that's present in all installations
|
||||||
name this policy anything you like, and if no policies are defined it will
|
(e.g., ``/etc/swift/object.ring.gz``). You can name this policy anything you
|
||||||
report itself as ``Policy-0``, however you cannot change the index as there must
|
like, and if no policies are defined it will report itself as ``Policy-0``,
|
||||||
always be a policy with index 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
|
Another important concept is the default policy which can be any policy
|
||||||
in the cluster. The default policy is the policy that is automatically
|
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 contain only letters, digits or a dash
|
||||||
* Policy names must be unique
|
* Policy names must be unique
|
||||||
* The policy name 'Policy-0' can only be used for the policy with index 0
|
* 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
|
* If any policies are defined, exactly one policy must be declared default
|
||||||
* Deprecated policies cannot be declared the default
|
* Deprecated policies cannot be declared the default
|
||||||
* If no ``policy_type`` is provided, ``replication`` is the default value.
|
* If no ``policy_type`` is provided, ``replication`` is the default value.
|
||||||
@ -288,6 +291,7 @@ example configuration.::
|
|||||||
|
|
||||||
[storage-policy:0]
|
[storage-policy:0]
|
||||||
name = gold
|
name = gold
|
||||||
|
aliases = yellow, orange
|
||||||
policy_type = replication
|
policy_type = replication
|
||||||
default = yes
|
default = yes
|
||||||
|
|
||||||
@ -301,8 +305,10 @@ information about the ``default`` and ``deprecated`` options.
|
|||||||
|
|
||||||
There are some other considerations when managing policies:
|
There are some other considerations when managing policies:
|
||||||
|
|
||||||
* Policy names can be changed (but be sure that users are aware, aliases are
|
* Policy names can be changed.
|
||||||
not currently supported but could be implemented in custom middleware!)
|
* 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
|
* 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
|
* The default policy can be changed at any time, by adding the
|
||||||
default directive to the desired policy section
|
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
|
configured policies via class :class:`.StoragePolicyCollection`. This
|
||||||
collection is made up of policies of class :class:`.StoragePolicy`. The
|
collection is made up of policies of class :class:`.StoragePolicy`. The
|
||||||
collection class includes handy functions for getting to a policy either by
|
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`.
|
very important function, :meth:`~.StoragePolicyCollection.get_object_ring`.
|
||||||
Object rings are members of the :class:`.StoragePolicy` class and are
|
Object rings are members of the :class:`.StoragePolicy` class and are
|
||||||
actually not instantiated until the :meth:`~.StoragePolicy.load_ring`
|
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]
|
[storage-policy:0]
|
||||||
name = gold
|
name = gold
|
||||||
|
aliases = yellow, orange
|
||||||
default = yes
|
default = yes
|
||||||
|
|
||||||
[storage-policy:1]
|
[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)::
|
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
|
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``
|
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
|
# 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
|
# defined you must define a policy with index 0 and you must specify a
|
||||||
# default. It is recommended you always define a section for
|
# 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
|
# A 'policy_type' argument is also supported but is not mandatory. Default
|
||||||
# policy type 'replication' is used when 'policy_type' is unspecified.
|
# policy type 'replication' is used when 'policy_type' is unspecified.
|
||||||
@ -29,6 +29,7 @@ swift_hash_path_prefix = changeme
|
|||||||
name = Policy-0
|
name = Policy-0
|
||||||
default = yes
|
default = yes
|
||||||
#policy_type = replication
|
#policy_type = replication
|
||||||
|
aliases = yellow, orange
|
||||||
|
|
||||||
# the following section would declare a policy called 'silver', the number of
|
# 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
|
# 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
|
# 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
|
# (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
|
# 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]
|
#[storage-policy:1]
|
||||||
#name = silver
|
#name = silver
|
||||||
#policy_type = replication
|
#policy_type = replication
|
||||||
@ -67,12 +71,13 @@ default = yes
|
|||||||
# refer to Swift documentation for details on how to configure EC policies.
|
# refer to Swift documentation for details on how to configure EC policies.
|
||||||
#
|
#
|
||||||
# The example 'deepfreeze10-4' policy defined below is a _sample_
|
# The example 'deepfreeze10-4' policy defined below is a _sample_
|
||||||
# configuration with 10 'data' and 4 'parity' fragments. 'ec_type'
|
# configuration with an alias of 'df10-4' as well as 10 'data' and 4 'parity'
|
||||||
# defines the Erasure Coding scheme. 'jerasure_rs_vand' (Reed-Solomon
|
# fragments. 'ec_type' defines the Erasure Coding scheme.
|
||||||
# Vandermonde) is used as an example below.
|
# 'jerasure_rs_vand' (Reed-Solomon Vandermonde) is used as an example below.
|
||||||
#
|
#
|
||||||
#[storage-policy:2]
|
#[storage-policy:2]
|
||||||
#name = deepfreeze10-4
|
#name = deepfreeze10-4
|
||||||
|
#aliases = df10-4
|
||||||
#policy_type = erasure_coding
|
#policy_type = erasure_coding
|
||||||
#ec_type = jerasure_rs_vand
|
#ec_type = jerasure_rs_vand
|
||||||
#ec_num_data_fragments = 10
|
#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 string
|
||||||
import textwrap
|
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)
|
config_true_value, SWIFT_CONF_FILE, whataremyips, list_from_csv)
|
||||||
from swift.common.ring import Ring, RingData
|
from swift.common.ring import Ring, RingData
|
||||||
from swift.common.utils import quorum_size
|
from swift.common.utils import quorum_size
|
||||||
from swift.common.exceptions import RingValidationError
|
from swift.common.exceptions import RingValidationError
|
||||||
@ -84,7 +82,6 @@ class BindPortsCache(object):
|
|||||||
|
|
||||||
|
|
||||||
class PolicyError(ValueError):
|
class PolicyError(ValueError):
|
||||||
|
|
||||||
def __init__(self, msg, index=None):
|
def __init__(self, msg, index=None):
|
||||||
if index is not None:
|
if index is not None:
|
||||||
msg += ', for index %r' % index
|
msg += ', for index %r' % index
|
||||||
@ -161,7 +158,7 @@ class BaseStoragePolicy(object):
|
|||||||
policy_type_to_policy_cls = {}
|
policy_type_to_policy_cls = {}
|
||||||
|
|
||||||
def __init__(self, idx, name='', is_default=False, is_deprecated=False,
|
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
|
# do not allow BaseStoragePolicy class to be instantiated directly
|
||||||
if type(self) == BaseStoragePolicy:
|
if type(self) == BaseStoragePolicy:
|
||||||
raise TypeError("Can't instantiate BaseStoragePolicy directly")
|
raise TypeError("Can't instantiate BaseStoragePolicy directly")
|
||||||
@ -172,18 +169,17 @@ class BaseStoragePolicy(object):
|
|||||||
raise PolicyError('Invalid index', idx)
|
raise PolicyError('Invalid index', idx)
|
||||||
if self.idx < 0:
|
if self.idx < 0:
|
||||||
raise PolicyError('Invalid index', idx)
|
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)
|
raise PolicyError('Invalid name %r' % name, idx)
|
||||||
# this is defensively restrictive, but could be expanded in the future
|
self.alias_list.append(name)
|
||||||
if not all(c in VALID_CHARS for c in name):
|
if aliases:
|
||||||
raise PolicyError('Names are used as HTTP headers, and can not '
|
names_list = list_from_csv(aliases)
|
||||||
'reliably contain any characters not in %r. '
|
for alias in names_list:
|
||||||
'Invalid name %r' % (VALID_CHARS, name))
|
if alias == name:
|
||||||
if name.upper() == LEGACY_POLICY_NAME.upper() and self.idx != 0:
|
continue
|
||||||
msg = 'The name %s is reserved for policy index 0. ' \
|
self._validate_policy_name(alias)
|
||||||
'Invalid name %r' % (LEGACY_POLICY_NAME, name)
|
self.alias_list.append(alias)
|
||||||
raise PolicyError(msg, idx)
|
|
||||||
self.name = name
|
|
||||||
self.is_deprecated = config_true_value(is_deprecated)
|
self.is_deprecated = config_true_value(is_deprecated)
|
||||||
self.is_default = config_true_value(is_default)
|
self.is_default = config_true_value(is_default)
|
||||||
if self.policy_type not in BaseStoragePolicy.policy_type_to_policy_cls:
|
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:
|
if self.is_deprecated and self.is_default:
|
||||||
raise PolicyError('Deprecated policy can not be default. '
|
raise PolicyError('Deprecated policy can not be default. '
|
||||||
'Invalid config', self.idx)
|
'Invalid config', self.idx)
|
||||||
|
|
||||||
self.ring_name = _get_policy_string('object', self.idx)
|
self.ring_name = _get_policy_string('object', self.idx)
|
||||||
self.object_ring = object_ring
|
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):
|
def __int__(self):
|
||||||
return self.idx
|
return self.idx
|
||||||
|
|
||||||
@ -203,8 +213,8 @@ class BaseStoragePolicy(object):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return ("%s(%d, %r, is_default=%s, "
|
return ("%s(%d, %r, is_default=%s, "
|
||||||
"is_deprecated=%s, policy_type=%r)") % \
|
"is_deprecated=%s, policy_type=%r)") % \
|
||||||
(self.__class__.__name__, self.idx, self.name,
|
(self.__class__.__name__, self.idx, self.alias_list,
|
||||||
self.is_default, self.is_deprecated, self.policy_type)
|
self.is_default, self.is_deprecated, self.policy_type)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register(cls, policy_type):
|
def register(cls, policy_type):
|
||||||
@ -213,6 +223,7 @@ class BaseStoragePolicy(object):
|
|||||||
their StoragePolicy class. This will also set the policy_type
|
their StoragePolicy class. This will also set the policy_type
|
||||||
attribute on the registered implementation.
|
attribute on the registered implementation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def register_wrapper(policy_cls):
|
def register_wrapper(policy_cls):
|
||||||
if policy_type in cls.policy_type_to_policy_cls:
|
if policy_type in cls.policy_type_to_policy_cls:
|
||||||
raise PolicyError(
|
raise PolicyError(
|
||||||
@ -222,6 +233,7 @@ class BaseStoragePolicy(object):
|
|||||||
cls.policy_type_to_policy_cls[policy_type] = policy_cls
|
cls.policy_type_to_policy_cls[policy_type] = policy_cls
|
||||||
policy_cls.policy_type = policy_type
|
policy_cls.policy_type = policy_type
|
||||||
return policy_cls
|
return policy_cls
|
||||||
|
|
||||||
return register_wrapper
|
return register_wrapper
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -231,6 +243,7 @@ class BaseStoragePolicy(object):
|
|||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'name': 'name',
|
'name': 'name',
|
||||||
|
'aliases': 'aliases',
|
||||||
'policy_type': 'policy_type',
|
'policy_type': 'policy_type',
|
||||||
'default': 'is_default',
|
'default': 'is_default',
|
||||||
'deprecated': 'is_deprecated',
|
'deprecated': 'is_deprecated',
|
||||||
@ -269,6 +282,77 @@ class BaseStoragePolicy(object):
|
|||||||
info.pop('policy_type')
|
info.pop('policy_type')
|
||||||
return info
|
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):
|
def _validate_ring(self):
|
||||||
"""
|
"""
|
||||||
Hook, called when the ring is loaded. Can be used to
|
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
|
:func:`~swift.common.storage_policy.reload_storage_policies` to load
|
||||||
POLICIES from ``swift.conf``.
|
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,
|
is_deprecated=False, object_ring=None,
|
||||||
ec_segment_size=DEFAULT_EC_OBJECT_SEGMENT_SIZE,
|
ec_segment_size=DEFAULT_EC_OBJECT_SEGMENT_SIZE,
|
||||||
ec_type=None, ec_ndata=None, ec_nparity=None):
|
ec_type=None, ec_ndata=None, ec_nparity=None):
|
||||||
|
|
||||||
super(ECStoragePolicy, self).__init__(
|
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
|
# Validate erasure_coding policy specific members
|
||||||
# ec_type is one of the EC implementations supported by PyEClib
|
# ec_type is one of the EC implementations supported by PyEClib
|
||||||
@ -441,9 +527,9 @@ class ECStoragePolicy(BaseStoragePolicy):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return ("%s, EC config(ec_type=%s, ec_segment_size=%d, "
|
return ("%s, EC config(ec_type=%s, ec_segment_size=%d, "
|
||||||
"ec_ndata=%d, ec_nparity=%d)") % (
|
"ec_ndata=%d, ec_nparity=%d)") % \
|
||||||
super(ECStoragePolicy, self).__repr__(), self.ec_type,
|
(super(ECStoragePolicy, self).__repr__(), self.ec_type,
|
||||||
self.ec_segment_size, self.ec_ndata, self.ec_nparity)
|
self.ec_segment_size, self.ec_ndata, self.ec_nparity)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _config_options_map(cls):
|
def _config_options_map(cls):
|
||||||
@ -532,6 +618,7 @@ class StoragePolicyCollection(object):
|
|||||||
* Deprecated policies can not be declared the default
|
* Deprecated policies can not be declared the default
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, pols):
|
def __init__(self, pols):
|
||||||
self.default = []
|
self.default = []
|
||||||
self.by_name = {}
|
self.by_name = {}
|
||||||
@ -542,7 +629,8 @@ class StoragePolicyCollection(object):
|
|||||||
"""
|
"""
|
||||||
Add pre-validated policies to internal indexes.
|
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
|
self.by_index[int(policy)] = policy
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -570,9 +658,10 @@ class StoragePolicyCollection(object):
|
|||||||
if int(policy) in self.by_index:
|
if int(policy) in self.by_index:
|
||||||
raise PolicyError('Duplicate index %s conflicts with %s' % (
|
raise PolicyError('Duplicate index %s conflicts with %s' % (
|
||||||
policy, self.get_by_index(int(policy))))
|
policy, self.get_by_index(int(policy))))
|
||||||
if policy.name.upper() in self.by_name:
|
for name in policy.alias_list:
|
||||||
raise PolicyError('Duplicate name %s conflicts with %s' % (
|
if name.upper() in self.by_name:
|
||||||
policy, self.get_by_name(policy.name)))
|
raise PolicyError('Duplicate name %s conflicts with %s' % (
|
||||||
|
policy, self.get_by_name(name)))
|
||||||
if policy.is_default:
|
if policy.is_default:
|
||||||
if not self.default:
|
if not self.default:
|
||||||
self.default = policy
|
self.default = policy
|
||||||
@ -667,6 +756,62 @@ class StoragePolicyCollection(object):
|
|||||||
policy_info.append(policy_entry)
|
policy_info.append(policy_entry)
|
||||||
return policy_info
|
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):
|
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 os
|
||||||
import mock
|
import mock
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from six.moves.configparser import ConfigParser
|
from six.moves.configparser import ConfigParser
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from test.unit import patch_policies, FakeRing, temptree, DEFAULT_TEST_EC_TYPE
|
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 StoragePolicy class - the only user at the moment is
|
||||||
test_validate_policies_type_invalid()
|
test_validate_policies_type_invalid()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, idx, name='', is_default=False, is_deprecated=False,
|
def __init__(self, idx, name='', is_default=False, is_deprecated=False,
|
||||||
object_ring=None):
|
object_ring=None):
|
||||||
super(FakeStoragePolicy, self).__init__(
|
super(FakeStoragePolicy, self).__init__(
|
||||||
@ -43,7 +43,6 @@ class FakeStoragePolicy(BaseStoragePolicy):
|
|||||||
|
|
||||||
|
|
||||||
class TestStoragePolicies(unittest.TestCase):
|
class TestStoragePolicies(unittest.TestCase):
|
||||||
|
|
||||||
def _conf(self, conf_str):
|
def _conf(self, conf_str):
|
||||||
conf_str = "\n".join(line.strip() for line in conf_str.split("\n"))
|
conf_str = "\n".join(line.strip() for line in conf_str.split("\n"))
|
||||||
conf = ConfigParser()
|
conf = ConfigParser()
|
||||||
@ -75,10 +74,10 @@ class TestStoragePolicies(unittest.TestCase):
|
|||||||
])
|
])
|
||||||
def test_swift_info(self):
|
def test_swift_info(self):
|
||||||
# the deprecated 'three' should not exist in expect
|
# the deprecated 'three' should not exist in expect
|
||||||
expect = [{'default': True, 'name': 'zero'},
|
expect = [{'aliases': 'zero', 'default': True, 'name': 'zero', },
|
||||||
{'name': 'two'},
|
{'aliases': 'two', 'name': 'two'},
|
||||||
{'name': 'one'},
|
{'aliases': 'one', 'name': 'one'},
|
||||||
{'name': 'ten'}]
|
{'aliases': 'ten', 'name': 'ten'}]
|
||||||
swift_info = POLICIES.get_policy_info()
|
swift_info = POLICIES.get_policy_info()
|
||||||
self.assertEqual(sorted(expect, key=lambda k: k['name']),
|
self.assertEqual(sorted(expect, key=lambda k: k['name']),
|
||||||
sorted(swift_info, 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):
|
def test_validate_policies_type_invalid(self):
|
||||||
class BogusStoragePolicy(FakeStoragePolicy):
|
class BogusStoragePolicy(FakeStoragePolicy):
|
||||||
policy_type = 'bogus'
|
policy_type = 'bogus'
|
||||||
|
|
||||||
# unsupported policy type - initialization with FakeStoragePolicy
|
# unsupported policy type - initialization with FakeStoragePolicy
|
||||||
self.assertRaisesWithMessage(PolicyError, 'Invalid type',
|
self.assertRaisesWithMessage(PolicyError, 'Invalid type',
|
||||||
BogusStoragePolicy, 1, 'one')
|
BogusStoragePolicy, 1, 'one')
|
||||||
@ -330,6 +330,221 @@ class TestStoragePolicies(unittest.TestCase):
|
|||||||
self.assertEqual(pol1, policies.get_by_name(name))
|
self.assertEqual(pol1, policies.get_by_name(name))
|
||||||
self.assertEqual(policies.get_by_name(name).name, 'One')
|
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):
|
def test_deprecated_default(self):
|
||||||
bad_conf = self._conf("""
|
bad_conf = self._conf("""
|
||||||
[storage-policy:1]
|
[storage-policy:1]
|
||||||
@ -815,7 +1030,7 @@ class TestStoragePolicies(unittest.TestCase):
|
|||||||
part_shift=24)
|
part_shift=24)
|
||||||
|
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
'swift.common.storage_policy.RingData.load'
|
'swift.common.storage_policy.RingData.load'
|
||||||
) as mock_ld, \
|
) as mock_ld, \
|
||||||
patch_policies(test_policies), \
|
patch_policies(test_policies), \
|
||||||
mock.patch('swift.common.storage_policy.whataremyips') \
|
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 ' \
|
msg = 'EC ring for policy %s needs to be configured with ' \
|
||||||
'exactly %d nodes.' % \
|
'exactly %d nodes.' % \
|
||||||
(policy.name, policy.ec_ndata + policy.ec_nparity)
|
(policy.name, policy.ec_ndata + policy.ec_nparity)
|
||||||
self.assertRaisesWithMessage(
|
self.assertRaisesWithMessage(RingValidationError, msg,
|
||||||
RingValidationError, msg,
|
policy._validate_ring)
|
||||||
policy._validate_ring)
|
|
||||||
|
|
||||||
def test_storage_policy_get_info(self):
|
def test_storage_policy_get_info(self):
|
||||||
test_policies = [
|
test_policies = [
|
||||||
StoragePolicy(0, 'zero', is_default=True),
|
StoragePolicy(0, 'zero', is_default=True),
|
||||||
StoragePolicy(1, 'one', is_deprecated=True),
|
StoragePolicy(1, 'one', is_deprecated=True,
|
||||||
|
aliases='tahi, uno'),
|
||||||
ECStoragePolicy(10, 'ten',
|
ECStoragePolicy(10, 'ten',
|
||||||
ec_type=DEFAULT_TEST_EC_TYPE,
|
ec_type=DEFAULT_TEST_EC_TYPE,
|
||||||
ec_ndata=10, ec_nparity=3),
|
ec_ndata=10, ec_nparity=3),
|
||||||
@ -953,28 +1168,33 @@ class TestStoragePolicies(unittest.TestCase):
|
|||||||
# default replication
|
# default replication
|
||||||
(0, True): {
|
(0, True): {
|
||||||
'name': 'zero',
|
'name': 'zero',
|
||||||
|
'aliases': 'zero',
|
||||||
'default': True,
|
'default': True,
|
||||||
'deprecated': False,
|
'deprecated': False,
|
||||||
'policy_type': REPL_POLICY
|
'policy_type': REPL_POLICY
|
||||||
},
|
},
|
||||||
(0, False): {
|
(0, False): {
|
||||||
'name': 'zero',
|
'name': 'zero',
|
||||||
|
'aliases': 'zero',
|
||||||
'default': True,
|
'default': True,
|
||||||
},
|
},
|
||||||
# deprecated replication
|
# deprecated replication
|
||||||
(1, True): {
|
(1, True): {
|
||||||
'name': 'one',
|
'name': 'one',
|
||||||
|
'aliases': 'one, tahi, uno',
|
||||||
'default': False,
|
'default': False,
|
||||||
'deprecated': True,
|
'deprecated': True,
|
||||||
'policy_type': REPL_POLICY
|
'policy_type': REPL_POLICY
|
||||||
},
|
},
|
||||||
(1, False): {
|
(1, False): {
|
||||||
'name': 'one',
|
'name': 'one',
|
||||||
|
'aliases': 'one, tahi, uno',
|
||||||
'deprecated': True,
|
'deprecated': True,
|
||||||
},
|
},
|
||||||
# enabled ec
|
# enabled ec
|
||||||
(10, True): {
|
(10, True): {
|
||||||
'name': 'ten',
|
'name': 'ten',
|
||||||
|
'aliases': 'ten',
|
||||||
'default': False,
|
'default': False,
|
||||||
'deprecated': False,
|
'deprecated': False,
|
||||||
'policy_type': EC_POLICY,
|
'policy_type': EC_POLICY,
|
||||||
@ -985,10 +1205,12 @@ class TestStoragePolicies(unittest.TestCase):
|
|||||||
},
|
},
|
||||||
(10, False): {
|
(10, False): {
|
||||||
'name': 'ten',
|
'name': 'ten',
|
||||||
|
'aliases': 'ten',
|
||||||
},
|
},
|
||||||
# deprecated ec
|
# deprecated ec
|
||||||
(11, True): {
|
(11, True): {
|
||||||
'name': 'done',
|
'name': 'done',
|
||||||
|
'aliases': 'done',
|
||||||
'default': False,
|
'default': False,
|
||||||
'deprecated': True,
|
'deprecated': True,
|
||||||
'policy_type': EC_POLICY,
|
'policy_type': EC_POLICY,
|
||||||
@ -999,6 +1221,7 @@ class TestStoragePolicies(unittest.TestCase):
|
|||||||
},
|
},
|
||||||
(11, False): {
|
(11, False): {
|
||||||
'name': 'done',
|
'name': 'done',
|
||||||
|
'aliases': 'done',
|
||||||
'deprecated': True,
|
'deprecated': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user