#
# partition_dialog_gui.py: dialog for editting a partition request
#
# Copyright (C) 2001, 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 gui
from storage.devices import PartitionDevice, LUKSDevice
from storage.deviceaction import *
from partition_ui_helpers_gui import *
from constants import *
from partIntfHelpers import *
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
class PartitionEditor:
def sizespinchangedCB(self, widget, fillmaxszsb):
size = widget.get_value_as_int()
maxsize = fillmaxszsb.get_value_as_int()
if size < 1:
widget.set_value(1)
size = 1
if size > maxsize:
fillmaxszsb.set_value(size)
# ugly got to be better way
adj = fillmaxszsb.get_adjustment()
adj.clamp_page(size, adj.upper)
fillmaxszsb.set_adjustment(adj)
def fillmaxszCB(self, widget, spin):
spin.set_sensitive(widget.get_active())
# pass in CB defined above because of two scope limitation of python!
def createSizeOptionsFrame(self, request, fillmaxszCB):
frame = gtk.Frame(_("Additional Size Options"))
sizeoptiontable = gtk.Table()
sizeoptiontable.set_row_spacings(5)
sizeoptiontable.set_border_width(4)
fixedrb = gtk.RadioButton(label=_("_Fixed size"))
fillmaxszrb = gtk.RadioButton(group=fixedrb,
label=_("Fill all space _up "
"to (MB):"))
maxsizeAdj = gtk.Adjustment(value = 1, lower = 1,
upper = MAX_PART_SIZE, step_incr = 1)
fillmaxszsb = gtk.SpinButton(maxsizeAdj, digits = 0)
fillmaxszsb.set_property('numeric', True)
fillunlimrb = gtk.RadioButton(group=fixedrb,
label=_("Fill to maximum _allowable "
"size"))
fillmaxszrb.connect("toggled", fillmaxszCB, fillmaxszsb)
# default to fixed, turn off max size spinbutton
fillmaxszsb.set_sensitive(0)
if request.req_grow:
if request.req_max_size:
fillmaxszrb.set_active(1)
fillmaxszsb.set_sensitive(1)
fillmaxszsb.set_value(request.req_max_size)
else:
fillunlimrb.set_active(1)
else:
fixedrb.set_active(1)
sizeoptiontable.attach(fixedrb, 0, 1, 0, 1)
sizeoptiontable.attach(fillmaxszrb, 0, 1, 1, 2)
sizeoptiontable.attach(fillmaxszsb, 1, 2, 1, 2)
sizeoptiontable.attach(fillunlimrb, 0, 1, 2, 3)
frame.add(sizeoptiontable)
return (frame, fixedrb, fillmaxszrb, fillmaxszsb)
def run(self):
if self.dialog is None:
return []
while 1:
rc = self.dialog.run()
actions = []
luksdev = None
# user hit cancel, do nothing
if rc in [2, gtk.RESPONSE_DELETE_EVENT]:
self.destroy()
return []
mountpoint = self.mountCombo.get_children()[0].get_text()
(sensitive,) = self.mountCombo.get_properties('sensitive')
if sensitive and mountpoint:
msg = sanityCheckMountPoint(mountpoint)
if msg:
self.intf.messageWindow(_("Mount Point Error"),
msg,
custom_icon="error")
self.dialog.present()
continue
used = False
for (mp, dev) in self.storage.mountpoints.iteritems():
if mp == mountpoint and \
dev.id != self.origrequest.id and \
not (self.origrequest.format.type == "luks" and
self.origrequest in dev.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")
self.dialog.present()
continue
if not self.origrequest.exists:
# read out UI into a partition specification
fmt_class = self.newfstypeCombo.get_active_value()
# there's nothing about origrequest we care about
#request = copy.copy(self.origrequest)
if self.primonlycheckbutton.get_active():
primary = True
else:
primary = None
if self.fixedrb.get_active():
grow = None
else:
grow = True
self.sizespin.update()
if self.fillmaxszrb.get_active():
self.fillmaxszsb.update()
maxsize = self.fillmaxszsb.get_value_as_int()
else:
maxsize = 0
allowdrives = []
model = self.driveview.get_model()
iter = model.get_iter_first()
while iter:
val = model.get_value(iter, 0)
drive = model.get_value(iter, 1)
if val:
allowdrives.append(drive)
iter = model.iter_next(iter)
if len(allowdrives) == len(self.storage.partitioned):
allowdrives = None
size = self.sizespin.get_value_as_int()
disks = []
if allowdrives:
for drive in allowdrives:
for disk in self.storage.partitioned:
if disk.name == drive:
disks.append(disk)
format = fmt_class(mountpoint=mountpoint)
err = doUIRAIDLVMChecks(format, disks, self.storage)
if err:
self.intf.messageWindow(_("Error With Request"),
err, custom_icon="error")
self.dialog.present()
continue
weight = self.anaconda.platform.weight(mountpoint=mountpoint,
fstype=format.type)
if self.isNew:
request = self.storage.newPartition(size=size,
grow=grow,
maxsize=maxsize,
primary=primary,
format=format,
parents=disks,
weight=weight)
else:
request = self.origrequest
request.weight = weight
usedev = request
if self.lukscb and self.lukscb.get_active() and \
request.format.type != "luks":
luksformat = format
format = getFormat("luks",
passphrase=self.storage.encryptionPassphrase)
luksdev = LUKSDevice("luks%d" % self.storage.nextID,
format=luksformat,
parents=request)
elif self.lukscb and not self.lukscb.get_active() and \
self.origrequest.format.type == "luks":
# destroy the luks format and the mapped device
try:
luksdev = self.storage.devicetree.getChildren(self.origrequest)[0]
except IndexError:
pass
else:
actions.append(ActionDestroyFormat(luksdev))
actions.append(ActionDestroyDevice(luksdev))
luksdev = None
actions.append(ActionDestroyFormat(request))
elif self.lukscb and self.lukscb.get_active() and \
self.origrequest.format.type == "luks":
# re-edit of new encrypted partition. we're only updating
# the formatting on the LUKS device
try:
usedev = self.storage.devicetree.getChildren(self.origrequest)[0]
except IndexError:
pass
if self.isNew:
# we're all set, so create the actions
actions.append(ActionCreateDevice(request))
else:
request.req_size = size
request.req_base_size = size
request.req_grow = grow
request.req_max_size = maxsize
request.req_primary = primary
request.req_disks = disks
actions.append(ActionCreateFormat(usedev, format))
if luksdev:
actions.append(ActionCreateDevice(luksdev))
actions.append(ActionCreateFormat(luksdev))
else:
# preexisting partition
request = self.origrequest
if request.format.type == "luks":
try:
usedev = self.storage.devicetree.getChildren(request)[0]
except IndexError:
usedev = request
else:
usedev = request
origformat = usedev.format
devicetree = self.anaconda.id.storage.devicetree
if self.fsoptionsDict.has_key("formatcb"):
if self.fsoptionsDict["formatcb"].get_active():
fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value()
# carry over exists, migrate, size, and device
# necessary for partition editor UI
format = fmt_class(mountpoint=mountpoint,
device=usedev.path)
luksdev = None
if self.fsoptionsDict.has_key("lukscb") and \
self.fsoptionsDict["lukscb"].get_active() and \
(request.format.type != "luks" or
(request.format.exists and
not request.format.hasKey)):
luksdev = LUKSDevice("luks%d" % self.storage.nextID,
format=format,
parents=request)
format = getFormat("luks",
device=self.origrequest.path,
passphrase=self.storage.encryptionPassphrase)
elif self.fsoptionsDict.has_key("lukscb") and \
not self.fsoptionsDict["lukscb"].get_active() and \
request.format.type == "luks":
# user elected to format the device w/o encryption
try:
luksdev = self.storage.devicetree.getChildren(request)[0]
except IndexError:
pass
else:
actions.append(ActionDestroyFormat(luksdev))
actions.append(ActionDestroyDevice(luksdev))
luksdev = None
actions.append(ActionDestroyFormat(request))
# we set the new format's device while under the
# impression that the device was going to be
# encrypted, so we need to remedy that now
format.device = request.path
usedev = request
actions.append(ActionCreateFormat(usedev, format))
if luksdev:
actions.append(ActionCreateDevice(luksdev))
actions.append(ActionCreateFormat(luksdev))
elif not self.fsoptionsDict["formatcb"].get_active():
# if the format checkbutton is inactive, cancel all
# actions on this device that create or destroy
# formats
cancel = []
if request.originalFormat.type == "luks":
path = "/dev/mapper/luks-%s" % request.originalFormat.uuid
cancel.extend(devicetree.findActions(path=path))
cancel.extend(devicetree.findActions(type="destroy",
object="format",
devid=request.id))
cancel.extend(devicetree.findActions(type="create",
object="format",
devid=request.id))
cancel.reverse()
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.
request.format = request.originalFormat
if request.format.type == "luks":
try:
usedev = devicetree.getChildren(request)[0]
except IndexError:
usedev = request
else:
usedev.format = usedev.originalFormat
else:
usedev = request
if usedev.format.mountable:
usedev.format.mountpoint = mountpoint
elif self.origrequest.protected and usedev.format.mountable:
# users can set a mountpoint for protected partitions
usedev.format.mountpoint = mountpoint
request.weight = self.anaconda.platform.weight(mountpoint=mountpoint,
fstype=request.format.type)
if self.fsoptionsDict.has_key("migratecb") and \
self.fsoptionsDict["migratecb"].get_active():
actions.append(ActionMigrateFormat(usedev))
if self.fsoptionsDict.has_key("resizecb") and \
self.fsoptionsDict["resizecb"].get_active():
sb = self.fsoptionsDict["resizesb"]
sb.update()
size = sb.get_value_as_int()
try:
actions.append(ActionResizeDevice(request, size))
if request.format.type and request.format.exists:
actions.append(ActionResizeFormat(request, size))
except ValueError:
pass
if request.format.exists and \
getattr(request.format, "mountpoint", None) and \
self.storage.formatByDefault(request):
if not queryNoFormatPreExisting(self.intf):
continue
# everything ok, fall out of loop
break
return actions
def destroy(self):
if self.dialog:
self.dialog.destroy()
self.dialog = None
def __init__(self, anaconda, parent, origrequest, isNew = 0,
restrictfs = None):
self.anaconda = anaconda
self.storage = self.anaconda.id.storage
self.intf = self.anaconda.intf
self.origrequest = origrequest
self.isNew = isNew
self.parent = parent
if isNew:
tstr = _("Add Partition")
else:
tstr = _("Edit Partition: %s") % (origrequest.path,)
self.dialog = gtk.Dialog(tstr, self.parent)
gui.addFrame(self.dialog)
self.dialog.add_button('gtk-cancel', 2)
self.dialog.add_button('gtk-ok', 1)
self.dialog.set_position(gtk.WIN_POS_CENTER)
maintable = gtk.Table()
maintable.set_row_spacings(5)
maintable.set_col_spacings(5)
row = 0
# if this is a luks device we need to grab info from two devices
# to make it seem like one device. wee!
if self.origrequest.format.type == "luks":
try:
luksdev = self.storage.devicetree.getChildren(self.origrequest)[0]
except IndexError:
usereq = self.origrequest
luksdev = None
else:
usereq = luksdev
else:
luksdev = None
usereq = self.origrequest
# Mount Point entry
lbl = createAlignedLabel(_("_Mount Point:"))
maintable.attach(lbl, 0, 1, row, row + 1)
self.mountCombo = createMountPointCombo(usereq)
lbl.set_mnemonic_widget(self.mountCombo)
maintable.attach(self.mountCombo, 1, 2, row, row + 1)
row = row + 1
# Partition Type
if not self.origrequest.exists:
lbl = createAlignedLabel(_("File System _Type:"))
maintable.attach(lbl, 0, 1, row, row + 1)
self.newfstypeCombo = createFSTypeMenu(usereq.format,
fstypechangeCB,
self.mountCombo,
availablefstypes = restrictfs)
lbl.set_mnemonic_widget(self.newfstypeCombo)
maintable.attach(self.newfstypeCombo, 1, 2, row, row + 1)
else:
self.newfstypeCombo = None
row = row + 1
# allowable drives
if not self.origrequest.exists:
lbl = createAlignedLabel(_("Allowable _Drives:"))
maintable.attach(lbl, 0, 1, row, row + 1)
req_disk_names = [d.name for d in self.origrequest.req_disks]
preselectAll = True
if restrictfs and 'mdmember' in restrictfs:
# do not preselect all available drives for new raid partitions
preselectAll = False
self.driveview = createAllowedDrivesList(self.storage.partitioned,
req_disk_names,
disallowDrives=[self.anaconda.updateSrc],
preselectAll=preselectAll)
lbl.set_mnemonic_widget(self.driveview)
sw = gtk.ScrolledWindow()
sw.add(self.driveview)
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
sw.set_shadow_type(gtk.SHADOW_IN)
maintable.attach(sw, 1, 2, row, row + 1)
self.driveview.set_size_request(375, 120)
row = row + 1
# original fs type and label
if self.origrequest.exists:
maintable.attach(createAlignedLabel(_("Original File System Type:")),
0, 1, row, row + 1)
self.fstypeCombo = gtk.Label(usereq.originalFormat.name)
maintable.attach(self.fstypeCombo, 1, 2, row, row + 1)
row += 1
if getattr(usereq.originalFormat, "label", None):
maintable.attach(createAlignedLabel(_("Original File System "
"Label:")),
0, 1, row, row + 1)
fslabel = gtk.Label(usereq.originalFormat.label)
maintable.attach(fslabel, 1, 2, row, row + 1)
row = row + 1
# size
if not self.origrequest.exists:
# Size specification
lbl = createAlignedLabel(_("_Size (MB):"))
maintable.attach(lbl, 0, 1, row, row + 1)
sizeAdj = gtk.Adjustment(value = 1, lower = 1,
upper = MAX_PART_SIZE, step_incr = 1)
self.sizespin = gtk.SpinButton(sizeAdj, digits = 0)
self.sizespin.set_property('numeric', True)
if self.origrequest.req_size:
self.sizespin.set_value(self.origrequest.req_size)
lbl.set_mnemonic_widget(self.sizespin)
maintable.attach(self.sizespin, 1, 2, row, row + 1)
else:
self.sizespin = None
row = row + 1
# format/migrate options for pre-existing partitions, as long as they
# aren't protected (we'd still like to be able to mount them, though)
self.fsoptionsDict = {}
if self.origrequest.exists and \
not self.origrequest.protected:
(row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo, self.storage, luksdev=luksdev)
# size options
if not self.origrequest.exists:
(sizeframe, self.fixedrb, self.fillmaxszrb,
self.fillmaxszsb) = self.createSizeOptionsFrame(self.origrequest,
self.fillmaxszCB)
self.sizespin.connect("value-changed", self.sizespinchangedCB,
self.fillmaxszsb)
maintable.attach(sizeframe, 0, 2, row, row + 1)
row = row + 1
else:
self.sizeoptiontable = None
# create only as primary
if not self.origrequest.exists:
self.primonlycheckbutton = gtk.CheckButton(_("Force to be a _primary "
"partition"))
self.primonlycheckbutton.set_active(0)
if self.origrequest.req_primary:
self.primonlycheckbutton.set_active(1)
# only show if we have something other than primary
if self.storage.extendedPartitionsSupported():
maintable.attach(self.primonlycheckbutton, 0, 2, row, row+1)
row = row + 1
# checkbutton for encryption using dm-crypt/LUKS
if not self.origrequest.exists:
self.lukscb = gtk.CheckButton(_("_Encrypt"))
self.lukscb.set_data("formatstate", 1)
if self.origrequest.format.type == "luks":
self.lukscb.set_active(1)
else:
self.lukscb.set_active(0)
maintable.attach(self.lukscb, 0, 2, row, row + 1)
row = row + 1
# put main table into dialog
self.dialog.vbox.pack_start(maintable)
self.dialog.show_all()