1465 lines
55 KiB
Python
1465 lines
55 KiB
Python
|
#
|
||
|
# lvm_dialog_gui.py: dialog for editing a volume group request
|
||
|
#
|
||
|
# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc.
|
||
|
# All rights reserved.
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation; either version 2 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
#
|
||
|
# Author(s): Michael Fulbright <msf@redhat.com>
|
||
|
#
|
||
|
|
||
|
import copy
|
||
|
|
||
|
import gobject
|
||
|
import gtk
|
||
|
import datacombo
|
||
|
|
||
|
import gui
|
||
|
from partition_ui_helpers_gui import *
|
||
|
from constants import *
|
||
|
from storage.devices import *
|
||
|
from storage.deviceaction import *
|
||
|
|
||
|
import gettext
|
||
|
_ = lambda x: gettext.ldgettext("anaconda", x)
|
||
|
P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z)
|
||
|
|
||
|
import logging
|
||
|
log = logging.getLogger("anaconda")
|
||
|
|
||
|
class VolumeGroupEditor:
|
||
|
|
||
|
def getTempVG(self):
|
||
|
pvs = [copy.deepcopy(pv) for pv in self.pvs]
|
||
|
vg = LVMVolumeGroupDevice('tmp-%s' % self.vg.name,
|
||
|
parents=pvs, peSize=self.peSize)
|
||
|
for lv in self.lvs.values():
|
||
|
_l = LVMLogicalVolumeDevice(lv['name'], vg, format=lv['format'],
|
||
|
size=lv['size'], exists=lv['exists'],
|
||
|
stripes=lv['stripes'],
|
||
|
logSize=lv['logSize'],
|
||
|
snapshotSpace=lv['snapshotSpace'])
|
||
|
_l.originalFormat = lv['originalFormat']
|
||
|
|
||
|
return vg
|
||
|
|
||
|
def numAvailableLVSlots(self):
|
||
|
return max(0, lvm.MAX_LV_SLOTS - len(self.lvs))
|
||
|
|
||
|
def computeSpaceValues(self):
|
||
|
vg = self.getTempVG()
|
||
|
vgsize = vg.size
|
||
|
vgfree = vg.freeSpace
|
||
|
vgused = vgsize - vgfree
|
||
|
return (vgsize, vgused, vgfree)
|
||
|
|
||
|
def getPVWastedRatio(self, newpe):
|
||
|
""" given a new pe value, return percentage of smallest PV wasted
|
||
|
|
||
|
newpe - (int) new value of PE, in KB
|
||
|
"""
|
||
|
pvlist = self.getSelectedPhysicalVolumes()
|
||
|
|
||
|
waste = 0.0
|
||
|
for pv in pvlist:
|
||
|
waste = max(waste, (long(pv.size*1024) % newpe)/(pv.size*1024.0))
|
||
|
|
||
|
return waste
|
||
|
|
||
|
def getSmallestPVSize(self):
|
||
|
""" finds the smallest PV and returns its size in MB
|
||
|
"""
|
||
|
first = 1
|
||
|
pvlist = self.getSelectedPhysicalVolumes()
|
||
|
for pv in pvlist:
|
||
|
try:
|
||
|
pesize = int(self.peCombo.get_active_value()) / 1024.0
|
||
|
except:
|
||
|
pesize = self.vg.peSize
|
||
|
|
||
|
# FIXME: move this logic into a property of LVMVolumeGroupDevice
|
||
|
pvsize = max(0, lvm.clampSize(pv.size, pesize) - pesize)
|
||
|
if first:
|
||
|
minpvsize = pvsize
|
||
|
first = 0
|
||
|
else:
|
||
|
minpvsize = min(pvsize, minpvsize)
|
||
|
|
||
|
return minpvsize
|
||
|
|
||
|
|
||
|
def reclampLV(self, newpe):
|
||
|
""" given a new pe value, set logical volume sizes accordingly
|
||
|
|
||
|
newpe - (int) new value of PE, in MB
|
||
|
"""
|
||
|
|
||
|
pvlist = self.getSelectedPhysicalVolumes()
|
||
|
availSpaceMB = self.computeVGSize(pvlist, newpe)
|
||
|
|
||
|
# see if total space is enough
|
||
|
used = 0
|
||
|
resize = False
|
||
|
for lv in self.lvs.values():
|
||
|
# total space required by an lv may be greater than lv size.
|
||
|
vg_space = lv['size'] * lv['stripes'] + lv['logSize'] \
|
||
|
+ lv['snapshotSpace']
|
||
|
clamped_vg_space = lvm.clampSize(vg_space, newpe, roundup=1)
|
||
|
used += clamped_vg_space
|
||
|
if lv['size'] != lvm.clampSize(lv['size'], newpe, roundup=1):
|
||
|
resize = True
|
||
|
|
||
|
if used > availSpaceMB:
|
||
|
self.intf.messageWindow(_("Not enough space"),
|
||
|
_("The physical extent size cannot be "
|
||
|
"changed because otherwise the space "
|
||
|
"required by the currently defined "
|
||
|
"logical volumes will be increased "
|
||
|
"to more than the available space."),
|
||
|
custom_icon="error")
|
||
|
return 0
|
||
|
|
||
|
if resize:
|
||
|
rc = self.intf.messageWindow(_("Confirm Physical Extent Change"),
|
||
|
_("This change in the value of the "
|
||
|
"physical extent will require the "
|
||
|
"sizes of the current logical "
|
||
|
"volume requests to be rounded "
|
||
|
"up in size to an integer multiple "
|
||
|
"of the "
|
||
|
"physical extent.\n\nThis change "
|
||
|
"will take effect immediately."),
|
||
|
type="custom", custom_icon="question",
|
||
|
custom_buttons=["gtk-cancel", _("C_ontinue")])
|
||
|
if not rc:
|
||
|
return 0
|
||
|
|
||
|
for lv in self.lvs.values():
|
||
|
lv['size'] = lvm.clampSize(lv['size'], newpe, roundup=1)
|
||
|
|
||
|
return 1
|
||
|
|
||
|
def peChangeCB(self, widget, *args):
|
||
|
""" handle changes in the Physical Extent option menu
|
||
|
|
||
|
widget - menu item which was activated
|
||
|
peOption - the Option menu containing the items. The data value for
|
||
|
"lastval" is the previous PE value.
|
||
|
"""
|
||
|
|
||
|
curval = int(widget.get_active_value())
|
||
|
# this one's in MB so we can stop with all this dividing by 1024
|
||
|
curpe = curval / 1024.0
|
||
|
lastval = widget.get_data("lastpe")
|
||
|
lastidx = widget.get_data("lastidx")
|
||
|
|
||
|
# see if PE is too large compared to smallest PV
|
||
|
maxpvsize = self.getSmallestPVSize()
|
||
|
if curpe > maxpvsize:
|
||
|
self.intf.messageWindow(_("Not enough space"),
|
||
|
_("The physical extent size cannot be "
|
||
|
"changed because the value selected "
|
||
|
"(%(curpe)10.2f MB) is larger than the "
|
||
|
"smallest physical volume "
|
||
|
"(%(maxpvsize)10.2f MB) in the volume "
|
||
|
"group.") % {'curpe': curpe,
|
||
|
'maxpvsize': maxpvsize},
|
||
|
custom_icon="error")
|
||
|
widget.set_active(lastidx)
|
||
|
return 0
|
||
|
|
||
|
# see if new PE will make any PV useless due to overhead
|
||
|
if lvm.clampSize(maxpvsize, curpe) < curpe:
|
||
|
self.intf.messageWindow(_("Not enough space"),
|
||
|
_("The physical extent size cannot be "
|
||
|
"changed because the value selected "
|
||
|
"(%(curpe)10.2f MB) is too large "
|
||
|
"compared to the size of the "
|
||
|
"smallest physical volume "
|
||
|
"(%(maxpvsize)10.2f MB) in the "
|
||
|
"volume group.")
|
||
|
% {'curpe': curpe, 'maxpvsize': maxpvsize},
|
||
|
custom_icon="error")
|
||
|
widget.set_active(lastidx)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
if self.getPVWastedRatio(curpe) > 0.10:
|
||
|
rc = self.intf.messageWindow(_("Too small"),
|
||
|
_("This change in the value of the "
|
||
|
"physical extent will waste "
|
||
|
"substantial space on one or more "
|
||
|
"of the physical volumes in the "
|
||
|
"volume group."),
|
||
|
type="custom", custom_icon="error",
|
||
|
custom_buttons=["gtk-cancel", _("C_ontinue")])
|
||
|
if not rc:
|
||
|
widget.set_active(lastidx)
|
||
|
return 0
|
||
|
|
||
|
# now see if we need to fixup effect PV and LV sizes based on PE
|
||
|
if curval > lastval:
|
||
|
rc = self.reclampLV(curpe)
|
||
|
if not rc:
|
||
|
widget.set_active(lastidx)
|
||
|
return 0
|
||
|
else:
|
||
|
self.updateLogVolStore()
|
||
|
else:
|
||
|
maxlv = lvm.getMaxLVSize()
|
||
|
for lv in self.lvs.values():
|
||
|
if lv['size'] > maxlv:
|
||
|
self.intf.messageWindow(_("Not enough space"),
|
||
|
_("The physical extent size "
|
||
|
"cannot be changed because the "
|
||
|
"resulting maximum logical "
|
||
|
"volume size (%10.2f MB) is "
|
||
|
"smaller "
|
||
|
"than one or more of the "
|
||
|
"currently defined logical "
|
||
|
"volumes.") % (maxlv,),
|
||
|
custom_icon="error")
|
||
|
widget.set_active(lastidx)
|
||
|
return 0
|
||
|
|
||
|
widget.set_data("lastpe", curval)
|
||
|
widget.set_data("lastidx", widget.get_active())
|
||
|
|
||
|
# now actually set the VG's extent size
|
||
|
self.peSize = curpe
|
||
|
self.updateAllowedLvmPartitionsList()
|
||
|
self.updateVGSpaceLabels()
|
||
|
|
||
|
def prettyFormatPESize(self, val):
|
||
|
""" Pretty print for PE size in KB """
|
||
|
if val < 1024:
|
||
|
return "%s KB" % (val,)
|
||
|
elif val < 1024*1024:
|
||
|
return "%s MB" % (val/1024,)
|
||
|
else:
|
||
|
return "%s GB" % (val/1024/1024,)
|
||
|
|
||
|
def createPEOptionMenu(self, default=4096):
|
||
|
peCombo = datacombo.DataComboBox()
|
||
|
|
||
|
actualPE = []
|
||
|
for curpe in lvm.getPossiblePhysicalExtents(floor=1024):
|
||
|
# don't show PE over 128M, unless it's the default
|
||
|
if curpe > 131072 and curpe != default:
|
||
|
continue
|
||
|
|
||
|
actualPE.append(curpe)
|
||
|
val = self.prettyFormatPESize(curpe)
|
||
|
|
||
|
peCombo.append(val, curpe)
|
||
|
|
||
|
# First try to set the combo's active value to the default we're
|
||
|
# passed. If that doesn't work, just set it to the first one to
|
||
|
# prevent TypeErrors everywhere.
|
||
|
try:
|
||
|
peCombo.set_active(actualPE.index(default))
|
||
|
except ValueError:
|
||
|
peCombo.set_active(0)
|
||
|
|
||
|
peCombo.set_data("lastidx", peCombo.get_active())
|
||
|
peCombo.connect("changed", self.peChangeCB)
|
||
|
peCombo.set_data("lastpe", default)
|
||
|
|
||
|
return peCombo
|
||
|
|
||
|
def clickCB(self, row, data):
|
||
|
model = self.lvmlist.get_model()
|
||
|
pvlist = self.getSelectedPhysicalVolumes()
|
||
|
|
||
|
# get the selected row
|
||
|
iter = model.get_iter((string.atoi(data),))
|
||
|
|
||
|
# we invert val because we get called before checklist
|
||
|
# changes the toggle state
|
||
|
val = not model.get_value(iter, 0)
|
||
|
partname = model.get_value(iter, 1)
|
||
|
pv = self.storage.devicetree.getDeviceByName(partname)
|
||
|
if val:
|
||
|
self.pvs.append(pv)
|
||
|
else:
|
||
|
self.pvs.remove(pv)
|
||
|
try:
|
||
|
vg = self.getTempVG()
|
||
|
except DeviceError as e:
|
||
|
self.intf.messageWindow(_("Not enough space"),
|
||
|
_("You cannot remove this physical "
|
||
|
"volume because otherwise the "
|
||
|
"volume group will be too small to "
|
||
|
"hold the currently defined logical "
|
||
|
"volumes."), custom_icon="error")
|
||
|
self.pvs.append(pv)
|
||
|
return False
|
||
|
|
||
|
self.updateVGSpaceLabels()
|
||
|
return True
|
||
|
|
||
|
def createAllowedLvmPartitionsList(self):
|
||
|
store = gtk.TreeStore(gobject.TYPE_BOOLEAN,
|
||
|
gobject.TYPE_STRING,
|
||
|
gobject.TYPE_STRING)
|
||
|
partlist = WideCheckList(2, store, self.clickCB)
|
||
|
|
||
|
sw = gtk.ScrolledWindow()
|
||
|
sw.add(partlist)
|
||
|
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||
|
sw.set_shadow_type(gtk.SHADOW_IN)
|
||
|
|
||
|
origpvs = self.pvs[:]
|
||
|
for device in self.availlvmparts:
|
||
|
# clip size to current PE
|
||
|
pesize = int(self.peCombo.get_active_value()) / 1024.0
|
||
|
size = lvm.clampSize(device.size, pesize)
|
||
|
size_string = "%10.2f MB" % size
|
||
|
include = True
|
||
|
selected = False
|
||
|
|
||
|
# now see if the pv is in use either by a vg in the tree or by
|
||
|
# the vg we are editing now
|
||
|
if device in origpvs:
|
||
|
selected = True
|
||
|
include = True
|
||
|
else:
|
||
|
for vg in self.storage.vgs:
|
||
|
if vg.name == self.vg.name:
|
||
|
continue
|
||
|
|
||
|
if device in vg.pvs:
|
||
|
include = False
|
||
|
break
|
||
|
|
||
|
if include and not origpvs:
|
||
|
selected = True
|
||
|
|
||
|
if include:
|
||
|
partlist.append_row((device.name, size_string), selected)
|
||
|
if selected and device not in self.pvs:
|
||
|
self.pvs.append(device)
|
||
|
|
||
|
return (partlist, sw)
|
||
|
|
||
|
def updateAllowedLvmPartitionsList(self):
|
||
|
""" update sizes in pv list """
|
||
|
row = 0
|
||
|
for part in self.availlvmparts:
|
||
|
size = part.size
|
||
|
|
||
|
# clip size to current PE
|
||
|
pesize = int(self.peCombo.get_active_value()) / 1024.0
|
||
|
size = lvm.clampSize(size, pesize)
|
||
|
partsize = "%10.2f MB" % size
|
||
|
|
||
|
iter = self.lvmlist.store.get_iter((int(row),))
|
||
|
self.lvmlist.store.set_value(iter, 2, partsize)
|
||
|
row = row + 1
|
||
|
|
||
|
def getCurrentLogicalVolume(self):
|
||
|
selection = self.logvollist.get_selection()
|
||
|
(model, iter) = selection.get_selected()
|
||
|
return iter
|
||
|
|
||
|
def editLogicalVolume(self, lv, isNew = 0):
|
||
|
# Mixing logical code and gtk code is confusing to me. So I am going
|
||
|
# to do the logic first and then create all the gtk crap!
|
||
|
#
|
||
|
# lv -- whatever self.logvolstore.get_value returns
|
||
|
|
||
|
#newfstypelabel = None # File system type label & combo
|
||
|
#newfstypeCombo = None
|
||
|
newfslabellabel = None # File system Label label & combo
|
||
|
newfslableCombo = None
|
||
|
#lvnamelabel = None # Logical Volume name label & entry
|
||
|
#lvnameentry = None
|
||
|
#lvsizelabel = None # Logical Volume size label & entry
|
||
|
#lvsizeentry = None
|
||
|
maxsizelabel = None # Maximum size label
|
||
|
#mountCombo = None # Mount Point Combo Box
|
||
|
#tstr = None # String that appears on top of the window
|
||
|
tempvg = self.getTempVG() # copy of self.vg
|
||
|
templv = None
|
||
|
cpefsos = None # lambda function that represents
|
||
|
# createPreExistFSOptionSection
|
||
|
|
||
|
# Define the string
|
||
|
if isNew:
|
||
|
tstr = _("Make Logical Volume")
|
||
|
else:
|
||
|
tstr = _("Edit Logical Volume: %s") % lv['name']
|
||
|
|
||
|
# Create the mountCombo. This is the box where the mountpoint will
|
||
|
# appear. Note that if the format is swap or Raiddevice, the mount
|
||
|
# point is none-sense.
|
||
|
templuks = None
|
||
|
templv = self.getLVByName(lv['name'], vg=tempvg)
|
||
|
usedev = templv
|
||
|
if templv.format.type == "luks":
|
||
|
templuks = LUKSDevice("luks-%s" % lv['name'],
|
||
|
parents=[templv],
|
||
|
format=self.luks[lv['name']],
|
||
|
exists=templv.format.exists)
|
||
|
usedev = templuks
|
||
|
|
||
|
if lv['format'].type == "luks":
|
||
|
format = self.luks[lv['name']]
|
||
|
else:
|
||
|
format = lv['format']
|
||
|
|
||
|
if lv['exists']:
|
||
|
_origlv = self.getLVByName(lv['name'])
|
||
|
originalFormat = _origlv.originalFormat
|
||
|
if originalFormat.type == "luks":
|
||
|
try:
|
||
|
_origluks = self.storage.devicetree.getChildren(_origlv)[0]
|
||
|
except IndexError:
|
||
|
pass
|
||
|
else:
|
||
|
originalFormat = _origluks.originalFormat
|
||
|
|
||
|
mountCombo = createMountPointCombo(usedev, excludeMountPoints=["/boot"])
|
||
|
|
||
|
|
||
|
# Stuff appears differently when the lv exists and when the lv is new.
|
||
|
# here we make that difference. Except for newfslabelCombo, and
|
||
|
# maxsizelabel all vars will have a value != None.
|
||
|
if not lv['exists']:
|
||
|
# File system type lables & combo
|
||
|
newfstypelabel = createAlignedLabel(_("_File System Type:"))
|
||
|
newfstypeCombo = createFSTypeMenu(format, fstypechangeCB,mountCombo,
|
||
|
ignorefs = ["mdmember", "lvmpv", "efi", "prepboot", "appleboot"])
|
||
|
newfstypelabel.set_mnemonic_widget(newfstypeCombo)
|
||
|
|
||
|
# Logical Volume name label & entry
|
||
|
lvnamelabel = createAlignedLabel(_("_Logical Volume Name:"))
|
||
|
lvnameentry = gtk.Entry(32)
|
||
|
lvnamelabel.set_mnemonic_widget(lvnameentry)
|
||
|
if lv['name']:
|
||
|
lvnameentry.set_text(lv['name'])
|
||
|
else:
|
||
|
lvnameentry.set_text(self.storage.createSuggestedLVName(self.getTempVG()))
|
||
|
|
||
|
# Logical Volume size label & entry
|
||
|
lvsizelabel = createAlignedLabel(_("_Size (MB):"))
|
||
|
lvsizeentry = gtk.Entry(16)
|
||
|
lvsizelabel.set_mnemonic_widget(lvsizeentry)
|
||
|
lvsizeentry.set_text("%Ld" % lv['size'])
|
||
|
|
||
|
# Maximum size label
|
||
|
max_grow = tempvg.freeSpace / lv['stripes']
|
||
|
maxsizelabel = createAlignedLabel(_("(Max size is %s MB)") %
|
||
|
min(lvm.getMaxLVSize(),
|
||
|
lv['size'] + max_grow))
|
||
|
|
||
|
# Encrypt Check Box button.
|
||
|
self.lukscb = gtk.CheckButton(_("_Encrypt"))
|
||
|
self.lukscb.set_data("formatstate", 1)
|
||
|
if lv['format'].type == "luks":
|
||
|
self.lukscb.set_active(1)
|
||
|
else:
|
||
|
self.lukscb.set_active(0)
|
||
|
|
||
|
else:
|
||
|
# File system type lable & combo
|
||
|
newfstypelabel = createAlignedLabel(_("Original File System Type:"))
|
||
|
newfstypeCombo = gtk.Label(originalFormat.name)
|
||
|
|
||
|
# File system label label & combo
|
||
|
if getattr(originalFormat, "label", None):
|
||
|
newfslabellabel = createAlignedLabel(_("Original File System "
|
||
|
"Label:"))
|
||
|
newfslableCombo = gtk.Label(originalFormat.label)
|
||
|
|
||
|
# Logical Volume name label & entry
|
||
|
lvnamelabel = createAlignedLabel(_("Logical Volume Name:"))
|
||
|
lvnameentry = gtk.Label(lv['name'])
|
||
|
|
||
|
# Logical Volume size label & entry
|
||
|
lvsizelabel = createAlignedLabel(_("Size (MB):"))
|
||
|
lvsizeentry = gtk.Label(str(lv['size']))
|
||
|
|
||
|
# Create the File System Format Section
|
||
|
self.fsoptionsDict = {}
|
||
|
# We are going to lambda the createPreExistFSOptionSection so we can call
|
||
|
# it latter with two arguments, row and mainttable.
|
||
|
cpefsos = lambda table, row: createPreExistFSOptionSection(templv,
|
||
|
maintable, row, mountCombo, self.storage,
|
||
|
ignorefs = ["software RAID", "physical volume (LVM)", "vfat"],
|
||
|
luksdev=templuks)
|
||
|
|
||
|
|
||
|
# Here is where the gtk crap begins.
|
||
|
dialog = gtk.Dialog(tstr, self.parent)
|
||
|
gui.addFrame(dialog)
|
||
|
dialog.add_button('gtk-cancel', 2)
|
||
|
dialog.add_button('gtk-ok', 1)
|
||
|
dialog.set_position(gtk.WIN_POS_CENTER)
|
||
|
|
||
|
# Initialize main table
|
||
|
maintable = gtk.Table()
|
||
|
maintable.set_row_spacings(5)
|
||
|
maintable.set_col_spacings(5)
|
||
|
row = 0
|
||
|
|
||
|
# Add the mountCombo that we previously created
|
||
|
lbl = createAlignedLabel(_("_Mount Point:"))
|
||
|
maintable.attach(lbl, 0, 1, row,row+1)
|
||
|
lbl.set_mnemonic_widget(mountCombo)
|
||
|
maintable.attach(mountCombo, 1, 2, row, row + 1)
|
||
|
row += 1
|
||
|
|
||
|
# Add the filesystem combo labels.
|
||
|
maintable.attach(newfstypelabel, 0, 1, row, row + 1)
|
||
|
maintable.attach(newfstypeCombo, 1, 2, row, row + 1)
|
||
|
row += 1
|
||
|
|
||
|
# If there is a File system lable, add it.
|
||
|
if newfslabellabel is not None and newfslableCombo is not None:
|
||
|
maintable.attach(newfslabellabel, 0, 1, row, row + 1)
|
||
|
maintable.attach(newfslableCombo, 1, 2, row, row + 1)
|
||
|
row += 1
|
||
|
|
||
|
# Add the logical volume name
|
||
|
maintable.attach(lvnamelabel, 0, 1, row, row + 1)
|
||
|
maintable.attach(lvnameentry, 1, 2, row, row + 1)
|
||
|
row += 1
|
||
|
|
||
|
# Add the logical volume size
|
||
|
maintable.attach(lvsizelabel, 0, 1, row, row + 1)
|
||
|
maintable.attach(lvsizeentry, 1, 2, row, row + 1)
|
||
|
row += 1
|
||
|
|
||
|
# If there is a maxsize, add it.
|
||
|
if maxsizelabel is not None:
|
||
|
maintable.attach(maxsizelabel, 1, 2, row, row + 1)
|
||
|
|
||
|
# If we have the createPreExistFSOptionSection lamda function it means
|
||
|
# that we have a preexisting lv and we must call the lambda function
|
||
|
# to create the Pre exsisting FS option section.
|
||
|
if cpefsos is not None:
|
||
|
(row, self.fsoptionsDict) = cpefsos(maintable, row)
|
||
|
|
||
|
# checkbutton for encryption using dm-crypt/LUKS
|
||
|
# FIXME: Here we could not decouple the gtk stuff from the logic because
|
||
|
# of the createPreExistFSOptionSection function call. We must
|
||
|
# decouple that function.
|
||
|
if not lv['exists']:
|
||
|
maintable.attach(self.lukscb, 0, 2, row, row + 1)
|
||
|
row = row + 1
|
||
|
else:
|
||
|
self.lukscb = self.fsoptionsDict.get("lukscb")
|
||
|
|
||
|
dialog.vbox.pack_start(maintable)
|
||
|
dialog.show_all()
|
||
|
# Here ends the gtk crap
|
||
|
|
||
|
while 1:
|
||
|
rc = dialog.run()
|
||
|
if rc in [2, gtk.RESPONSE_DELETE_EVENT]:
|
||
|
if isNew:
|
||
|
del self.lvs[lv['name']]
|
||
|
dialog.destroy()
|
||
|
return
|
||
|
|
||
|
actions = []
|
||
|
targetSize = None
|
||
|
migrate = None
|
||
|
format = None
|
||
|
newluks = None
|
||
|
|
||
|
if templv.format.type == "luks":
|
||
|
format = self.luks[lv['name']]
|
||
|
else:
|
||
|
format = templv.format
|
||
|
|
||
|
if not templv.exists:
|
||
|
fmt_class = newfstypeCombo.get_active_value()
|
||
|
else:
|
||
|
# existing lv
|
||
|
fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value()
|
||
|
|
||
|
mountpoint = mountCombo.get_children()[0].get_text().strip()
|
||
|
if mountpoint == _("<Not Applicable>"):
|
||
|
mountpoint = ""
|
||
|
|
||
|
# validate logical volume name
|
||
|
lvname = lvnameentry.get_text().strip()
|
||
|
if not templv.exists:
|
||
|
err = sanityCheckLogicalVolumeName(lvname)
|
||
|
if err:
|
||
|
self.intf.messageWindow(_("Illegal Logical Volume Name"),
|
||
|
err, custom_icon="error")
|
||
|
continue
|
||
|
|
||
|
# check that the name is not already in use
|
||
|
used = 0
|
||
|
for _lv in self.lvs.values():
|
||
|
if _lv == lv:
|
||
|
continue
|
||
|
|
||
|
if _lv['name'] == lvname:
|
||
|
used = 1
|
||
|
break
|
||
|
|
||
|
if used:
|
||
|
self.intf.messageWindow(_("Illegal logical volume name"),
|
||
|
_("The logical volume name \"%s\" is "
|
||
|
"already in use. Please pick "
|
||
|
"another.") % (lvname,), custom_icon="error")
|
||
|
continue
|
||
|
|
||
|
# test mount point
|
||
|
# check in pending logical volume requests
|
||
|
# these may not have been put in master list of requests
|
||
|
# yet if we have not hit 'OK' for the volume group creation
|
||
|
if fmt_class().mountable and mountpoint:
|
||
|
used = False
|
||
|
curmntpt = getattr(format, "mountpoint", None)
|
||
|
|
||
|
for _lv in self.lvs.values():
|
||
|
if _lv['format'].type == "luks":
|
||
|
_format = self.luks[_lv['name']]
|
||
|
else:
|
||
|
_format = _lv['format']
|
||
|
|
||
|
if not _format.mountable or curmntpt and \
|
||
|
_format.mountpoint == curmntpt:
|
||
|
continue
|
||
|
|
||
|
if _format.mountpoint == mountpoint:
|
||
|
used = True
|
||
|
break
|
||
|
|
||
|
if not used:
|
||
|
# we checked this VG's LVs above; now check the rest of
|
||
|
# the devices in the tree
|
||
|
mountdevs = self.lvs.values()
|
||
|
full_name = "%s-%s" % (self.vg.name, lv['name'])
|
||
|
for (mp,d) in self.storage.mountpoints.iteritems():
|
||
|
if (d.type != "lvmlv" or d.vg.id != self.vg.id) and \
|
||
|
mp == mountpoint and \
|
||
|
not (isinstance(d, LUKSDevice) and
|
||
|
full_name in [dev.name for dev in d.parents]):
|
||
|
used = True
|
||
|
break
|
||
|
|
||
|
if used:
|
||
|
self.intf.messageWindow(_("Mount point in use"),
|
||
|
_("The mount point \"%s\" is in "
|
||
|
"use. Please pick another.") %
|
||
|
(mountpoint,),
|
||
|
custom_icon="error")
|
||
|
continue
|
||
|
|
||
|
# check that size specification is numeric and positive
|
||
|
if not templv.exists:
|
||
|
badsize = 0
|
||
|
try:
|
||
|
size = long(lvsizeentry.get_text())
|
||
|
except:
|
||
|
badsize = 1
|
||
|
|
||
|
if badsize or size <= 0:
|
||
|
self.intf.messageWindow(_("Illegal size"),
|
||
|
_("The requested size as entered is "
|
||
|
"not a valid number greater "
|
||
|
"than 0."), custom_icon="error")
|
||
|
continue
|
||
|
else:
|
||
|
size = templv.size
|
||
|
|
||
|
# check that size specification is within limits
|
||
|
pesize = int(self.peCombo.get_active_value()) / 1024.0
|
||
|
size = lvm.clampSize(size, pesize, roundup=True)
|
||
|
maxlv = lvm.getMaxLVSize()
|
||
|
if size > maxlv:
|
||
|
self.intf.messageWindow(_("Not enough space"),
|
||
|
_("The current requested size "
|
||
|
"(%(size)10.2f MB) is larger than "
|
||
|
"the maximum logical volume size "
|
||
|
"(%(maxlv)10.2f MB). "
|
||
|
"To increase this limit you can "
|
||
|
"create more Physical Volumes from "
|
||
|
"unpartitioned disk space and "
|
||
|
"add them to this Volume Group.")
|
||
|
% {'size': size, 'maxlv': maxlv},
|
||
|
custom_icon="error")
|
||
|
continue
|
||
|
|
||
|
# Ok -- now we've done all the checks to validate the
|
||
|
# user-specified parameters. Time to set up the device...
|
||
|
origname = templv.lvname
|
||
|
if not templv.exists:
|
||
|
templv._name = lvname
|
||
|
try:
|
||
|
templv.size = size
|
||
|
except ValueError:
|
||
|
self.intf.messageWindow(_("Not enough space"),
|
||
|
_("The logical volumes you have "
|
||
|
"configured require %(size)d MB,"
|
||
|
" but the volume group only has "
|
||
|
"%(tempvgsize)d MB. Please "
|
||
|
"either make the volume group "
|
||
|
"larger or make the logical "
|
||
|
"volume(s) smaller.")
|
||
|
% {'size': size,
|
||
|
'tempvgsize': tempvg.size},
|
||
|
custom_icon="error")
|
||
|
continue
|
||
|
|
||
|
format = fmt_class(mountpoint=mountpoint)
|
||
|
if self.lukscb and self.lukscb.get_active():
|
||
|
if templv.format.type != "luks":
|
||
|
newluks = format
|
||
|
format = getFormat("luks",
|
||
|
passphrase=self.storage.encryptionPassphrase)
|
||
|
else:
|
||
|
newluks = format
|
||
|
format = templv.format
|
||
|
|
||
|
templv.format = format
|
||
|
else:
|
||
|
# existing lv
|
||
|
if self.fsoptionsDict.has_key("formatcb") and \
|
||
|
self.fsoptionsDict["formatcb"].get_active():
|
||
|
format = fmt_class(mountpoint=mountpoint)
|
||
|
if self.lukscb and self.lukscb.get_active() and \
|
||
|
templv.format.type != "luks":
|
||
|
newluks = format
|
||
|
format = getFormat("luks",
|
||
|
device=templv.path,
|
||
|
passphrase=self.storage.encryptionPassphrase)
|
||
|
elif self.lukscb and self.lukscb.get_active():
|
||
|
newluks = format
|
||
|
format = templv.format
|
||
|
|
||
|
templv.format = format
|
||
|
elif self.fsoptionsDict.has_key("formatcb") and \
|
||
|
not self.fsoptionsDict["formatcb"].get_active():
|
||
|
templv.format = templv.originalFormat
|
||
|
format = templv.format
|
||
|
|
||
|
if format.mountable:
|
||
|
format.mountpoint = mountpoint
|
||
|
|
||
|
if self.fsoptionsDict.has_key("migratecb") and \
|
||
|
self.fsoptionsDict["migratecb"].get_active():
|
||
|
format.migrate = True
|
||
|
|
||
|
if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active():
|
||
|
targetSize = self.fsoptionsDict["resizesb"].get_value_as_int()
|
||
|
templv.targetSize = targetSize
|
||
|
|
||
|
if format.exists and format.mountable and format.mountpoint:
|
||
|
tempdev = StorageDevice('tmp', format=format)
|
||
|
if self.storage.formatByDefault(tempdev) and \
|
||
|
not queryNoFormatPreExisting(self.intf):
|
||
|
continue
|
||
|
|
||
|
# everything ok
|
||
|
break
|
||
|
|
||
|
if templv.format.type == "luks":
|
||
|
if newluks:
|
||
|
self.luks[templv.lvname] = newluks
|
||
|
|
||
|
if self.luks.has_key(origname) and origname != templv.lvname:
|
||
|
self.luks[templv.lvname] = self.luks[origname]
|
||
|
del self.luks[templv.lvname]
|
||
|
elif templv.format.type != "luks" and self.luks.has_key(origname):
|
||
|
del self.luks[origname]
|
||
|
|
||
|
self.lvs[templv.lvname] = {'name': templv.lvname,
|
||
|
'size': templv.size,
|
||
|
'format': templv.format,
|
||
|
'originalFormat': templv.originalFormat,
|
||
|
'stripes': templv.stripes,
|
||
|
'logSize': templv.logSize,
|
||
|
'snapshotSpace': templv.snapshotSpace,
|
||
|
'exists': templv.exists}
|
||
|
if self.lvs.has_key(origname) and origname != templv.lvname:
|
||
|
del self.lvs[origname]
|
||
|
|
||
|
self.updateLogVolStore()
|
||
|
self.updateVGSpaceLabels()
|
||
|
dialog.destroy()
|
||
|
return
|
||
|
|
||
|
def editCurrentLogicalVolume(self):
|
||
|
iter = self.getCurrentLogicalVolume()
|
||
|
|
||
|
if iter is None:
|
||
|
return
|
||
|
|
||
|
logvolname = self.logvolstore.get_value(iter, 0)
|
||
|
lv = self.lvs[logvolname]
|
||
|
self.editLogicalVolume(lv)
|
||
|
|
||
|
def addLogicalVolumeCB(self, widget):
|
||
|
if self.numAvailableLVSlots() < 1:
|
||
|
self.intf.messageWindow(_("No free slots"),
|
||
|
P_("You cannot create more than %d logical volume "
|
||
|
"per volume group.",
|
||
|
"You cannot create more than %d logical volumes "
|
||
|
"per volume group.", lvm.MAX_LV_SLOTS)
|
||
|
% (lvm.MAX_LV_SLOTS,),
|
||
|
custom_icon="error")
|
||
|
return
|
||
|
|
||
|
(total, used, free) = self.computeSpaceValues()
|
||
|
if free <= 0:
|
||
|
self.intf.messageWindow(_("No free space"),
|
||
|
_("There is no room left in the "
|
||
|
"volume group to create new logical "
|
||
|
"volumes. "
|
||
|
"To add a logical volume you must "
|
||
|
"reduce the size of one or more of "
|
||
|
"the currently existing "
|
||
|
"logical volumes"), custom_icon="error")
|
||
|
return
|
||
|
|
||
|
tempvg = self.getTempVG()
|
||
|
name = self.storage.createSuggestedLVName(tempvg)
|
||
|
format = getFormat(self.storage.defaultFSType)
|
||
|
self.lvs[name] = {'name': name,
|
||
|
'size': free,
|
||
|
'format': format,
|
||
|
'originalFormat': format,
|
||
|
'stripes': 1,
|
||
|
'logSize': 0,
|
||
|
'snapshotSpace': 0,
|
||
|
'exists': False}
|
||
|
self.editLogicalVolume(self.lvs[name], isNew = 1)
|
||
|
return
|
||
|
|
||
|
def editLogicalVolumeCB(self, widget):
|
||
|
self.editCurrentLogicalVolume()
|
||
|
return
|
||
|
|
||
|
def delLogicalVolumeCB(self, widget):
|
||
|
iter = self.getCurrentLogicalVolume()
|
||
|
if iter is None:
|
||
|
return
|
||
|
|
||
|
logvolname = self.logvolstore.get_value(iter, 0)
|
||
|
if logvolname is None:
|
||
|
return
|
||
|
|
||
|
rc = self.intf.messageWindow(_("Confirm Delete"),
|
||
|
_("Are you sure you want to delete the "
|
||
|
"logical volume \"%s\"?") % (logvolname,),
|
||
|
type = "custom", custom_buttons=["gtk-cancel", _("_Delete")], custom_icon="warning")
|
||
|
if not rc:
|
||
|
return
|
||
|
|
||
|
del self.lvs[logvolname]
|
||
|
self.logvolstore.remove(iter)
|
||
|
self.updateVGSpaceLabels()
|
||
|
return
|
||
|
|
||
|
def logvolActivateCb(self, view, path, col):
|
||
|
self.editCurrentLogicalVolume()
|
||
|
|
||
|
def getSelectedPhysicalVolumes(self):
|
||
|
model = self.lvmlist.get_model()
|
||
|
pv = []
|
||
|
next = model.get_iter_first()
|
||
|
currow = 0
|
||
|
while next is not None:
|
||
|
iter = next
|
||
|
val = model.get_value(iter, 0)
|
||
|
partname = model.get_value(iter, 1)
|
||
|
|
||
|
if val:
|
||
|
dev = self.storage.devicetree.getDeviceByName(partname)
|
||
|
pv.append(dev)
|
||
|
|
||
|
next = model.iter_next(iter)
|
||
|
currow = currow + 1
|
||
|
|
||
|
return pv
|
||
|
|
||
|
def computeVGSize(self, pvlist, curpe):
|
||
|
availSpaceMB = 0L
|
||
|
for pv in pvlist:
|
||
|
# have to clamp pvsize to multiple of PE
|
||
|
# XXX why the subtraction? fudging metadata?
|
||
|
pvsize = lvm.clampSize(pv.size, curpe) - (curpe/1024)
|
||
|
|
||
|
availSpaceMB = availSpaceMB + pvsize
|
||
|
|
||
|
log.info("computeVGSize: vgsize is %s" % (availSpaceMB,))
|
||
|
return availSpaceMB
|
||
|
|
||
|
def updateLogVolStore(self):
|
||
|
self.logvolstore.clear()
|
||
|
for lv in self.lvs.values():
|
||
|
iter = self.logvolstore.append()
|
||
|
if lv['format'].type == "luks":
|
||
|
format = self.luks[lv['name']]
|
||
|
else:
|
||
|
format = lv['format']
|
||
|
|
||
|
mntpt = getattr(format, "mountpoint", "")
|
||
|
if lv['name']:
|
||
|
self.logvolstore.set_value(iter, 0, lv['name'])
|
||
|
|
||
|
if format.type and format.mountable:
|
||
|
self.logvolstore.set_value(iter, 1, mntpt)
|
||
|
else:
|
||
|
self.logvolstore.set_value(iter, 1, "N/A")
|
||
|
|
||
|
self.logvolstore.set_value(iter, 2, "%Ld" % lv['size'])
|
||
|
|
||
|
def updateVGSpaceLabels(self):
|
||
|
(total, used, free) = self.computeSpaceValues()
|
||
|
|
||
|
self.totalSpaceLabel.set_text("%10.2f MB" % (total,))
|
||
|
self.usedSpaceLabel.set_text("%10.2f MB" % (used,))
|
||
|
|
||
|
if total > 0:
|
||
|
usedpercent = (100.0*used)/total
|
||
|
else:
|
||
|
usedpercent = 0.0
|
||
|
|
||
|
self.usedPercentLabel.set_text("(%4.1f %%)" % (usedpercent,))
|
||
|
|
||
|
self.freeSpaceLabel.set_text("%10.2f MB" % (free,))
|
||
|
if total > 0:
|
||
|
freepercent = (100.0*free)/total
|
||
|
else:
|
||
|
freepercent = 0.0
|
||
|
|
||
|
self.freePercentLabel.set_text("(%4.1f %%)" % (freepercent,))
|
||
|
|
||
|
#
|
||
|
# run the VG editor we created
|
||
|
#
|
||
|
def run(self):
|
||
|
if self.dialog is None:
|
||
|
return []
|
||
|
|
||
|
while 1:
|
||
|
rc = self.dialog.run()
|
||
|
|
||
|
if rc in [2, gtk.RESPONSE_DELETE_EVENT]:
|
||
|
self.destroy()
|
||
|
return []
|
||
|
|
||
|
pvlist = self.getSelectedPhysicalVolumes()
|
||
|
|
||
|
# check volume name
|
||
|
volname = self.volnameEntry.get_text().strip()
|
||
|
err = sanityCheckVolumeGroupName(volname)
|
||
|
if err:
|
||
|
self.intf.messageWindow(_("Invalid Volume Group Name"), err,
|
||
|
custom_icon="error")
|
||
|
continue
|
||
|
|
||
|
origvname = self.vg.name
|
||
|
|
||
|
if origvname != volname:
|
||
|
# maybe we should see if _any_ device has this name
|
||
|
if volname in [vg.name for vg in self.storage.vgs]:
|
||
|
self.intf.messageWindow(_("Name in use"),
|
||
|
_("The volume group name \"%s\" is "
|
||
|
"already in use. Please pick "
|
||
|
"another." % (volname,)),
|
||
|
custom_icon="error")
|
||
|
continue
|
||
|
|
||
|
# get physical extent
|
||
|
pesize = int(self.peCombo.get_active_value()) / 1024.0
|
||
|
|
||
|
# everything ok
|
||
|
break
|
||
|
return self.convertToActions()
|
||
|
|
||
|
def convertToActions(self):
|
||
|
# here we have to figure out what all was done and convert it to
|
||
|
# devices and actions
|
||
|
#
|
||
|
# set up the vg with the right pvs
|
||
|
# set up the lvs
|
||
|
# set up the lvs' formats
|
||
|
#
|
||
|
log.debug("finished editing vg")
|
||
|
log.debug("pvs: %s" % [p.name for p in self.pvs])
|
||
|
log.debug("luks: %s" % self.luks.keys())
|
||
|
volname = self.volnameEntry.get_text().strip()
|
||
|
pesize = int(self.peCombo.get_active_value()) / 1024.0
|
||
|
for lv in self.lvs.itervalues():
|
||
|
log.debug("lv %s" % lv)
|
||
|
_luks = self.luks.get(lv['name'])
|
||
|
if _luks:
|
||
|
log.debug(" luks: %s" % _luks)
|
||
|
|
||
|
actions = []
|
||
|
origlvs = self.vg.lvs
|
||
|
if not self.vg.exists:
|
||
|
log.debug("non-existing vg -- setting up lvs, pvs, name, pesize")
|
||
|
# remove all of the lvs
|
||
|
for lv in self.vg.lvs:
|
||
|
self.vg._removeLogVol(lv)
|
||
|
|
||
|
# set up the pvs
|
||
|
for pv in self.vg.pvs:
|
||
|
if pv not in self.pvs:
|
||
|
self.vg._removePV(pv)
|
||
|
for pv in self.pvs:
|
||
|
if pv not in self.vg.pvs:
|
||
|
self.vg._addPV(pv)
|
||
|
|
||
|
self.vg.name = volname
|
||
|
self.vg.peSize = pesize
|
||
|
|
||
|
if self.isNew:
|
||
|
actions = [ActionCreateDevice(self.vg)]
|
||
|
|
||
|
# Schedule destruction of all non-existing lvs, their formats,
|
||
|
# luks devices, &c. Also destroy devices that have been removed.
|
||
|
for lv in origlvs:
|
||
|
log.debug("old lv %s..." % lv.lvname)
|
||
|
if not lv.exists or lv.lvname not in self.lvs or \
|
||
|
(not self.lvs[lv.lvname]['exists'] and lv.exists):
|
||
|
log.debug("removing lv %s" % lv.lvname)
|
||
|
if lv.format.type == "luks":
|
||
|
try:
|
||
|
_luksdev = self.storage.devicetree.getChildren(lv)[0]
|
||
|
except IndexError:
|
||
|
pass
|
||
|
else:
|
||
|
if _luksdev.format.type:
|
||
|
actions.append(ActionDestroyFormat(_luksdev))
|
||
|
|
||
|
actions.append(ActionDestroyDevice(_luksdev))
|
||
|
|
||
|
if lv.format.type:
|
||
|
actions.append(ActionDestroyFormat(lv))
|
||
|
|
||
|
if lv in self.vg.lvs:
|
||
|
self.vg._removeLogVol(lv)
|
||
|
|
||
|
actions.append(ActionDestroyDevice(lv))
|
||
|
|
||
|
# schedule creation of all new lvs, their formats, luks devices, &c
|
||
|
tempvg = self.getTempVG()
|
||
|
for lv in tempvg.lvs:
|
||
|
log.debug("new lv %s" % lv)
|
||
|
if not lv.exists:
|
||
|
log.debug("creating lv %s" % lv.lvname)
|
||
|
# create the device
|
||
|
newlv = LVMLogicalVolumeDevice(lv.lvname,
|
||
|
self.vg,
|
||
|
size=lv.size)
|
||
|
actions.append(ActionCreateDevice(newlv))
|
||
|
|
||
|
# create the format
|
||
|
mountpoint = getattr(lv.format, "mountpoint", None)
|
||
|
format = getFormat(lv.format.type,
|
||
|
mountpoint=mountpoint,
|
||
|
device=newlv.path)
|
||
|
actions.append(ActionCreateFormat(newlv, format))
|
||
|
|
||
|
if lv.format.type == "luks":
|
||
|
# create the luks device
|
||
|
newluks = LUKSDevice("luks-%s" % newlv.name,
|
||
|
parents=[newlv])
|
||
|
actions.append(ActionCreateDevice(newluks))
|
||
|
|
||
|
# create the luks format
|
||
|
oldfmt = self.luks[lv.lvname]
|
||
|
mountpoint = getattr(oldfmt, "mountpoint", None)
|
||
|
format = getFormat(oldfmt.type,
|
||
|
mountpoint=mountpoint,
|
||
|
device=newluks.path)
|
||
|
actions.append(ActionCreateFormat(newluks, format))
|
||
|
else:
|
||
|
log.debug("lv %s already exists" % lv.lvname)
|
||
|
# this lv is preexisting. check for resize and reformat.
|
||
|
# first, get the real/original lv
|
||
|
origlv = self.getLVByName(lv.lvname)
|
||
|
if lv.resizable and lv.targetSize != origlv.size:
|
||
|
actions.append(ActionResizeDevice(origlv, lv.targetSize))
|
||
|
|
||
|
if lv.format.exists:
|
||
|
log.debug("format already exists")
|
||
|
if lv.format.type == "luks":
|
||
|
# see if the luks device already exists
|
||
|
try:
|
||
|
usedev = self.storage.devicetree.getChildren(origlv)[0]
|
||
|
except IndexError:
|
||
|
# the luks device does not exist, meaning we
|
||
|
# do not have a key for it
|
||
|
continue
|
||
|
|
||
|
format = self.luks[lv.lvname]
|
||
|
if not format.exists:
|
||
|
actions.append(ActionCreateFormat(usedev, format))
|
||
|
else:
|
||
|
usedev = origlv
|
||
|
format = lv.format
|
||
|
|
||
|
# no formatting action requested, meaning we should
|
||
|
# cancel all format create/destroy actions
|
||
|
if format == usedev.originalFormat:
|
||
|
devicetree = self.storage.devicetree
|
||
|
cancel = []
|
||
|
if origlv.originalFormat.type == "luks":
|
||
|
path = "/dev/mapper/luks-%s" % origlv.originalFormat.uuid
|
||
|
cancel.extend(devicetree.findActions(path=path))
|
||
|
|
||
|
cancel.extend(devicetree.findActions(type="create",
|
||
|
object="format",
|
||
|
devid=origlv.id))
|
||
|
cancel.extend(devicetree.findActions(type="destroy",
|
||
|
object="format",
|
||
|
devid=origlv.id))
|
||
|
for action in cancel:
|
||
|
devicetree.cancelAction(action)
|
||
|
|
||
|
# even though we cancelled a bunch of actions, it's
|
||
|
# pretty much impossible to be sure we cancelled them
|
||
|
# in the correct order. make sure things are back to
|
||
|
# their original state.
|
||
|
if origlv.format.type == "luks":
|
||
|
try:
|
||
|
usedev = devicetree.getChildren(origlv)[0]
|
||
|
except IndexError:
|
||
|
usedev = origlv
|
||
|
else:
|
||
|
usedev.format = usedev.originalFormat
|
||
|
else:
|
||
|
usedev = origlv
|
||
|
|
||
|
if hasattr(format, "mountpoint"):
|
||
|
usedev.format.mountpoint = format.mountpoint
|
||
|
|
||
|
if format.migratable and format.migrate and \
|
||
|
not usedev.format.migrate:
|
||
|
usedev.format.migrate = format.migrate
|
||
|
actions.append(ActionMigrateFormat(usedev))
|
||
|
|
||
|
# check the lv's format also, explicitly, in case it is
|
||
|
# encrypted. in this case we must check them both.
|
||
|
if format.resizable and lv.format.resizable and \
|
||
|
lv.targetSize != format.currentSize and \
|
||
|
usedev.format.exists:
|
||
|
new_size = lv.targetSize
|
||
|
actions.append(ActionResizeFormat(usedev, new_size))
|
||
|
elif lv.format.type:
|
||
|
log.debug("new format: %s" % lv.format.type)
|
||
|
# destroy old format and any associated luks devices
|
||
|
if origlv.format.type:
|
||
|
if origlv.format.type == "luks":
|
||
|
# destroy the luks device and its format
|
||
|
try:
|
||
|
_luksdev = self.storage.devicetree.getChildren(origlv)[0]
|
||
|
except IndexError:
|
||
|
pass
|
||
|
else:
|
||
|
if _luksdev.format.type:
|
||
|
# this is probably unnecessary
|
||
|
actions.append(ActionDestroyFormat(_luksdev))
|
||
|
|
||
|
actions.append(ActionDestroyDevice(_luksdev))
|
||
|
|
||
|
actions.append(ActionDestroyFormat(origlv))
|
||
|
|
||
|
# create the format
|
||
|
mountpoint = getattr(lv.format, "mountpoint", None)
|
||
|
format = getFormat(lv.format.type,
|
||
|
mountpoint=mountpoint,
|
||
|
device=origlv.path)
|
||
|
actions.append(ActionCreateFormat(origlv, format))
|
||
|
|
||
|
if lv.format.type == "luks":
|
||
|
# create the luks device
|
||
|
newluks = LUKSDevice("luks-%s" % origlv.name,
|
||
|
parents=[origlv])
|
||
|
actions.append(ActionCreateDevice(newluks))
|
||
|
|
||
|
# create the luks format
|
||
|
tmpfmt = self.luks[lv.lvname]
|
||
|
mountpoint = getattr(tmpfmt, "mountpoint", None)
|
||
|
format = getFormat(tmpfmt.type,
|
||
|
mountpoint=mountpoint,
|
||
|
device=newluks.path)
|
||
|
actions.append(ActionCreateFormat(newluks, format))
|
||
|
else:
|
||
|
log.debug("no format!?")
|
||
|
|
||
|
return actions
|
||
|
|
||
|
def destroy(self):
|
||
|
if self.dialog:
|
||
|
self.dialog.destroy()
|
||
|
self.dialog = None
|
||
|
|
||
|
def getLVByName(self, name, vg=None):
|
||
|
if vg is None:
|
||
|
vg = self.vg
|
||
|
|
||
|
for lv in vg.lvs:
|
||
|
if lv.lvname == name or lv.name == name:
|
||
|
return lv
|
||
|
|
||
|
def __init__(self, anaconda, intf, parent, vg, isNew = 0):
|
||
|
self.storage = anaconda.storage
|
||
|
|
||
|
# the vg instance we were passed
|
||
|
self.vg = vg
|
||
|
self.peSize = vg.peSize
|
||
|
self.pvs = self.vg.pvs[:]
|
||
|
|
||
|
# a dict of dicts
|
||
|
# keys are lv names
|
||
|
# values are dicts representing the lvs
|
||
|
# name, size, format instance, exists
|
||
|
self.lvs = {}
|
||
|
|
||
|
# a dict of luks devices
|
||
|
# keys are lv names
|
||
|
# values are formats of the mapped devices
|
||
|
self.luks = {}
|
||
|
|
||
|
self.isNew = isNew
|
||
|
self.intf = intf
|
||
|
self.parent = parent
|
||
|
self.actions = []
|
||
|
|
||
|
for lv in self.vg.lvs:
|
||
|
self.lvs[lv.lvname] = {"name": lv.lvname,
|
||
|
"size": lv.size,
|
||
|
"format": copy.copy(lv.format),
|
||
|
"originalFormat": lv.originalFormat,
|
||
|
"stripes": lv.stripes,
|
||
|
"logSize": lv.logSize,
|
||
|
"snapshotSpace": lv.snapshotSpace,
|
||
|
"exists": lv.exists}
|
||
|
|
||
|
if lv.format.type == "luks":
|
||
|
try:
|
||
|
self.luks[lv.lvname] = self.storage.devicetree.getChildren(lv)[0].format
|
||
|
except IndexError:
|
||
|
self.luks[lv.lvname] = lv.format
|
||
|
|
||
|
self.availlvmparts = self.storage.unusedPVs(vg=vg)
|
||
|
|
||
|
# if no PV exist, raise an error message and return
|
||
|
if len(self.availlvmparts) < 1:
|
||
|
self.intf.messageWindow(_("Not enough physical volumes"),
|
||
|
_("At least one unused physical "
|
||
|
"volume partition is "
|
||
|
"needed to create an LVM Volume Group.\n\n"
|
||
|
"Create a partition or RAID array "
|
||
|
"of type \"physical volume (LVM)\" and then "
|
||
|
"select the \"LVM\" option again."),
|
||
|
custom_icon="error")
|
||
|
self.dialog = None
|
||
|
return
|
||
|
|
||
|
if isNew:
|
||
|
tstr = _("Make LVM Volume Group")
|
||
|
else:
|
||
|
try:
|
||
|
tstr = _("Edit LVM Volume Group: %s") % (vg.name,)
|
||
|
except AttributeError:
|
||
|
tstr = _("Edit LVM Volume Group")
|
||
|
|
||
|
dialog = gtk.Dialog(tstr, self.parent)
|
||
|
gui.addFrame(dialog)
|
||
|
dialog.add_button('gtk-cancel', 2)
|
||
|
dialog.add_button('gtk-ok', 1)
|
||
|
|
||
|
dialog.set_position(gtk.WIN_POS_CENTER)
|
||
|
|
||
|
maintable = gtk.Table()
|
||
|
maintable.set_row_spacings(5)
|
||
|
maintable.set_col_spacings(5)
|
||
|
row = 0
|
||
|
|
||
|
# volume group name
|
||
|
if not vg.exists:
|
||
|
lbl = createAlignedLabel(_("_Volume Group Name:"))
|
||
|
self.volnameEntry = gtk.Entry(16)
|
||
|
lbl.set_mnemonic_widget(self.volnameEntry)
|
||
|
if not self.isNew:
|
||
|
self.volnameEntry.set_text(self.vg.name)
|
||
|
else:
|
||
|
self.volnameEntry.set_text(self.storage.createSuggestedVGName(anaconda.network))
|
||
|
else:
|
||
|
lbl = createAlignedLabel(_("Volume Group Name:"))
|
||
|
self.volnameEntry = gtk.Label(self.vg.name)
|
||
|
|
||
|
maintable.attach(lbl, 0, 1, row, row + 1,
|
||
|
gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
maintable.attach(self.volnameEntry, 1, 2, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
row = row + 1
|
||
|
|
||
|
lbl = createAlignedLabel(_("_Physical Extent:"))
|
||
|
self.peCombo = self.createPEOptionMenu(self.vg.peSize * 1024)
|
||
|
lbl.set_mnemonic_widget(self.peCombo)
|
||
|
if vg.exists:
|
||
|
self.peCombo.set_sensitive(False)
|
||
|
|
||
|
maintable.attach(lbl, 0, 1, row, row + 1,
|
||
|
gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
maintable.attach(self.peCombo, 1, 2, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
row = row + 1
|
||
|
|
||
|
(self.lvmlist, sw) = self.createAllowedLvmPartitionsList()
|
||
|
if vg.exists:
|
||
|
self.lvmlist.set_sensitive(False)
|
||
|
self.lvmlist.set_size_request(275, 80)
|
||
|
lbl = createAlignedLabel(_("Physical Volumes to _Use:"))
|
||
|
lbl.set_mnemonic_widget(self.lvmlist)
|
||
|
maintable.attach(lbl, 0, 1, row, row + 1)
|
||
|
maintable.attach(sw, 1, 2, row, row + 1)
|
||
|
row = row + 1
|
||
|
|
||
|
maintable.attach(createAlignedLabel(_("Used Space:")), 0, 1, row,
|
||
|
row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
lbox = gtk.HBox()
|
||
|
self.usedSpaceLabel = gtk.Label("")
|
||
|
labelalign = gtk.Alignment()
|
||
|
labelalign.set(1.0, 0.5, 0.0, 0.0)
|
||
|
labelalign.add(self.usedSpaceLabel)
|
||
|
lbox.pack_start(labelalign, False, False)
|
||
|
self.usedPercentLabel = gtk.Label("")
|
||
|
labelalign = gtk.Alignment()
|
||
|
labelalign.set(1.0, 0.5, 0.0, 0.0)
|
||
|
labelalign.add(self.usedPercentLabel)
|
||
|
lbox.pack_start(labelalign, False, False, padding=10)
|
||
|
maintable.attach(lbox, 1, 2, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
maintable.set_row_spacing(row, 0)
|
||
|
row = row + 1
|
||
|
|
||
|
maintable.attach(createAlignedLabel(_("Free Space:")), 0, 1, row,
|
||
|
row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
lbox = gtk.HBox()
|
||
|
self.freeSpaceLabel = gtk.Label("")
|
||
|
labelalign = gtk.Alignment()
|
||
|
labelalign.set(1.0, 0.5, 0.0, 0.0)
|
||
|
labelalign.add(self.freeSpaceLabel)
|
||
|
lbox.pack_start(labelalign, False, False)
|
||
|
self.freePercentLabel = gtk.Label("")
|
||
|
labelalign = gtk.Alignment()
|
||
|
labelalign.set(1.0, 0.5, 0.0, 0.0)
|
||
|
labelalign.add(self.freePercentLabel)
|
||
|
lbox.pack_start(labelalign, False, False, padding=10)
|
||
|
|
||
|
maintable.attach(lbox, 1, 2, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
maintable.set_row_spacing(row, 0)
|
||
|
row = row + 1
|
||
|
|
||
|
maintable.attach(createAlignedLabel(_("Total Space:")), 0, 1, row,
|
||
|
row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
self.totalSpaceLabel = gtk.Label("")
|
||
|
labelalign = gtk.Alignment()
|
||
|
labelalign.set(0.0, 0.5, 0.0, 0.0)
|
||
|
labelalign.add(self.totalSpaceLabel)
|
||
|
maintable.attach(labelalign, 1, 2, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK)
|
||
|
maintable.set_row_spacing(row, 5)
|
||
|
row = row + 1
|
||
|
|
||
|
# populate list of logical volumes
|
||
|
lvtable = gtk.Table()
|
||
|
lvtable.set_row_spacings(5)
|
||
|
lvtable.set_col_spacings(5)
|
||
|
self.logvolstore = gtk.ListStore(gobject.TYPE_STRING,
|
||
|
gobject.TYPE_STRING,
|
||
|
gobject.TYPE_STRING)
|
||
|
|
||
|
if self.vg.lvs:
|
||
|
for lv in self.vg.lvs:
|
||
|
iter = self.logvolstore.append()
|
||
|
self.logvolstore.set_value(iter, 0, lv.lvname)
|
||
|
if lv.format.type == "luks":
|
||
|
try:
|
||
|
format = self.storage.devicetree.getChildren(lv)[0].format
|
||
|
except IndexError:
|
||
|
format = lv.format
|
||
|
else:
|
||
|
format = lv.format
|
||
|
|
||
|
if getattr(format, "mountpoint", None):
|
||
|
self.logvolstore.set_value(iter, 1,
|
||
|
format.mountpoint)
|
||
|
else:
|
||
|
self.logvolstore.set_value(iter, 1, "")
|
||
|
self.logvolstore.set_value(iter, 2, "%Ld" % lv.size)
|
||
|
|
||
|
self.logvollist = gtk.TreeView(self.logvolstore)
|
||
|
col = gtk.TreeViewColumn(_("Logical Volume Name"),
|
||
|
gtk.CellRendererText(), text=0)
|
||
|
self.logvollist.append_column(col)
|
||
|
col = gtk.TreeViewColumn(_("Mount Point"),
|
||
|
gtk.CellRendererText(), text=1)
|
||
|
self.logvollist.append_column(col)
|
||
|
col = gtk.TreeViewColumn(_("Size (MB)"),
|
||
|
gtk.CellRendererText(), text=2)
|
||
|
self.logvollist.append_column(col)
|
||
|
self.logvollist.connect('row-activated', self.logvolActivateCb)
|
||
|
|
||
|
sw = gtk.ScrolledWindow()
|
||
|
sw.add(self.logvollist)
|
||
|
sw.set_size_request(100, 100)
|
||
|
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||
|
sw.set_shadow_type(gtk.SHADOW_IN)
|
||
|
lvtable.attach(sw, 0, 1, 0, 1)
|
||
|
|
||
|
# button box of options
|
||
|
lvbbox = gtk.VBox()
|
||
|
add = gtk.Button(_("_Add"))
|
||
|
add.connect("clicked", self.addLogicalVolumeCB)
|
||
|
lvbbox.pack_start(add)
|
||
|
edit = gtk.Button(_("_Edit"))
|
||
|
edit.connect("clicked", self.editLogicalVolumeCB)
|
||
|
lvbbox.pack_start(edit)
|
||
|
delete = gtk.Button(_("_Delete"))
|
||
|
delete.connect("clicked", self.delLogicalVolumeCB)
|
||
|
lvbbox.pack_start(delete)
|
||
|
|
||
|
lvalign = gtk.Alignment()
|
||
|
lvalign.set(0.5, 0.0, 0.0, 0.0)
|
||
|
lvalign.add(lvbbox)
|
||
|
lvtable.attach(lvalign, 1, 2, 0, 1, gtk.SHRINK, gtk.SHRINK)
|
||
|
|
||
|
# pack all logical volumne stuff in a frame
|
||
|
lvtable.set_border_width(12)
|
||
|
l = gtk.Label()
|
||
|
l.set_markup_with_mnemonic("<b>%s</b>" %(_("_Logical Volumes"),))
|
||
|
l.set_mnemonic_widget(self.logvollist)
|
||
|
frame = gtk.Frame()
|
||
|
frame.set_label_widget(l)
|
||
|
frame.add(lvtable)
|
||
|
frame.set_shadow_type(gtk.SHADOW_NONE)
|
||
|
|
||
|
# dialog.vbox.pack_start(frame)
|
||
|
maintable.attach(frame, 0, 2, row, row+1)
|
||
|
row = row + 1
|
||
|
|
||
|
dialog.vbox.pack_start(maintable)
|
||
|
dialog.set_size_request(550, 450)
|
||
|
dialog.show_all()
|
||
|
|
||
|
# set space labels to correct values
|
||
|
self.updateVGSpaceLabels()
|
||
|
|
||
|
self.dialog = dialog
|