mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
f1bc68beb4
When we use udev or have lvm call back into the dbus service when a change occurs, even if that change originated from the dbus service we end up refreshing the state of the system twice which is not needed or wanted. This change handles this case by removing any pending refreshes in the worker queue if the state of the system was just updated. Signed-off-by: Tony Asleson <tasleson@redhat.com>
412 lines
10 KiB
Python
Executable File
412 lines
10 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# 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 collections import OrderedDict
|
|
|
|
import pprint as prettyprint
|
|
|
|
try:
|
|
from . import cmdhandler
|
|
from .utils import log_debug
|
|
except SystemError:
|
|
import cmdhandler
|
|
from utils import log_debug
|
|
|
|
|
|
class DataStore(object):
|
|
def __init__(self):
|
|
self.pvs = {}
|
|
self.vgs = {}
|
|
self.lvs = {}
|
|
self.pv_lvs = {}
|
|
self.lv_pvs = {}
|
|
self.lvs_hidden = {}
|
|
|
|
self.pv_path_to_uuid = {}
|
|
self.vg_name_to_uuid = {}
|
|
self.lv_full_name_to_uuid = {}
|
|
|
|
self.lvs_in_vgs = {}
|
|
self.pvs_in_vgs = {}
|
|
|
|
# self.refresh()
|
|
self.num_refreshes = 0
|
|
|
|
@staticmethod
|
|
def _insert_record(table, key, record, allowed_multiple):
|
|
if key in table:
|
|
existing = table[key]
|
|
|
|
for rec_k, rec_v in record.items():
|
|
if rec_k in allowed_multiple:
|
|
# This column name allows us to store multiple value for
|
|
# each type
|
|
if not isinstance(existing[rec_k], list):
|
|
existing_value = existing[rec_k]
|
|
existing[rec_k] = [existing_value, rec_v]
|
|
else:
|
|
existing[rec_k].append(rec_v)
|
|
else:
|
|
# If something is not expected to have changing values
|
|
# lets ensure that
|
|
if existing[rec_k] != rec_v:
|
|
raise RuntimeError(
|
|
"existing[%s]=%s != %s" %
|
|
(rec_k, str(existing[rec_k]),
|
|
str(rec_v)))
|
|
else:
|
|
table[key] = record
|
|
|
|
@staticmethod
|
|
def _parse_pvs(_pvs):
|
|
pvs = sorted(_pvs, key=lambda pk: pk['pv_name'])
|
|
|
|
c_pvs = OrderedDict()
|
|
c_lookup = {}
|
|
c_pvs_in_vgs = {}
|
|
|
|
for p in pvs:
|
|
DataStore._insert_record(
|
|
c_pvs, p['pv_uuid'], p,
|
|
['pv_seg_start', 'pvseg_size', 'segtype'])
|
|
|
|
for p in c_pvs.values():
|
|
# Capture which PVs are associated with which VG
|
|
if p['vg_uuid'] not in c_pvs_in_vgs:
|
|
c_pvs_in_vgs[p['vg_uuid']] = []
|
|
|
|
if p['vg_name']:
|
|
c_pvs_in_vgs[p['vg_uuid']].append(
|
|
(p['pv_name'], p['pv_uuid']))
|
|
|
|
# Lookup for translating between /dev/<name> and pv uuid
|
|
c_lookup[p['pv_name']] = p['pv_uuid']
|
|
|
|
return c_pvs, c_lookup, c_pvs_in_vgs
|
|
|
|
@staticmethod
|
|
def _parse_vgs(_vgs):
|
|
vgs = sorted(_vgs, key=lambda vk: vk['vg_name'])
|
|
|
|
c_vgs = OrderedDict()
|
|
c_lookup = {}
|
|
|
|
for i in vgs:
|
|
c_lookup[i['vg_name']] = i['vg_uuid']
|
|
DataStore._insert_record(c_vgs, i['vg_uuid'], i, [])
|
|
|
|
return c_vgs, c_lookup
|
|
|
|
@staticmethod
|
|
def _parse_lvs(_lvs):
|
|
lvs = sorted(_lvs, key=lambda vk: vk['lv_name'])
|
|
|
|
c_lvs = OrderedDict()
|
|
c_lvs_in_vgs = {}
|
|
c_lvs_hidden = {}
|
|
c_lv_full_lookup = {}
|
|
|
|
for i in lvs:
|
|
full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
|
|
c_lv_full_lookup[full_name] = i['lv_uuid']
|
|
DataStore._insert_record(
|
|
c_lvs, i['lv_uuid'], i,
|
|
['seg_pe_ranges', 'segtype'])
|
|
|
|
for i in c_lvs.values():
|
|
if i['vg_uuid'] not in c_lvs_in_vgs:
|
|
c_lvs_in_vgs[i['vg_uuid']] = []
|
|
|
|
c_lvs_in_vgs[
|
|
i['vg_uuid']].append(
|
|
(i['lv_name'],
|
|
(i['lv_attr'], i['lv_layout'], i['lv_role']),
|
|
i['lv_uuid']))
|
|
|
|
if i['lv_parent']:
|
|
# Lookup what the parent refers too
|
|
parent_name = i['lv_parent']
|
|
full_parent_name = "%s/%s" % (i['vg_name'], parent_name)
|
|
if full_parent_name not in c_lv_full_lookup:
|
|
parent_name = '[%s]' % (parent_name)
|
|
full_parent_name = "%s/%s" % (i['vg_name'], parent_name)
|
|
|
|
parent_uuid = c_lv_full_lookup[full_parent_name]
|
|
|
|
if parent_uuid not in c_lvs_hidden:
|
|
c_lvs_hidden[parent_uuid] = []
|
|
|
|
c_lvs_hidden[parent_uuid].append(
|
|
(i['lv_uuid'], i['lv_name']))
|
|
|
|
return c_lvs, c_lvs_in_vgs, c_lvs_hidden, c_lv_full_lookup
|
|
|
|
@staticmethod
|
|
def _make_list(l):
|
|
if not isinstance(l, list):
|
|
l = [l]
|
|
return l
|
|
|
|
@staticmethod
|
|
def _parse_seg_entry(se, segtype):
|
|
if se:
|
|
# print("_parse_seg_entry %s %s" % (str(se), str(segtype)))
|
|
device, segs = se.split(":")
|
|
start, end = segs.split('-')
|
|
return (device, (start, end), segtype)
|
|
else:
|
|
return ("", (), segtype)
|
|
|
|
@staticmethod
|
|
def _build_segments(l, seg_types):
|
|
rc = []
|
|
l = DataStore._make_list(l)
|
|
s = DataStore._make_list(seg_types)
|
|
|
|
assert len(l) == len(s)
|
|
ls = list(zip(l, s))
|
|
|
|
for i in ls:
|
|
if ' ' in i[0]:
|
|
tmp = i[0].split(' ')
|
|
for t in tmp:
|
|
rc.append(DataStore._parse_seg_entry(t, i[1]))
|
|
else:
|
|
rc.append(DataStore._parse_seg_entry(*i))
|
|
return rc
|
|
|
|
@staticmethod
|
|
def _pv_device_lv_entry(table, pv_device, lv_uuid, meta, lv_attr,
|
|
segment_info):
|
|
|
|
if pv_device not in table:
|
|
table[pv_device] = {}
|
|
|
|
if lv_uuid not in table[pv_device]:
|
|
table[pv_device][lv_uuid] = {}
|
|
table[pv_device][lv_uuid]['segs'] = [segment_info]
|
|
table[pv_device][lv_uuid]['name'] = meta
|
|
table[pv_device][lv_uuid]['meta'] = lv_attr
|
|
else:
|
|
table[pv_device][lv_uuid]['segs'].append(segment_info)
|
|
|
|
@staticmethod
|
|
def _pv_device_lv_format(pv_device_lvs):
|
|
rc = {}
|
|
|
|
for pv_device, pd in pv_device_lvs.items():
|
|
lvs = []
|
|
for lv_uuid, ld in sorted(pd.items()):
|
|
lvs.append((lv_uuid, ld['name'], ld['meta'], ld['segs']))
|
|
|
|
rc[pv_device] = lvs
|
|
return rc
|
|
|
|
@staticmethod
|
|
def _lvs_device_pv_entry(table, lv_uuid, pv_device, pv_uuid, segment_info):
|
|
if lv_uuid not in table:
|
|
table[lv_uuid] = {}
|
|
|
|
if pv_device not in table[lv_uuid]:
|
|
table[lv_uuid][pv_device] = {}
|
|
table[lv_uuid][pv_device]['segs'] = [segment_info]
|
|
table[lv_uuid][pv_device]['pv_uuid'] = pv_uuid
|
|
else:
|
|
table[lv_uuid][pv_device]['segs'].append(segment_info)
|
|
|
|
@staticmethod
|
|
def _lvs_device_pv_format(lvs_device_pvs):
|
|
rc = {}
|
|
|
|
for lv_uuid, ld in lvs_device_pvs.items():
|
|
pvs = []
|
|
for pv_device, pd in sorted(ld.items()):
|
|
pvs.append((pd['pv_uuid'], pv_device, pd['segs']))
|
|
|
|
rc[lv_uuid] = pvs
|
|
return rc
|
|
|
|
def _parse_pv_in_lvs(self):
|
|
pv_device_lvs = {} # What LVs are stored on a PV
|
|
lvs_device_pv = {} # Where LV data is stored
|
|
|
|
for i in self.lvs.values():
|
|
segs = self._build_segments(i['seg_pe_ranges'], i['segtype'])
|
|
for s in segs:
|
|
# We are referring to physical device
|
|
if '/dev/' in s[0]:
|
|
device, r, seg_type = s
|
|
|
|
DataStore._pv_device_lv_entry(
|
|
pv_device_lvs, device, i['lv_uuid'], i['lv_name'],
|
|
(i['lv_attr'], i['lv_layout'], i['lv_role']),
|
|
(r[0], r[1], seg_type))
|
|
|
|
# (pv_name, pv_segs, pv_uuid)
|
|
DataStore._lvs_device_pv_entry(
|
|
lvs_device_pv, i['lv_uuid'], device,
|
|
self.pv_path_to_uuid[device], (r[0], r[1], seg_type))
|
|
else:
|
|
# TODO Handle the case where the segments refer to a LV
|
|
# and not a PV
|
|
pass
|
|
# print("Handle this %s %s %s" % (s[0], s[1], s[2]))
|
|
|
|
# Convert form to needed result for consumption
|
|
pv_device_lvs_result = DataStore._pv_device_lv_format(pv_device_lvs)
|
|
lvs_device_pv_result = DataStore._lvs_device_pv_format(lvs_device_pv)
|
|
|
|
return pv_device_lvs_result, lvs_device_pv_result
|
|
|
|
def refresh(self, log=True):
|
|
"""
|
|
Go out and query lvm for the latest data in as few trips as possible
|
|
:param log Add debug log entry/exit messages
|
|
:return: None
|
|
"""
|
|
self.num_refreshes += 1
|
|
if log:
|
|
log_debug("lvmdb - refresh entry")
|
|
|
|
# Grab everything first then parse it
|
|
_raw_pvs = cmdhandler.pv_retrieve_with_segs()
|
|
_raw_vgs = cmdhandler.vg_retrieve(None)
|
|
_raw_lvs = cmdhandler.lv_retrieve_with_segments()
|
|
|
|
_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs(_raw_pvs)
|
|
_vgs, _vgs_lookup = self._parse_vgs(_raw_vgs)
|
|
_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs(_raw_lvs)
|
|
|
|
# Set all
|
|
self.pvs = _pvs
|
|
self.pv_path_to_uuid = _pvs_lookup
|
|
self.vg_name_to_uuid = _vgs_lookup
|
|
self.lv_full_name_to_uuid = _lvs_lookup
|
|
|
|
self.vgs = _vgs
|
|
self.lvs = _lvs
|
|
self.lvs_in_vgs = _lvs_in_vgs
|
|
self.pvs_in_vgs = _pvs_in_vgs
|
|
self.lvs_hidden = _lvs_hidden
|
|
|
|
# Create lookup table for which LV and segments are on each PV
|
|
self.pv_lvs, self.lv_pvs = self._parse_pv_in_lvs()
|
|
|
|
if log:
|
|
log_debug("lvmdb - refresh exit")
|
|
|
|
def fetch_pvs(self, pv_name):
|
|
if not pv_name:
|
|
return self.pvs.values()
|
|
else:
|
|
rc = []
|
|
for s in pv_name:
|
|
rc.append(self.pvs[self.pv_path_to_uuid[s]])
|
|
return rc
|
|
|
|
def fetch_vgs(self, vg_name):
|
|
if not vg_name:
|
|
return self.vgs.values()
|
|
else:
|
|
rc = []
|
|
for s in vg_name:
|
|
rc.append(self.vgs[self.vg_name_to_uuid[s]])
|
|
return rc
|
|
|
|
def fetch_lvs(self, lv_names):
|
|
try:
|
|
if not lv_names:
|
|
return self.lvs.values()
|
|
else:
|
|
rc = []
|
|
for s in lv_names:
|
|
rc.append(self.lvs[self.lv_full_name_to_uuid[s]])
|
|
return rc
|
|
except KeyError as ke:
|
|
print("Key %s not found!" % (str(lv_names)))
|
|
print("lv name to uuid lookup")
|
|
for keys in sorted(self.lv_full_name_to_uuid.keys()):
|
|
print("%s" % (keys))
|
|
print("lvs entries by uuid")
|
|
for keys in sorted(self.lvs.keys()):
|
|
print("%s" % (keys))
|
|
raise ke
|
|
|
|
def pv_pe_segments(self, pv_uuid):
|
|
pv = self.pvs[pv_uuid]
|
|
return list(zip(pv['pv_seg_start'], pv['pvseg_size']))
|
|
|
|
def pv_contained_lv(self, pv_device):
|
|
rc = []
|
|
if pv_device in self.pv_lvs:
|
|
rc = self.pv_lvs[pv_device]
|
|
return rc
|
|
|
|
def lv_contained_pv(self, lv_uuid):
|
|
rc = []
|
|
if lv_uuid in self.lv_pvs:
|
|
rc = self.lv_pvs[lv_uuid]
|
|
return rc
|
|
|
|
def lvs_in_vg(self, vg_uuid):
|
|
# Return an array of
|
|
# (lv_name, (lv_attr, lv_layout, lv_role), lv_uuid)
|
|
rc = []
|
|
if vg_uuid in self.lvs_in_vgs:
|
|
rc = self.lvs_in_vgs[vg_uuid]
|
|
return rc
|
|
|
|
def pvs_in_vg(self, vg_uuid):
|
|
# Returns an array of (pv_name, pv_uuid)
|
|
rc = []
|
|
if vg_uuid in self.pvs_in_vgs:
|
|
rc = self.pvs_in_vgs[vg_uuid]
|
|
return rc
|
|
|
|
def hidden_lvs(self, lv_uuid):
|
|
# For a specified LV, return a list of hidden lv_uuid, lv_name
|
|
# for it
|
|
rc = []
|
|
if lv_uuid in self.lvs_hidden:
|
|
rc = self.lvs_hidden[lv_uuid]
|
|
return rc
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pp = prettyprint.PrettyPrinter(indent=4)
|
|
|
|
ds = DataStore()
|
|
ds.refresh()
|
|
|
|
for v in ds.pvs.values():
|
|
pp.pprint(v)
|
|
|
|
for v in ds.vgs.values():
|
|
pp.pprint(v)
|
|
|
|
print("LVS")
|
|
for v in ds.lvs.values():
|
|
pp.pprint(v)
|
|
|
|
print("LVS in VG")
|
|
for k, v in ds.lvs_in_vgs.items():
|
|
print("VG uuid = %s" % (k))
|
|
pp.pprint(v)
|
|
|
|
print("pv_in_lvs")
|
|
for k, v in ds.pv_lvs.items():
|
|
print("PV %s contains LVS:" % (k))
|
|
pp.pprint(v)
|
|
|
|
for k, v in ds.lv_pvs.items():
|
|
print("LV device = %s" % (k))
|
|
pp.pprint(v)
|