mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
1949afa24b
When we sort the LVs, we can stumble on a missing key, protect against this as well. Seen in error injection testing: Traceback (most recent call last): File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/fetch.py", line 198, in update_thread num_changes = load(*_load_args(queued_requests)) File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/fetch.py", line 83, in load rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done() File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/utils.py", line 726, in done raise self.exception File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/utils.py", line 732, in _run self.rc = self.f(*self.args) File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/fetch.py", line 40, in _main_thread_load (lv_changes, remove) = load_lvs( File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/lv.py", line 148, in load_lvs return common( File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/loader.py", line 37, in common objects = retrieve(search_keys, cache_refresh=False) File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/lv.py", line 72, in lvs_state_retrieve lvs = sorted(cfg.db.fetch_lvs(selection), key=get_key) File "/home/tasleson/projects/lvm2/daemons/lvmdbusd/lv.py", line 35, in get_key pool = i['pool_lv'] KeyError: 'pool_lv'
1049 lines
32 KiB
Python
1049 lines
32 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, log_error, _handle_execute, LvmBug
|
|
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, VDO_POOL_INTERFACE
|
|
from .request import RequestEntry
|
|
from .utils import n, n32, d
|
|
from .loader import common
|
|
from .state import State
|
|
from . import background
|
|
from .utils import round_size, mt_remove_dbus_objects, lvm_column_key
|
|
from .job import JobState
|
|
|
|
|
|
# Try and build a key for a LV, so that we sort the LVs with the 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()
|
|
|
|
try:
|
|
# 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:
|
|
if cfg.vdo_support:
|
|
rc.append(LvStateVdo(
|
|
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'],
|
|
n32(l['snap_percent']),
|
|
n32(l['metadata_percent']),
|
|
n32(l['copy_percent']),
|
|
n32(l['sync_percent']),
|
|
n(l['lv_metadata_size']),
|
|
l['move_pv'],
|
|
l['move_pv_uuid'],
|
|
l['vdo_operating_mode'],
|
|
l['vdo_compression_state'],
|
|
l['vdo_index_state'],
|
|
n(l['vdo_used_size']),
|
|
d(l['vdo_saving_percent']),
|
|
l['vdo_compression'],
|
|
l['vdo_deduplication'],
|
|
l['vdo_use_metadata_hints'],
|
|
n32(l['vdo_minimum_io_size']),
|
|
n(l['vdo_block_map_cache_size']),
|
|
n32(l['vdo_block_map_era_length']),
|
|
l['vdo_use_sparse_index'],
|
|
n(l['vdo_index_memory_size']),
|
|
n(l['vdo_slab_size']),
|
|
n32(l['vdo_ack_threads']),
|
|
n32(l['vdo_bio_threads']),
|
|
n32(l['vdo_bio_rotation']),
|
|
n32(l['vdo_cpu_threads']),
|
|
n32(l['vdo_hash_zone_threads']),
|
|
n32(l['vdo_logical_threads']),
|
|
n32(l['vdo_physical_threads']),
|
|
n32(l['vdo_max_discard']),
|
|
l['vdo_write_policy'],
|
|
n32(l['vdo_header_size'])))
|
|
else:
|
|
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'],
|
|
n32(l['snap_percent']),
|
|
n32(l['metadata_percent']),
|
|
n32(l['copy_percent']),
|
|
n32(l['sync_percent']),
|
|
n(l['lv_metadata_size']),
|
|
l['move_pv'],
|
|
l['move_pv_uuid']))
|
|
except KeyError as ke:
|
|
# Sometimes lvm omits returning one of the keys we requested.
|
|
key = ke.args[0]
|
|
if lvm_column_key(key):
|
|
raise LvmBug("missing JSON key: '%s'" % key)
|
|
raise ke
|
|
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)
|
|
|
|
segs_decorate = []
|
|
for i in pv_segs:
|
|
segs_decorate.append((dbus.UInt64(i[0]),
|
|
dbus.UInt64(i[1]),
|
|
dbus.String(i[2])))
|
|
|
|
rc.append((dbus.ObjectPath(pv_obj), segs_decorate))
|
|
|
|
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)
|
|
assert op
|
|
rc.append(dbus.ObjectPath(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, SnapPercent,
|
|
MetaDataPercent, CopyPercent, SyncPercent, MetaDataSizeBytes,
|
|
move_pv, move_pv_uuid):
|
|
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(dbus.String(segtypes))
|
|
else:
|
|
self._segs.extend([dbus.String(x) for x in 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] == 'd':
|
|
return LvVdoPool
|
|
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)
|
|
|
|
|
|
class LvStateVdo(LvState):
|
|
|
|
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, SnapPercent,
|
|
MetaDataPercent, CopyPercent, SyncPercent,
|
|
MetaDataSizeBytes, move_pv, move_pv_uuid,
|
|
vdo_operating_mode, vdo_compression_state, vdo_index_state,
|
|
vdo_used_size, vdo_saving_percent, vdo_compression,
|
|
vdo_deduplication, vdo_use_metadata_hints,
|
|
vdo_minimum_io_size, vdo_block_map_cache_size,
|
|
vdo_block_map_era_length, vdo_use_sparse_index,
|
|
vdo_index_memory_size, vdo_slab_size, vdo_ack_threads,
|
|
vdo_bio_threads, vdo_bio_rotation, vdo_cpu_threads,
|
|
vdo_hash_zone_threads, vdo_logical_threads,
|
|
vdo_physical_threads, vdo_max_discard,
|
|
vdo_write_policy, vdo_header_size):
|
|
super(LvStateVdo, self).__init__(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, SnapPercent,
|
|
MetaDataPercent, CopyPercent, SyncPercent,
|
|
MetaDataSizeBytes, move_pv, move_pv_uuid)
|
|
|
|
utils.init_class_from_arguments(self, "vdo_", snake_to_pascal=True)
|
|
|
|
|
|
# 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, '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")
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Attr', 's')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'SnapPercent', 'u')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataPercent', 'u')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'CopyPercent', 'u')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'SyncPercent', 'u')
|
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataSizeBytes', 't')
|
|
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)
|
|
_MovePv_meta = ('o', LV_COMMON_INTERFACE)
|
|
|
|
def _get_move_pv(self):
|
|
path = None
|
|
|
|
# It's likely that the move_pv is empty
|
|
if self.state.move_pv_uuid and self.state.move_pv:
|
|
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
self.state.move_pv_uuid, self.state.move_pv)
|
|
if not path:
|
|
path = '/'
|
|
return path
|
|
|
|
# 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
|
|
self._move_pv = self._get_move_pv()
|
|
|
|
@staticmethod
|
|
def handle_execute(rc, out, err):
|
|
_handle_execute(rc, out, err, LV_INTERFACE)
|
|
|
|
@staticmethod
|
|
def validate_dbus_object(lv_uuid, lv_name):
|
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
if not dbo:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE,
|
|
'LV with uuid %s and name %s not present!' %
|
|
(lv_uuid, lv_name))
|
|
return dbo
|
|
|
|
def attr_struct(self, index, type_map, default='undisclosed'):
|
|
try:
|
|
if self.state.Attr[index] not in type_map:
|
|
log_error("LV %s %s with lv_attr %s, lv_attr[%d] = "
|
|
"'%s' is not known" %
|
|
(self.Uuid, self.Name, self.Attr, index,
|
|
self.state.Attr[index]))
|
|
|
|
return dbus.Struct((self.state.Attr[index],
|
|
type_map.get(self.state.Attr[index], default)),
|
|
signature="(ss)")
|
|
except BaseException as b:
|
|
st = utils.extract_stack_trace(b)
|
|
log_error("attr_struct: \n%s" % st)
|
|
return dbus.Struct(('?', 'Unavailable'), signature="(ss)")
|
|
|
|
@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',
|
|
'd': 'vdo pool', 'D': 'vdo pool data', 'g': 'integrity',
|
|
'-': 'Unspecified'}
|
|
return self.attr_struct(0, type_map)
|
|
|
|
@property
|
|
def Permissions(self):
|
|
type_map = {'w': 'writable', 'r': 'read-only',
|
|
'R': 'Read-only activation of non-read-only volume',
|
|
'-': 'Unspecified'}
|
|
return self.attr_struct(1, type_map)
|
|
|
|
@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.attr_struct(2, type_map)
|
|
|
|
@property
|
|
def FixedMinor(self):
|
|
return dbus.Boolean(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',
|
|
'h': 'historical',
|
|
'c': 'check needed suspended thin-pool',
|
|
'C': 'check needed',
|
|
'X': 'unknown',
|
|
'-': 'Unspecified'}
|
|
return self.attr_struct(4, type_map)
|
|
|
|
@property
|
|
def TargetType(self):
|
|
type_map = {'C': 'Cache', 'm': 'mirror', 'r': 'raid',
|
|
's': 'snapshot', 't': 'thin', 'u': 'unknown',
|
|
'v': 'virtual', '-': 'Unspecified'}
|
|
return dbus.Struct((self.state.Attr[6], type_map[self.state.Attr[6]]),
|
|
signature="(ss)")
|
|
|
|
@property
|
|
def ZeroBlocks(self):
|
|
return dbus.Boolean(self.state.Attr[7] == 'z')
|
|
|
|
@property
|
|
def Health(self):
|
|
type_map = {'p': 'partial',
|
|
'r': 'refresh needed',
|
|
'm': 'mismatches',
|
|
'w': 'writemostly',
|
|
'X': 'unknown',
|
|
'-': 'unspecified',
|
|
's': 'reshaping',
|
|
'F': 'failed',
|
|
'D': 'Data space',
|
|
'R': 'Remove',
|
|
'M': 'Metadata'}
|
|
return self.attr_struct(8, type_map)
|
|
|
|
@property
|
|
def SkipActivation(self):
|
|
return dbus.Boolean(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 dbus.Boolean(self.state.Attr[0] == 'V')
|
|
|
|
@property
|
|
def IsThinPool(self):
|
|
return dbus.Boolean(self.state.Attr[0] == 't')
|
|
|
|
@property
|
|
def Active(self):
|
|
return dbus.Boolean(self.state.active == "active")
|
|
|
|
@property
|
|
def MovePv(self):
|
|
return dbus.ObjectPath(self._move_pv)
|
|
|
|
|
|
# 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
|
|
LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
|
# Remove the LV, if successful then remove from the model
|
|
LvCommon.handle_execute(*cmdhandler.lv_remove(lv_name, remove_options))
|
|
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
|
|
LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
|
# Rename the logical volume
|
|
LvCommon.handle_execute(*cmdhandler.lv_rename(lv_name, new_name,
|
|
rename_options))
|
|
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)
|
|
|
|
background.cmd_runner(r)
|
|
|
|
@staticmethod
|
|
def _snap_shot(lv_uuid, lv_name, name, optional_size,
|
|
snapshot_options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
|
# 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:
|
|
space = dbo.SizeBytes // 80
|
|
remainder = space % 512
|
|
optional_size = space + 512 - remainder
|
|
|
|
LvCommon.handle_execute(*cmdhandler.vg_lv_snapshot(
|
|
lv_name, snapshot_options, name, optional_size))
|
|
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
|
return cfg.om.get_object_path_by_lvm_id(full_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 = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
|
|
|
# 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
|
|
LvCommon.handle_execute(*cmdhandler.lv_resize(
|
|
dbo.lvm_id, size_change, pv_dests, resize_options))
|
|
return "/"
|
|
|
|
@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
|
|
LvCommon.validate_dbus_object(uuid, lv_name)
|
|
LvCommon.handle_execute(*cmdhandler.activate_deactivate(
|
|
'lvchange', lv_name, activate, control_flags, options))
|
|
return '/'
|
|
|
|
@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
|
|
LvCommon.validate_dbus_object(uuid, lv_name)
|
|
LvCommon.handle_execute(*cmdhandler.lv_tag(
|
|
lv_name, tags_add, tags_del, tag_options))
|
|
return '/'
|
|
|
|
@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)
|
|
|
|
@staticmethod
|
|
def _caching_common(method, lv_uuid, lv_name, lv_object_path, cache_options):
|
|
# Make sure we have a dbus object representing it
|
|
dbo = LvCommon.validate_dbus_object(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 lv_to_cache:
|
|
fcn = lv_to_cache.lv_full_name()
|
|
rc, out, err = method(
|
|
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!
|
|
mt_remove_dbus_objects((dbo, lv_to_cache))
|
|
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:
|
|
raise dbus.exceptions.DBusException(
|
|
LV_INTERFACE, 'LV to cache with object path %s not present!' %
|
|
lv_object_path)
|
|
return lv_converted
|
|
|
|
@staticmethod
|
|
def _writecache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
|
|
return Lv._caching_common(cmdhandler.lv_writecache_lv, lv_uuid,
|
|
lv_name, lv_object_path, cache_options)
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=LV_INTERFACE,
|
|
in_signature='oia{sv}',
|
|
out_signature='(oo)',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def WriteCacheLv(self, lv_object, tmo, cache_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, Lv._writecache_lv,
|
|
(self.Uuid, self.lvm_id, lv_object,
|
|
cache_options), cb, cbe)
|
|
cfg.worker_q.put(r)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'OperatingMode', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'CompressionState', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'IndexState', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'UsedSize', 't')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'SavingPercent', 'd')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'Compression', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'Deduplication', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'UseMetadataHints', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'MinimumIoSize', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'BlockMapCacheSize', "t")
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'BlockMapEraLength', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'UseSparseIndex', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'IndexMemorySize', 't')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'SlabSize', 't')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'AckThreads', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'BioThreads', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'BioRotation', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'CpuThreads', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'HashZoneThreads', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'LogicalThreads', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'PhysicalThreads', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'MaxDiscard', 'u')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'WritePolicy', 's')
|
|
@utils.dbus_property(VDO_POOL_INTERFACE, 'HeaderSize', 'u')
|
|
class LvVdoPool(Lv):
|
|
_DataLv_meta = ("o", VDO_POOL_INTERFACE)
|
|
|
|
def __init__(self, object_path, object_state):
|
|
super(LvVdoPool, self).__init__(object_path, object_state)
|
|
self.set_interface(VDO_POOL_INTERFACE)
|
|
self._data_lv, _ = self._get_data_meta()
|
|
|
|
@property
|
|
def DataLv(self):
|
|
return dbus.ObjectPath(self._data_lv)
|
|
|
|
@staticmethod
|
|
def _enable_disable_compression(pool_uuid, pool_name, enable, comp_options):
|
|
# Make sure we have a dbus object representing it
|
|
LvCommon.validate_dbus_object(pool_uuid, pool_name)
|
|
# Rename the logical volume
|
|
LvCommon.handle_execute(*cmdhandler.lv_vdo_compression(
|
|
pool_name, enable, comp_options))
|
|
return '/'
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=VDO_POOL_INTERFACE,
|
|
in_signature='ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def EnableCompression(self, tmo, comp_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, LvVdoPool._enable_disable_compression,
|
|
(self.Uuid, self.lvm_id, True, comp_options),
|
|
cb, cbe, False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=VDO_POOL_INTERFACE,
|
|
in_signature='ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def DisableCompression(self, tmo, comp_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, LvVdoPool._enable_disable_compression,
|
|
(self.Uuid, self.lvm_id, False, comp_options),
|
|
cb, cbe, False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@staticmethod
|
|
def _enable_disable_deduplication(pool_uuid, pool_name, enable, dedup_options):
|
|
# Make sure we have a dbus object representing it
|
|
LvCommon.validate_dbus_object(pool_uuid, pool_name)
|
|
# Rename the logical volume
|
|
LvCommon.handle_execute(*cmdhandler.lv_vdo_deduplication(
|
|
pool_name, enable, dedup_options))
|
|
return '/'
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=VDO_POOL_INTERFACE,
|
|
in_signature='ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def EnableDeduplication(self, tmo, dedup_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, LvVdoPool._enable_disable_deduplication,
|
|
(self.Uuid, self.lvm_id, True, dedup_options),
|
|
cb, cbe, False)
|
|
cfg.worker_q.put(r)
|
|
|
|
@dbus.service.method(
|
|
dbus_interface=VDO_POOL_INTERFACE,
|
|
in_signature='ia{sv}',
|
|
out_signature='o',
|
|
async_callbacks=('cb', 'cbe'))
|
|
def DisableDeduplication(self, tmo, dedup_options, cb, cbe):
|
|
r = RequestEntry(
|
|
tmo, LvVdoPool._enable_disable_deduplication,
|
|
(self.Uuid, self.lvm_id, False, dedup_options),
|
|
cb, cbe, 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 dbus.ObjectPath(self._data_lv)
|
|
|
|
@property
|
|
def MetaDataLv(self):
|
|
return dbus.ObjectPath(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 = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
|
LvCommon.handle_execute(*cmdhandler.lv_lv_create(
|
|
lv_name, create_options, name, size_bytes))
|
|
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
|
return cfg.om.get_object_path_by_lvm_id(full_name)
|
|
|
|
@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 dbus.ObjectPath(self._data_lv)
|
|
|
|
@property
|
|
def MetaDataLv(self):
|
|
return dbus.ObjectPath(self._metadata_lv)
|
|
|
|
@staticmethod
|
|
def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
|
|
return Lv._caching_common(cmdhandler.lv_cache_lv, lv_uuid, lv_name,
|
|
lv_object_path, cache_options)
|
|
|
|
@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 dbus.ObjectPath(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 = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
|
|
|
# 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
|
|
mt_remove_dbus_objects((cache_pool, dbo))
|
|
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))
|
|
|
|
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)
|
|
background.cmd_runner(r)
|