Package pyanaconda :: Package iw :: Module partition_gui
[hide private]
[frames] | no frames]

Source Code for Module pyanaconda.iw.partition_gui

   1  # 
   2  # partition_gui.py: allows the user to choose how to partition their disks 
   3  # 
   4  # Copyright (C) 2001, 2002  Red Hat, Inc.  All rights reserved. 
   5  # 
   6  # This program is free software; you can redistribute it and/or modify 
   7  # it under the terms of the GNU General Public License as published by 
   8  # the Free Software Foundation; either version 2 of the License, or 
   9  # (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, 
  12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  14  # GNU General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU General Public License 
  17  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  18  # 
  19  # Author(s): Matt Wilson <msw@redhat.com> 
  20  #            Michael Fulbright <msf@redhat.com> 
  21  # 
  22   
  23  import os 
  24  import gobject 
  25  import gtk 
  26  import gtk.glade 
  27  try: 
  28      import gnomecanvas 
  29  except ImportError: 
  30      import gnome.canvas as gnomecanvas 
  31  import pango 
  32  from pyanaconda import gui 
  33  import parted 
  34  import string 
  35  import types 
  36  import copy 
  37  from decimal import Decimal 
  38   
  39  from pyanaconda import storage 
  40  from iw_gui import * 
  41  from pyanaconda.flags import flags 
  42   
  43  import datacombo 
  44  import lvm_dialog_gui as l_d_g 
  45  import raid_dialog_gui as r_d_g 
  46  import partition_dialog_gui as p_d_g 
  47   
  48  from pyanaconda.partIntfHelpers import * 
  49  from pyanaconda.constants import * 
  50  from partition_ui_helpers_gui import * 
  51  from pyanaconda.storage.partitioning import doPartitioning 
  52  from pyanaconda.storage.devicelibs import lvm 
  53  from pyanaconda.storage.devices import devicePathToName 
  54  from pyanaconda.storage.devices import PartitionDevice 
  55  from pyanaconda.storage.devices import BTRFSVolumeDevice 
  56  from pyanaconda.storage.devices import deviceNameToDiskByPath 
  57  from pyanaconda.storage.errors import DeviceNotFoundError 
  58   
  59  import gettext 
  60  _ = lambda x: gettext.ldgettext("anaconda", x) 
  61  P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) 
  62   
  63  import logging 
  64  log = logging.getLogger("anaconda") 
  65   
  66  STRIPE_HEIGHT = 35.0 
  67  LOGICAL_INSET = 3.0 
  68  TREE_SPACING = 2 
  69   
  70  # XXX hack but will work for now 
  71  if gtk.gdk.screen_width() > 640: 
  72      CANVAS_WIDTH = 490 
  73  else: 
  74      CANVAS_WIDTH = 390 
  75  CANVAS_HEIGHT = 200 
  76   
  77  MODE_ADD = 1 
  78  MODE_EDIT = 2 
