ignore devices that have already been processed

Similar to how osdize in charms.ceph checks for already processed
devices we need to avoid checking if they are pristine or not.

Additionally, mapped LUKS devices need to be filtered from being zapped
as they may hold valuable data. They are only used as underlying devices
for device mapper and dmcrypt to provide a decrypted block device
abstration so if they really need to be zapped a mapping needs to be
removed first.

This change also pulls charms.ceph modifications.

Change-Id: I96b3d40b3f9e56681be142377e454b15f9e22be3
Co-Authored-By: Dmitrii Shcherbakov <dmitrii.shcherbakov@canonical.com>
Co-Authored-By: Chris Procter <chris.procter@canonical.com>
Closes-Bug: 1781453
This commit is contained in:
Dmitrii Shcherbakov 2018-07-14 22:48:20 +03:00
parent 4801d10da3
commit e340cc851c
5 changed files with 111 additions and 6 deletions

View File

@ -28,6 +28,7 @@ from charmhelpers.contrib.storage.linux.utils import (
)
from charmhelpers.core.unitdata import kv
from ceph.utils import is_active_bluestore_device
from ceph.utils import is_mapped_luks_device
def get_devices():
@ -53,7 +54,9 @@ def zap():
for device in devices:
if not is_block_device(device):
not_block_devices.append(device)
if is_device_mounted(device) or is_active_bluestore_device(device):
if (is_device_mounted(device) or
is_active_bluestore_device(device) or
is_mapped_luks_device(device)):
failed_devices.append(device)
if failed_devices or not_block_devices:

View File

