213 lines
8.2 KiB
Python
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)
|