79 80 -class Slice:
81 """Class representing a slice of a stripe. 82 83 parent -- the stripe that the slice belongs too. 84 text -- what will appear in the slice 85 type -- either SLICE or SUBSLICE 86 xoffset -- start percentage 87 xlength -- a length percentage 88 dcCB -- function that is called on a double click. 89 cCB -- function that is called when one click (selected) 90 sel_col -- color when selected 91 unsel_col -- color when unselected 92 obj -- some python object that is related to this slice. 93 selected -- initial state of slice. 94 """ 95 SLICE = 0 96 SUBSLICE = 1 97 CONTAINERSLICE = 2 98
99 - def __init__(self, parent, text, type, xoffset, xlength, dcCB=lambda: None, 100 cCB=lambda x: None, sel_col="cornsilk1", unsel_col="white", 101 obj = None, selected = False):
102 self.text = text 103 self.type = type 104 self.xoffset = xoffset 105 self.xlength = xlength 106 self.parent = parent 107 self.dcCB = dcCB 108 self.cCB = cCB 109 self.sel_col = sel_col 110 self.unsel_col = unsel_col 111 self.obj = obj 112 self.selected = selected
113
114 - def eventHandler(self, widget, event):
115 if event.type == gtk.gdk.BUTTON_PRESS: 116 if event.button == 1: 117 self.select() 118 self.cCB(self.obj) 119 elif event.type == gtk.gdk._2BUTTON_PRESS: 120 #self.select() 121 self.dcCB() 122 123 return True
124
125 - def putOnCanvas(self):
126 pgroup = self.parent.getGroup() 127 self.group = pgroup.add(gnomecanvas.CanvasGroup) 128 self.box = self.group.add(gnomecanvas.CanvasRect) 129 self.group.connect("event", self.eventHandler) 130 canvas_text = self.group.add(gnomecanvas.CanvasText, 131 font="sans", size_points=8) 132 133 xoffset = self.xoffset * CANVAS_WIDTH 134 xlength = self.xlength * CANVAS_WIDTH 135 136 if self.type == Slice.SUBSLICE: 137 yoffset = 0.0 + LOGICAL_INSET 138 yheight = STRIPE_HEIGHT - (LOGICAL_INSET * 2) 139 texty = 0.0 140 else: 141 yoffset = 0.0 142 yheight = STRIPE_HEIGHT 143 texty = LOGICAL_INSET 144 145 if self.selected: 146 fill_color = self.sel_col 147 else: 148 fill_color = self.unsel_col 149 150 self.group.set(x=xoffset, y=yoffset) 151 self.box.set(x1=0.0, y1=0.0, x2=xlength, 152 y2=yheight, fill_color=fill_color, 153 outline_color='black', width_units=1.0) 154 canvas_text.set(x=2.0, y=texty + 2.0, text=self.text, 155 fill_color='black', 156 anchor=gtk.ANCHOR_NW, clip=True, 157 clip_width=xlength-1, clip_height=yheight-1)
158
159 - def shutDown(self):
160 self.parent = None 161 if self.group: 162 self.group.destroy() 163 self.group = None
164
165 - def select(self):
166 for slice in self.parent.slices: 167 slice.deselect() 168 self.selected = True 169 170 if self.group and self.box: 171 if self.type != Slice.CONTAINERSLICE: 172 self.group.raise_to_top() 173 self.box.set(outline_color="red") 174 self.box.set(fill_color=self.sel_col)
175
176 - def deselect(self):
177 self.selected = False 178 if self.box: 179 self.box.set(outline_color="black", fill_color=self.unsel_col)
180
181 -class Stripe(object):
182 """ 183 canvas -- the canvas where everything goes 184 text -- the text that will appear on top of the stripe 185 yoff -- its the position in the y axis where this stripe should be drawn 186 dcCB -- function that should be called on a double click 187 obj -- some python object that is related to this stripe 188 189 """
190 - def __init__(self, canvas, text, dcCB, obj = None):
191 self.canvas_text = None 192 self.canvas = canvas 193 self.text = text 194 self.group = None 195 self._slices = [] 196 self.dcCB = dcCB 197 self.selected = None 198 self.obj = obj
199
200 - def putOnCanvas(self, yoff):
201 """ 202 returns the yposition after drawhing this stripe. 203 204 """ 205 # We set the text for the stripe. 206 self.canvas_text = self.canvas.root().add(gnomecanvas.CanvasText, 207 x=0.0, y=yoff, font="sans", size_points=9) 208 self.canvas_text.set(text=self.text, fill_color='black', 209 anchor=gtk.ANCHOR_NW, weight=pango.WEIGHT_BOLD) 210 211 (xxx1, yyy1, xxx2, yyy2) = self.canvas_text.get_bounds() 212 textheight = yyy2 - yyy1 + 2 213 self.group = self.canvas.root().add(gnomecanvas.CanvasGroup, 214 x=0, y=yoff+textheight) 215 216 self.group.add(gnomecanvas.CanvasRect, x1=0.0, y1=0.0, x2=CANVAS_WIDTH, 217 y2=STRIPE_HEIGHT, fill_color='green', 218 outline_color='grey71', width_units=1.0) 219 self.group.lower_to_bottom() 220 221 # We paint all the container slices first. So the contained slices 222 # actually show up. 223 for slice in [s for s in self.slices if s.type == Slice.CONTAINERSLICE]: 224 slice.putOnCanvas() 225 # After painting the containers we paint the rest. 226 for slice in [s for s in self.slices if s.type != Slice.CONTAINERSLICE]: 227 slice.putOnCanvas() 228 229 # 10 is a separator space. 230 return yoff + STRIPE_HEIGHT+textheight+10
231
232 - def shutDown(self):
233 for slice in self.slices: 234 slice.shutDown() 235 self._slices = [] 236 237 if self.canvas_text: 238 self.canvas_text.destroy() 239 240 if self.group: 241 self.group.destroy() 242 self.group = None
243
244 - def getGroup(self):
245 return self.group
246 247 @property
248 - def slices(self):
249 return self._slices
250
251 - def addSlice(self, new_slice):
252 # check to see if they overlap. 253 for slice in self.slices: 254 # Container slices and subslices can overlap. 255 if new_slice.type+slice.type == Slice.CONTAINERSLICE+Slice.SUBSLICE: 256 continue 257 258 if new_slice.xoffset > slice.xoffset \ 259 and new_slice.xoffset < slice.xoffset + slice.xlength: 260 # there is a colission, we cannot add. 261 return 262 263 self._slices.append(new_slice)
264
265 - def getSelectedSlice(self):
266 for slice in self.slices: 267 if slice.selected: 268 return slice 269 return None
270
271 -class StripeGraph:
272 """ This class will only handle one stripe.""" 273 274 __canvas = None
275 - def __init__(self):
276 self.stripe = None 277 self.next_ypos = 0.0
278
279 - def __del__(self):
280 self.shutDown()
281
282 - def shutDown(self):
283 if self.stripe: 284 self.stripe.shutDown() 285 self.stripe = None 286 287 self.next_ypos = 0.0
288 289 @classmethod
290 - def getCanvas(cls):
291 if not StripeGraph.__canvas: 292 StripeGraph.__canvas = gnomecanvas.Canvas() 293 return StripeGraph.__canvas
294
295 - def setDisplayed(self, obj):
296 # Check to see if we already have the correct obj displayed. 297 if self.getDisplayed() and self.getDisplayed().obj == obj: 298 return 299 300 if self.stripe: 301 self.stripe.shutDown() 302 303 self.stripe = self._createStripe(obj) 304 self.stripe.putOnCanvas(0) 305 306 # Trying to center the picture. 307 apply(self.getCanvas().set_scroll_region, self.getCanvas().root().get_bounds())
308
309 - def getDisplayed(self):
310 return self.stripe
311
312 - def selectSliceFromObj(self, obj):
313 """Search for obj in the slices """ 314 stripe = self.getDisplayed() 315 if not stripe: 316 return 317 318 for slice in stripe.slices: 319 # There is a part object in each slice. 320 if not slice.obj: 321 continue 322 323 if obj == slice.obj and not slice.selected: 324 slice.select() 325 break
326
327 - def _createStripe(self, obj):
328 #This method needs to be overridden 329 pass
330
331 - def getSelectedSlice(self):
332 return self.stripe.getSelectedSlice()
333
334 335 -class DiskStripeGraph(StripeGraph):
336 """Handles the creation of a bar view for the 'normal' devies. 337 338 storage -- the storage object 339 340 cCB -- call back function used when the user clicks on a slice. This function 341 is passed a device object when its executed. 342 dcCB -- call back function used when the user double clicks on a slice. 343 drive -- drive to display 344 """
345 - def __init__(self, storage, drive=None, cCB=lambda x:None, dcCB=lambda:None):
346 StripeGraph.__init__(self) 347 self.storage = storage 348 self.cCB = cCB 349 self.dcCB = dcCB 350 # Define the default colors per partition type. 351 self.part_type_colors = \ 352 {"sel_logical": "cornsilk1", "unsel_logical": "white", 353 "sel_extended": "cornsilk1", "unsel_extended": "white", 354 "sel_normal": "cornsilk1", "unsel_normal": "white", 355 "sel_freespace": "grey88", "unsel_freespace": "grey88"} 356 if drive: 357 self.setDisplayed(drive)
358
359 - def _createStripe(self, drive):
360 # Create the stripe 361 drivetext = _("Drive %(drive)s (%(size)-0.f MB) (Model: %(model)s)") \ 362 % {'drive': drive.path, 363 'size': drive.size, 364 'model': drive.model} 365 stripe = Stripe(self.getCanvas(), drivetext, self.dcCB, obj = drive) 366 367 # Create the slices. 368 369 # These offsets are where the partition/slices end. 0<offset<1 370 for part in drive.format.partedDisk.getFreeSpacePartitions() \ 371 + [d for d in drive.format.partitions]: 372 if part.getSize(unit="MB") <= 1.0 or \ 373 part.type & parted.PARTITION_METADATA: 374 continue 375 376 # Create the start and length for the slice. 377 xoffset = (Decimal(str(part.geometry.start)) 378 / Decimal(str(drive.partedDevice.length))) 379 xlength = (Decimal(str(part.geometry.length)) 380 / Decimal(str(drive.partedDevice.length))) 381 382 if part.type & parted.PARTITION_LOGICAL: 383 if part.type & parted.PARTITION_FREESPACE: 384 name = _("Free") 385 unsel_col = self.part_type_colors["unsel_freespace"] 386 sel_col = self.part_type_colors["sel_freespace"] 387 else: 388 name = part.path 389 unsel_col = self.part_type_colors["unsel_logical"] 390 sel_col = self.part_type_colors["sel_logical"] 391 392 partstr = "%s\n%.0f MB" % (name, float(part.getSize())) 393 stype = Slice.SUBSLICE 394 395 elif part.type & parted.PARTITION_FREESPACE: 396 partstr = "%s\n%.0f MB" % (_("Free"), float(part.getSize())) 397 stype = Slice.SLICE 398 unsel_col = self.part_type_colors["unsel_freespace"] 399 sel_col = self.part_type_colors["sel_freespace"] 400 401 elif part.type & parted.PARTITION_EXTENDED: 402 partstr = "" 403 stype = Slice.CONTAINERSLICE 404 unsel_col = self.part_type_colors["unsel_extended"] 405 sel_col = self.part_type_colors["sel_extended"] 406 407 else: 408 partstr = "%s\n%.0f MB" % (part.path, float(part.getSize())) 409 stype = Slice.SLICE 410 unsel_col = self.part_type_colors["unsel_normal"] 411 sel_col = self.part_type_colors["sel_normal"] 412 413 # We need to use the self.storage objects not the partedDisk ones. 414 # The free space has not storage object. 415 if part.type != parted.PARTITION_FREESPACE: 416 partName = devicePathToName(part.getDeviceNodeName()) 417 o_part = self.storage.devicetree.getDeviceByName(partName) 418 else: 419 o_part = None 420 421 slice = Slice(stripe, partstr, stype, xoffset, xlength, 422 dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col, 423 unsel_col = unsel_col, obj = o_part) 424 stripe.addSlice(slice) 425 426 return stripe
427
428 -class LVMStripeGraph(StripeGraph):
429 """ 430 storage -- the storage object 431 432 cCB -- call back function used when the user clicks on a slice. This function 433 is passed a device object when its executed. 434 dcCB -- call back function used when the user double clicks on a slice. 435 vg -- volume group to display 436 """
437 - def __init__(self, storage, vg=None, cCB=lambda x:None, dcCB=lambda:None):
438 StripeGraph.__init__(self) 439 self.storage = storage 440 self.cCB = cCB 441 self.dcCB = dcCB 442 # Define the default colors per partition type. 443 self.part_type_colors = \ 444 {"sel_lv": "cornsilk1", "unsel_lv": "white", 445 "sel_freespace": "grey88", "unsel_freespace": "grey88"} 446 if vg: 447 self.setDisplayed(vg)
448
449 - def _createStripe(self, vg):
450 # Create the stripe 451 vgtext = _("LVM Volume Group %(vgName)s (%(vgSize)-0.f MB)") % {"vgName": vg.name, "vgSize": vg.size} 452 stripe = Stripe(self.getCanvas(), vgtext, self.dcCB, obj = vg) 453 454 # Create the slices. 455 # Since se don't have a start and length like in the partitions, we 456 # put all the LVs next to each other and put the free space at the end. 457 curr_offset = Decimal(0) 458 for lv in vg.lvs: 459 lvstr = "%s\n%.0f MB" % (lv.name, float(lv.size)) 460 stype = Slice.SLICE 461 sel_col = self.part_type_colors["sel_lv"] 462 unsel_col = self.part_type_colors["unsel_lv"] 463 464 #xoffset = float(curr_offset) / float(vg.size) 465 xoffset = curr_offset 466 xlength = Decimal(str(lv.size)) / Decimal(str(vg.size)) 467 468 slice = Slice(stripe, lvstr, stype, xoffset, xlength, 469 dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col, 470 unsel_col = unsel_col, obj = lv) 471 stripe.addSlice(slice) 472 473 curr_offset += xlength 474 475 # We add the free space if there is any space left. 476 if curr_offset < 1: 477 #freestr = _("Free") 478 stype = Slice.SLICE 479 sel_col = self.part_type_colors["sel_freespace"] 480 unsel_col = self.part_type_colors["unsel_freespace"] 481 482 xoffset = curr_offset 483 xlength = Decimal(1 - curr_offset) 484 485 # with the xlength we give an approximate size 486 freestr = "%s\n%.0f MB" % (_("Free"), Decimal(str(vg.size)) * xlength) 487 488 # We append no object. 489 slice = Slice(stripe, freestr, stype, xoffset, xlength, 490 dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col, 491 unsel_col = unsel_col) 492 493 stripe.addSlice(slice) 494 495 return stripe
496
497 -class MDStripeGraph(StripeGraph):
498 desc = "MD" 499 """ 500 storage -- the storage object 501 502 cCB -- call back function used when the user clicks on a slice. This function 503 is passed a device object when its executed. 504 dcCB -- call back function used when the user double clicks on a slice. 505 md -- md device to display. 506 """
507 - def __init__(self, storage, device=None, cCB=lambda x:None, dcCB=lambda:None):
508 StripeGraph.__init__(self) 509 self.storage = storage 510 self.cCB = cCB 511 self.dcCB = dcCB 512 self.part_type_colors = \ 513 {"sel_md": "cornsilk1", "unsel_md": "white"} 514 if device: 515 self.setDisplayed(device)
516
517 - def _get_text(self, md):
518 return (_("%(desc)s %(mdPath)s (%(mdSize)-0.f MB)") 519 % {"mdPath": md.path, "mdSize": md.size, "desc": self.desc})
520
521 - def _createStripe(self, md):
522 mdtext = self._get_text(md) 523 stripe = Stripe(self.getCanvas(), mdtext, self.dcCB, obj = md) 524 525 # Since we can't really create subslices with md devices we will only 526 # show the md device size in the bar. 527 mdstr = "%s\n%.0f MB" % (md.path, float(md.size)) 528 stype = Slice.SLICE 529 sel_col = self.part_type_colors["sel_md"] 530 unsel_col = self.part_type_colors["unsel_md"] 531 xoffset = 0 532 xlength = 1 533 534 slice = Slice(stripe, mdstr, stype, xoffset, xlength, 535 dcCB = self.dcCB, cCB = self.cCB, sel_col = sel_col, 536 unsel_col = unsel_col, obj = md) 537 stripe.addSlice(slice) 538 539 return stripe
540
541 -class MDRaidArrayStripeGraph(MDStripeGraph):
542 desc = "MD RAID Array"
543
544 -class BTRFSStripeGraph(MDStripeGraph):
545 desc = "BTRFS Pool" 546
547 - def _get_text(self, md):
548 return (_("%(desc)s %(mdUUID)s (%(mdSize)-0.f MB)") 549 % {"mdUUID": md.uuid, "mdSize": md.size, "desc": self.desc})
550
551 -class MessageGraph:
552 - def __init__(self, canvas, message):
553 self.canvas = canvas 554 self.message = message 555 self.canvas_text = None
556
557 - def display(self):
558 if self.canvas_text != None: 559 # This means that its already displayed. 560 return 561 562 self.canvas_text = self.canvas.root().add(gnomecanvas.CanvasText, 563 x=0.0, y=20, font="sans", size_points=16) 564 self.canvas_text.set(text=self.message, fill_color='black', 565 anchor=gtk.ANCHOR_CENTER, weight=pango.WEIGHT_BOLD) 566 567 # Trying to center the picture. 568 apply(self.canvas.set_scroll_region, self.canvas.root().get_bounds())
569
570 - def destroy(self):
571 if self.canvas_text: 572 self.canvas_text.destroy() 573 self.canvas_text = None
574
575 -class DiskTreeModelHelper:
576 - def __init__(self, model, columns, iter):
577 self.model = model 578 self.iter = iter 579 self.columns = columns
580
581 - def __getitem__(self, key):
582 if type(key) == types.StringType: 583 key = self.columns[key] 584 try: 585 return self.model.get_value(self.iter, key) 586 except Exception: 587 # FIXME: what exceptions might actually get raised here? 588 return None
589
590 - def __setitem__(self, key, value):
591 if type(key) == types.StringType: 592 key = self.columns[key] 593 self.model.set_value(self.iter, key, value)
594
595 -class DiskTreeModel(gtk.TreeStore):
596 isLeaf = -3 597 isFormattable = -2 598 599 # format: column header, type, x alignment, hide?, visibleKey 600 titles = ((N_("Device"), gobject.TYPE_STRING, 0.0, 0, 0), 601 (N_("Label"), gobject.TYPE_STRING, 0.0, 1, 0), 602 (N_("Size (MB)"), gobject.TYPE_STRING, 1.0, 0, 0), 603 (N_("Mount Point"), gobject.TYPE_STRING, 0.0, 0, isLeaf), 604 (N_("Type"), gobject.TYPE_STRING, 0.0, 0, 0), 605 (N_("Format"), gobject.TYPE_OBJECT, 0.5, 0, isFormattable), 606 ("", gobject.TYPE_STRING, 0.0, 0, 0), 607 # the following must be the last two 608 ("IsLeaf", gobject.TYPE_BOOLEAN, 0.0, 1, 0), 609 ("IsFormattable", gobject.TYPE_BOOLEAN, 0.0, 1, 0), 610 ("PyObject", gobject.TYPE_PYOBJECT, 0.0, 1, 0)) 611
612 - def __init__(self):
613 self.hiddenPartitions = [] 614 self.titleSlot = {} 615 i = 0 616 types = [self] 617 self.columns = [] 618 for title, kind, alignment, hide, key in self.titles: 619 self.titleSlot[title] = i 620 types.append(kind) 621 if hide: 622 i += 1 623 continue 624 elif kind == gobject.TYPE_OBJECT: 625 renderer = gtk.CellRendererPixbuf() 626 propertyMapping = {'pixbuf': i} 627 elif kind == gobject.TYPE_BOOLEAN: 628 renderer = gtk.CellRendererToggle() 629 propertyMapping = {'active': i} 630 elif (kind == gobject.TYPE_STRING or 631 kind == gobject.TYPE_INT): 632 renderer = gtk.CellRendererText() 633 propertyMapping = {'markup': i} 634 635 # wire in the cells that we want only visible on leaf nodes to 636 # the special leaf node column. 637 if key < 0: 638 propertyMapping['visible'] = len(self.titles) + key 639 640 renderer.set_property('xalign', alignment) 641 if title == "Mount Point": 642 title = _("Mount Point/\nRAID/Volume") 643 elif title == "Size (MB)": 644 title = _("Size\n(MB)") 645 elif title != "": 646 title = _(title) 647 col = apply(gtk.TreeViewColumn, (title, renderer), 648 propertyMapping) 649 col.set_alignment(0.5) 650 if kind == gobject.TYPE_STRING or kind == gobject.TYPE_INT: 651 col.set_property('sizing', gtk.TREE_VIEW_COLUMN_AUTOSIZE) 652 self.columns.append(col) 653 i += 1 654 655 apply(gtk.TreeStore.__init__, types) 656 657 self.view = gtk.TreeView(self) 658 # append all of the columns 659 map(self.view.append_column, self.columns)
660
661 - def getTreeView(self):
662 return self.view
663
664 - def selectRowFromObj(self, obj, iter=None):
665 """Find the row in the tree containing obj and select it. 666 667 obj -- the object that we are searching 668 iter -- an iter from the tree. If None, get the first one. 669 670 Returns the iter where obj was found. None otherwise. 671 """ 672 retval = None 673 r_obj = None 674 #FIXME: watch out for hidden rows. 675 676 if not iter: 677 iter = self.get_iter_first() 678 679 while iter: 680 # r_obj -> (row object) 681 r_obj = self[iter]["PyObject"] 682 683 if obj and r_obj == obj: 684 # We have fond our object, select this row and break. 685 selection = self.view.get_selection() 686 if selection is not None: 687 selection.unselect_all() 688 selection.select_iter(iter) 689 690 # Make sure the tree view shows what we have selected. 691 path = self.get_path(iter) 692 col = self.view.get_column(0) 693 self.view.set_cursor(path, col, False) 694 self.view.scroll_to_cell(path, col, True, 0.5, 0.5) 695 retval = iter 696 break 697 698 if self.iter_has_child(iter): 699 # Call recursively if row has children. 700 rv = self.selectRowFromObj(obj, iter=self.iter_children(iter)) 701 if rv != None: 702 retval = rv 703 break 704 705 iter = self.iter_next(iter) 706 707 return iter
708
709 - def getCurrentDevice(self):
710 """ Return the device representing the current selection, 711 None otherwise. 712 """ 713 selection = self.view.get_selection() 714 model, iter = selection.get_selected() 715 if not iter: 716 return None 717 718 return model[iter]['PyObject']
719
720 - def getCurrentDeviceParent(self):
721 """ Return the parent of the selected row. Returns an iter. 722 None if there is no parent. 723 """ 724 selection = self.view.get_selection() 725 model, iter = selection.get_selected() 726 if not iter: 727 return None 728 729 return model.iter_parent(iter)
730
731 - def resetSelection(self):
732 pass
733
734 - def clear(self):
735 selection = self.view.get_selection() 736 if selection is not None: 737 selection.unselect_all() 738 gtk.TreeStore.clear(self)
739
740 - def __getitem__(self, iter):
741 if type(iter) == gtk.TreeIter: 742 return DiskTreeModelHelper(self, self.titleSlot, iter) 743 raise KeyError, iter
744
745 746 -class PartitionWindow(InstallWindow):
747 - def __init__(self, ics):
748 InstallWindow.__init__(self, ics) 749 ics.setTitle(_("Partitioning")) 750 ics.setNextEnabled(True) 751 self.parent = ics.getICW().window
752
753 - def quit(self):
754 pass
755
756 - def presentPartitioningComments(self,title, labelstr1, labelstr2, comments, 757 type="ok", custom_buttons=None):
758 759 if flags.autostep: 760 return 1 761 762 win = gtk.Dialog(title) 763 gui.addFrame(win) 764 765 if type == "ok": 766 win.add_button('gtk-ok', 1) 767 defaultchoice = 0 768 elif type == "yesno": 769 win.add_button('gtk-no', 2) 770 win.add_button('gtk-yes', 1) 771 defaultchoice = 1 772 elif type == "continue": 773 win.add_button('gtk-cancel', 0) 774 win.add_button(_("Continue"), 1) 775 defaultchoice = 1 776 elif type == "custom": 777 rid=0 778 779 for button in custom_buttons: 780 widget = win.add_button(button, rid) 781 rid = rid + 1 782 783 defaultchoice = rid - 1 784 785 image = gtk.Image() 786 image.set_from_stock('gtk-dialog-warning', gtk.ICON_SIZE_DIALOG) 787 hbox = gtk.HBox(False, 9) 788 al=gtk.Alignment(0.0, 0.0) 789 al.add(image) 790 hbox.pack_start(al, False) 791 792 buffer = gtk.TextBuffer(None) 793 buffer.set_text(comments) 794 text = gtk.TextView() 795 text.set_buffer(buffer) 796 text.set_property("editable", False) 797 text.set_property("cursor_visible", False) 798 text.set_wrap_mode(gtk.WRAP_WORD) 799 800 sw = gtk.ScrolledWindow() 801 sw.add(text) 802 sw.set_size_request(400, 200) 803 sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 804 sw.set_shadow_type(gtk.SHADOW_IN) 805 806 info1 = gtk.Label(labelstr1) 807 info1.set_line_wrap(True) 808 info1.set_size_request(400, -1) 809 810 info2 = gtk.Label(labelstr2) 811 info2.set_line_wrap(True) 812 info2.set_size_request(400, -1) 813 814 vbox = gtk.VBox(False, 9) 815 816 al=gtk.Alignment(0.0, 0.0) 817 al.add(info1) 818 vbox.pack_start(al, False) 819 820 vbox.pack_start(sw, True, True) 821 822 al=gtk.Alignment(0.0, 0.0) 823 al.add(info2) 824 vbox.pack_start(al, True) 825 826 hbox.pack_start(vbox, True, True) 827 828 win.vbox.pack_start(hbox) 829 win.set_position(gtk.WIN_POS_CENTER) 830 win.set_default_response(defaultchoice) 831 win.show_all() 832 rc = win.run() 833 win.destroy() 834 return rc
835
836 - def getNext(self):
837 (errors, warnings) = self.storage.sanityCheck() 838 if errors: 839 labelstr1 = _("The partitioning scheme you requested " 840 "caused the following critical errors.") 841 labelstr2 = _("You must correct these errors before " 842 "you continue your installation of " 843 "%s.") % (productName,) 844 845 commentstr = string.join(errors, "\n\n") 846 847 self.presentPartitioningComments(_("Partitioning Errors"), 848 labelstr1, labelstr2, 849 commentstr, type="ok") 850 raise gui.StayOnScreen 851 852 if warnings: 853 # "storage configuration" 854 labelstr1 = _("The partitioning scheme you requested " 855 "generated the following warnings.") 856 labelstr2 = _("Would you like to continue with " 857 "your requested partitioning " 858 "scheme?") 859 860 commentstr = string.join(warnings, "\n\n") 861 rc = self.presentPartitioningComments(_("Partitioning Warnings"), 862 labelstr1, labelstr2, 863 commentstr, 864 type="yesno") 865 if rc != 1: 866 raise gui.StayOnScreen 867 868 formatWarnings = getPreExistFormatWarnings(self.storage) 869 if formatWarnings: 870 labelstr1 = _("The following pre-existing devices have been " 871 "selected to be formatted, destroying all data.") 872 873 # labelstr2 = _("Select 'Yes' to continue and format these " 874 # "partitions, or 'No' to go back and change these " 875 # "settings.") 876 labelstr2 = "" 877 commentstr = "" 878 for (dev, type, mntpt) in formatWarnings: 879 commentstr = commentstr + \ 880 "%s %s %s\n" % (dev,type,mntpt) 881 882 rc = self.presentPartitioningComments(_("Format Warnings"), 883 labelstr1, labelstr2, 884 commentstr, 885 type="custom", 886 custom_buttons=["gtk-cancel", 887 _("_Format")]) 888 if rc != 1: 889 raise gui.StayOnScreen 890 891 self.stripeGraph.shutDown() 892 self.tree.clear() 893 del self.parent 894 return None
895
896 - def getPrev(self):
897 self.stripeGraph.shutDown() 898 self.tree.clear() 899 del self.parent 900 return None
901
902 - def addDevice(self, device, treeiter):
903 if device.format.hidden: 904 return 905 906 if device.format.type == "luks": 907 # we'll want to grab format info from the mapped 908 # device, not the encrypted one 909 try: 910 dm_dev = self.storage.devicetree.getChildren(device)[0] 911 except IndexError: 912 format = device.format 913 else: 914 format = dm_dev.format 915 else: 916 format = device.format 917 918 # icon for the format column 919 if device.format.type == "luks" and not device.format.exists: 920 # we're creating the LUKS header 921 format_icon = self.lock_pixbuf 922 elif not format.exists: 923 # we're creating a format on the device 924 format_icon = self.checkmark_pixbuf 925 else: 926 format_icon = None 927 928 # mount point string 929 if format.type == "lvmpv": 930 vg = None 931 for _vg in self.storage.vgs: 932 if _vg.dependsOn(device): 933 vg = _vg 934 break 935 936 mnt_str = getattr(vg, "name", "") 937 elif format.type == "mdmember": 938 array = None 939 for _array in self.storage.mdarrays: 940 if _array.dependsOn(device): 941 array = _array 942 break 943 944 mnt_str = getattr(array, "name", "") 945 elif format.type == "btrfs" and not isinstance(device, BTRFSVolumeDevice): 946 btrfs_dev = self.storage.devicetree.getChildren(device)[0] 947 mnt_str = btrfs_dev.name 948 else: 949 mnt_str = getattr(format, "mountpoint", "") 950 if mnt_str is None: 951 mnt_str = "" 952 953 isleaf = True 954 955 # device name 956 name_str = getattr(device, "lvname", device.name) 957 958 # label 959 label_str = getattr(format, "label", "") 960 if label_str is None: 961 label_str = "" 962 963 self.tree[treeiter]['Device'] = name_str 964 self.tree[treeiter]['Size (MB)'] = "%Ld" % device.size 965 self.tree[treeiter]['PyObject'] = device 966 self.tree[treeiter]['IsFormattable'] = format.formattable 967 self.tree[treeiter]['Format'] = format_icon 968 self.tree[treeiter]['Mount Point'] = mnt_str 969 self.tree[treeiter]['IsLeaf'] = isleaf 970 self.tree[treeiter]['Type'] = format.name 971 self.tree[treeiter]['Label'] = label_str 972 973 # XXX can this move up one level? 974 if isinstance(device, BTRFSVolumeDevice): 975 # list subvolumes as children of the main volume 976 for s in device.subvolumes: 977 log.debug("%r" % s.format) 978 isleaf = False 979 if s.format.exists: 980 sub_format_icon = None 981 else: 982 sub_format_icon = self.checkmark_pixbuf 983 subvol_iter = self.tree.append(treeiter) 984 self.tree[subvol_iter]['Device'] = s.name 985 self.tree[subvol_iter]['PyObject'] = s 986 self.tree[subvol_iter]['IsFormattable'] = True 987 self.tree[subvol_iter]['Format'] = sub_format_icon 988 self.tree[subvol_iter]['Mount Point'] = s.format.mountpoint 989 self.tree[subvol_iter]['Type'] = s.type 990 self.tree[subvol_iter]['IsLeaf'] = True
991 992
993 - def populate(self, initial = 0):
994 self.tree.resetSelection() 995 996 # first do LVM 997 vgs = self.storage.vgs 998 if vgs: 999 lvmparent = self.tree.append(None) 1000 self.tree[lvmparent]['Device'] = _("LVM Volume Groups") 1001 for vg in vgs: 1002 vgparent = self.tree.append(lvmparent) 1003 self.addDevice(vg, vgparent) 1004 self.tree[vgparent]['Type'] = "" 1005 for lv in vg.lvs: 1006 iter = self.tree.append(vgparent) 1007 self.addDevice(lv, iter) 1008 1009 # We add a row for the VG free space. 1010 if vg.freeSpace > 0: 1011 iter = self.tree.append(vgparent) 1012 self.tree[iter]['Device'] = _("Free") 1013 self.tree[iter]['Size (MB)'] = str(vg.freeSpace) 1014 self.tree[iter]['PyObject'] = None 1015 self.tree[iter]['Mount Point'] = "" 1016 self.tree[iter]['IsLeaf'] = True 1017 1018 # handle RAID next 1019 mdarrays = self.storage.mdarrays 1020 if mdarrays: 1021 raidparent = self.tree.append(None) 1022 self.tree[raidparent]['Device'] = _("RAID Devices") 1023 for array in mdarrays: 1024 iter = self.tree.append(raidparent) 1025 self.addDevice(array, iter) 1026 name = "%s <span size=\"small\" color=\"gray\">(%s)</span>" % \ 1027 (array.name, array.path) 1028 self.tree[iter]['Device'] = name 1029 1030 # BTRFS volumes 1031 btrfs_devs = self.storage.btrfsVolumes 1032 if btrfs_devs: 1033 btrfsparent = self.tree.append(None) 1034 self.tree[btrfsparent]['Device'] = _("BTRFS Volumes") 1035 for dev in btrfs_devs: 1036 iter = self.tree.append(btrfsparent) 1037 self.addDevice(dev, iter) 1038 1039 # now normal partitions 1040 disks = self.storage.partitioned 1041 # also include unpartitioned disks that aren't mpath or biosraid 1042 whole = filter(lambda d: not d.partitioned and not d.format.hidden, 1043 self.storage.disks) 1044 disks.extend(whole) 1045 disks.sort(key=lambda d: d.name) 1046 drvparent = self.tree.append(None) 1047 self.tree[drvparent]['Device'] = _("Hard Drives") 1048 for disk in disks: 1049 # add a parent node to the tree 1050 parent = self.tree.append(drvparent) 1051 1052 self.tree[parent]['PyObject'] = disk 1053 if disk.partitioned: 1054 part = disk.format.firstPartition 1055 extendedParent = None 1056 while part: 1057 if part.type & parted.PARTITION_METADATA: 1058 part = part.nextPartition() 1059 continue 1060 1061 partName = devicePathToName(part.getDeviceNodeName()) 1062 device = self.storage.devicetree.getDeviceByName(partName) 1063 if not device and not part.type & parted.PARTITION_FREESPACE: 1064 log.debug("can't find partition %s in device" 1065 " tree" % partName) 1066 1067 # ignore any free space region that is less than the 1068 # grain size of the disklabel alignment we are using 1069 if part.type & parted.PARTITION_FREESPACE: 1070 min_length = disk.format.alignment.grainSize 1071 if part.type & parted.PARTITION_LOGICAL: 1072 # ignored free regions in the extended can be up 1073 # to twice the alignment grain size, to account 1074 # for logical partition metadata 1075 min_length *= 2 1076 1077 if part.geometry.length < min_length: 1078 part = part.nextPartition() 1079 continue 1080 1081 if device and device.isExtended: 1082 if extendedParent: 1083 raise RuntimeError, ("can't handle more than " 1084 "one extended partition per disk") 1085 extendedParent = self.tree.append(parent) 1086 iter = extendedParent 1087 elif part.type & parted.PARTITION_LOGICAL: 1088 if not extendedParent: 1089 raise RuntimeError, ("crossed logical partition " 1090 "before extended") 1091 iter = self.tree.append(extendedParent) 1092 else: 1093 iter = self.tree.append(parent) 1094 1095 if device and not device.isExtended: 1096 self.addDevice(device, iter) 1097 else: 1098 # either extended or freespace 1099 if part.type & parted.PARTITION_FREESPACE: 1100 devstring = _("Free") 1101 ptype = "" 1102 else: 1103 devstring = partName 1104 ptype = _("Extended") 1105 1106 self.tree[iter]['Device'] = devstring 1107 self.tree[iter]['Type'] = ptype 1108 size = part.getSize(unit="MB") 1109 if size < 1.0: 1110 sizestr = "< 1" 1111 else: 1112 sizestr = "%Ld" % (size) 1113 self.tree[iter]['Size (MB)'] = sizestr 1114 self.tree[iter]['PyObject'] = device 1115 1116 part = part.nextPartition() 1117 else: 1118 # whole-disk formatting 1119 self.addDevice(disk, parent) 1120 1121 ident = None 1122 try: 1123 if disk.type == "dasd" or disk.type == "zfcp": 1124 ident = deviceNameToDiskByPath(disk.name) 1125 if ident.startswith("/dev/disk/by-path/"): 1126 ident = os.path.basename(ident) 1127 elif disk.type == "dm-multipath": 1128 ident = disk.wwid 1129 except DeviceNotFoundError: 1130 ident = None 1131 1132 if not ident: 1133 ident = disk.path 1134 1135 # Insert a '\n' when device string is too long. Usually when it 1136 # contains '/dev/mapper'. First column should be around 20 chars. 1137 if len(disk.name) + len(ident) > 20: 1138 separator = "\n" 1139 else: 1140 separator= " " 1141 self.tree[parent]['Device'] = \ 1142 "%s%s<span size=\"small\" color=\"gray\">(%s)</span>" \ 1143 % (disk.name, separator, ident) 1144 1145 self.treeView.expand_all() 1146 self.messageGraph.display()
1147
1148 - def barviewActivateCB(self):
1149 """ Should be called when we double click on a slice""" 1150 # This is a bit of a hack to make the double click on free space work. 1151 # This function is useful when the selected slice is a free space, 1152 # in any other case it calls self.treeActiveCB. 1153 1154 # We first see if the double click was from a free space or from another 1155 # slice. 1156 sel_slice = self.stripeGraph.getSelectedSlice() 1157 1158 if sel_slice == None: 1159 # This really should not happen. Do nothing. 1160 return 1161 1162 # The selected slice is a free slice if the object contained in it is 1163 # None. 1164 if sel_slice.obj != None: 1165 # This is not a free slice, we should call treeActivateCB 1166 return self.treeActivateCB() 1167 else: 1168 # Display a create window according to the stripe object. 1169 # Get the device from the stripe.obj 1170 disp_stripe = self.stripeGraph.getDisplayed() 1171 if disp_stripe == None: 1172 # this should not happen 1173 return 1174 1175 # Display a create dialog. 1176 stripe_dev = disp_stripe.obj 1177 if stripe_dev.partitioned: 1178 tempformat = self.storage.defaultFSType 1179 device = self.storage.newPartition(fmt_type=tempformat) 1180 self.editPartition(device, isNew = True) 1181 1182 elif isinstance(stripe_dev, storage.LVMVolumeGroupDevice): 1183 self.editLVMLogicalVolume(vg = stripe_dev) 1184 return
1185
1186 - def treeActivateCB(self, *args):
1187 curr_dev = self.tree.getCurrentDevice() 1188 if isinstance(curr_dev, storage.PartitionDevice) and \ 1189 not curr_dev.isExtended: 1190 self.editCB() 1191 1192 elif isinstance(curr_dev, storage.LVMLogicalVolumeDevice) \ 1193 or isinstance(curr_dev, storage.LVMVolumeGroupDevice) \ 1194 or isinstance(curr_dev, storage.MDRaidArrayDevice): 1195 self.editCB() 1196 1197 elif curr_dev == None: 1198 # Its probably a free space 1199 iparent = self.tree.getCurrentDeviceParent() 1200 if iparent == None: 1201 # it was not free space, it is a root row. 1202 return 1203 1204 # We execute a create function given the type of parent that was 1205 # found. 1206 # FIXME: This code might repeat itself. might be a good idea to 1207 # put it in a function. 1208 curr_parent = self.tree[iparent]["PyObject"] 1209 if curr_parent.partitioned: 1210 tempformat = self.storage.defaultFSType 1211 device = self.storage.newPartition(fmt_type=tempformat) 1212 self.editPartition(device, isNew = True) 1213 1214 elif isinstance(curr_parent, storage.LVMVolumeGroupDevice): 1215 self.editLVMLogicalVolume(vg = curr_parent) 1216 return
1217
1218 - def treeSelectCB(self, selection, *args):
1219 # The edit and create buttons will be enabled if the user has chosen 1220 # something editable and/or deletable. 1221 self.deleteButton.set_sensitive(False) 1222 self.editButton.set_sensitive(False) 1223 1224 # I have no idea why this iter might be None. Its best to return 1225 # without any action. 1226 model, iter = selection.get_selected() 1227 if not iter: 1228 return 1229 1230 # If we return because there is no parent, make sure we show the user 1231 # the infoGraph and no stripeGraph. The 'create' and 'delete' buttons 1232 # will be deactivated. 1233 iparent = model.iter_parent(iter) 1234 if not iparent: 1235 self.stripeGraph.shutDown() 1236 self.messageGraph.display() 1237 return # This is a root row. 1238 1239 # We destroy the message first. We will make sure to repaint it later 1240 # if no stipe is displayed. Can't destroy it at the end of this func 1241 # because it uncenters the created stripe, if any. 1242 self.messageGraph.destroy() 1243 1244 device = model[iter]['PyObject'] 1245 1246 # See if we need to change what is in the canvas. In all possibilities 1247 # we must make sure we have the correct StripeGraph class. 1248 if not device: 1249 # This is free space. 1250 parent = self.tree[iparent]["PyObject"] 1251 if parent.partitioned: 1252 if not isinstance(self.stripeGraph, DiskStripeGraph): 1253 self.stripeGraph.shutDown() 1254 self.stripeGraph = DiskStripeGraph(self.storage, 1255 drive = parent, cCB = self.tree.selectRowFromObj, 1256 dcCB = self.barviewActivateCB) 1257 self.stripeGraph.setDisplayed(parent) 1258 1259 elif isinstance(parent, storage.LVMVolumeGroupDevice): 1260 if not isinstance(self.stripeGraph, LVMStripeGraph): 1261 self.stripeGraph.shutDown() 1262 self.stripeGraph = LVMStripeGraph(self.storage, 1263 vg = parent, cCB = self.tree.selectRowFromObj, 1264 dcCB = self.barviewActivateCB) 1265 self.stripeGraph.setDisplayed(parent) 1266 1267 elif device.partitioned: 1268 if not isinstance(self.stripeGraph, DiskStripeGraph): 1269 self.stripeGraph.shutDown() 1270 self.stripeGraph = DiskStripeGraph(self.storage, 1271 drive = device, 1272 cCB = self.tree.selectRowFromObj, 1273 dcCB = self.barviewActivateCB) 1274 self.stripeGraph.setDisplayed(device) 1275 # this is deletable but not editable. 1276 self.deleteButton.set_sensitive(True) 1277 1278 elif isinstance(device, storage.PartitionDevice): 1279 if not isinstance(self.stripeGraph, DiskStripeGraph): 1280 self.stripeGraph.shutDown() 1281 self.stripeGraph = DiskStripeGraph(self.storage, 1282 drive = device.parents[0], 1283 cCB = self.tree.selectRowFromObj, 1284 dcCB = self.barviewActivateCB) 1285 self.stripeGraph.setDisplayed(device.parents[0]) 1286 self.stripeGraph.selectSliceFromObj(device) 1287 self.deleteButton.set_sensitive(True) 1288 if not device.isExtended: 1289 self.editButton.set_sensitive(True) 1290 1291 elif isinstance(device, storage.LVMVolumeGroupDevice): 1292 if not isinstance(self.stripeGraph, LVMStripeGraph): 1293 self.stripeGraph.shutDown() 1294 self.stripeGraph = LVMStripeGraph(self.storage, vg = device, 1295 cCB = self.tree.selectRowFromObj, 1296 dcCB = self.barviewActivateCB) 1297 self.stripeGraph.setDisplayed(device) 1298 self.deleteButton.set_sensitive(True) 1299 self.editButton.set_sensitive(True) 1300 1301 elif isinstance(device, storage.LVMLogicalVolumeDevice): 1302 if not isinstance(self.stripeGraph, LVMStripeGraph): 1303 self.stripeGraph.shutDown() 1304 self.stripeGraph = LVMStripeGraph(self.storage, vg = device.vg, 1305 cCB = self.tree.selectRowFromObj, 1306 dcCB = self.barviewActivateCB) 1307 self.stripeGraph.setDisplayed(device.vg) 1308 self.stripeGraph.selectSliceFromObj(device) 1309 self.deleteButton.set_sensitive(True) 1310 self.editButton.set_sensitive(True) 1311 1312 elif isinstance(device, storage.MDRaidArrayDevice): 1313 if not isinstance(self.stripeGraph, MDRaidArrayStripeGraph): 1314 self.stripeGraph.shutDown() 1315 self.stripeGraph = MDRaidArrayStripeGraph(self.storage, 1316 device = device, 1317 cCB = self.tree.selectRowFromObj, 1318 dcCB = self.barviewActivateCB) 1319 self.stripeGraph.setDisplayed(device) 1320 self.deleteButton.set_sensitive(True) 1321 self.editButton.set_sensitive(True) 1322 1323 elif isinstance(device, storage.BTRFSDevice): 1324 # BTRFSDevice can be edited but not explicitly deleted. It is 1325 # deleted when its last member device is removed. 1326 if not isinstance(self.stripeGraph, BTRFSStripeGraph): 1327 self.stripeGraph.shutDown() 1328 self.stripeGraph = BTRFSStripeGraph(self.storage, 1329 device = device, 1330 cCB = self.tree.selectRowFromObj, 1331 dcCB = self.barviewActivateCB) 1332 self.stripeGraph.setDisplayed(device) 1333 self.deleteButton.set_sensitive(False) 1334 self.editButton.set_sensitive(True) 1335 1336 else: 1337 # This means that the user selected something that is not showable 1338 # in the bar view. Just show the information message. 1339 self.stripeGraph.shutDown() 1340 self.messageGraph.display() 1341 self.deleteButton.set_sensitive(False) 1342 self.editButton.set_sensitive(False)
1343
1344 - def deleteCB(self, widget):
1345 """ Right now we can say that if the device is partitioned we 1346 want to delete all of the devices it contains. At some point 1347 we will want to support creation and removal of partitionable 1348 devices. This will need some work when that time comes. 1349 """ 1350 device = self.tree.getCurrentDevice() 1351 if device.partitioned: 1352 if doClearPartitionedDevice(self.intf, 1353 self.storage, 1354 device): 1355 self.refresh() 1356 elif doDeleteDevice(self.intf, 1357 self.storage, 1358 device): 1359 if isinstance(device, storage.devices.PartitionDevice): 1360 justRedraw = False 1361 else: 1362 justRedraw = True 1363 if device.type == "lvmlv" and device in device.vg.lvs: 1364 device.vg._removeLogVol(device) 1365 1366 self.refresh(justRedraw=justRedraw)
1367
1368 - def createCB(self, *args):
1369 # First we must decide what parts of the create_storage_dialog 1370 # we will activate. 1371 1372 activate_create_partition = True 1373 1374 # We activate the create Volume Group radio button if there is a free 1375 # partition with a Physical Volume format. 1376 activate_create_vg = False 1377 availpvs = len(self.storage.unusedPVs()) 1378 if (lvm.has_lvm() 1379 and getFormat("lvmpv").supported 1380 and availpvs > 0): 1381 activate_create_vg = True 1382 1383 # We activate the create RAID dev if there are partitions that have 1384 # raid format and are not related to any raid dev. 1385 activate_create_raid_dev = False 1386 availraidparts = len(self.storage.unusedMDMembers()) 1387 availminors = self.storage.unusedMDMinors 1388 if (len(availminors) > 0 1389 and getFormat("software RAID").supported 1390 and availraidparts > 1): 1391 activate_create_raid_dev = True 1392 1393 # Must check if all the possibilities are False. In this case tell the 1394 # user that he can't create anything and the reasons. 1395 if (not activate_create_partition 1396 and not activate_create_vg 1397 and not activate_create_raid_dev): 1398 self.intf.messageWindow(_("Cannot perform any creation action"), 1399 _("Note that the creation action requires one of the " 1400 "following:\n\n" 1401 "* Free space in one of the Hard Drives.\n" 1402 "* At least two free Software RAID partitions.\n" 1403 "* At least one free physical volume (LVM) partition.\n" 1404 "* At least one Volume Group with free space."), 1405 custom_icon="warning") 1406 return 1407 1408 # We will activate the create lv button when we have a VG to put the 1409 # LVs on. 1410 activate_create_lv = False 1411 vgs_with_free_space = [] 1412 for vg in self.storage.vgs: 1413 if vg.freeSpace > 0: 1414 vgs_with_free_space.append(vg) 1415 if len(vgs_with_free_space) > 0: 1416 activate_create_lv = True 1417 1418 # GTK crap starts here. 1419 create_storage_xml = gtk.glade.XML( 1420 gui.findGladeFile("create-storage.glade"), domain="anaconda") 1421 self.dialog = create_storage_xml.get_widget("create_storage_dialog") 1422 1423 # Activate the partition radio buttons if needed. 1424 # sp_rb -> standard partition 1425 sp_rb = create_storage_xml.get_widget("create_storage_rb_standard_part") 1426 # lp_rb -> lvm partition (physical volume) 1427 lp_rb = create_storage_xml.get_widget("create_storage_rb_lvm_part") 1428 # rp_rb -> RAID partition 1429 rp_rb = create_storage_xml.get_widget("create_storage_rb_raid_part") 1430 if activate_create_partition: 1431 sp_rb.set_sensitive(True) 1432 lp_rb.set_sensitive(True) 1433 rp_rb.set_sensitive(True) 1434 1435 # Activate the Volume Group radio buttons if needed. 1436 # vg_rb -> Volume Group 1437 vg_rb = create_storage_xml.get_widget("create_storage_rb_lvm_vg") 1438 if activate_create_vg: 1439 vg_rb.set_sensitive(True) 1440 1441 # Activate the Logical Volume radio button if needed. 1442 # We must also take care to control the combo box. 1443 lv_rb = create_storage_xml.get_widget("create_storage_rb_lvm_lv") 1444 if activate_create_lv: 1445 # The combobox will be visible if the radio button is active. 1446 # The combobox will be sensitive when the radio button is active. 1447 def toggle_vg_cb_CB(button, vg_cb, selected_dev): 1448 if button.get_active(): 1449 vg_cb.set_sensitive(True) 1450 1451 # We set the VG to whatever the user has chosen in the tree 1452 # view. We will fall back on the first item on the list if 1453 # there is no chosen VG. 1454 if selected_dev and selected_dev.name \ 1455 and vg_cb.set_active_text(selected_dev.name): 1456 # if set_active is True, we don't need to do anything else 1457 pass 1458 else: 1459 vg_cb.set_active_text(vgs_with_free_space[0].name) 1460 1461 else: 1462 vg_cb.set_sensitive(False)
1463 1464 vg_cb_st = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) 1465 vg_cb = datacombo.DataComboBox(store = vg_cb_st) 1466 vg_cb.set_sensitive(False) 1467 1468 for vg in vgs_with_free_space: 1469 # FIXME: the name length might be a problem. 1470 vg_cb.append(vg.name, vg) 1471 lv_hb = create_storage_xml.get_widget("create_storage_hb_lvm_lv") 1472 lv_hb.pack_start(vg_cb) 1473 1474 lv_rb.set_sensitive(True) 1475 selected_dev = self.tree.getCurrentDevice() 1476 lv_rb.connect("toggled", toggle_vg_cb_CB, vg_cb, selected_dev) 1477 1478 # Activate the RAID dev if needed. 1479 # rd_rb -> RAID device 1480 rd_rb = create_storage_xml.get_widget("create_storage_rb_raid_dev") 1481 if activate_create_raid_dev: 1482 rd_rb.set_sensitive(True) 1483 1484 # Before drawing lets select the first radio button that is sensitive: 1485 # How can I get sensitivity from gtk.radiobutton? 1486 if activate_create_partition: 1487 sp_rb.set_active(True) 1488 sp_rb.grab_focus() 1489 elif activate_create_vg: 1490 vg_rb.set_active(True) 1491 vg_rb.grab_focus() 1492 elif activate_create_raid_dev: 1493 rd_rb.set_active(True) 1494 rd_rb.grab_focus() 1495 1496 gui.addFrame(self.dialog) 1497 self.dialog.show_all() 1498 1499 # Lets work the information messages with CB 1500 # The RAID info message 1501 rinfo_button = create_storage_xml.get_widget("create_storage_info_raid") 1502 whatis_r = _("Software RAID allows you to combine several disks into " 1503 "a larger RAID device. A RAID device can be configured " 1504 "to provide additional speed and reliability compared " 1505 "to using an individual drive. For more information on " 1506 "using RAID devices please consult the %s " 1507 "documentation.\n") % (productName,) 1508 whatneed_r = _("To use RAID you must first create at least two " 1509 "partitions of type 'software RAID'. Then you can create a " 1510 "RAID device that can be formatted and mounted.\n\n") 1511 whathave_r = P_( 1512 "You currently have %d software RAID partition free to use.", 1513 "You currently have %d software RAID partitions free to use.", 1514 availraidparts) % (availraidparts,) 1515 rinfo_message = "%s\n%s%s" % (whatis_r, whatneed_r, whathave_r) 1516 rinfo_cb = lambda x : self.intf.messageWindow(_("About RAID"), 1517 rinfo_message, custom_icon="information") 1518 rinfo_button.connect("clicked", rinfo_cb) 1519 1520 # The LVM info message 1521 lvminfo_button = create_storage_xml.get_widget("create_storage_info_lvm") 1522 whatis_lvm = _("Logical Volume Manager (LVM) is a 3 level construct. " 1523 "The first level is made up of disks or partitions formatted with " 1524 "LVM metadata called Physical Volumes (PV). A Volume Group " 1525 "(VG) sits on top of one or more PVs. The VG, in turn, is the " 1526 "base to create one or more Logical Volumes (LV). Note that a " 1527 "VG can be an aggregate of PVs from multiple physical disks. For " 1528 "more information on using LVM please consult the %s " 1529 "documentation\n") % (productName, ) 1530 whatneed_lvm = _("To create a PV you need a partition with " 1531 "free space. To create a VG you need a PV that is not " 1532 "part of any existing VG. To create an LV you need a VG with " 1533 "free space.\n\n") 1534 whathave_lvm = P_("You currently have %d available PV free to use.\n", 1535 "You currently have %d available PVs free to use.\n", 1536 availpvs) % (availpvs, ) 1537 lvminfo_message = "%s\n%s%s" % (whatis_lvm, whatneed_lvm, whathave_lvm) 1538 lvminfo_cb = lambda x : self.intf.messageWindow(_("About LVM"), 1539 lvminfo_message, custom_icon="information") 1540 lvminfo_button.connect("clicked", lvminfo_cb) 1541 1542 dialog_rc = self.dialog.run() 1543 1544 # If Cancel was pressed 1545 if dialog_rc == 0: 1546 self.dialog.destroy() 1547 return 1548 1549 # If Create was pressed Make sure we do a dialog.destroy before 1550 # calling any other screen. We don't want the create dialog to show 1551 # in the back when we pop up other screens. 1552 if dialog_rc != 1: 1553 log.error("I received a dialog_rc != 1 (%d) witch should not " 1554 "happen" % dialog_rc) 1555 self.dialog.destroy() 1556 return 1557 1558 self.dialog.destroy() 1559 if rp_rb.get_active(): 1560 member = self.storage.newPartition(fmt_type="mdmember") 1561 self.editPartition(member, isNew = True, restrictfs=["mdmember"]) 1562 return 1563 1564 elif rd_rb.get_active(): 1565 array = self.storage.newMDArray(fmt_type=self.storage.defaultFSType) 1566 self.editRaidArray(array, isNew = True) 1567 return 1568 1569 elif lp_rb.get_active(): 1570 member = self.storage.newPartition(fmt_type="lvmpv") 1571 self.editPartition(member, isNew = True, restrictfs=["lvmpv"]) 1572 return 1573 1574 elif vg_rb.get_active(): 1575 tempvg = self.storage.newVG() 1576 self.editLVMVolumeGroup(tempvg, isNew = True) 1577 return 1578 1579 elif lv_rb.get_active(): 1580 selected_vg = vg_cb.get_active_value() 1581 self.editLVMLogicalVolume(vg = selected_vg) 1582 return 1583 1584 elif sp_rb.get_active(): 1585 tempformat = self.storage.defaultFSType 1586 device = self.storage.newPartition(fmt_type=tempformat) 1587 self.editPartition(device, isNew = True) 1588 return
1589
1590 - def resetCB(self, *args):
1591 if not confirmResetPartitionState(self.intf): 1592 return 1593 1594 self.stripeGraph.shutDown() 1595 # temporarily unset storage.config.clearPartType so that all devices 1596 # will be found during storage reset 1597 clearPartType = self.storage.config.clearPartType 1598 self.storage.config.clearPartType = None 1599 self.storage.reset() 1600 self.storage.config.clearPartType = clearPartType 1601 self.tree.clear() 1602 self.populate()
1603
1604 - def refresh(self, justRedraw=None):
1605 log.debug("refresh: justRedraw=%s" % justRedraw) 1606 self.stripeGraph.shutDown() 1607 self.tree.clear() 1608 1609 if justRedraw: 1610 rc = 0 1611 else: 1612 try: 1613 doPartitioning(self.storage) 1614 rc = 0 1615 except PartitioningError as msg: 1616 self.intf.messageWindow(_("Error Partitioning"), 1617 _("Could not allocate requested partitions: %s.") % (msg), 1618 custom_icon="error") 1619 rc = -1 1620 except PartitioningWarning as msg: 1621 # XXX somebody other than me should make this look better 1622 # XXX this doesn't handle the 'delete /boot partition spec' case 1623 # (it says 'add anyway') 1624 dialog = gtk.MessageDialog(self.parent, 0, gtk.MESSAGE_WARNING, 1625 gtk.BUTTONS_NONE, 1626 _("Warning: %s.") % (msg)) 1627 gui.addFrame(dialog) 1628 button = gtk.Button(_("_Modify Partition")) 1629 dialog.add_action_widget(button, 1) 1630 button = gtk.Button(_("_Continue")) 1631 dialog.add_action_widget(button, 2) 1632 dialog.set_position(gtk.WIN_POS_CENTER) 1633 1634 dialog.show_all() 1635 rc = dialog.run() 1636 dialog.destroy() 1637 1638 if rc == 1: 1639 rc = -1 1640 else: 1641 rc = 0 1642 all_devices = self.storage.devicetree.devices 1643 bootDevs = [d for d in all_devices if d.bootable] 1644 #if reqs: 1645 # for req in reqs: 1646 # req.ignoreBootConstraints = 1 1647 1648 if not rc == -1: 1649 self.populate() 1650 1651 return rc
1652
1653 - def editCB(self, *args):
1654 device = self.tree.getCurrentDevice() 1655 reason = self.storage.deviceImmutable(device, ignoreProtected=True) 1656 if reason: 1657 self.intf.messageWindow(_("Unable To Edit"), 1658 _("You cannot edit this device:\n\n%s") 1659 % reason, 1660 custom_icon="error") 1661 return 1662 1663 if device.type == "mdarray": 1664 self.editRaidArray(device) 1665 elif device.type == "lvmvg": 1666 self.editLVMVolumeGroup(device) 1667 elif device.type == "lvmlv": 1668 self.editLVMLogicalVolume(lv = device) 1669 elif isinstance(device, storage.devices.PartitionDevice): 1670 self.editPartition(device)
1671 1672 # isNew implies that this request has never been successfully used before
1673 - def editRaidArray(self, raiddev, isNew = False):
1674 # r_d_g -> raid_dialog_gui 1675 raideditor = r_d_g.RaidEditor(self.storage, self.intf, self.parent, 1676 raiddev, isNew) 1677 1678 while True: 1679 actions = raideditor.run() 1680 1681 for action in actions: 1682 # FIXME: this needs to handle exceptions 1683 self.storage.devicetree.registerAction(action) 1684 1685 if self.refresh(justRedraw=True): 1686 actions.reverse() 1687 for action in actions: 1688 self.storage.devicetree.cancelAction(action) 1689 if self.refresh(): 1690 raise RuntimeError, ("Returning partitions to state " 1691 "prior to RAID edit failed") 1692 continue 1693 else: 1694 break 1695 1696 raideditor.destroy()
1697 1698
1699 - def editPartition(self, device, isNew = False, restrictfs = None):
1700 # p_d_g -> partition_dialog_gui 1701 parteditor = p_d_g.PartitionEditor(self.anaconda, self.parent, device, 1702 isNew = isNew, restrictfs = restrictfs) 1703 1704 while True: 1705 orig_device = copy.copy(device) 1706 actions = parteditor.run() 1707 1708 for action in actions: 1709 # XXX we should handle exceptions here 1710 self.anaconda.storage.devicetree.registerAction(action) 1711 1712 if self.refresh(justRedraw=not actions): 1713 # autopart failed -- cancel the actions and try to get 1714 # back to previous state 1715 actions.reverse() 1716 for action in actions: 1717 self.anaconda.storage.devicetree.cancelAction(action) 1718 1719 # FIXME: proper action/device management would be better 1720 if not isNew: 1721 device.req_size = orig_device.req_size 1722 device.req_base_size = orig_device.req_base_size 1723 device.req_grow = orig_device.req_grow 1724 device.req_max_size = orig_device.req_max_size 1725 device.req_primary = orig_device.req_primary 1726 device.req_disks = orig_device.req_disks 1727 1728 if self.refresh(): 1729 # this worked before and doesn't now... 1730 raise RuntimeError, ("Returning partitions to state " 1731 "prior to edit failed") 1732 else: 1733 break 1734 1735 parteditor.destroy() 1736 return 1
1737
1738 - def editLVMVolumeGroup(self, device, isNew = False):
1739 # l_d_g -> lvm_dialog_gui 1740 vgeditor = l_d_g.VolumeGroupEditor(self.anaconda, self.intf, self.parent, 1741 device, isNew) 1742 1743 while True: 1744 actions = vgeditor.run() 1745 1746 for action in actions: 1747 # FIXME: handle exceptions 1748 self.storage.devicetree.registerAction(action) 1749 1750 if self.refresh(justRedraw=True): 1751 actions.reverse() 1752 for action in actions: 1753 self.storage.devicetree.cancelAction(action) 1754 1755 if self.refresh(): 1756 raise RuntimeError, ("Returning partitions to state " 1757 "prior to edit failed") 1758 continue 1759 else: 1760 break 1761 1762 vgeditor.destroy()
1763
1764 - def editLVMLogicalVolume (self, lv = None, vg = None):
1765 """Will be consistent with the state of things and use this funciton 1766 for creating and editing LVs. 1767 1768 lv -- the logical volume to edit. If this is set there is no need 1769 for the other two arguments. 1770 vg -- the volume group where the new lv is going to be created. This 1771 will only be relevant when we are createing an LV. 1772 """ 1773 1774 if lv != None: 1775 # l_d_g -> lvm_dialog_gui 1776 vgeditor = l_d_g.VolumeGroupEditor(self.anaconda, self.intf, self.parent, 1777 lv.vg, isNew = False) 1778 lv = vgeditor.lvs[lv.lvname] 1779 isNew = False 1780 1781 elif vg != None: 1782 # l_d_g -> lvm_dialog_gui 1783 vgeditor = l_d_g.VolumeGroupEditor(self.anaconda, self.intf, self.parent, 1784 vg, isNew = False) 1785 tempvg = vgeditor.getTempVG() 1786 name = self.storage.suggestDeviceName(parent=tempvg, prefix="lv") 1787 format = getFormat(self.storage.defaultFSType) 1788 vgeditor.lvs[name] = {'name': name, 1789 'size': vg.freeSpace, 1790 'format': format, 1791 'originalFormat': format, 1792 'stripes': 1, 1793 'logSize': 0, 1794 'snapshotSpace': 0, 1795 'exists': False} 1796 lv = vgeditor.lvs[name] 1797 isNew = True 1798 1799 else: 1800 # This is non-sense. 1801 return 1802 1803 1804 while True: 1805 vgeditor.editLogicalVolume(lv, isNew = isNew) 1806 actions = vgeditor.convertToActions() 1807 1808 for action in actions: 1809 # FIXME: handle exceptions 1810 self.storage.devicetree.registerAction(action) 1811 1812 if self.refresh(justRedraw=True): 1813 actions.reverse() 1814 for action in actions: 1815 self.storage.devicetree.cancelAction(action) 1816 1817 if self.refresh(): 1818 raise RuntimeError, ("Returning partitions to state " 1819 "prior to edit failed") 1820 continue 1821 else: 1822 break 1823 1824 vgeditor.destroy()
1825
1826 - def getScreen(self, anaconda):
1827 self.anaconda = anaconda 1828 self.storage = anaconda.storage 1829 self.intf = anaconda.intf 1830 self.checkmark_pixbuf = gui.getPixbuf("checkMark.png") 1831 self.lock_pixbuf = gui.getPixbuf("gnome-lock.png") 1832 1833 checkForSwapNoMatch(anaconda) 1834 1835 # Beginning of the GTK stuff. 1836 # create the operational buttons 1837 buttonBox = gtk.HButtonBox() 1838 buttonBox.set_spacing(6) 1839 buttonBox.set_layout(gtk.BUTTONBOX_END) 1840 1841 ops = ((_("_Create"), self.createCB), 1842 (_("_Edit"), self.editCB), 1843 (_("_Delete"), self.deleteCB), 1844 (_("Re_set"), self.resetCB)) 1845 1846 for label, cb in ops: 1847 button = gtk.Button(label) 1848 buttonBox.add (button) 1849 button.connect ("clicked", cb) 1850 1851 # We need these to control their sensitivity. 1852 if label == _("_Edit"): 1853 self.editButton = button 1854 self.editButton.set_sensitive(False) 1855 elif label == _("_Delete"): 1856 self.deleteButton = button 1857 self.deleteButton.set_sensitive(False) 1858 1859 # Create the disk tree (Fills the tree and the Bar View) 1860 self.tree = DiskTreeModel() 1861 self.treeView = self.tree.getTreeView() 1862 self.treeView.connect('row-activated', self.treeActivateCB) 1863 self.treeViewSelection = self.treeView.get_selection() 1864 self.treeViewSelection.connect("changed", self.treeSelectCB) 1865 self.stripeGraph = StripeGraph() 1866 self.messageGraph = MessageGraph(self.stripeGraph.getCanvas(), 1867 _("Please Select A Device")) 1868 self.populate(initial = 1) 1869 1870 # Create the top scroll window 1871 # We don't actually need a *scroll* window but nuthing else worked. 1872 hadj = gtk.Adjustment(step_incr = 5.0) 1873 vadj = gtk.Adjustment(step_incr = 5.0) 1874 swt = gtk.ScrolledWindow(hadjustment = hadj, vadjustment = vadj) 1875 swt.add(self.stripeGraph.getCanvas()) 1876 swt.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 1877 swt.set_shadow_type(gtk.SHADOW_IN) 1878 1879 # Create the bottom scroll window 1880 swb = gtk.ScrolledWindow() 1881 swb.add(self.treeView) 1882 swb.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 1883 swb.set_shadow_type(gtk.SHADOW_IN) 1884 1885 # Create main vertical box and add everything. 1886 MVbox = gtk.VBox(False, 5) 1887 MVbox.pack_start(swt, False, False) 1888 MVbox.pack_start(swb, True) 1889 MVbox.pack_start(buttonBox, False, False) 1890 MVbox.pack_start(gtk.HSeparator(), False) 1891 1892 return MVbox
1893