Add support for storage policies to have more than one name
This patch alters storage_policy.py to allow storage policies to have multiple names. Now users are able to add a number of human-readable aliases for storage policies. Policies now have a .name (the default name), .aliases (a string of comma seperated aliases), and .aliases_list (a list of all human readable names). Policies will always have an .aliases value if no aliases are set it will contain the default name. The policy docs and tests have been updated to reflect changes and policy.get_policy_info has been altered to display the name and aliases Change-Id: I02967ca8d7c790595e5ee551581196aa64552eea
This commit is contained in:
parent
09133f5bd9
commit
211758f8cb
@ -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…
Reference in New Issue
Block a user