mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
dd5d865020
The following operations would hang if lvm was compiled with 'enable-notify-dbus' and the client specified -1 for the timeout: * LV snapshot merge * VG move * LV move This was caused because the implementation of these three dbus methods is different. Most of the dbus method calls are executed by gathering information needed to fulfill it, placing that information on a thread safe queue and returning. The results later to be returned to the client with callbacks. With this approach we can process an arbitrary number of commands without any of them blocking other dbus commands. However, the 3 dbus methods listed above did not utilize this functionality because they were implemented with a separate thread that handles the fork & exec of lvm. This is done because these operations can be very slow to complete. However, because of this the lvm command that we were waiting on is trying to call back into the dbus service to notify it that something changed. Because the code was blocking the process that handles the incoming dbus activity the lvm command blocked. We were stuck until the client timed-out the connection, which then causes the service to unblock and continue. If the client did not have a timeout, we would have been hung indefinitely. The fix is to always utilize the worker queue on all dbus methods. We need to ensure that lvm is tested with 'enable-notify-dbus' enabled and disabled.
896 lines
25 KiB
Python
896 lines
25 KiB
Python
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
#
|
|
# This copyrighted material is made available to anyone wishing to use,
|
|
# modify, copy, or redistribute it subject to the terms and conditions
|
|
# of the GNU General Public License v.2.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from .automatedproperties import AutomatedProperties
|
|
|
|
from . import utils
|
|
from .utils import vg_obj_path_generate
|
|
import dbus
|
|
from . import cmdhandler
|
|
from . import cfg
|
|
from .cfg import LV_INTERFACE, THIN_POOL_INTERFACE, SNAPSHOT_INTERFACE, \
|
|
LV_COMMON_INTERFACE, CACHE_POOL_INTERFACE, LV_CACHED
|
|
from .request import RequestEntry
|
|
from .utils import n, n32
|
|
from .loader import common
|
|
from .state import State
|
|
from . import background
|
|
from .utils import round_size
|
|
from .job import JobState
|
|
|
|
|
|
# Try and build a key for a LV, so that we sort the LVs with least dependencies
|
|
# first. This may be error prone because of the flexibility LVM
|
|
# provides and what you can stack.
|
|
def get_key(i):
|
|
|
|
name = i['lv_name']
|
|
parent = i['lv_parent']
|
|
pool = i['pool_lv']
|
|
a1 = ""
|
|
a2 = ""
|
|
|
|
if name[0] == '[':
|
|
a1 = '#'
|
|
|
|
# We have a parent
|
|
if parent:
|
|
# Check if parent is hidden
|
|
if parent[0] == '[':
|
|
a2 = '##'
|
|
else:
|
|
a2 = '#'
|
|
|
|
# If a LV has a pool, then it should be sorted/loaded after the pool
|
|
# lv, unless it's a hidden too, then after other hidden, but before visible
|
|
if pool:
|
|
if pool[0] != '[':
|
|
a2 += '~'
|
|
else:
|
|
a1 = '$' + a1
|
|
|
|
return "%s%s%s" % (a1, a2, name)
|
|
|
|
|
|
# noinspection PyUnusedLocal
|
|
def lvs_state_retrieve(selection, cache_refresh=True):
|
|
rc = []
|
|
|
|
if cache_refresh:
|
|
cfg.db.refresh()
|
|
|
|
# When building up the model, it's best to process LVs with the least
|
|
# dependencies to those that are dependant upon other LVs. Otherwise, when
|
|
# we are trying to gather information we could be in a position where we
|
|
# don't have information available yet.
|
|
lvs = sorted(cfg.db.fetch_lvs(selection), key=get_key)
|
|
|
|
for l in lvs:
|
|
rc.append(LvState(
|
|
l['lv_uuid'], l['lv_name'],
|
|
l['lv_path'], n(l['lv_size']),
|
|
l['vg_name'],
|
|
l['vg_uuid'], l['pool_lv_uuid'],
|
|
l['pool_lv'], l['origin_uuid'], l['origin'],
|
|
n32(l['data_percent']), l['lv_attr'],
|
|
l['lv_tags'], l['lv_active'], l['data_lv'],
|
|
l['metadata_lv'], l['segtype'], l['lv_role'],
|
|
l['lv_layout']))
|
|
return rc
|
|
|
|
|
|
def load_lvs(lv_name=None, object_path=None, refresh=False, emit_signal=False,
|
|
cache_refresh=True):
|
|
# noinspection PyUnresolvedReferences
|
|
return common(
|
|
lvs_state_retrieve,
|
|
(LvCommon, Lv, LvThinPool, LvSnapShot),
|
|
lv_name, object_path, refresh, emit_signal, cache_refresh)
|
|
|
|
|
|
# noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal
|
|
class LvState(State):
|
|
@staticmethod
|
|
def _pv_devices(uuid):
|
|
rc = []
|
|
for pv in sorted(cfg.db.lv_contained_pv(uuid)):
|
|
(pv_uuid, pv_name, pv_segs) = pv
|
|
pv_obj = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
pv_uuid, pv_name, gen_new=False)
|
|
rc.append((pv_obj, pv_segs))
|
|
|
|
return dbus.Array(rc, signature="(oa(tts))")
|
|
|
|
def vg_name_lookup(self):
|
|
return cfg.om.get_object_by_path(self.Vg).Name
|
|
|
|
@property
|
|
def lvm_id(self):
|
|
return "%s/%s" % (self.vg_name_lookup(), self.Name)
|
|
|
|
def identifiers(self):
|
|
return (self.Uuid, self.lvm_id)
|
|
|
|
def _get_hidden_lv(self):
|
|
rc = dbus.Array([], "o")
|
|
|
|
vg_name = self.vg_name_lookup()
|
|
|
|
for l in cfg.db.hidden_lvs(self.Uuid):
|
|
full_name = "%s/%s" % (vg_name, l[1])
|
|
op = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
l[0], full_name, gen_new=False)
|
|
assert op
|
|
rc.append(op)
|
|
return rc
|
|
|
|
def __init__(self, Uuid, Name, Path, SizeBytes,
|
|
vg_name, vg_uuid, pool_lv_uuid, PoolLv,
|
|
origin_uuid, OriginLv, DataPercent, Attr, Tags, active,
|
|
data_lv, metadata_lv, segtypes, role, layout):
|
|
utils.init_class_from_arguments(self)
|
|
|
|
# The segtypes is possibly an array with potentially dupes or a single
|
|
# value
|
|
self._segs = dbus.Array([], signature='s')
|
|
if not isinstance(segtypes, list):
|
|
self._segs.append(segtypes)
|
|
else:
|
|
self._segs.extend(set(segtypes))
|
|
|
|
self.Vg = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
vg_uuid, vg_name, vg_obj_path_generate)
|
|
|
|
self.Devices = LvState._pv_devices(self.Uuid)
|
|
|
|
if PoolLv:
|
|
gen = utils.lv_object_path_method(Name, (Attr, layout, role))
|
|
|
|
self.PoolLv = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
pool_lv_uuid, '%s/%s' % (vg_name, PoolLv),
|
|
gen)
|
|
else:
|
|
self.PoolLv = '/'
|
|
|
|
if OriginLv:
|
|
self.OriginLv = \
|
|
cfg.om.get_object_path_by_uuid_lvm_id(
|
|
origin_uuid, '%s/%s' % (vg_name, OriginLv),
|
|
vg_obj_path_generate)
|
|
else:
|
|
self.OriginLv = '/'
|
|
|
|
self.HiddenLvs = self._get_hidden_lv()
|
|
|
|
@property
|
|
def SegType(self):
|
|
return self._segs
|
|
|
|
def _object_path_create(self):
|
|
return utils.lv_object_path_method(
|
|
self.Name, (self.Attr, self.layout, self.role))
|
|
|
|
def _object_type_create(self):
|
|
if self.Attr[0] == 't':
|
|
return LvThinPool
|
|
elif self.Attr[0] == 'C':
|
|
if 'pool' in self.layout:
|
|
return LvCachePool
|
|
else:
|
|
return LvCacheLv
|
|
elif self.Name[0] == '[':
|
|
return LvCommon
|
|
elif self.OriginLv != '/':
|
|
return LvSnapShot
|
|
else:
|
|
return Lv
|
|
|
|
def create_dbus_object(self, path):
|
|
if not path:
|
|
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
self.Uuid, self.lvm_id, self._object_path_create())
|
|
|
|
obj_ctor = self._object_type_create()
|
|
return obj_ctor(path, self)
|
|
|
|
def creation_signature(self):
|
|
klass = self._object_type_create()
|
|
path_method = self._object_path_create()
|
|
return (klass, path_method)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Uuid', 's')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Name', 's')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Path', 's')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'SizeBytes', 't')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'SegType', 'as')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Vg', 'o')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'OriginLv', 'o')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'PoolLv', 'o')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Devices', "a(oa(tts))")
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'HiddenLvs', "ao")
|
|
class LvCommon(AutomatedProperties):
|
|
_Tags_meta = ("as", LV_COMMON_INTERFACE)
|
|
_Roles_meta = ("as", LV_COMMON_INTERFACE)
|
|
_IsThinVolume_meta = ("b", LV_COMMON_INTERFACE)
|
|
_IsThinPool_meta = ("b", LV_COMMON_INTERFACE)
|
|
_Active_meta = ("b", LV_COMMON_INTERFACE)
|
|
_VolumeType_meta = ("(ss)", LV_COMMON_INTERFACE)
|
|
_Permissions_meta = ("(ss)", LV_COMMON_INTERFACE)
|
|
_AllocationPolicy_meta = ("(ss)", LV_COMMON_INTERFACE)
|
|
_State_meta = ("(ss)", LV_COMMON_INTERFACE)
|
|
_TargetType_meta = ("(ss)", LV_COMMON_INTERFACE)
|
|
_Health_meta = ("(ss)", LV_COMMON_INTERFACE)
|
|
_FixedMinor_meta = ('b', LV_COMMON_INTERFACE)
|
|
_ZeroBlocks_meta = ('b', LV_COMMON_INTERFACE)
|
|
_SkipActivation_meta = ('b', LV_COMMON_INTERFACE)
|
|
|
|
# noinspection PyUnusedLocal,PyPep8Naming
|
|
def __init__(self, object_path, object_state):
|
|
super(LvCommon, self).__init__(object_path, lvs_state_retrieve)
|
|
self.set_interface(LV_COMMON_INTERFACE)
|
|
self.state = object_state
|
|
|
|
@property
|
|
def VolumeType(self):
|
|
type_map = {'C': 'Cache', 'm': 'mirrored',
|
|
'M': 'Mirrored without initial sync', 'o': 'origin',
|
|
'O': 'Origin with merging snapshot', 'r': 'raid',
|
|
'R': 'Raid without initial sync', 's': 'snapshot',
|
|
'S': 'merging Snapshot', 'p': 'pvmove',
|
|
'v': 'virtual', 'i': 'mirror or raid image',
|
|
'I': 'mirror or raid Image out-of-sync',
|
|
'l': 'mirror log device', 'c': 'under conversion',
|
|
'V': 'thin Volume', 't': 'thin pool', 'T': 'Thin pool data',
|
|
'e': 'raid or pool metadata or pool metadata spare',
|
|
'-': 'Unspecified'}
|
|
return (self.state.Attr[0], type_map[self.state.Attr[0]])
|
|
|
|
@property
|
|
def Permissions(self):
|
|
type_map = {'w': 'writable', 'r': 'read-only',
|
|
'R': 'Read-only activation of non-read-only volume',
|
|
'-': 'Unspecified'}
|
|
return (self.state.Attr[1], type_map[self.state.Attr[1]])
|
|
|
|
@property
|
|
def AllocationPolicy(self):
|
|
type_map = {'a': 'anywhere', 'A': 'anywhere locked',
|
|
'c': 'contiguous', 'C': 'contiguous locked',
|
|
'i': 'inherited', 'I': 'inherited locked',
|
|
'l': 'cling', 'L': 'cling locked',
|
|
'n': 'normal', 'N': 'normal locked', '-': 'Unspecified'}
|
|
return (self.state.Attr[2], type_map[self.state.Attr[2]])
|
|
|
|
@property
|
|
def FixedMinor(self):
|
|
return self.state.Attr[3] == 'm'
|
|
|
|
@property
|
|
def State(self):
|
|
type_map = {'a': 'active', 's': 'suspended', 'I': 'Invalid snapshot',
|
|
'S': 'invalid Suspended snapshot',
|
|
'm': 'snapshot merge failed',
|
|
'M': 'suspended snapshot (M)erge failed',
|
|
'd': 'mapped device present without tables',
|
|
'i': 'mapped device present with inactive table',
|
|
'X': 'unknown', '-': 'Unspecified'}
|
|
return (self.state.Attr[4], type_map[self.state.Attr[4]])
|
|
|
|
@property
|
|
def TargetType(self):
|
|
type_map = {'C': 'Cache', 'm': 'mirror', 'r': 'raid',
|
|
's': 'snapshot', 't': 'thin', 'u': 'unknown',
|
|
'v': 'virtual', '-': 'Unspecified'}
|
|
return (self.state.Attr[6], type_map[self.state.Attr[6]])
|
|
|
|
@property
|
|
def ZeroBlocks(self):
|
|
return self.state.Attr[7] == 'z'
|
|
|
|
@property
|
|
def Health(self):
|
|
type_map = {'p': 'partial', 'r': 'refresh',
|
|
'm': 'mismatches', 'w': 'writemostly',
|
|
'X': 'X unknown', '-': 'Unspecified'}
|
|
return (self.state.Attr[8], type_map[self.state.Attr[8]])
|
|
|
|
@property
|
|
def SkipActivation(self):
|
|
return self.state.Attr[9] == 'k'
|
|
|
|
def vg_name_lookup(self):
|
|
return self.state.vg_name_lookup()
|
|
|
|
def lv_full_name(self):
|
|
return "%s/%s" % (self.state.vg_name_lookup(), self.state.Name)
|
|
|
|
@property
|
|
def identifiers(self):
|
|
return self.state.identifiers
|
|
|
|
@property
|
|
def Tags(self):
|
|
return utils.parse_tags(self.state.Tags)
|
|
|
|
@property
|
|
def Roles(self):
|
|
return utils.parse_tags(self.state.role)
|
|
|
|
@property
|
|
def lvm_id(self):
|
|
return self.state.lvm_id
|
|
|
|
@property
|
|
def IsThinVolume(self):
|
|
return self.state.Attr[0] == 'V'
|
|
|
|
@property
|
|
def IsThinPool(self):
|
|
return self.state.Attr[0] == 't'
|
|
|
|
@property
|
|
def Active(self):
|
|
return self.state.active == "active"
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_COMMON_INTERFACE,
|
|
in_signature='ia{sv}',
|
|
out_signature='o')
|
|
def _Future(self, tmo, open_options):
|
|
raise dbus.exceptions.DBusException(LV_COMMON_INTERFACE, 'Do not use!')
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class Lv(LvCommon):
|
|
def _fetch_hidden(self, name):
|
|
|
|
# The name is vg/name
|
|
full_name = "%s/%s" % (self.vg_name_lookup(), name)
|
|
return cfg.om.get_object_path_by_lvm_id(full_name)
|
|
|
|
def _get_data_meta(self):
|
|
|
|
# Get the data
|
|
return (self._fetch_hidden(self.state.data_lv),
|
|
self._fetch_hidden(self.state.metadata_lv))
|
|
|
|
# noinspection PyUnusedLocal,PyPep8Naming
|
|
def __init__(self, object_path, object_state):
|
|
super(Lv, self).__init__(object_path, object_state)
|
|
self.set_interface(LV_INTERFACE)
|
|
self.state = object_state
|
|
|
|
@staticmethod
|
|
def _remove(lv_uuid, lv_name, remove_options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
|
|
if dbo:
|
|
# Remove the LV, if successful then remove from the model
|
|
rc, out, err = cmdhandler.lv_remove(lv_name, remove_options)
|
|
|
|
if rc == 0:
|
|
cfg.om.remove_object(dbo, True)
|
|
cfg.load()
|
|
else:
|
|
# Need to work on error handling, need consistent
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(lv_uuid, lv_name))
|
|
return '/'
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Remove(self, tmo, remove_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, Lv._remove,
|
|
(self.Uuid, self.lvm_id, remove_options),
|
|
cb, cbe, False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@staticmethod
|
|
def _rename(lv_uuid, lv_name, new_name, rename_options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
|
|
if dbo:
|
|
# Rename the logical volume
|
|
rc, out, err = cmdhandler.lv_rename(lv_name, new_name,
|
|
rename_options)
|
|
if rc == 0:
|
|
cfg.load()
|
|
else:
|
|
# Need to work on error handling, need consistent
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(lv_uuid, lv_name))
|
|
return '/'
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='sia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Rename(self, name, tmo, rename_options, cb, cbe):
|
|
utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name)
|
|
|
|
r = RequestEntry(
|
|
tmo, Lv._rename,
|
|
(self.Uuid, self.lvm_id, name, rename_options),
|
|
cb, cbe, False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='o(tt)a(ott)ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Move(self, pv_src_obj, pv_source_range,
|
|
pv_dests_and_ranges,
|
|
tmo, move_options, cb, cbe):
|
|
|
|
job_state = JobState()
|
|
|
|
r = RequestEntry(
|
|
tmo, background.move,
|
|
(LV_INTERFACE, self.lvm_id, pv_src_obj, pv_source_range,
|
|
pv_dests_and_ranges, move_options, job_state), cb, cbe, False,
|
|
job_state)
|
|
|
|
cfg.worker_q.put(r)
|
|
|
|
@staticmethod
|
|
def _snap_shot(lv_uuid, lv_name, name, optional_size,
|
|
snapshot_options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
|
|
if dbo:
|
|
# If you specify a size you get a 'thick' snapshot even if
|
|
# it is a thin lv
|
|
if not dbo.IsThinVolume:
|
|
if optional_size == 0:
|
|
# TODO: Should we pick a sane default or force user to
|
|
# make a decision?
|
|
space = dbo.SizeBytes / 80
|
|
remainder = space % 512
|
|
optional_size = space + 512 - remainder
|
|
|
|
rc, out, err = cmdhandler.vg_lv_snapshot(
|
|
lv_name, snapshot_options, name, optional_size)
|
|
if rc == 0:
|
|
return_path = '/'
|
|
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
|
lvs = load_lvs([full_name], emit_signal=True)[0]
|
|
for l in lvs:
|
|
return_path = l.dbus_object_path()
|
|
|
|
# Refresh self and all included PVs
|
|
cfg.load(cache_refresh=False)
|
|
return return_path
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(lv_uuid, lv_name))
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='stia{sv}',
|
|
out_signature='(oo)',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Snapshot(self, name, optional_size, tmo,
|
|
snapshot_options, cb, cbe):
|
|
|
|
utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name)
|
|
|
|
r = RequestEntry(
|
|
tmo, Lv._snap_shot,
|
|
(self.Uuid, self.lvm_id, name,
|
|
optional_size, snapshot_options), cb, cbe)
|
|
cfg.worker_q.put(r)
|
|
|
|
@staticmethod
|
|
def _resize(lv_uuid, lv_name, new_size_bytes, pv_dests_and_ranges,
|
|
resize_options):
|
|
# Make sure we have a dbus object representing it
|
|
pv_dests = []
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
|
|
if dbo:
|
|
# If we have PVs, verify them
|
|
if len(pv_dests_and_ranges):
|
|
for pr in pv_dests_and_ranges:
|
|
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
|
if not pv_dbus_obj:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'PV Destination (%s) not found' % pr[0])
|
|
|
|
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
|
|
|
size_change = new_size_bytes - dbo.SizeBytes
|
|
|
|
rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change,
|
|
pv_dests, resize_options)
|
|
|
|
if rc == 0:
|
|
# Refresh what's changed
|
|
cfg.load()
|
|
return "/"
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(lv_uuid, lv_name))
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='ta(ott)ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Resize(self, new_size_bytes, pv_dests_and_ranges, tmo,
|
|
resize_options, cb, cbe):
|
|
"""
|
|
Resize a LV
|
|
:param new_size_bytes: The requested final size in bytes
|
|
:param pv_dests_and_ranges: An array of pv object paths and src &
|
|
dst. segment ranges
|
|
:param tmo: -1 to wait forever, 0 to return job immediately, else
|
|
number of seconds to wait for operation to complete
|
|
before getting a job
|
|
:param resize_options: key/value hash of options
|
|
:param cb: Used by framework not client facing API
|
|
:param cbe: Used by framework not client facing API
|
|
:return: '/' if complete, else job object path
|
|
"""
|
|
r = RequestEntry(
|
|
tmo, Lv._resize,
|
|
(self.Uuid, self.lvm_id, round_size(new_size_bytes),
|
|
pv_dests_and_ranges,
|
|
resize_options), cb, cbe, return_tuple=False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@staticmethod
|
|
def _lv_activate_deactivate(uuid, lv_name, activate, control_flags,
|
|
options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
|
|
|
|
if dbo:
|
|
rc, out, err = cmdhandler.activate_deactivate(
|
|
'lvchange', lv_name, activate, control_flags, options)
|
|
if rc == 0:
|
|
dbo.refresh()
|
|
return '/'
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(uuid, lv_name))
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='tia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Activate(self, control_flags, tmo, activate_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, Lv._lv_activate_deactivate,
|
|
(self.state.Uuid, self.state.lvm_id, True,
|
|
control_flags, activate_options),
|
|
cb, cbe, return_tuple=False)
|
|
cfg.worker_q.put(r)
|
|
|
|
# noinspection PyProtectedMember
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='tia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Deactivate(self, control_flags, tmo, activate_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, Lv._lv_activate_deactivate,
|
|
(self.state.Uuid, self.state.lvm_id, False,
|
|
control_flags, activate_options),
|
|
cb, cbe, return_tuple=False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@staticmethod
|
|
def _add_rm_tags(uuid, lv_name, tags_add, tags_del, tag_options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
|
|
|
|
if dbo:
|
|
|
|
rc, out, err = cmdhandler.lv_tag(
|
|
lv_name, tags_add, tags_del, tag_options)
|
|
if rc == 0:
|
|
dbo.refresh()
|
|
return '/'
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(uuid, lv_name))
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='asia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def TagsAdd(self, tags, tmo, tag_options, cb, cbe):
|
|
|
|
for t in tags:
|
|
utils.validate_tag(LV_INTERFACE, t)
|
|
|
|
r = RequestEntry(
|
|
tmo, Lv._add_rm_tags,
|
|
(self.state.Uuid, self.state.lvm_id,
|
|
tags, None, tag_options),
|
|
cb, cbe, return_tuple=False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='asia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def TagsDel(self, tags, tmo, tag_options, cb, cbe):
|
|
|
|
for t in tags:
|
|
utils.validate_tag(LV_INTERFACE, t)
|
|
|
|
r = RequestEntry(
|
|
tmo, Lv._add_rm_tags,
|
|
(self.state.Uuid, self.state.lvm_id,
|
|
None, tags, tag_options),
|
|
cb, cbe, return_tuple=False)
|
|
cfg.worker_q.put(r)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class LvThinPool(Lv):
|
|
_DataLv_meta = ("o", THIN_POOL_INTERFACE)
|
|
_MetaDataLv_meta = ("o", THIN_POOL_INTERFACE)
|
|
|
|
def __init__(self, object_path, object_state):
|
|
super(LvThinPool, self).__init__(object_path, object_state)
|
|
self.set_interface(THIN_POOL_INTERFACE)
|
|
self._data_lv, self._metadata_lv = self._get_data_meta()
|
|
|
|
@property
|
|
def DataLv(self):
|
|
return self._data_lv
|
|
|
|
@property
|
|
def MetaDataLv(self):
|
|
return self._metadata_lv
|
|
|
|
@staticmethod
|
|
def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
|
|
lv_created = '/'
|
|
|
|
if dbo:
|
|
rc, out, err = cmdhandler.lv_lv_create(
|
|
lv_name, create_options, name, size_bytes)
|
|
if rc == 0:
|
|
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
|
lvs = load_lvs([full_name], emit_signal=True)[0]
|
|
for l in lvs:
|
|
lv_created = l.dbus_object_path()
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(lv_uuid, lv_name))
|
|
return lv_created
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=THIN_POOL_INTERFACE,
|
|
in_signature='stia{sv}',
|
|
out_signature='(oo)',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def LvCreate(self, name, size_bytes, tmo, create_options, cb, cbe):
|
|
utils.validate_lv_name(THIN_POOL_INTERFACE, self.vg_name_lookup(), name)
|
|
|
|
r = RequestEntry(
|
|
tmo, LvThinPool._lv_create,
|
|
(self.Uuid, self.lvm_id, name,
|
|
round_size(size_bytes), create_options), cb, cbe)
|
|
cfg.worker_q.put(r)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class LvCachePool(Lv):
|
|
_DataLv_meta = ("o", CACHE_POOL_INTERFACE)
|
|
_MetaDataLv_meta = ("o", CACHE_POOL_INTERFACE)
|
|
|
|
def __init__(self, object_path, object_state):
|
|
super(LvCachePool, self).__init__(object_path, object_state)
|
|
self.set_interface(CACHE_POOL_INTERFACE)
|
|
self._data_lv, self._metadata_lv = self._get_data_meta()
|
|
|
|
@property
|
|
def DataLv(self):
|
|
return self._data_lv
|
|
|
|
@property
|
|
def MetaDataLv(self):
|
|
return self._metadata_lv
|
|
|
|
@staticmethod
|
|
def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
|
|
|
|
# Make sure we have a dbus object representing cache pool
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
|
|
# Make sure we have dbus object representing lv to cache
|
|
lv_to_cache = cfg.om.get_object_by_path(lv_object_path)
|
|
|
|
if dbo and lv_to_cache:
|
|
fcn = lv_to_cache.lv_full_name()
|
|
rc, out, err = cmdhandler.lv_cache_lv(
|
|
dbo.lv_full_name(), fcn, cache_options)
|
|
if rc == 0:
|
|
# When we cache an LV, the cache pool and the lv that is getting
|
|
# cached need to be removed from the object manager and
|
|
# re-created as their interfaces have changed!
|
|
cfg.om.remove_object(dbo, emit_signal=True)
|
|
cfg.om.remove_object(lv_to_cache, emit_signal=True)
|
|
cfg.load()
|
|
|
|
lv_converted = cfg.om.get_object_path_by_lvm_id(fcn)
|
|
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
msg = ""
|
|
if not dbo:
|
|
dbo += 'CachePool LV with uuid %s and name %s not present!' % \
|
|
(lv_uuid, lv_name)
|
|
|
|
if not lv_to_cache:
|
|
dbo += 'LV to cache with object path %s not present!' % \
|
|
(lv_object_path)
|
|
|
|
raise dbus.exceptions.DBusException(LV_INTERFACE, msg)
|
|
return lv_converted
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=CACHE_POOL_INTERFACE,
|
|
in_signature='oia{sv}',
|
|
out_signature='(oo)',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def CacheLv(self, lv_object, tmo, cache_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, LvCachePool._cache_lv,
|
|
(self.Uuid, self.lvm_id, lv_object,
|
|
cache_options), cb, cbe)
|
|
cfg.worker_q.put(r)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class LvCacheLv(Lv):
|
|
_CachePool_meta = ("o", LV_CACHED)
|
|
|
|
def __init__(self, object_path, object_state):
|
|
super(LvCacheLv, self).__init__(object_path, object_state)
|
|
self.set_interface(LV_CACHED)
|
|
|
|
@property
|
|
def CachePool(self):
|
|
return self.state.PoolLv
|
|
|
|
@staticmethod
|
|
def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache):
|
|
# Make sure we have a dbus object representing cache pool
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
|
|
if dbo:
|
|
|
|
# Get current cache name
|
|
cache_pool = cfg.om.get_object_by_path(dbo.CachePool)
|
|
|
|
rc, out, err = cmdhandler.lv_detach_cache(
|
|
dbo.lv_full_name(), detach_options, destroy_cache)
|
|
if rc == 0:
|
|
# The cache pool gets removed as hidden and put back to
|
|
# visible, so lets delete
|
|
cfg.om.remove_object(cache_pool, emit_signal=True)
|
|
cfg.om.remove_object(dbo, emit_signal=True)
|
|
cfg.load()
|
|
|
|
uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name)
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
else:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(lv_uuid, lv_name))
|
|
return uncached_lv_path
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_CACHED,
|
|
in_signature='bia{sv}',
|
|
out_signature='(oo)',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def DetachCachePool(self, destroy_cache, tmo, detach_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, LvCacheLv._detach_lv,
|
|
(self.Uuid, self.lvm_id, detach_options,
|
|
destroy_cache), cb, cbe)
|
|
cfg.worker_q.put(r)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class LvSnapShot(Lv):
|
|
def __init__(self, object_path, object_state):
|
|
super(LvSnapShot, self).__init__(object_path, object_state)
|
|
self.set_interface(SNAPSHOT_INTERFACE)
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=SNAPSHOT_INTERFACE,
|
|
in_signature='ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def Merge(self, tmo, merge_options, cb, cbe):
|
|
job_state = JobState()
|
|
|
|
r = RequestEntry(tmo, background.merge,
|
|
(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
|
|
merge_options, job_state), cb, cbe, False,
|
|
job_state)
|
|
cfg.worker_q.put(r)
|