bareon/bareon/actions/partitioning.py

213 lines
8.2 KiB
Python

# Copyright 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from oslo_config import cfg
from oslo_log import log as logging
from bareon.actions import base
from bareon import errors
from bareon.utils import fs as fu
from bareon.utils import hardware as hu
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
opts = [
cfg.StrOpt(
'udev_rules_dir',
default='/etc/udev/rules.d',
help='Path where to store actual rules for udev daemon',
),
cfg.StrOpt(
'udev_rules_lib_dir',
default='/lib/udev/rules.d',
help='Path where to store default rules for udev daemon',
),
cfg.StrOpt(
'udev_rename_substr',
default='.renamedrule',
help='Substring to which file extension .rules be renamed',
),
cfg.StrOpt(
'udev_empty_rule',
default='empty_rule',
help='Correct empty rule for udev daemon',
),
cfg.BoolOpt(
'skip_md_containers',
default=True,
help='Allow to skip MD containers (fake raid leftovers) while '
'cleaning the rest of MDs',
),
cfg.StrOpt(
'partition_alignment',
default='optimal',
help='Set alignment for newly created partitions, valid alignment '
'types are: none, cylinder, minimal, optimal'
),
]
CONF = cfg.CONF
CONF.register_opts(opts)
LOG = logging.getLogger(__name__)
class PartitioningAction(base.BaseAction):
"""PartitioningAction
performs disks partitioning
"""
def validate(self):
# TODO(agordeev): implement validate for partitioning
pass
def execute(self):
if self.driver.partition_scheme.skip_partitioning:
LOG.debug('Some of fs has keep_data (preserve) flag, '
'skipping partitioning')
self._do_clean_filesystems()
else:
LOG.debug('No keep_data (preserve) flag passed, wiping out all'
'disks and re-partitioning')
self._do_partitioning()
# TODO(agordeev): separate to entirely another action?
def _do_clean_filesystems(self):
# NOTE(agordeev): it turns out that only mkfs.xfs needs '-f' flag in
# order to force recreation of filesystem.
# This option will be added to mkfs.xfs call explicitly in fs utils.
# TODO(asvechnikov): need to refactor processing keep_flag logic when
# data model will become flat
for fs in self.driver.partition_scheme.fss:
found_images = [img for img in self.driver.image_scheme.images
if img.target_device == fs.device]
if not fs.keep_data and not found_images:
fu.make_fs(fs.type, fs.options, fs.label, fs.device)
@staticmethod
def _make_partitions(parteds):
for parted in parteds:
pu.make_label(parted.name, parted.label)
for prt in parted.partitions:
pu.make_partition(prt.device, prt.begin, prt.end, prt.type,
alignment=CONF.partition_alignment)
utils.udevadm_trigger_blocks()
for flag in prt.flags:
pu.set_partition_flag(prt.device, prt.count, flag)
if prt.guid:
pu.set_gpt_type(prt.device, prt.count, prt.guid)
# If any partition to be created doesn't exist it's an error.
# Probably it's again 'device or resource busy' issue.
# FIXME(dbogun): prt.name is not reliable! And this is
# incorrect place to check is partition created.
if not os.path.exists(prt.name):
raise errors.PartitionNotFoundError(
'Partition %s not found after creation' % prt.name)
def _do_partitioning(self):
LOG.debug('--- Partitioning disks (do_partitioning) ---')
# If disks are not wiped out at all, it is likely they contain lvm
# and md metadata which will prevent re-creating a partition table
# with 'device is busy' error.
mu.mdclean_all(skip_containers=CONF.skip_md_containers)
lu.lvremove_all()
lu.vgremove_all()
lu.pvremove_all()
for parted in self.driver.partition_scheme.parteds:
for prt in parted.partitions:
# We wipe out the beginning of every new partition
# right after creating it. It allows us to avoid possible
# interactive dialog if some data (metadata or file system)
# present on this new partition and it also allows udev not
# hanging trying to parse this data.
begin = utils.B2MiB(prt.begin)
end = utils.B2MiB(prt.end + 1)
utils.execute('dd', 'if=/dev/zero', 'bs=1M',
'seek=%s' % max(begin - 3, 0), 'count=5',
'of=%s' % prt.device, check_exit_code=[0])
# Also wipe out the ending of every new partition.
# Different versions of md stores metadata in different places.
# Adding exit code 1 to be accepted as for handling situation
# when 'no space left on device' occurs.
utils.execute('dd', 'if=/dev/zero', 'bs=1M',
'seek=%s' % max(end - 3, 0), 'count=5',
'of=%s' % prt.device, check_exit_code=[0, 1])
parteds = []
parteds_with_rules = []
for parted in self.driver.partition_scheme.parteds:
if hu.is_multipath_device(parted.name):
parteds_with_rules.append(parted)
else:
parteds.append(parted)
utils.blacklist_udev_rules(udev_rules_dir=CONF.udev_rules_dir,
udev_rules_lib_dir=CONF.udev_rules_lib_dir,
udev_rename_substr=CONF.udev_rename_substr,
udev_empty_rule=CONF.udev_empty_rule)
self._make_partitions(parteds)
utils.unblacklist_udev_rules(
udev_rules_dir=CONF.udev_rules_dir,
udev_rename_substr=CONF.udev_rename_substr)
self._make_partitions(parteds_with_rules)
# If one creates partitions with the same boundaries as last time,
# there might be md and lvm metadata on those partitions. To prevent
# failing of creating md and lvm devices we need to make sure
# unused metadata are wiped out.
mu.mdclean_all(skip_containers=CONF.skip_md_containers)
lu.lvremove_all()
lu.vgremove_all()
lu.pvremove_all()
if parteds_with_rules:
utils.refresh_multipath()
# creating meta disks
for md in self.driver.partition_scheme.mds:
mu.mdcreate(md.name, md.level, md.devices, md.metadata)
# creating physical volumes
for pv in self.driver.partition_scheme.pvs:
lu.pvcreate(pv.name, metadatasize=utils.B2MiB(pv.metadatasize),
metadatacopies=pv.metadatacopies)
# creating volume groups
for vg in self.driver.partition_scheme.vgs:
lu.vgcreate(vg.name, *vg.pvnames)
# creating logical volumes
for lv in self.driver.partition_scheme.lvs:
lu.lvcreate(lv.vgname, lv.name, utils.B2MiB(lv.size))
# making file systems
for fs in self.driver.partition_scheme.fss:
found_images = [img for img in self.driver.image_scheme.images
if img.target_device == fs.device]
if not found_images:
fu.make_fs(fs.type, fs.options, fs.label, fs.device)