Add SpaceClaim object

We need to "transfer" non numeric sizes from deployment config to
deployment driver. Now we have only one such "size" - this is
"ramaining" constant.

Also this object knows(will know), how to pass this claim to the storage
object.

Change-Id: I1956a0fcf60bfa5c0c3eda7ecb7c3dda176880ca
This commit is contained in:
Dmitry Bogun 2016-12-26 12:51:09 +02:00 committed by Andrii Ostapenko
parent 9abb3c5d80
commit 31486e41d4
3 changed files with 106 additions and 2 deletions

View File

@ -95,6 +95,22 @@ class TestDeviceFinder(unittest2.TestCase):
errors.BlockDeviceNotFoundError, finder, kind, needle)
class TestSpaceClaim(unittest2.TestCase):
def test_percentage(self):
claim = block_device.SpaceClaim.new_by_sizeunit(
block_device.SizeUnit.new_by_string('20 %'))
self.assertEqual(
(block_device.SpaceClaim.KIND_PERCENTAGE, (20, '%')),
(claim.kind, (claim.size.value, claim.size.unit)))
def test_exact(self):
claim = block_device.SpaceClaim.new_by_sizeunit(
block_device.SizeUnit.new_by_string('100 MiB'))
self.assertEqual(
(block_device.SpaceClaim.KIND_EXACT, (100, 'MiB')),
(claim.kind, (claim.size.value, claim.size.unit)))
class TestSizeUnit(unittest2.TestCase):
def test_all_suffixes(self):
for value, suffix, expect in (

View File

@ -94,11 +94,65 @@ class DeviceFinder(object):
return end
class SizeUnit(object):
# TODO(dbogun): this object can/should be removed
# The only reason why it exists - to keep "remaining" size. All other kinds can
# be stored into SizeUnit.
class SpaceClaim(utils.EqualComparisonMixin, object):
_kind = itertools.count()
KIND_EXACT = next(_kind)
KIND_PERCENTAGE = next(_kind)
KIND_BIGGEST = next(_kind)
del _kind
_kind_names = {
KIND_EXACT: 'EXACT',
KIND_PERCENTAGE: 'PERCENTAGE',
KIND_BIGGEST: 'REMAINING'}
@classmethod
def new_biggest(cls):
return cls(None, cls.KIND_BIGGEST)
@classmethod
def new_by_sizeunit(cls, size):
if size.unit == '%':
return cls.new_percent(size)
return cls.new_exact(size)
@classmethod
def new_percent(cls, value):
if not isinstance(value, SizeUnit):
value = SizeUnit.new_by_string('{} %'.format(value))
if value.unit != '%':
raise TypeError('Unsuitable value for percentage space claim: '
'{!r}'.format(value))
return cls(value, cls.KIND_PERCENTAGE)
@classmethod
def new_exact(cls, value):
if not isinstance(value, SizeUnit):
value = SizeUnit.new_by_string('{} B'.format(value))
if value.bytes is None:
raise TypeError('Unsuitable value for exact space claim: '
'{!r}'.format(value))
return cls(value, cls.KIND_EXACT)
def __init__(self, size, kind):
self.size = size
self.kind = kind
def __call__(self, storage, from_tail=False):
raise NotImplementedError
def __repr__(self):
return '<{} {}:{!r}>'.format(
type(self).__name__, self._kind_names[self.kind], self.size)
class SizeUnit(utils.EqualComparisonMixin, object):
bytes = None
_unit_multiplier = {
'max': None,
'%': None,
's': 512,
'B': 1
@ -183,6 +237,12 @@ class SizeUnit(object):
if multiplier is not None:
self.bytes = int(value * multiplier)
def __repr__(self):
value = str(self)
if self.bytes is not None and self.unit != 'B':
value = '{} == {} B'.format(value, self.bytes)
return '<{}: {}>'.format(type(self).__name__, value)
def __str__(self):
if self.value == self.value_int:
value = self.value_int
@ -219,6 +279,18 @@ class SizeUnit(object):
'Illegal input for {}.value field: {!r}'.format(cls, value))
return value
@classmethod
def _comparable_shape(cls, target):
value = super(SizeUnit, cls)._comparable_shape(target)
if target.bytes is not None:
fields = ('bytes', )
else:
fields = ('value', 'value_int', 'unit')
value['payload'] = {
k: v for k, v in value['payload'].items()
if k in fields}
return value
class BlockDevicePayload(object):
def __init__(self, block, guid=None):

View File

@ -517,3 +517,19 @@ def list_opts():
:returns: a list of (group_name, opts) tuples
"""
return [(None, (u_opts))]
class EqualComparisonMixin(object):
def __eq__(self, other):
if not isinstance(other, type(self)):
return NotImplemented
return self._comparable_shape(self) == other._comparable_shape(other)
def __ne__(self, other):
return not self.__eq__(other)
@classmethod
def _comparable_shape(cls, target):
return {
'cls': cls,
'payload': vars(target)}