Implement new partioning mechanism
This mechanism is capable to work with new objects model and new block device utils. Change-Id: I16cb7ec25aa4c6a6fabffc047f50a5758bb19c06
This commit is contained in:
parent
2bd7569719
commit
a70b832447
@ -13,18 +13,26 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import abc
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from bareon.actions import partitioning
|
||||
from bareon.drivers.deploy.base import BaseDeployDriver
|
||||
from bareon.drivers.deploy import mixins
|
||||
from bareon import errors
|
||||
from bareon import objects
|
||||
from bareon.utils import block_device
|
||||
from bareon.utils import fs as fu
|
||||
from bareon.utils import grub as gu
|
||||
from bareon.utils import lvm as lu
|
||||
from bareon.utils import md as mu
|
||||
from bareon.utils import partition as pu
|
||||
from bareon.utils import utils
|
||||
|
||||
@ -447,3 +455,317 @@ class PartitionSchemaCompareTool(object):
|
||||
p['size'] = p['end'] - p['begin']
|
||||
del p['begin']
|
||||
del p['end']
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AbstractPartitionPolicy(object):
|
||||
space_allocation_accuracy = block_device.SizeUnit(1, 'MiB')
|
||||
|
||||
def __init__(self, deploy, storage):
|
||||
self.deploy_driver = deploy
|
||||
self.storage_claim = storage
|
||||
|
||||
self.dev_finder = block_device.DeviceFinder()
|
||||
|
||||
self.partition = self._make_partition_plan()
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self):
|
||||
pass
|
||||
|
||||
def dev_by_guid(self, guid):
|
||||
needle = 'disk/by-partuuid/{}'.format(guid.lower())
|
||||
dev_info = self.dev_finder('path', needle)
|
||||
return dev_info['device']
|
||||
|
||||
def _make_partition_plan(self):
|
||||
disks = {}
|
||||
for claim in self.storage_claim.items_by_kind(
|
||||
objects.block_device.Disk):
|
||||
LOG.info('Make partition plan for "%s"', claim.dev)
|
||||
disks[claim.dev] = self._disk_partition(claim)
|
||||
|
||||
return disks
|
||||
|
||||
def _disk_partition(self, claim):
|
||||
disk = block_device.Disk.new_by_scan(claim.dev, partitions=False)
|
||||
disk.allocate_accuracy = self.space_allocation_accuracy
|
||||
|
||||
remaining = None
|
||||
from_tail = []
|
||||
for idx, claim in enumerate(claim.items):
|
||||
if claim.size.kind == claim.size.KIND_BIGGEST:
|
||||
remaining = claim
|
||||
from_tail = claim.items[idx + 1:]
|
||||
break
|
||||
self._apply_claim(disk, claim)
|
||||
|
||||
from_tail.reverse()
|
||||
for claim in from_tail:
|
||||
self._apply_claim(disk, claim, from_tail=True)
|
||||
|
||||
if remaining is not None:
|
||||
self._apply_claim(disk, remaining)
|
||||
|
||||
return disk
|
||||
|
||||
def _lvm_vg_partition(self, claim):
|
||||
vg = block_device.LVM.new_by_scan(claim.idnr, lv=False)
|
||||
vg.allocate_accuracy = self.space_allocation_accuracy
|
||||
|
||||
remaining = None
|
||||
for lv in claim.items_by_kind(objects.block_device.LVMlv):
|
||||
if lv.size.kind == lv.size.KIND_BIGGEST:
|
||||
remaining = lv
|
||||
continue
|
||||
self._apply_claim(vg, lv)
|
||||
|
||||
if remaining is not None:
|
||||
self._apply_claim(vg, remaining)
|
||||
|
||||
return vg
|
||||
|
||||
def _handle_filesystems(self):
|
||||
for claim in self.storage_claim.items_by_kind(
|
||||
objects.block_device.Disk):
|
||||
self._resolv_disk_partitions(claim)
|
||||
|
||||
for claim in self.storage_claim.items_by_kind(
|
||||
objects.block_device.FileSystemMixin, recursion=True):
|
||||
self._make_filesystem(claim)
|
||||
|
||||
def _resolv_disk_partitions(self, claim_disk):
|
||||
partition_disk = self.partition[claim_disk.dev]
|
||||
actual_disk = block_device.Disk.new_by_scan(claim_disk.dev)
|
||||
|
||||
fuzzy_factor = actual_disk.sizeunit_to_blocks(
|
||||
self.space_allocation_accuracy)
|
||||
|
||||
claim_segments = []
|
||||
actual_segments = []
|
||||
for storage, target in (
|
||||
(partition_disk, claim_segments),
|
||||
(actual_disk, actual_segments)):
|
||||
for segment in storage.segments:
|
||||
if segment.kind != segment.KIND_BUSY:
|
||||
continue
|
||||
|
||||
segment.set_fuzzy_cmp_factor(fuzzy_factor)
|
||||
target.append(segment)
|
||||
|
||||
idx_iter = itertools.count()
|
||||
for claim, actual in itertools.izip_longest(
|
||||
claim_segments, actual_segments):
|
||||
idx = next(idx_iter)
|
||||
if claim == actual:
|
||||
if isinstance(
|
||||
claim.payload, objects.block_device.Partition):
|
||||
claim.payload.guid = actual.payload.guid
|
||||
continue
|
||||
|
||||
raise errors.PartitionSchemaMismatchError(
|
||||
'Unable to resolv claim devices into physical devices. '
|
||||
'Claim and physical devices partitions are different. '
|
||||
'(dev={}, {}: {!r} != {!r})'.format(
|
||||
claim_disk.dev, idx, claim, actual))
|
||||
|
||||
def _make_filesystem(self, claim):
|
||||
if not claim.file_system:
|
||||
return
|
||||
|
||||
if isinstance(claim, objects.block_device.Partition):
|
||||
dev = self.dev_by_guid(claim.guid)
|
||||
elif isinstance(claim, objects.block_device.MDRaid):
|
||||
dev = claim.name
|
||||
elif isinstance(claim, objects.block_device.LVMlv):
|
||||
dev = claim.dev
|
||||
else:
|
||||
raise errors.InternalError(exc_info=False)
|
||||
|
||||
# FIXME(dbogun): label
|
||||
fu.make_fs(claim.file_system, '', '', dev)
|
||||
|
||||
def _apply_claim(self, storage, claim, from_tail=False):
|
||||
segment = claim.size(storage, from_tail=from_tail)
|
||||
if isinstance(claim, objects.block_device.BlockDevice):
|
||||
if claim.is_service:
|
||||
segment.set_is_service()
|
||||
segment.payload = claim
|
||||
|
||||
|
||||
class PartitionPolicyClean(AbstractPartitionPolicy):
|
||||
def __call__(self):
|
||||
LOG.info('Apply "clean" partitions policy')
|
||||
|
||||
self._remove_all_compound_devices()
|
||||
|
||||
for dev in sorted(self.partition):
|
||||
self._handle_disk(self.partition[dev])
|
||||
|
||||
# update dev finder after all changes to disks
|
||||
self.dev_finder = block_device.DeviceFinder()
|
||||
|
||||
for md in self.storage_claim.items_by_kind(
|
||||
objects.block_device.MDRaid):
|
||||
self._handle_mdraid(md)
|
||||
for vg in self.storage_claim.items_by_kind(objects.block_device.LVMvg):
|
||||
self._handle_lvm(vg)
|
||||
|
||||
self._handle_filesystems()
|
||||
|
||||
def _remove_all_compound_devices(self):
|
||||
mu.mdclean_all()
|
||||
lu.lvremove_all()
|
||||
lu.vgremove_all()
|
||||
lu.pvremove_all()
|
||||
|
||||
def _handle_disk(self, disk):
|
||||
gdisk = block_device.GDisk(disk.dev)
|
||||
|
||||
gdisk.zap()
|
||||
try:
|
||||
idx = itertools.count(1)
|
||||
for segment in disk.segments:
|
||||
if segment.is_free():
|
||||
continue
|
||||
partition = block_device.Partition.new_by_disk_segment(
|
||||
segment, next(idx), segment.payload.guid_code)
|
||||
partition.guid = segment.payload.guid
|
||||
segment.payload.guid = gdisk.new(partition)
|
||||
finally:
|
||||
pu.reread_partitions(disk.dev)
|
||||
|
||||
def _handle_mdraid(self, md):
|
||||
components = set()
|
||||
for item in md.items:
|
||||
components.add(self.dev_by_guid(item.guid))
|
||||
|
||||
mu.mdcreate(md.name, md.level, sorted(components))
|
||||
|
||||
def _handle_lvm(self, vg):
|
||||
components = set()
|
||||
for pv in vg.items_by_kind(objects.block_device.LVMpv):
|
||||
dev = self.dev_by_guid(pv.guid)
|
||||
components.add(dev)
|
||||
|
||||
args = {}
|
||||
if pv.meta_size is not None:
|
||||
args['metadatasize'] = pv.meta_size.size.in_unit(
|
||||
'MiB').value_int
|
||||
lu.pvcreate(dev, **args)
|
||||
|
||||
lu.vgcreate(vg.idnr, *sorted(components))
|
||||
|
||||
partition = self._lvm_vg_partition(vg)
|
||||
for segment in partition.segments:
|
||||
if segment.kind != segment.KIND_BUSY:
|
||||
continue
|
||||
lu.lvcreate(vg.idnr, segment.payload.name, segment.size)
|
||||
|
||||
def _resolv_disk_partitions(self, claim_dist):
|
||||
"""Dummy to avoid already done operation
|
||||
|
||||
Actual disk partition resolv have happened in __call__ method, during
|
||||
disks partitioning.
|
||||
"""
|
||||
|
||||
|
||||
class PartitionPolicyNailgun(PartitionPolicyClean):
|
||||
_respect_keep_data = True
|
||||
|
||||
def __call__(self):
|
||||
self._respect_keep_data = self._check_keep_data_claim()
|
||||
if self._respect_keep_data:
|
||||
LOG.debug('Some of fs has keep_data (preserve) flag, '
|
||||
'skipping partitioning')
|
||||
|
||||
self._handle_filesystems()
|
||||
return
|
||||
|
||||
super(PartitionPolicyNailgun, self).__call__()
|
||||
|
||||
def _check_keep_data_claim(self):
|
||||
for item in itertools.chain(
|
||||
self.storage_claim.items_by_kind(
|
||||
objects.block_device.FileSystemMixin, recursion=True),
|
||||
self.storage_claim.items_by_kind(objects.block_device.LVMvg)):
|
||||
if not item.keep_data_flag:
|
||||
continue
|
||||
break
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _make_filesystem(self, claim):
|
||||
if self._respect_keep_data and claim.keep_data_flag:
|
||||
return
|
||||
|
||||
super(PartitionPolicyNailgun, self)._make_filesystem(claim)
|
||||
|
||||
|
||||
class PartitionPolicyVerify(AbstractPartitionPolicy):
|
||||
_lvm_fuzzy_cmp_factor = 2
|
||||
|
||||
def __call__(self):
|
||||
LOG.info('Apply "verify" partitions policy')
|
||||
|
||||
for dev in self.partition:
|
||||
self._handle_disk(self.partition[dev])
|
||||
|
||||
for vg in self.storage_claim.items_by_kind(objects.block_device.LVMvg):
|
||||
self._handle_lvm(vg)
|
||||
|
||||
self._handle_filesystems()
|
||||
|
||||
def _handle_disk(self, disk):
|
||||
actual_disk = block_device.Disk.new_by_scan(disk.dev)
|
||||
actual_partition = self._grab_storage_segments(actual_disk)
|
||||
desired_partition = self._grab_storage_segments(disk)
|
||||
|
||||
if actual_partition == desired_partition:
|
||||
return
|
||||
|
||||
self._report_mismatch(disk.dev, desired_partition, actual_partition)
|
||||
|
||||
def _handle_lvm(self, vg_claim):
|
||||
try:
|
||||
vg = block_device.LVM.new_by_scan(vg_claim.idnr)
|
||||
actual_partition = self._grab_storage_segments(
|
||||
vg, self._lvm_fuzzy_cmp_factor)
|
||||
|
||||
vg = self._lvm_vg_partition(vg_claim)
|
||||
desired_partition = self._grab_storage_segments(
|
||||
vg, self._lvm_fuzzy_cmp_factor)
|
||||
except errors.VGNotFoundError:
|
||||
raise errors.PartitionSchemaMismatchError(
|
||||
'There is no LVMvg {}'.format(vg_claim.idnr))
|
||||
|
||||
if actual_partition == desired_partition:
|
||||
return
|
||||
|
||||
self._report_mismatch(
|
||||
vg_claim.idnr, desired_partition, actual_partition)
|
||||
|
||||
def _grab_storage_segments(self, storage, factor=None):
|
||||
if factor is None:
|
||||
factor = storage.sizeunit_to_blocks(self.space_allocation_accuracy)
|
||||
|
||||
result = []
|
||||
for segment in storage.segments:
|
||||
if segment.kind != segment.KIND_BUSY:
|
||||
continue
|
||||
segment.set_fuzzy_cmp_factor(factor)
|
||||
result.append(segment)
|
||||
|
||||
return result
|
||||
|
||||
# TODO(dbogun): increase verbosity
|
||||
def _report_mismatch(self, dev, desired, actual):
|
||||
raise errors.PartitionSchemaMismatchError(
|
||||
'Partition mismatch on {}'.format(dev))
|
||||
|
||||
def _make_filesystem(self, claim):
|
||||
if claim.keep_data_flag:
|
||||
return
|
||||
|
||||
super(PartitionPolicyVerify, self)._make_filesystem(claim)
|
||||
|
@ -32,6 +32,38 @@ from bareon.utils import utils
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GDisk(object):
|
||||
def __init__(self, dev):
|
||||
self.dev = dev
|
||||
|
||||
def zap(self):
|
||||
LOG.info('Erase block device "%s"', self.dev)
|
||||
utils.execute('sgdisk', '--zap-all', self.dev)
|
||||
|
||||
def new(self, partition):
|
||||
LOG.info(
|
||||
'Create new partition %d:%d (0x%04x) on %s',
|
||||
partition.begin, partition.end, partition.code, self.dev)
|
||||
utils.execute(
|
||||
'sgdisk', '--new={}:{}:{}'.format(
|
||||
partition.index, partition.begin, partition.end), self.dev)
|
||||
utils.execute('sgdisk', '--typecode={}:{:04x}'.format(
|
||||
partition.index, partition.code), self.dev)
|
||||
if partition.index < 5:
|
||||
utils.execute('sgdisk', '--change-name={}:{}'.format(
|
||||
partition.index, 'primary'), self.dev)
|
||||
|
||||
if partition.guid is not None:
|
||||
guid = partition.guid
|
||||
utils.execute('sgdisk', '--disk-guid={}'.format(guid))
|
||||
else:
|
||||
output = utils.execute(
|
||||
'sgdisk', '--info', '{}'.format(partition.index),
|
||||
self.dev)[0]
|
||||
guid = _GDiskInfo(output).guid
|
||||
return guid
|
||||
|
||||
|
||||
class DeviceFinder(object):
|
||||
def __init__(self):
|
||||
self.dev_list = []
|
||||
@ -833,6 +865,13 @@ class LVMSegment(AbstractSegment):
|
||||
class Partition(BlockDevicePayload):
|
||||
suffix_number = None
|
||||
|
||||
@classmethod
|
||||
def new_by_disk_segment(cls, space, index, code):
|
||||
block = _BlockDevice(
|
||||
None, space.size, space.owner.block_size,
|
||||
physical_block_size=space.owner.physical_block_size)
|
||||
return cls(space.owner, block, space.begin, index, code)
|
||||
|
||||
def __init__(self, disk, block, begin, index, code, guid=None,
|
||||
attributes=0):
|
||||
super(Partition, self).__init__(block, guid=guid)
|
||||
|
Loading…
Reference in New Issue
Block a user