@ -93,6 +93,8 @@ from charmhelpers.contrib.storage.linux.utils import (
from charmhelpers.contrib.charmsupport import nrpe
from charmhelpers.contrib.hardening.harden import harden
from charmhelpers.core.unitdata import kv
import charmhelpers.contrib.openstack.vaultlocker as vaultlocker
hooks = Hooks()
@ -426,6 +428,15 @@ def prepare_disks_and_activate():
# pre-flight check of eligible device pristinity
devices = get_devices()
# if a device has been previously touched we need to consider it as
# non-pristine. If it needs to be re-processed it has to be zapped
# via the respective action which also clears the unitdata entry.
db = kv()
touched_devices = db.get('osd-devices', [])
devices = [dev for dev in devices if dev not in touched_devices]
log('Skipping osd devices previously processed by this unit: {}'
.format(touched_devices))
# filter osd-devices that are file system paths
devices = [dev for dev in devices if dev.startswith('/dev')]
# filter osd-devices that does not exist on this unit
@ -435,6 +446,7 @@ def prepare_disks_and_activate():
# filter osd-devices that are active bluestore devices
devices = [dev for dev in devices
if not ceph.is_active_bluestore_device(dev)]
log('Checking for pristine devices: "{}"'.format(devices), level=DEBUG)
if not all(ceph.is_pristine_disk(dev) for dev in devices):
status_set('blocked',

View File

@ -81,6 +81,10 @@ POOL_KEYS = {
"cache_min_flush_age": [int],
"cache_min_evict_age": [int],
"fast_read": [bool],
"allow_ec_overwrites": [bool],
"compression_mode": [str, ["none", "passive", "aggressive", "force"]],
"compression_algorithm": [str, ["lz4", "snappy", "zlib", "zstd"]],
"compression_required_ratio": [float, [0.0, 1.0]],
}
CEPH_BUCKET_TYPES = [
@ -251,7 +255,8 @@ def pool_permission_list_for_service(service):
for prefix in prefixes:
permissions.append("allow {} object_prefix {}".format(permission,
prefix))
return ["mon", "allow r", "osd", ', '.join(permissions)]
return ['mon', 'allow r, allow command "osd blacklist"',
'osd', ', '.join(permissions)]
def get_service_groups(service, namespace=None):

View File

@ -1096,7 +1096,8 @@ def get_mds_bootstrap_key():
_default_caps = collections.OrderedDict([
('mon', ['allow r']),
('mon', ['allow r',
'allow command "osd blacklist"']),
('osd', ['allow rwx']),
])
@ -1163,6 +1164,7 @@ def get_named_key(name, caps=None, pool_list=None):
:param caps: dict of cephx capabilities
:returns: Returns a cephx key
"""
key_name = 'client.{}'.format(name)
try:
# Does the key already exist?
output = str(subprocess.check_output(
@ -1177,8 +1179,14 @@ def get_named_key(name, caps=None, pool_list=None):
),
'auth',
'get',
'client.{}'.format(name),
key_name,
]).decode('UTF-8')).strip()
# NOTE(jamespage);
# Apply any changes to key capabilities, dealing with
# upgrades which requires new caps for operation.
upgrade_key_caps(key_name,
caps or _default_caps,
pool_list)
return parse_key(output)
except subprocess.CalledProcessError:
# Couldn't get the key, time to create it!
@ -1194,7 +1202,7 @@ def get_named_key(name, caps=None, pool_list=None):
'/var/lib/ceph/mon/ceph-{}/keyring'.format(
socket.gethostname()
),
'auth', 'get-or-create', 'client.{}'.format(name),
'auth', 'get-or-create', key_name,
]
# Add capabilities
for subsystem, subcaps in caps.items():
@ -1213,7 +1221,7 @@ def get_named_key(name, caps=None, pool_list=None):
.strip()) # IGNORE:E1103
def upgrade_key_caps(key, caps):
def upgrade_key_caps(key, caps, pool_list=None):
""" Upgrade key to have capabilities caps """
if not is_leader():
# Not the MON leader OR not clustered
@ -1222,6 +1230,12 @@ def upgrade_key_caps(key, caps):
"sudo", "-u", ceph_user(), 'ceph', 'auth', 'caps', key
]
for subsystem, subcaps in caps.items():
if subsystem == 'osd':
if pool_list:
# This will output a string similar to:
# "pool=rgw pool=rbd pool=something"
pools = " ".join(['pool={0}'.format(i) for i in pool_list])
subcaps[0] = subcaps[0] + " " + pools
cmd.extend([subsystem, '; '.join(subcaps)])
subprocess.check_call(cmd)
@ -1453,6 +1467,11 @@ def osdize_dev(dev, osd_format, osd_journal, ignore_errors=False,
' skipping.'.format(dev))
return
if is_mapped_luks_device(dev):
log('{} is a mapped LUKS device,'
' skipping.'.format(dev))
return
if cmp_pkgrevno('ceph', '12.2.4') >= 0:
cmd = _ceph_volume(dev,
osd_journal,
@ -1664,6 +1683,29 @@ def is_active_bluestore_device(dev):
return False
def is_luks_device(dev):
"""
Determine if dev is a LUKS-formatted block device.
:param: dev: A full path to a block device to check for LUKS header
presence
:returns: boolean: indicates whether a device is used based on LUKS header.
"""
return True if _luks_uuid(dev) else False
def is_mapped_luks_device(dev):
"""
Determine if dev is a mapped LUKS device
:param: dev: A full path to a block device to be checked
:returns: boolean: indicates whether a device is mapped
"""
_, dirs, _ = next(os.walk('/sys/class/block/{}/holders/'
.format(os.path.basename(dev))))
is_held = len(dirs) > 0
return is_held and is_luks_device(dev)
def get_conf(variable):
"""
Get the value of the given configuration variable from the

View File

@ -26,10 +26,12 @@ class ZapDiskActionTests(CharmTestCase):
'is_block_device',
'is_device_mounted',
'is_active_bluestore_device',
'is_mapped_luks_device',
'kv'])
self.is_device_mounted.return_value = False
self.is_block_device.return_value = True
self.is_active_bluestore_device.return_value = False
self.is_mapped_luks_device.return_value = False
self.kv.return_value = self.kv
self.hookenv.local_unit.return_value = "ceph-osd-test/0"
@ -127,3 +129,44 @@ class ZapDiskActionTests(CharmTestCase):
_zap_disk.assert_not_called()
self.hookenv.action_fail.assert_called_with(
"1 devices are mounted: /dev/vdb")
@mock.patch.object(zap_disk, 'zap_disk')
def test_wont_zap__mapped_luks_device(self, _zap_disk):
"""Will not zap a disk that has a LUKS header"""
def side_effect(arg):
return {
'devices': '/dev/vdb',
'i-really-mean-it': True,
}.get(arg)
self.hookenv.action_get.side_effect = side_effect
self.is_active_bluestore_device.return_value = False
self.is_mapped_luks_device.return_value = True
zap_disk.zap()
_zap_disk.assert_not_called()
self.hookenv.action_fail.assert_called_with(
"1 devices are mounted: /dev/vdb")
@mock.patch.object(zap_disk, 'zap_disk')
def test_zap_luks_not_mapped(self, _zap_disk):
"""Will zap disk with extra config set"""
def side_effect(arg):
return {
'devices': '/dev/vdb',
'i-really-mean-it': True,
}.get(arg)
self.is_active_bluestore_device.return_value = False
self.is_mapped_luks_device.return_value = False
self.hookenv.action_get.side_effect = side_effect
self.kv.get.return_value = ['/dev/vdb', '/dev/vdz']
zap_disk.zap()
_zap_disk.assert_called_with('/dev/vdb')
self.kv.get.assert_called_with('osd-devices', [])
self.kv.set.assert_called_with('osd-devices', ['/dev/vdz'])
self.hookenv.action_set.assert_called_with({
'message': "1 disk(s) have been zapped, to use "
"them as OSDs, run: \njuju "
"run-action ceph-osd-test/0 add-disk "
"osd-devices=\"/dev/vdb\""
})