#
# lvm_dialog_gui.py: dialog for editting 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 .
#
# Author(s): Michael Fulbright
#
import copy
import gobject
import gtk
import datacombo
import gui
from fsset import *
from partRequests import *
from partition_ui_helpers_gui import *
from constants import *
import lvm
from cryptodev import LUKSDevice
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
import logging
log = logging.getLogger("anaconda")
class VolumeGroupEditor:
def numAvailableLVSlots(self):
return max(0, lvm.MAX_LV_SLOTS - len(self.logvolreqs))
def computeSpaceValues(self, alt_pvlist=None, usepe=None):
if usepe is None:
pesize = long(self.peCombo.get_active_value())
else:
pesize = long(usepe)
if alt_pvlist is None:
pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model())
else:
pvlist = alt_pvlist
tspace = self.computeVGSize(pvlist, pesize)
uspace = self.computeLVSpaceNeeded(self.logvolreqs)
fspace = tspace - uspace
return (tspace, uspace, fspace)
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(self.lvmlist.get_model())
waste = 0.0
for id in pvlist:
pvreq = self.partitions.getRequestByID(id)
pvsize = pvreq.getActualSize(self.partitions, self.diskset)
waste = max(waste, (long(pvsize*1024) % newpe)/(pvsize*1024.0))
return waste
def getSmallestPVSize(self):
""" finds the smallest PV and returns its size in MB
"""
first = 1
pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model())
for id in pvlist:
try:
pesize = int(self.peCombo.get_active_value())
except:
pesize = 32768
pvreq = self.partitions.getRequestByID(id)
pvsize = pvreq.getActualSize(self.partitions, self.diskset)
pvsize = lvm.clampPVSize(pvsize, pesize) - (pesize/1024)
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 KB
"""
pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model())
availSpaceMB = self.computeVGSize(pvlist, newpe)
# see if total space is enough
oldused = 0
used = 0
resize = 0
for lv in self.logvolreqs:
osize = lv.getActualSize(self.partitions, self.diskset, True)
oldused = oldused + osize
nsize = lvm.clampLVSizeRequest(osize, newpe, roundup=1)
if nsize != osize:
resize = 1
used = used + nsize
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.logvolreqs:
osize = lv.getActualSize(self.partitions, self.diskset, True)
nsize = lvm.clampLVSizeRequest(osize, newpe, roundup=1)
lv.setSize(nsize)
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())
lastval = widget.get_data("lastpe")
lastidx = widget.get_data("lastidx")
# see if PE is too large compared to smallest PV
# remember PE is in KB, PV size is in MB
maxpvsize = self.getSmallestPVSize()
if curval > maxpvsize * 1024:
self.intf.messageWindow(_("Not enough space"),
_("The physical extent size cannot be "
"changed because the value selected "
"(%10.2f MB) is larger than the smallest "
"physical volume (%10.2f MB) in the "
"volume group.") % (curval/1024.0, maxpvsize), custom_icon="error")
widget.set_active(lastidx)
return 0
# see if new PE will make any PV useless due to overhead
if lvm.clampPVSize(maxpvsize, curval) * 1024 < curval:
self.intf.messageWindow(_("Not enough space"),
_("The physical extent size cannot be "
"changed because the value selected "
"(%10.2f MB) is too large compared "
"to the size of the "
"smallest physical volume "
"(%10.2f MB) in the "
"volume group.") % (curval/1024.0,
maxpvsize),
custom_icon="error")
widget.set_active(lastidx)
return 0
if self.getPVWastedRatio(curval) > 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(curval)
if not rc:
widget.set_active(lastidx)
return 0
else:
self.updateLogVolStore()
else:
maxlv = lvm.getMaxLVSize(curval)
for lv in self.logvolreqs:
lvsize = lv.getActualSize(self.partitions, self.diskset, True)
if lvsize > 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())
self.updateAllowedLvmPartitionsList(self.availlvmparts,
self.partitions,
self.lvmlist)
self.updateVGSpaceLabels()
def prettyFormatPESize(self, val):
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 = lvm.getPossiblePhysicalExtents(floor=1024)
for curpe in actualPE:
# don't show PE over 128M, unless it's the default
if curpe > 131072 and curpe != default:
continue
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(model)
# 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)
id = self.partitions.getRequestByDeviceName(partname).uniqueID
if val:
pvlist.append(id)
else:
pvlist.remove(id)
(availSpaceMB, neededSpaceMB, fspace) = self.computeSpaceValues(alt_pvlist=pvlist)
if availSpaceMB < neededSpaceMB:
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")
return False
self.updateVGSpaceLabels(alt_pvlist = pvlist)
return True
def createAllowedLvmPartitionsList(self, alllvmparts, reqlvmpart, partitions, preexist = 0):
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)
for part in alllvmparts:
uid = part[0]
request = partitions.getRequestByID(uid)
if request.type != REQUEST_RAID:
partname = "%s" % (request.device,)
else:
partname = "md%d" % (request.raidminor,)
size = request.getActualSize (partitions, self.diskset)
used = part[2]
# clip size to current PE
pesize = int(self.peCombo.get_active_value())
size = lvm.clampPVSize(size, pesize)
partsize = "%10.2f MB" % size
if used or not reqlvmpart:
selected = 1
else:
selected = 0
if preexist == 0 or selected == 1:
partlist.append_row((partname, partsize), selected)
return (partlist, sw)
def updateAllowedLvmPartitionsList(self, alllvmparts, partitions, partlist):
""" update sizes in pv list
alllvmparts - list of pv from partitions.getAvailLVMPartitions
partitions - object holding all partition requests
partlist - the checklist containing pv list
"""
row = 0
for part in alllvmparts:
uid = part[0]
request = partitions.getRequestByID(uid)
size = request.getActualSize(partitions, self.diskset)
# clip size to current PE
pesize = int(self.peCombo.get_active_value())
size = lvm.clampPVSize(size, pesize)
partsize = "%10.2f MB" % size
iter = partlist.store.get_iter((int(row),))
partlist.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, logrequest, isNew = 0):
if isNew:
tstr = _("Make Logical Volume")
else:
try:
tstr = _("Edit Logical Volume: %s") % (logrequest.logicalVolumeName,)
except:
tstr = _("Edit Logical Volume")
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
lbl = createAlignedLabel(_("_Mount Point:"))
maintable.attach(lbl, 0, 1, row,row+1)
mountCombo = createMountPointCombo(logrequest, excludeMountPoints=["/boot"])
lbl.set_mnemonic_widget(mountCombo)
maintable.attach(mountCombo, 1, 2, row, row + 1)
row += 1
if not logrequest or not logrequest.getPreExisting():
lbl = createAlignedLabel(_("_File System Type:"))
maintable.attach(lbl, 0, 1, row, row + 1)
newfstypeCombo = createFSTypeMenu(logrequest.fstype,
fstypechangeCB,
mountCombo,
ignorefs = ["software RAID", "physical volume (LVM)", "vfat", "efi", "PPC PReP Boot", "Apple Bootstrap"])
lbl.set_mnemonic_widget(newfstypeCombo)
maintable.attach(newfstypeCombo, 1, 2, row, row + 1)
row += 1
else:
maintable.attach(createAlignedLabel(_("Original File System Type:")),
0, 1, row, row + 1)
if logrequest.origfstype and logrequest.origfstype.getName():
newfstypeCombo = gtk.Label(logrequest.origfstype.getName())
else:
newfstypeCombo = gtk.Label(_("Unknown"))
maintable.attach(newfstypeCombo, 1, 2, row, row + 1)
row += 1
if logrequest.fslabel:
maintable.attach(createAlignedLabel(_("Original File System "
"Label:")),
0, 1, row, row + 1)
maintable.attach(gtk.Label(logrequest.fslabel), 1, 2, row,
row + 1)
row += 1
if not logrequest or not logrequest.getPreExisting():
lbl = createAlignedLabel(_("_Logical Volume Name:"))
lvnameEntry = gtk.Entry(32)
lbl.set_mnemonic_widget(lvnameEntry)
if logrequest and logrequest.logicalVolumeName:
lvnameEntry.set_text(logrequest.logicalVolumeName)
else:
lvnameEntry.set_text(lvm.createSuggestedLVName(self.logvolreqs))
else:
lbl = createAlignedLabel(_("Logical Volume Name:"))
lvnameEntry = gtk.Label(logrequest.logicalVolumeName)
maintable.attach(lbl, 0, 1, row, row + 1)
maintable.attach(lvnameEntry, 1, 2, row, row + 1)
row += 1
if not logrequest or not logrequest.getPreExisting():
lbl = createAlignedLabel(_("_Size (MB):"))
sizeEntry = gtk.Entry(16)
lbl.set_mnemonic_widget(sizeEntry)
if logrequest:
sizeEntry.set_text("%Ld" % (logrequest.getActualSize(self.partitions, self.diskset, True),))
else:
lbl = createAlignedLabel(_("Size (MB):"))
sizeEntry = gtk.Label(str(logrequest.size))
maintable.attach(lbl, 0, 1, row, row+1)
maintable.attach(sizeEntry, 1, 2, row, row + 1)
row += 1
if not logrequest or not logrequest.getPreExisting():
pesize = int(self.peCombo.get_active_value())
(tspace, uspace, fspace) = self.computeSpaceValues(usepe=pesize)
maxlv = min(lvm.getMaxLVSize(pesize), fspace)
# add in size of current logical volume if it has a size
if logrequest and not isNew:
maxlv = maxlv + logrequest.getActualSize(self.partitions, self.diskset)
maxlabel = createAlignedLabel(_("(Max size is %s MB)") % (maxlv,))
maintable.attach(maxlabel, 1, 2, row, row + 1)
self.fsoptionsDict = {}
if logrequest.getPreExisting():
(row, self.fsoptionsDict) = createPreExistFSOptionSection(logrequest, maintable, row, mountCombo, self.partitions, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"])
# checkbutton for encryption using dm-crypt/LUKS
if not logrequest.getPreExisting():
self.lukscb = gtk.CheckButton(_("_Encrypt"))
if logrequest.format or logrequest.type == REQUEST_NEW:
self.lukscb.set_data("formatstate", 1)
else:
self.lukscb.set_data("formatstate", 0)
if logrequest.encryption:
self.lukscb.set_active(1)
else:
self.lukscb.set_active(0)
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()
while 1:
rc = dialog.run()
if rc == 2:
dialog.destroy()
return
if not logrequest or not logrequest.getPreExisting():
fsystem = newfstypeCombo.get_active_value()
format = 1
migrate = 0
targetSize = None
else:
if self.fsoptionsDict.has_key("formatcb"):
format = self.fsoptionsDict["formatcb"].get_active()
if format:
fsystem = self.fsoptionsDict["fstypeCombo"].get_active_value()
else:
format = 0
if self.fsoptionsDict.has_key("migratecb"):
migrate = self.fsoptionsDict["migratecb"].get_active()
if migrate:
fsystem = self.fsoptionsDict["migfstypeCombo"].get_active_value()
else:
migrate = 0
if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active():
targetSize = self.fsoptionsDict["resizesb"].get_value_as_int()
else:
targetSize = None
# set back if we are not formatting or migrating
origfstype = logrequest.origfstype
if not format and not migrate:
fsystem = origfstype
mntpt = string.strip(mountCombo.get_children()[0].get_text())
if not logrequest or not logrequest.getPreExisting():
# check size specification
badsize = 0
try:
size = long(sizeEntry.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 = logrequest.size
# is this an existing logical volume or one we're editting
if logrequest:
preexist = logrequest.getPreExisting()
else:
preexist = 0
# 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 fsystem.isMountable():
used = 0
if logrequest:
curmntpt = logrequest.mountpoint
else:
curmntpt = None
for lv in self.logvolreqs:
if curmntpt and lv.mountpoint == curmntpt:
continue
if len(mntpt) == 0:
continue
if lv.mountpoint == mntpt:
used = 1
break
if used:
self.intf.messageWindow(_("Mount point in use"),
_("The mount point \"%s\" is in "
"use. Please pick another.") %
(mntpt,), custom_icon="error")
continue
# check out logical volumne name
lvname = string.strip(lvnameEntry.get_text())
if not logrequest or not logrequest.getPreExisting():
err = sanityCheckLogicalVolumeName(lvname)
if err:
self.intf.messageWindow(_("Illegal Logical Volume Name"),err, custom_icon="error")
continue
# is it in use?
used = 0
if logrequest:
origlvname = logrequest.logicalVolumeName
else:
origlvname = None
for lv in self.logvolreqs:
if origlvname and lv.logicalVolumeName == origlvname:
continue
if lv.logicalVolumeName == 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
# create potential request
request = copy.copy(logrequest)
request.encryption = copy.deepcopy(logrequest.encryption)
pesize = int(self.peCombo.get_active_value())
size = lvm.clampLVSizeRequest(size, pesize, roundup=1)
# do some final tests
maxlv = lvm.getMaxLVSize(pesize)
if size > maxlv:
self.intf.messageWindow(_("Not enough space"),
_("The current requested size "
"(%10.2f MB) is larger than the maximum "
"logical volume size (%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, maxlv),
custom_icon="error")
continue
request.fstype = fsystem
if request.fstype.isMountable():
request.mountpoint = mntpt
else:
request.mountpoint = None
request.preexist = preexist
request.logicalVolumeName = lvname
request.size = size
request.format = format
request.migrate = migrate
request.targetSize = targetSize
request.grow = 0
# this is needed to clear out any cached info about the device
# only a workaround - need to change way this is handled in
# partRequest.py really.
request.dev = None
if self.lukscb and self.lukscb.get_active():
if request.encryption:
passphrase = request.encryption.passphrase
else:
passphrase = ""
isglobal = False
if not request.encryption or request.encryption.format:
if not passphrase and self.partitions.globalPassphrase:
passphrase = self.partitions.globalPassphrase
else:
if passphrase and \
passphrase == self.partitions.globalPassphrase:
isglobal = True
(passphrase, isglobal) = self.intf.getLuksPassphrase(passphrase, isglobal=isglobal)
if passphrase and not request.encryption:
request.encryption = LUKSDevice(passphrase=passphrase,
format=1)
elif passphrase and request.encryption.format:
request.encryption.setPassphrase(passphrase)
else:
isglobal = False
if isglobal and not self.partitions.globalPassphrase:
self.partitions.globalPassphrase = passphrase
else:
request.encryption = None
# make list of original logvol requests so we can skip them
# in tests below. We check for mount point name conflicts
# above within the current volume group, so it is not
# necessary to do now.
err = request.sanityCheckRequest(self.partitions, skipMntPtExistCheck=1, pesize=pesize)
if err is None:
skiplist = []
for lv in self.origvolreqs:
skiplist.append(lv.uniqueID)
err = request.isMountPointInUse(self.partitions, requestSkipList=skiplist)
if err:
self.intf.messageWindow(_("Error With Request"),
"%s" % (err), custom_icon="error")
continue
if (not request.format and
request.mountpoint and request.formatByDefault()):
if not queryNoFormatPreExisting(self.intf):
continue
# see if there is room for request
(availSpaceMB, neededSpaceMB, fspace) = self.computeSpaceValues(usepe=pesize)
tmplogreqs = []
for l in self.logvolreqs:
if origlvname and l.logicalVolumeName == origlvname:
continue
tmplogreqs.append(l)
tmplogreqs.append(request)
neededSpaceMB = self.computeLVSpaceNeeded(tmplogreqs)
if neededSpaceMB > availSpaceMB:
self.intf.messageWindow(_("Not enough space"),
_("The logical volumes you have "
"configured require %d MB, but the "
"volume group only has %d MB. Please "
"either make the volume group larger "
"or make the logical volume(s) smaller.") % (neededSpaceMB, availSpaceMB), custom_icon="error")
del tmplogreqs
continue
# everything ok
break
# now remove the previous request, insert request created above
if not isNew:
self.logvolreqs.remove(logrequest)
iter = self.getCurrentLogicalVolume()
self.logvolstore.remove(iter)
if request.targetSize is not None:
size = request.targetSize
# adjust the free space in the vg
if logrequest.targetSize is not None:
diff = request.targetSize - logrequest.targetSize
else:
diff = request.targetSize - request.size
self.origvgrequest.free -= diff
self.logvolreqs.append(request)
iter = self.logvolstore.append()
self.logvolstore.set_value(iter, 0, lvname)
if request.fstype and request.fstype.isMountable():
self.logvolstore.set_value(iter, 1, mntpt)
else:
self.logvolstore.set_value(iter, 1, "N/A")
self.logvolstore.set_value(iter, 2, "%Ld" % (size,))
self.updateVGSpaceLabels()
dialog.destroy()
def editCurrentLogicalVolume(self):
iter = self.getCurrentLogicalVolume()
if iter is None:
return
logvolname = self.logvolstore.get_value(iter, 0)
logrequest = None
for lv in self.logvolreqs:
if lv.logicalVolumeName == logvolname:
logrequest = lv
if logrequest is None:
return
self.editLogicalVolume(logrequest)
def addLogicalVolumeCB(self, widget):
if self.numAvailableLVSlots() < 1:
self.intf.messageWindow(_("No free slots"),
_("You cannot create more than %s logical "
"volumes per volume group.") % (lvm.MAX_LV_SLOTS,), custom_icon="error")
return
(tspace, uspace, fspace) = self.computeSpaceValues()
if fspace <= 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
request = LogicalVolumeRequestSpec(fileSystemTypeGetDefault(),
size = fspace)
self.editLogicalVolume(request, 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
for lv in self.logvolreqs:
if lv.logicalVolumeName == logvolname:
self.logvolreqs.remove(lv)
self.logvolstore.remove(iter)
self.updateVGSpaceLabels()
return
def logvolActivateCb(self, view, path, col):
self.editCurrentLogicalVolume()
def getSelectedPhysicalVolumes(self, 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:
pvreq = self.partitions.getRequestByDeviceName(partname)
id = pvreq.uniqueID
pv.append(id)
next = model.iter_next(iter)
currow = currow + 1
return pv
def computeVGSize(self, pvlist, curpe):
availSpaceMB = 0L
for id in pvlist:
pvreq = self.partitions.getRequestByID(id)
pvsize = pvreq.getActualSize(self.partitions, self.diskset)
pvsize = lvm.clampPVSize(pvsize, curpe) - (curpe/1024)
# have to clamp pvsize to multiple of PE
availSpaceMB = availSpaceMB + pvsize
log.info("computeVGSize: vgsize is %s" % (availSpaceMB,))
return availSpaceMB
def computeLVSpaceNeeded(self, logreqs):
neededSpaceMB = 0
for lv in logreqs:
neededSpaceMB = neededSpaceMB + lv.getActualSize(self.partitions, self.diskset, True)
return neededSpaceMB
def updateLogVolStore(self):
self.logvolstore.clear()
for lv in self.logvolreqs:
iter = self.logvolstore.append()
size = lv.getActualSize(self.partitions, self.diskset, True)
lvname = lv.logicalVolumeName
mntpt = lv.mountpoint
if lvname:
self.logvolstore.set_value(iter, 0, lvname)
if lv.fstype and lv.fstype.isMountable():
if mntpt:
self.logvolstore.set_value(iter, 1, mntpt)
else:
self.logvolstore.set_value(iter, 1, "")
else:
self.logvolstore.set_value(iter, 1, "N/A")
self.logvolstore.set_value(iter, 2, "%Ld" % (size,))
def updateVGSpaceLabels(self, alt_pvlist=None):
if alt_pvlist == None:
pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model())
else:
pvlist = alt_pvlist
(tspace, uspace, fspace) = self.computeSpaceValues(alt_pvlist=pvlist)
self.totalSpaceLabel.set_text("%10.2f MB" % (tspace,))
self.usedSpaceLabel.set_text("%10.2f MB" % (uspace,))
if tspace > 0:
usedpercent = (100.0*uspace)/tspace
else:
usedpercent = 0.0
self.usedPercentLabel.set_text("(%4.1f %%)" % (usedpercent,))
self.freeSpaceLabel.set_text("%10.2f MB" % (fspace,))
if tspace > 0:
freepercent = (100.0*fspace)/tspace
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 None
while 1:
rc = self.dialog.run()
if rc == 2:
self.destroy()
return None
pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model())
pesize = int(self.peCombo.get_active_value())
availSpaceMB = self.computeVGSize(pvlist, pesize)
neededSpaceMB = self.computeLVSpaceNeeded(self.logvolreqs)
if neededSpaceMB > availSpaceMB:
self.intf.messageWindow(_("Not enough space"),
_("The logical volumes you have "
"configured require %d MB, but the "
"volume group only has %d MB. Please "
"either make the volume group larger "
"or make the logical volume(s) smaller.") % (neededSpaceMB, availSpaceMB), custom_icon="error")
continue
# check volume name
volname = string.strip(self.volnameEntry.get_text())
err = sanityCheckVolumeGroupName(volname)
if err:
self.intf.messageWindow(_("Invalid Volume Group Name"), err,
custom_icon="error")
continue
if self.origvgrequest:
origvname = self.origvgrequest.volumeGroupName
else:
origname = None
if origvname != volname:
if self.partitions.isVolumeGroupNameInUse(volname):
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())
# everything ok
break
request = VolumeGroupRequestSpec(physvols = pvlist, vgname = volname,
pesize = pesize)
# if it was preexisting, it still should be
if self.origvgrequest and self.origvgrequest.getPreExisting():
request.preexist = 1
elif self.origvgrequest:
request.format = self.origvgrequest.format
return (request, self.logvolreqs)
def destroy(self):
if self.dialog:
self.dialog.destroy()
self.dialog = None
def __init__(self, partitions, diskset, intf, parent, origvgrequest, isNew = 0):
self.partitions = partitions
self.diskset = diskset
self.origvgrequest = origvgrequest
self.isNew = isNew
self.intf = intf
self.parent = parent
self.availlvmparts = self.partitions.getAvailLVMPartitions(self.origvgrequest,
self.diskset)
self.logvolreqs = self.partitions.getLVMLVForVG(self.origvgrequest)
self.origvolreqs = copy.copy(self.logvolreqs)
# 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") % (origvgrequest.volumeGroupName,)
except:
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 origvgrequest.getPreExisting():
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.origvgrequest.volumeGroupName)
else:
self.volnameEntry.set_text(lvm.createSuggestedVGName(self.partitions))
else:
lbl = createAlignedLabel(_("Volume Group Name:"))
self.volnameEntry = gtk.Label(self.origvgrequest.volumeGroupName)
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.origvgrequest.pesize)
lbl.set_mnemonic_widget(self.peCombo)
if origvgrequest.getPreExisting():
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(self.availlvmparts, self.origvgrequest.physicalVolumes, self.partitions, origvgrequest.getPreExisting())
if origvgrequest.getPreExisting():
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.logvolreqs:
for lvrequest in self.logvolreqs:
iter = self.logvolstore.append()
self.logvolstore.set_value(iter, 0, lvrequest.logicalVolumeName)
if lvrequest.mountpoint is not None:
self.logvolstore.set_value(iter, 1, lvrequest.mountpoint)
else:
self.logvolstore.set_value(iter, 1, "")
self.logvolstore.set_value(iter, 2, "%Ld" % (lvrequest.getActualSize(self.partitions, self.diskset, True)))
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("%s" %(_("_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