mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
b717c5efae
When we are processing the LVs we need to build up dbus objects from least dependent to most dependent, so that we have information available when constructing.
890 lines
25 KiB
Python
890 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
|
|
|
|
|
|
# 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_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_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_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_lvm_id(
|
|
pool_lv_uuid, '%s/%s' % (vg_name, PoolLv),
|
|
gen)
|
|
else:
|
|
self.PoolLv = '/'
|
|
|
|
if OriginLv:
|
|
self.OriginLv = \
|
|
cfg.om.get_object_path_by_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.Name[0] == '[':
|
|
return LvCommon
|
|
if self.Attr[0] == 't':
|
|
return LvThinPool
|
|
elif self.Attr[0] == 'C':
|
|
if 'pool' in self.layout:
|
|
return LvCachePool
|
|
else:
|
|
return LvCacheLv
|
|
elif self.OriginLv != '/':
|
|
return LvSnapShot
|
|
else:
|
|
return Lv
|
|
|
|
def create_dbus_object(self, path):
|
|
if not path:
|
|
path = cfg.om.get_object_path_by_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)
|
|
|
|
o = cfg.om.get_object_by_lvm_id(full_name)
|
|
if o:
|
|
return o.dbus_object_path()
|
|
|
|
return '/'
|
|
|
|
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')
|
|
def Move(self, pv_src_obj, pv_source_range,
|
|
pv_dests_and_ranges,
|
|
tmo, move_options):
|
|
return background.move(
|
|
LV_INTERFACE, self.lvm_id, pv_src_obj,
|
|
pv_source_range, pv_dests_and_ranges,
|
|
move_options, tmo)
|
|
|
|
@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_by_lvm_id(fcn).dbus_object_path()
|
|
|
|
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_by_lvm_id(lv_name).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 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')
|
|
def Merge(self, tmo, merge_options):
|
|
return background.merge(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
|
|
merge_options, tmo)
|