535 lines
22 KiB
Python
535 lines
22 KiB
Python
|
#
|
||
|
# 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 <http://www.gnu.org/licenses/>.
|
||
|
#
|
||
|
# Author(s): Michael Fulbright <msf@redhat.com>
|
||
|
#
|
||
|
|
||
|
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 *
|
||
|
|
||
|
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()
|
||
|
if mountpoint == _("<Not Applicable>"):
|
||
|
mountpoint = ""
|
||
|
|
||
|
if mountpoint:
|
||
|
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")
|
||
|
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)
|
||
|
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
|
||
|
|
||
|
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))
|
||
|
|
||
|
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(request, 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.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():
|
||
|
size = self.fsoptionsDict["resizesb"].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, "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.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]
|
||
|
self.driveview = createAllowedDrivesList(self.storage.partitioned,
|
||
|
req_disk_names,
|
||
|
disallowDrives=[self.anaconda.updateSrc])
|
||
|
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, 80)
|
||
|
|
||
|
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()
|
||
|
|