# # autopart_type.py: Allows the user to choose how they want to partition # # Copyright (C) 2005, 2006 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): Jeremy Katz # import gtk import gobject import math from constants import * import gui from partition_ui_helpers_gui import * from netconfig_dialog import NetworkConfigurator from iw_gui import * from flags import flags import network from storage import iscsi from storage import fcoe from storage.deviceaction import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) def whichToShrink(storage, intf): def getActive(combo): act = combo.get_active_iter() return combo.get_model().get_value(act, 1) def comboCB(combo, shrinkSB): # partition to resize changed, let's update our spinbutton newSize = shrinkSB.get_value_as_int() part = getActive(combo) reqlower = long(math.ceil(part.format.minSize)) requpper = long(math.floor(part.format.currentSize)) adj = shrinkSB.get_adjustment() adj.lower = reqlower adj.upper = requpper adj.set_value(reqlower) (dxml, dialog) = gui.getGladeWidget("autopart.glade", "shrinkDialog") store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) combo = dxml.get_widget("shrinkPartCombo") combo.set_model(store) crt = gtk.CellRendererText() combo.pack_start(crt, True) combo.set_attributes(crt, text = 0) combo.connect("changed", comboCB, dxml.get_widget("shrinkSB")) biggest = -1 for part in storage.partitions: if not part.exists: continue entry = None if part.resizable and part.format.resizable: entry = ("%s (%s, %d MB)" % (part.name, part.format.name, math.floor(part.format.size)), part) if entry: i = store.append(None) store[i] = entry combo.set_active_iter(i) if biggest == -1: biggest = i else: current = store.get_value(biggest, 1) if part.format.targetSize > current.format.targetSize: biggest = i if biggest > -1: combo.set_active_iter(biggest) if len(store) == 0: dialog.destroy() intf.messageWindow(_("Error"), _("No partitions are available to resize. Only " "physical partitions with specific filesystems " "can be resized."), type="warning", custom_icon="error") return (gtk.RESPONSE_CANCEL, []) gui.addFrame(dialog) dialog.show_all() runResize = True while runResize: rc = dialog.run() if rc != gtk.RESPONSE_OK: dialog.destroy() return (rc, []) request = getActive(combo) newSize = dxml.get_widget("shrinkSB").get_value_as_int() actions = [] try: actions.append(ActionResizeFormat(request, newSize)) except ValueError as e: intf.messageWindow(_("Resize FileSystem Error"), _("%s: %s") % (request.format.device, e.message,), type="warning", custom_icon="error") continue try: actions.append(ActionResizeDevice(request, newSize)) except ValueError as e: intf.messageWindow(_("Resize Device Error"), _("%s: %s") % (request.name, e.message,), type="warning", custom_icon="error") continue runResize = False dialog.destroy() return (rc, actions) class PartitionTypeWindow(InstallWindow): def __init__(self, ics): InstallWindow.__init__(self, ics) ics.setTitle("Automatic Partitioning") ics.setNextEnabled(True) def getNext(self): if self.storage.checkNoDisks(): raise gui.StayOnScreen active = self.combo.get_active_iter() val = self.combo.get_model().get_value(active, 1) if val == -1: self.dispatch.skipStep("autopartitionexecute", skip = 1) self.dispatch.skipStep("partition", skip = 0) self.dispatch.skipStep("bootloader", skip = 0) else: if val == -2: (rc, actions) = whichToShrink(self.storage, self.intf) if rc == gtk.RESPONSE_OK: for action in actions: self.storage.devicetree.registerAction(action) else: raise gui.StayOnScreen # we're not going to delete any partitions in the resize case val = CLEARPART_TYPE_NONE self.dispatch.skipStep("autopartitionexecute", skip = 0) if self.xml.get_widget("encryptButton").get_active(): self.storage.encryptedAutoPart = True else: self.storage.encryptionPassphrase = "" self.storage.retrofitPassphrase = False self.storage.encryptedAutoPart = False self.storage.doAutoPart = True self.storage.clearPartType = val allowdrives = [] model = self.drivelist.get_model() for row in model: if row[0]: allowdrives.append(row[1]) if len(allowdrives) < 1: mustHaveSelectedDrive(self.intf) raise gui.StayOnScreen self.storage.clearPartDisks = allowdrives # pop the boot device to be first in the drive list defiter = self.bootcombo.get_active_iter() if defiter is None: self.intf.messageWindow(_("Error"), "Must select a drive to use as " "the bootable device.", type="warning", custom_icon="error") raise gui.StayOnScreen defboot = self.bootcombo.get_model().get_value(defiter, 1) if not defboot in allowdrives: msg = _("Do you really want to boot from a disk which is not used for installation?") rc = self.intf.messageWindow(_("Warning"), msg, type="yesno", default="no", custom_icon ="warning") if not rc: raise gui.StayOnScreen self.anaconda.id.bootloader.drivelist.remove(defboot) self.anaconda.id.bootloader.drivelist.insert(0, defboot) if self.xml.get_widget("reviewButton").get_active(): self.dispatch.skipStep("partition", skip = 0) self.dispatch.skipStep("bootloader", skip = 0) else: self.dispatch.skipStep("partition") self.dispatch.skipStep("bootloader") self.dispatch.skipStep("bootloaderadvanced") return None def comboChanged(self, *args): active = self.combo.get_active_iter() val = self.combo.get_model().get_value(active, 1) self.review = self.xml.get_widget("reviewButton").get_active() # -1 is the combo box choice for 'create custom layout' if val == -1: if self.prevrev == None: self.prevrev = self.xml.get_widget("reviewButton").get_active() self.xml.get_widget("reviewButton").set_active(True) self.xml.get_widget("reviewButton").set_sensitive(False) self.xml.get_widget("driveScroll").set_sensitive(False) self.xml.get_widget("bootDriveCombo").set_sensitive(False) self.xml.get_widget("encryptButton").set_sensitive(False) else: if self.prevrev == None: self.xml.get_widget("reviewButton").set_active(self.review) else: self.xml.get_widget("reviewButton").set_active(self.prevrev) self.prevrev = None self.xml.get_widget("reviewButton").set_sensitive(True) self.xml.get_widget("driveScroll").set_sensitive(True) self.xml.get_widget("bootDriveCombo").set_sensitive(True) self.xml.get_widget("encryptButton").set_sensitive(True) def addIscsiDrive(self): if not network.hasActiveNetDev(): net = NetworkConfigurator(self.anaconda.id.network) ret = net.run() net.destroy() if ret != gtk.RESPONSE_OK: return ret (dxml, dialog) = gui.getGladeWidget("iscsi-config.glade", "iscsiDialog") gui.addFrame(dialog) dialog.show_all() sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) map(lambda x: sg.add_widget(dxml.get_widget(x)), ("iscsiAddrEntry", "iscsiInitiatorEntry", "userEntry", "passEntry", "userinEntry", "passinEntry")) # get the initiator name if it exists and don't allow changing # once set e = dxml.get_widget("iscsiInitiatorEntry") e.set_text(self.storage.iscsi.initiator) if self.storage.iscsi.initiatorSet: # this is uglyyyy.... e.set_sensitive(False) while 1: rc = dialog.run() if rc == gtk.RESPONSE_CANCEL: break initiator = dxml.get_widget("iscsiInitiatorEntry").get_text() initiator.strip() if len(initiator) == 0: self.intf.messageWindow(_("Invalid Initiator Name"), _("You must provide an initiator name.")) continue self.storage.iscsi.initiator = initiator target = dxml.get_widget("iscsiAddrEntry").get_text().strip() user = dxml.get_widget("userEntry").get_text().strip() pw = dxml.get_widget("passEntry").get_text().strip() user_in = dxml.get_widget("userinEntry").get_text().strip() pw_in = dxml.get_widget("passinEntry").get_text().strip() err = None try: idx = target.rfind(":") if idx != -1: ip = target[:idx] port = target[idx:] else: ip = target port = "3260" network.sanityCheckIPString(ip) except network.IPMissing, msg: err = msg except network.IPError, msg: err = msg if err: self.intf.messageWindow(_("Error with Data"), "%s" %(err,)) continue try: self.storage.iscsi.addTarget(ip, port, user, pw, user_in, pw_in, self.intf) except ValueError, e: self.intf.messageWindow(_("Error"), str(e)) continue except IOError, e: self.intf.messageWindow(_("Error"), str(e)) rc = gtk.RESPONSE_CANCEL break dialog.destroy() return rc def addFcoeDrive(self): (dxml, dialog) = gui.getGladeWidget("fcoe-config.glade", "fcoeDialog") # Populate the combo combo = dxml.get_widget("fcoeNicCombo") cell = gtk.CellRendererText() combo.pack_start(cell, True) combo.set_attributes(cell, text = 0) cell.set_property("wrap-width", 525) combo.set_size_request(480, -1) store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING) combo.set_model(store) netdevs = self.anaconda.id.network.available() keys = netdevs.keys() keys.sort() selected_interface = None for dev in keys: # Skip NIC's which are connected (iow in use for a net install) if dev in network.getActiveNetDevs(): continue i = store.append(None) desc = netdevs[dev].get("DESC") if desc: desc = "%s - %s" %(dev, desc) else: desc = "%s" %(dev,) mac = netdevs[dev].get("HWADDR") if mac: desc = "%s - %s" %(desc, mac) if selected_interface is None: selected_interface = i store[i] = (desc, dev) if selected_interface: combo.set_active_iter(selected_interface) else: combo.set_active(0) # Show the dialog gui.addFrame(dialog) dialog.show_all() sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) sg.add_widget(dxml.get_widget("fcoeNicCombo")) while 1: rc = dialog.run() if rc == gtk.RESPONSE_CANCEL: break; iter = combo.get_active_iter() if iter is None: self.intf.messageWindow(_("Error"), "Must select a NIC to use.", type="warning", custom_icon="error") continue; try: self.storage.fcoe.addSan(store.get_value(iter, 1), self.intf) except IOError, e: self.intf.messageWindow(_("Error"), str(e)) rc = gtk.RESPONSE_CANCEL break dialog.destroy() return rc def addZfcpDrive(self): (dxml, dialog) = gui.getGladeWidget("zfcp-config.glade", "zfcpDialog") gui.addFrame(dialog) dialog.show_all() sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) map(lambda x: sg.add_widget(dxml.get_widget(x)), ("devnumEntry", "wwpnEntry", "fcplunEntry")) while 1: rc = dialog.run() if rc != gtk.RESPONSE_APPLY: break devnum = dxml.get_widget("devnumEntry").get_text().strip() wwpn = dxml.get_widget("wwpnEntry").get_text().strip() fcplun = dxml.get_widget("fcplunEntry").get_text().strip() try: self.storage.zfcp.addFCP(devnum, wwpn, fcplun) except ValueError, e: self.intf.messageWindow(_("Error"), str(e)) continue break dialog.destroy() return rc def addDrive(self, button): (dxml, dialog) = gui.getGladeWidget("adddrive.glade", "addDriveDialog") gui.addFrame(dialog) dialog.show_all() if not iutil.isS390(): dxml.get_widget("zfcpRadio").hide() dxml.get_widget("zfcpRadio").set_group(None) if not iscsi.has_iscsi(): dxml.get_widget("iscsiRadio").set_sensitive(False) dxml.get_widget("iscsiRadio").set_active(False) if not fcoe.has_fcoe(): dxml.get_widget("fcoeRadio").set_sensitive(False) dxml.get_widget("fcoeRadio").set_active(False) #figure out what advanced devices we have available and set sensible default group = dxml.get_widget("iscsiRadio").get_group() for button in group: if button is not None and button.get_property("sensitive"): button.set_active(True) break rc = dialog.run() dialog.hide() if rc == gtk.RESPONSE_CANCEL: return if dxml.get_widget("iscsiRadio").get_active() and iscsi.has_iscsi(): rc = self.addIscsiDrive() elif dxml.get_widget("fcoeRadio").get_active() and fcoe.has_fcoe(): rc = self.addFcoeDrive() elif dxml.get_widget("zfcpRadio") is not None and dxml.get_widget("zfcpRadio").get_active(): rc = self.addZfcpDrive() dialog.destroy() if rc != gtk.RESPONSE_CANCEL: w = self.intf.waitWindow(_("Rescanning disks"), _("Rescanning disks")) self.storage.reset() createAllowedDrivesStore(self.storage.disks, self.storage.clearPartDisks, self.drivelist, disallowDrives=[self.anaconda.updateSrc]) self.anaconda.id.bootloader.updateDriveList() self._fillBootStore() w.pop() def _fillBootStore(self): bootstore = self.bootcombo.get_model() bootstore.clear() if len(self.anaconda.id.bootloader.drivelist) > 0: defaultBoot = self.anaconda.id.bootloader.drivelist[0] else: defaultBoot = None for disk in self.storage.disks: if disk.name not in self.anaconda.id.bootloader.drivelist: continue dispstr = "%s %8.0f MB %s" %(disk.name, disk.size, disk.description) i = bootstore.append(None) bootstore[i] = (dispstr, disk.name) if disk.name == defaultBoot: self.bootcombo.set_active_iter(i) if len(bootstore) <= 1: self.bootcombo.set_sensitive(False) def getScreen(self, anaconda): self.anaconda = anaconda self.storage = anaconda.id.storage self.intf = anaconda.intf self.dispatch = anaconda.dispatch (self.xml, vbox) = gui.getGladeWidget("autopart.glade", "parttypeBox") # make some labels bold... map(lambda l: l and l.set_markup("%s" %(l.get_text(),)), map(lambda x: self.xml.get_widget(x),("selectLabel", "bootLabel"))) gui.widgetExpander(self.xml.get_widget("mainlabel")) self.combo = self.xml.get_widget("partitionTypeCombo") gui.widgetExpander(self.combo) cell = gtk.CellRendererText() self.combo.pack_start(cell, True) self.combo.set_attributes(cell, text = 0) cell.set_property("wrap-width", 495) self.combo.set_size_request(500, -1) store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_INT) self.combo.set_model(store) opts = ((_("Use entire drive"), CLEARPART_TYPE_ALL), (_("Replace existing Linux system"), CLEARPART_TYPE_LINUX), (_("Shrink current system"), -2), (_("Use free space"), CLEARPART_TYPE_NONE), (_("Create custom layout"), -1)) # if not set in ks, use UI default if self.storage.clearPartType is None: preselected = CLEARPART_TYPE_LINUX else: preselected = self.storage.clearPartType for (txt, val) in opts: iter = store.append(None) store[iter] = (txt, val) if val == preselected: self.combo.set_active_iter(iter) if ((self.combo.get_active() == -1) or self.dispatch.stepInSkipList("autopartitionexecute")): self.combo.set_active(len(opts) - 1) # yeah, it's a hack self.drivelist = createAllowedDrivesList(self.storage.disks, self.storage.clearPartDisks, disallowDrives=[self.anaconda.updateSrc]) self.drivelist.set_size_request(375, 80) self.xml.get_widget("driveScroll").add(self.drivelist) self.bootcombo = self.xml.get_widget("bootDriveCombo") thecell = gtk.CellRendererText() self.bootcombo.pack_start(thecell, True) bootstore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING) self.bootcombo.set_model(bootstore) self._fillBootStore() self.prevrev = None self.review = not self.dispatch.stepInSkipList("partition") self.xml.get_widget("reviewButton").set_active(self.review) self.xml.get_widget("encryptButton").set_active(self.storage.encryptedAutoPart) active = self.combo.get_active_iter() val = self.combo.get_model().get_value(active, 1) # -1 is the combo box choice for 'create custom layout' if val == -1: # make sure reviewButton is active and not sensitive if self.prevrev == None: self.prevrev = self.xml.get_widget("reviewButton").get_active() self.xml.get_widget("reviewButton").set_active(True) self.xml.get_widget("reviewButton").set_sensitive(False) self.xml.get_widget("driveScroll").set_sensitive(False) self.xml.get_widget("bootDriveCombo").set_sensitive(False) self.xml.get_widget("encryptButton").set_sensitive(False) if not iutil.isS390() and not iscsi.has_iscsi() and not fcoe.has_fcoe(): self.xml.get_widget("addButton").set_sensitive(False) sigs = { "on_partitionTypeCombo_changed": self.comboChanged, "on_addButton_clicked": self.addDrive } self.xml.signal_autoconnect(sigs) return vbox