summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Keating <jkeating@redhat.com>2012-08-13 16:06:05 -0700
committerJesse Keating <jkeating@redhat.com>2012-08-13 16:06:05 -0700
commit5a8b2eeca2d6c3a0ae872305508942feb6b47ec0 (patch)
tree630ba822419772d676e124d61948153cbb45442e
parent7a5776929775c3869ee912e53f4414405c52d59f (diff)
parent168051f182baa3f00c9bdb83ba47fbf85d3ce021 (diff)
downloadanaconda-5a8b2eeca2d6c3a0ae872305508942feb6b47ec0.tar.gz
anaconda-5a8b2eeca2d6c3a0ae872305508942feb6b47ec0.tar.xz
anaconda-5a8b2eeca2d6c3a0ae872305508942feb6b47ec0.zip
Merge master into newtui
Conflicts: pyanaconda/ui/gui/__init__.py pyanaconda/ui/gui/spokes/custom.py One last merge before pushing to master. The conflicts were due to more development happening on those files, the result is what I hope is a clean merge.
-rw-r--r--anaconda.spec.in64
-rw-r--r--configure.ac2
-rw-r--r--dracut/Makefile.am1
-rwxr-xr-xdracut/anaconda-ks-sendheaders.sh32
-rwxr-xr-xdracut/anaconda-lib.sh2
-rwxr-xr-xdracut/module-setup.sh1
-rwxr-xr-xdracut/parse-anaconda-kickstart.sh30
-rwxr-xr-xdracut/parse-kickstart2
-rw-r--r--po/anaconda.pot256
-rw-r--r--pyanaconda/bootloader.py6
-rw-r--r--pyanaconda/constants.py5
-rw-r--r--pyanaconda/installclass.py15
-rw-r--r--pyanaconda/installclasses/fedora.py4
-rw-r--r--pyanaconda/installclasses/rhel.py4
-rw-r--r--pyanaconda/kickstart.py18
-rw-r--r--pyanaconda/platform.py2
-rw-r--r--pyanaconda/storage/__init__.py523
-rw-r--r--pyanaconda/storage/devicelibs/btrfs.py30
-rw-r--r--pyanaconda/storage/devicelibs/lvm.py15
-rw-r--r--pyanaconda/storage/devicelibs/mdraid.py38
-rw-r--r--pyanaconda/storage/devices.py109
-rw-r--r--pyanaconda/storage/devicetree.py11
-rw-r--r--pyanaconda/storage/fcoe.py4
-rw-r--r--pyanaconda/storage/formats/__init__.py3
-rw-r--r--pyanaconda/storage/formats/biosboot.py4
-rw-r--r--pyanaconda/storage/formats/disklabel.py5
-rw-r--r--pyanaconda/storage/formats/fs.py22
-rw-r--r--pyanaconda/storage/formats/luks.py3
-rw-r--r--pyanaconda/storage/formats/lvmpv.py5
-rw-r--r--pyanaconda/storage/formats/mdraid.py3
-rw-r--r--pyanaconda/storage/formats/prepboot.py4
-rw-r--r--pyanaconda/storage/formats/swap.py7
-rw-r--r--pyanaconda/storage/iscsi.py26
-rw-r--r--pyanaconda/storage/partitioning.py239
-rw-r--r--pyanaconda/storage/size.py24
-rw-r--r--pyanaconda/storage/zfcp.py8
-rw-r--r--pyanaconda/ui/gui/__init__.py17
-rw-r--r--pyanaconda/ui/gui/spokes/custom.py341
-rw-r--r--pyanaconda/ui/gui/spokes/lib/accordion.py7
-rw-r--r--pyanaconda/ui/gui/spokes/storage.py18
40 files changed, 1239 insertions, 671 deletions
diff --git a/anaconda.spec.in b/anaconda.spec.in
index 7deffe3ec..ebde10d3d 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -293,6 +293,70 @@ update-desktop-database &> /dev/null || :
/usr/lib/dracut/modules.d/80%{name}/*
%changelog
+* Mon Aug 13 2012 Chris Lumens <clumens@redhat.com> - 18.4-1
+- dracut: fix inst.ks.sendmac (#826657) (wwoods)
+- dracut: suppress ks errors from missing %include (wwoods)
+- dracut: add comment to run_kickstart() (wwoods)
+- Remove unused writeKS methods. (clumens)
+- Only show unused devices that haven't been removed/deleted. (dlehman)
+- Don't unexpand already-expanded pages when trying to expand them again.
+ (dlehman)
+- Make parents of hidden devices appear to be leaves. (dlehman)
+- Remove the right device name from the lvm filter when unhiding device.
+ (dlehman)
+- Take configured filesystems into account when checking package space.
+ (dlehman)
+- Make sure the ksdata autopart type matches the storage one. (dlehman)
+- Base auto-generated name prefixes on productName, not device type. (dlehman)
+- Remove shrink code that was a workaround for the old ui flow. (dlehman)
+- Remove old ui progress args from devicelibs.btrfs. (dlehman)
+- Make sure we allocate partitions and grow lvm as needed in kickstart.
+ (dlehman)
+- Streamline autopart request setup slightly. (dlehman)
+- Make it possible to call setUpBootLoader safely at any time. (dlehman)
+- Move setup of new partition weight arg to Storage.newPartition. (dlehman)
+- Use a copy of the main Storage instance during custom partitioning. (dlehman)
+- Track requested sizes of btrfs subvols. (dlehman)
+- Add a method to retrieve a devicetree device by id number. (dlehman)
+- Fix DiskLabel so it can be deep-copied. (dlehman)
+- Add a method to produce a deep copy of a Storage instance. (dlehman)
+- Fix subtraction for Size. (dlehman)
+- Add support for creating device based on a top-down specification. (dlehman)
+- Add size-set managers to keep a set of growable requests in sync. (dlehman)
+- Add a function to estimate required disk space for an md array. (dlehman)
+- Add a method to estimate disk space needs for a new logical volume. (dlehman)
+- Add a convenience method for new btrfs subvols and drop subvol size args.
+ (dlehman)
+- Use the UEFI shim to load grub. (pjones)
+- Check that Gtk.main is not already running before starting another one
+ (vpodzime)
+- With tmux, we no longer need to start up a shell during VNC installs.
+ (clumens)
+- We no longer need getkeymaps, mapshdr, or readmap. (clumens)
+- Remove the last references to isysLoadKeymap. (clumens)
+- remove Security class (bcl)
+- replace lokkit for selinux settings (#815540) (bcl)
+- tests: Add tests for new SimpleConfigFile features (bcl)
+- tests: cleanup whitespace in simpleconfig_test.py (bcl)
+- simpleconfig: rewrite to better support commented config files (bcl)
+- If the anaconda process crashes, don't delete its window. (clumens)
+- On interactive installs, default the root account to locked. (clumens)
+- Make the keyboard layout test a big text area instead of a single line.
+ (clumens)
+- Remove our loadKeymap code from isys (vpodzime)
+- Replace system-config-keyboard with our methods using ksdata.keyboard
+ (vpodzime)
+- A little fix of newui -> master merge (iscsi offload devices) (rvykydal)
+- Require new version of python-meh (vpodzime)
+- Modify kernelPackages to select the right kernel for ARM systems. (dmarlin)
+- ARM: clean up the kernel selection to be consistent with the rest of the code
+ (dennis)
+- add command line option to set the arm platform. (dennis)
+- Add support to determine the ARM processor variety and select the correct
+ kernel to install. (dmarlin)
+- TODO list updates. (clumens)
+- Sent pot file updates to the master branch in transifex, not f17. (clumens)
+
* Fri Aug 03 2012 Chris Lumens <clumens@redhat.com> - 18.3-1
- New graphical user interface.
- Removed loader.
diff --git a/configure.ac b/configure.ac
index 530dd6778..4ed38adc6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,7 +20,7 @@
m4_define(python_required_version, 2.5)
AC_PREREQ([2.63])
-AC_INIT([anaconda], [18.3], [anaconda-devel-list@redhat.com])
+AC_INIT([anaconda], [18.4], [anaconda-devel-list@redhat.com])
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2])
AC_CONFIG_HEADERS([config.h])
diff --git a/dracut/Makefile.am b/dracut/Makefile.am
index 032476676..8c56bf3eb 100644
--- a/dracut/Makefile.am
+++ b/dracut/Makefile.am
@@ -29,6 +29,7 @@ dist_dracut_SCRIPTS = module-setup.sh \
kickstart-genrules.sh \
updates-genrules.sh \
anaconda-udevprop.sh \
+ anaconda-ks-sendheaders.sh \
anaconda-netroot.sh \
anaconda-diskroot \
anaconda-copy-ks.sh \
diff --git a/dracut/anaconda-ks-sendheaders.sh b/dracut/anaconda-ks-sendheaders.sh
new file mode 100755
index 000000000..aa178cea3
--- /dev/null
+++ b/dracut/anaconda-ks-sendheaders.sh
@@ -0,0 +1,32 @@
+#/bin/sh
+# anaconda-ks-sendheaders.sh - set various HTTP headers for kickstarting
+
+[ -f /tmp/.ks_sendheaders ] && return
+
+# inst.ks.sendmac: send MAC addresses in HTTP headers
+if getargbool 0 kssendmac inst.ks.sendmac; then
+ ifnum=0
+ for ifname in /sys/class/net/*; do
+ [ -e "$ifname/address" ] || continue
+ mac=$(cat $ifname/address)
+ ifname=${ifname#/sys/class/net/}
+ # TODO: might need to choose devices better
+ if [ "$ifname" != "lo" ] && [ -n "$mac" ]; then
+ # set_http_header is from url-lib.sh, sourced earlier
+ set_http_header "X-RHN-Provisioning-MAC-$ifnum" "$ifname $mac"
+ ifnum=$(($ifnum+1))
+ fi
+ done
+fi
+
+# inst.ks.sendsn: send system serial number in HTTP headers
+if getargbool 0 kssendsn inst.ks.sendsn; then
+ system_serial=$(cat /sys/class/dmi/id/product_serial 2>/dev/null)
+ if [ -z "$system_serial" ]; then
+ warn "inst.ks.sendsn: can't find system serial number"
+ else
+ set_http_header "X-System-Serial-Number" "$system_serial"
+ fi
+fi
+
+> /tmp/.ks_sendheaders
diff --git a/dracut/anaconda-lib.sh b/dracut/anaconda-lib.sh
index a045bab0f..98b80e6e2 100755
--- a/dracut/anaconda-lib.sh
+++ b/dracut/anaconda-lib.sh
@@ -183,6 +183,8 @@ parse_kickstart() {
#
# Really what we want to do here is just start over from the "cmdline"
# phase, but since we can't do that, we'll kind of fake it.
+#
+# XXX THIS IS KIND OF A GROSS HACK AND WE NEED A BETTER WAY TO DO IT
run_kickstart() {
local do_disk="" do_net=""
diff --git a/dracut/module-setup.sh b/dracut/module-setup.sh
index 39ecc58d3..78da00244 100755
--- a/dracut/module-setup.sh
+++ b/dracut/module-setup.sh
@@ -26,6 +26,7 @@ install() {
inst_hook pre-udev 40 "$moddir/kickstart-genrules.sh"
inst_hook pre-udev 40 "$moddir/updates-genrules.sh"
inst_hook pre-trigger 40 "$moddir/anaconda-udevprop.sh"
+ inst_hook initqueue/settled 00 "$moddir/anaconda-ks-sendheaders.sh"
inst_hook initqueue/online 80 "$moddir/anaconda-netroot.sh"
inst "$moddir/anaconda-diskroot" "/sbin/anaconda-diskroot"
inst_hook pre-pivot 99 "$moddir/anaconda-copy-ks.sh"
diff --git a/dracut/parse-anaconda-kickstart.sh b/dracut/parse-anaconda-kickstart.sh
index afc3d6356..d445e2751 100755
--- a/dracut/parse-anaconda-kickstart.sh
+++ b/dracut/parse-anaconda-kickstart.sh
@@ -31,33 +31,3 @@ case "${kickstart%%:*}" in
;;
esac
export kickstart
-
-# inst.ks.sendmac: send MAC addresses in HTTP headers
-set_ks_sendmac() {
- local ifnum=0 mac="" ifname=""
- for ifname in /sys/class/net/*; do
- [ -e "$ifname/address" ] || continue
- mac=$(cat $ifname/address)
- ifname=${ifname#/sys/class/net/}
- # TODO: might need to choose devices better
- if [ "$ifname" != "lo" ] && [ -n "$mac" ]; then
- set_http_header "X-RHN-Provisioning-MAC-$ifnum" "$ifname $mac"
- ifnum=$(($ifnum+1))
- fi
- done
-}
-if getargbool 0 kssendmac inst.ks.sendmac; then
- # needs to run in mainloop (after modules load etc.)
- initqueue --unique --settled --onetime --name "01-sendmac" set_ks_sendmac
-fi
-
-# inst.ks.sendsn: send system serial number as HTTP header
-if getargbool 0 kssendsn inst.ks.sendsn; then
- # doesn't need to wait - dmi is builtin
- system_serial=$(cat /sys/class/dmi/id/product_serial 2>/dev/null)
- if [ -z "$system_serial" ]; then
- warn "inst.ks.sendsn: can't find system serial number"
- else
- set_http_header "X-System-Serial-Number" "$system_serial"
- fi
-fi
diff --git a/dracut/parse-kickstart b/dracut/parse-kickstart
index 13a0e8f43..0cbb4368a 100755
--- a/dracut/parse-kickstart
+++ b/dracut/parse-kickstart
@@ -285,7 +285,7 @@ def ksnet_to_ifcfg(net, filename=None):
def process_kickstart(ksfile):
handler = DracutHandler()
handler.ksdevice = os.environ.get('ksdevice')
- parser = KickstartParser(handler)
+ parser = KickstartParser(handler, missingIncludeIsFatal=False)
log.info("processing kickstart file %s", ksfile)
processed_file = preprocessKickstart(ksfile)
try:
diff --git a/po/anaconda.pot b/po/anaconda.pot
index 9e5267a0a..89abc5ed0 100644
--- a/po/anaconda.pot
+++ b/po/anaconda.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: anaconda 18.2\n"
+"Project-Id-Version: anaconda 18.3\n"
"Report-Msgid-Bugs-To: anaconda-devel-list@redhat.com\n"
-"POT-Creation-Date: 2012-08-03 16:44-0400\n"
+"POT-Creation-Date: 2012-08-13 15:30-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,28 +18,24 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-#: anaconda:357
-msgid "Press <enter> for a shell"
-msgstr ""
-
-#: anaconda:371
+#: anaconda:348
#, c-format
msgid ""
"%s requires %s MB of memory to install, but you only have %s MB on this "
"machine.\n"
msgstr ""
-#: anaconda:373
+#: anaconda:350
#, c-format
msgid ""
"The %s graphical installer requires %s MB of memory, but you only have %s MB."
msgstr ""
-#: anaconda:378
+#: anaconda:355
msgid "Not enough RAM"
msgstr ""
-#: anaconda:379
+#: anaconda:356
msgid ""
" Try the text mode installer by running:\n"
"\n"
@@ -48,41 +44,41 @@ msgid ""
" from a root terminal."
msgstr ""
-#: anaconda:382
+#: anaconda:359
msgid " Starting text mode."
msgstr ""
-#: anaconda:409 pyanaconda/rescue.py:288 pyanaconda/rescue.py:321
+#: anaconda:386 pyanaconda/rescue.py:288 pyanaconda/rescue.py:321
#: pyanaconda/rescue.py:334 pyanaconda/rescue.py:402 pyanaconda/rescue.py:418
#: pyanaconda/rescue.py:429
msgid "OK"
msgstr ""
-#: anaconda:492
+#: anaconda:469
msgid "Would you like to use VNC?"
msgstr ""
-#: anaconda:493
+#: anaconda:470
msgid ""
"Text mode provides a limited set of installation options. It does not allow "
"you to specify your own partitioning layout or package selections. Would you "
"like to use VNC mode instead?"
msgstr ""
-#: anaconda:514
+#: anaconda:491
msgid "DISPLAY variable not set. Starting text mode."
msgstr ""
-#: anaconda:522
+#: anaconda:499
msgid "Graphical installation is not available. Starting text mode."
msgstr ""
-#: anaconda:647
+#: anaconda:624
#, c-format
msgid "Please ssh install@%s to begin the install."
msgstr ""
-#: anaconda:649
+#: anaconda:626
msgid "Please ssh install@<host> to continue installation."
msgstr ""
@@ -201,17 +197,17 @@ msgstr ""
msgid "In interactive step can't continue. (%s)"
msgstr ""
-#: pyanaconda/constants.py:52
+#: pyanaconda/constants.py:57
msgid ""
"An unhandled exception has occurred. This is most likely a bug. Please "
"save a copy of the detailed exception and file a bug report"
msgstr ""
-#: pyanaconda/constants.py:58
+#: pyanaconda/constants.py:63
msgid " with the provider of this software."
msgstr ""
-#: pyanaconda/constants.py:62
+#: pyanaconda/constants.py:67
#, python-format
msgid " against anaconda at %s"
msgstr ""
@@ -259,31 +255,31 @@ msgstr ""
#: pyanaconda/yuminstall.py:1056 pyanaconda/yuminstall.py:1108
#: pyanaconda/yuminstall.py:1114 pyanaconda/yuminstall.py:1270
#: pyanaconda/yuminstall.py:1285 pyanaconda/yuminstall.py:1340
-#: pyanaconda/yuminstall.py:1530 pyanaconda/yuminstall.py:1563
-#: pyanaconda/yuminstall.py:1586
+#: pyanaconda/yuminstall.py:1535 pyanaconda/yuminstall.py:1568
+#: pyanaconda/yuminstall.py:1591
msgid "_Exit installer"
msgstr ""
-#: pyanaconda/iutil.py:848
+#: pyanaconda/iutil.py:865
msgid "Error determining boot device's disk name"
msgstr ""
-#: pyanaconda/iutil.py:871
+#: pyanaconda/iutil.py:888
msgid "the device containing /boot"
msgstr ""
-#: pyanaconda/iutil.py:873
+#: pyanaconda/iutil.py:890
#, python-format
msgid ""
"After shutdown, please perform a manual IPL from %s to continue installation."
msgstr ""
-#: pyanaconda/kickstart.py:153
+#: pyanaconda/kickstart.py:155
#, python-format
msgid "Escrow certificate %s requires the network."
msgstr ""
-#: pyanaconda/kickstart.py:161
+#: pyanaconda/kickstart.py:163
#, python-format
msgid ""
"The following error was encountered while downloading the escrow "
@@ -292,7 +288,7 @@ msgid ""
"%s"
msgstr ""
-#: pyanaconda/kickstart.py:1502
+#: pyanaconda/kickstart.py:1521
msgid "Running pre-installation scripts"
msgstr ""
@@ -343,7 +339,7 @@ msgstr ""
msgid "First sector of boot partition"
msgstr ""
-#: pyanaconda/platform.py:158 pyanaconda/platform.py:342
+#: pyanaconda/platform.py:158 pyanaconda/platform.py:341
msgid "Master Boot Record"
msgstr ""
@@ -430,7 +426,7 @@ msgid ""
"\n"
msgstr ""
-#: pyanaconda/rescue.py:255 pyanaconda/storage/__init__.py:135
+#: pyanaconda/rescue.py:255 pyanaconda/storage/__init__.py:142
#: pyanaconda/storage/devicetree.py:95
msgid "Continue"
msgstr ""
@@ -751,14 +747,14 @@ msgid "_Eject"
msgstr ""
#: pyanaconda/yuminstall.py:922 pyanaconda/yuminstall.py:1285
-#: pyanaconda/yuminstall.py:1340 pyanaconda/yuminstall.py:1563
+#: pyanaconda/yuminstall.py:1340 pyanaconda/yuminstall.py:1568
msgid "_Retry"
msgstr ""
#: pyanaconda/yuminstall.py:925 pyanaconda/yuminstall.py:1012
#: pyanaconda/yuminstall.py:1017 pyanaconda/yuminstall.py:1290
-#: pyanaconda/yuminstall.py:1350 pyanaconda/yuminstall.py:1557
-#: pyanaconda/yuminstall.py:1579 pyanaconda/storage/zfcp.py:375
+#: pyanaconda/yuminstall.py:1350 pyanaconda/yuminstall.py:1562
+#: pyanaconda/yuminstall.py:1584 pyanaconda/storage/zfcp.py:375
msgid "Error"
msgstr ""
@@ -788,7 +784,7 @@ msgid ""
msgstr ""
#: pyanaconda/yuminstall.py:1019 pyanaconda/yuminstall.py:1114
-#: pyanaconda/yuminstall.py:1532 pyanaconda/yuminstall.py:1586
+#: pyanaconda/yuminstall.py:1537 pyanaconda/yuminstall.py:1591
msgid "_Back"
msgstr ""
@@ -867,12 +863,12 @@ msgstr ""
msgid "Edit"
msgstr ""
-#: pyanaconda/yuminstall.py:1345 pyanaconda/yuminstall.py:1530
+#: pyanaconda/yuminstall.py:1345 pyanaconda/yuminstall.py:1535
#: pyanaconda/ui/gui/spokes/storage.glade:45
msgid "_Continue"
msgstr ""
-#: pyanaconda/yuminstall.py:1351 pyanaconda/yuminstall.py:1558
+#: pyanaconda/yuminstall.py:1351 pyanaconda/yuminstall.py:1563
#, python-format
msgid ""
"Unable to read package metadata. This may be due to a missing repodata "
@@ -882,11 +878,11 @@ msgid ""
"%s"
msgstr ""
-#: pyanaconda/yuminstall.py:1534
+#: pyanaconda/yuminstall.py:1539
msgid "Warning"
msgstr ""
-#: pyanaconda/yuminstall.py:1535
+#: pyanaconda/yuminstall.py:1540
msgid ""
"Some of the packages you have selected for install are missing "
"dependencies. You can exit the installation, go back and change your "
@@ -895,7 +891,7 @@ msgid ""
"missing components."
msgstr ""
-#: pyanaconda/yuminstall.py:1580
+#: pyanaconda/yuminstall.py:1585
#, python-format
msgid ""
"Your selected packages require %d MB of free space for installation, but you "
@@ -903,48 +899,48 @@ msgid ""
"installer."
msgstr ""
-#: pyanaconda/yuminstall.py:1665
+#: pyanaconda/yuminstall.py:1670
msgid "Post Upgrade"
msgstr ""
-#: pyanaconda/yuminstall.py:1666
+#: pyanaconda/yuminstall.py:1671
msgid "Performing post-upgrade configuration"
msgstr ""
-#: pyanaconda/yuminstall.py:1668
+#: pyanaconda/yuminstall.py:1673
msgid "Post Installation"
msgstr ""
-#: pyanaconda/yuminstall.py:1669
+#: pyanaconda/yuminstall.py:1674
msgid "Performing post-installation configuration"
msgstr ""
-#: pyanaconda/yuminstall.py:1828
+#: pyanaconda/yuminstall.py:1833
msgid "Installation Starting"
msgstr ""
-#: pyanaconda/yuminstall.py:1829
+#: pyanaconda/yuminstall.py:1834
msgid "Starting installation process"
msgstr ""
-#: pyanaconda/yuminstall.py:1867
+#: pyanaconda/yuminstall.py:1872
msgid "Dependency Check"
msgstr ""
-#: pyanaconda/yuminstall.py:1868
+#: pyanaconda/yuminstall.py:1873
msgid "Checking dependencies in packages selected for installation"
msgstr ""
-#: pyanaconda/yuminstall.py:1924 pyanaconda/yuminstall.py:1940
+#: pyanaconda/yuminstall.py:1929 pyanaconda/yuminstall.py:1945
msgid "Retrieving installation information."
msgstr ""
-#: pyanaconda/yuminstall.py:1926 pyanaconda/yuminstall.py:1942
+#: pyanaconda/yuminstall.py:1931 pyanaconda/yuminstall.py:1947
#, python-format
msgid "Retrieving installation information for %s."
msgstr ""
-#: pyanaconda/yuminstall.py:1927 pyanaconda/yuminstall.py:1943
+#: pyanaconda/yuminstall.py:1932 pyanaconda/yuminstall.py:1948
msgid "Installation Progress"
msgstr ""
@@ -1003,149 +999,149 @@ msgstr ""
msgid "Performing post-install setup tasks"
msgstr ""
-#: pyanaconda/storage/__init__.py:127
+#: pyanaconda/storage/__init__.py:134
msgid "Encrypt device?"
msgstr ""
-#: pyanaconda/storage/__init__.py:128
+#: pyanaconda/storage/__init__.py:135
msgid ""
"You specified block device encryption should be enabled, but you have not "
"supplied a passphrase. If you do not go back and provide a passphrase, block "
"device encryption will be disabled."
msgstr ""
-#: pyanaconda/storage/__init__.py:135 pyanaconda/storage/devicetree.py:95
+#: pyanaconda/storage/__init__.py:142 pyanaconda/storage/devicetree.py:95
msgid "Back"
msgstr ""
-#: pyanaconda/storage/__init__.py:155 pyanaconda/storage/devicetree.py:104
+#: pyanaconda/storage/__init__.py:162 pyanaconda/storage/devicetree.py:104
msgid "Confirm"
msgstr ""
-#: pyanaconda/storage/__init__.py:156
+#: pyanaconda/storage/__init__.py:163
msgid ""
"The partitioning options you have selected will now be written to disk. Any "
"data on deleted or reformatted partitions will be lost."
msgstr ""
-#: pyanaconda/storage/__init__.py:161
+#: pyanaconda/storage/__init__.py:168
msgid "Go _Back"
msgstr ""
-#: pyanaconda/storage/__init__.py:162
+#: pyanaconda/storage/__init__.py:169
msgid "_Write Changes to Disk"
msgstr ""
-#: pyanaconda/storage/__init__.py:1027
+#: pyanaconda/storage/__init__.py:1009
msgid "This partition is holding the data for the hard drive install."
msgstr ""
-#: pyanaconda/storage/__init__.py:1032
+#: pyanaconda/storage/__init__.py:1014
msgid "You cannot delete a partition of a LDL formatted DASD."
msgstr ""
-#: pyanaconda/storage/__init__.py:1038
+#: pyanaconda/storage/__init__.py:1020
#, python-format
msgid "This device is part of the RAID device %s."
msgstr ""
-#: pyanaconda/storage/__init__.py:1041
+#: pyanaconda/storage/__init__.py:1023
msgid "This device is part of a RAID device."
msgstr ""
-#: pyanaconda/storage/__init__.py:1044
+#: pyanaconda/storage/__init__.py:1026
msgid "This device is part of an inconsistent LVM Volume Group."
msgstr ""
-#: pyanaconda/storage/__init__.py:1049
+#: pyanaconda/storage/__init__.py:1031
#, python-format
msgid "This device is part of the LVM volume group '%s'."
msgstr ""
-#: pyanaconda/storage/__init__.py:1052
+#: pyanaconda/storage/__init__.py:1034
msgid "This device is part of a LVM volume group."
msgstr ""
-#: pyanaconda/storage/__init__.py:1068
+#: pyanaconda/storage/__init__.py:1050
msgid ""
"This device is an extended partition which contains logical partitions that "
"cannot be deleted:\n"
"\n"
msgstr ""
-#: pyanaconda/storage/__init__.py:1277
+#: pyanaconda/storage/__init__.py:1268
msgid "You must create a new filesystem on the root device."
msgstr ""
-#: pyanaconda/storage/__init__.py:1429
+#: pyanaconda/storage/__init__.py:1424
#, python-format
msgid ""
"You have not defined a root partition (/), which is required for "
"installation of %s to continue."
msgstr ""
-#: pyanaconda/storage/__init__.py:1434
+#: pyanaconda/storage/__init__.py:1429
#, python-format
msgid ""
"Your root partition is less than 250 megabytes which is usually too small to "
"install %s."
msgstr ""
-#: pyanaconda/storage/__init__.py:1448
+#: pyanaconda/storage/__init__.py:1443
msgid ""
"This platform requires /boot on a dedicated partition or logical volume. If "
"you do not want a /boot volume, you must place / on a dedicated non-LVM "
"partition."
msgstr ""
-#: pyanaconda/storage/__init__.py:1459
+#: pyanaconda/storage/__init__.py:1454
#, python-format
msgid ""
"Your / partition does not match the the live image you are installing from. "
"It must be formatted as %s."
msgstr ""
-#: pyanaconda/storage/__init__.py:1466
+#: pyanaconda/storage/__init__.py:1461
#, python-format
msgid ""
"Your %(mount)s partition is less than %(size)s megabytes which is lower than "
"recommended for a normal %(productName)s install."
msgstr ""
-#: pyanaconda/storage/__init__.py:1476
+#: pyanaconda/storage/__init__.py:1471
#, python-format
msgid ""
"Your %(mount)s partition is too small for %(format)s formatting (allowable "
"size is %(minSize)d MB to %(maxSize)d MB)"
msgstr ""
-#: pyanaconda/storage/__init__.py:1481
+#: pyanaconda/storage/__init__.py:1476
#, python-format
msgid ""
"Your %(mount)s partition is too large for %(format)s formatting (allowable "
"size is %(minSize)d MB to %(maxSize)d MB)"
msgstr ""
-#: pyanaconda/storage/__init__.py:1508
+#: pyanaconda/storage/__init__.py:1503
msgid ""
"Installing on a USB device. This may or may not produce a working system."
msgstr ""
-#: pyanaconda/storage/__init__.py:1511
+#: pyanaconda/storage/__init__.py:1506
msgid ""
"Installing on a FireWire device. This may or may not produce a working "
"system."
msgstr ""
-#: pyanaconda/storage/__init__.py:1517
+#: pyanaconda/storage/__init__.py:1512
msgid "you have not created a bootloader stage1 target device"
msgstr ""
-#: pyanaconda/storage/__init__.py:1526
+#: pyanaconda/storage/__init__.py:1521
msgid "You have not created a bootable partition."
msgstr ""
-#: pyanaconda/storage/__init__.py:1545
+#: pyanaconda/storage/__init__.py:1540
#, python-format
msgid ""
"Your BIOS-based system needs a special partition to boot with %s's new disk "
@@ -1153,7 +1149,7 @@ msgid ""
"partition."
msgstr ""
-#: pyanaconda/storage/__init__.py:1558
+#: pyanaconda/storage/__init__.py:1553
#, python-format
msgid ""
"You have not specified a swap partition. %(requiredMem)s MB of memory is "
@@ -1161,13 +1157,13 @@ msgid ""
"have %(installedMem)s MB."
msgstr ""
-#: pyanaconda/storage/__init__.py:1564
+#: pyanaconda/storage/__init__.py:1559
msgid ""
"You have not specified a swap partition. Although not strictly required in "
"all cases, it will significantly improve performance for most installations."
msgstr ""
-#: pyanaconda/storage/__init__.py:1570
+#: pyanaconda/storage/__init__.py:1565
msgid ""
"At least one of your swap devices does not have a UUID, which is common in "
"swap space created using older versions of mkswap. These devices will be "
@@ -1175,13 +1171,13 @@ msgid ""
"paths can change under a variety of circumstances. "
msgstr ""
-#: pyanaconda/storage/__init__.py:1580
+#: pyanaconda/storage/__init__.py:1575
#, python-format
msgid ""
"This mount point is invalid. The %s directory must be on the / file system."
msgstr ""
-#: pyanaconda/storage/__init__.py:1584
+#: pyanaconda/storage/__init__.py:1579
#, python-format
msgid "The mount point %s must be on a linux file system."
msgstr ""
@@ -1214,17 +1210,17 @@ msgstr ""
msgid "Migrating filesystem on %(device)s"
msgstr ""
-#: pyanaconda/storage/devicelibs/lvm.py:296
+#: pyanaconda/storage/devicelibs/lvm.py:311
#, python-format
msgid "vginfo failed for %s"
msgstr ""
-#: pyanaconda/storage/devicelibs/lvm.py:325
+#: pyanaconda/storage/devicelibs/lvm.py:340
#, python-format
msgid "lvs failed for %s"
msgstr ""
-#: pyanaconda/storage/devices.py:2724
+#: pyanaconda/storage/devices.py:2639
#, python-format
msgid "A RAID%(raidLevel)d set requires at least %(minMembers)d member"
msgid_plural "A RAID%(raidLevel)d set requires at least %(minMembers)d members"
@@ -1271,33 +1267,33 @@ msgstr ""
msgid "Unrecoverable Error"
msgstr ""
-#: pyanaconda/storage/formats/fs.py:889
+#: pyanaconda/storage/formats/fs.py:879
msgid "File system errors left uncorrected."
msgstr ""
-#: pyanaconda/storage/formats/fs.py:890
+#: pyanaconda/storage/formats/fs.py:880
msgid "Operational error."
msgstr ""
-#: pyanaconda/storage/formats/fs.py:891
+#: pyanaconda/storage/formats/fs.py:881
msgid "Usage or syntax error."
msgstr ""
-#: pyanaconda/storage/formats/fs.py:892
+#: pyanaconda/storage/formats/fs.py:882
msgid "e2fsck cancelled by user request."
msgstr ""
-#: pyanaconda/storage/formats/fs.py:893
+#: pyanaconda/storage/formats/fs.py:883
msgid "Shared library error."
msgstr ""
-#: pyanaconda/storage/formats/fs.py:1061
+#: pyanaconda/storage/formats/fs.py:1051
msgid ""
"Recoverable errors have been detected or dosfsck has discovered an internal "
"inconsistency."
msgstr ""
-#: pyanaconda/storage/formats/fs.py:1063
+#: pyanaconda/storage/formats/fs.py:1053
msgid "Usage error."
msgstr ""
@@ -1425,20 +1421,20 @@ msgstr ""
msgid "Could not set zFCP device %(devnum)s offline (%(e)s)."
msgstr ""
-#: pyanaconda/ui/gui/__init__.py:98
+#: pyanaconda/ui/gui/__init__.py:113
#, python-format
msgid "%(productName)s %(productVersion)s INSTALLATION"
msgstr ""
-#: pyanaconda/ui/gui/__init__.py:114
+#: pyanaconda/ui/gui/__init__.py:129
msgid "_Exit Installer"
msgstr ""
-#: pyanaconda/ui/gui/__init__.py:127
+#: pyanaconda/ui/gui/__init__.py:142
msgid "_No"
msgstr ""
-#: pyanaconda/ui/gui/__init__.py:127
+#: pyanaconda/ui/gui/__init__.py:142
msgid "_Yes"
msgstr ""
@@ -1470,76 +1466,76 @@ msgstr ""
msgid "Complete!"
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:56
+#: pyanaconda/ui/gui/spokes/custom.py:63
#, python-format
msgid "New %s %s Installation"
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:121
+#: pyanaconda/ui/gui/spokes/custom.py:141
msgid "MANUAL PARTITIONING"
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:239
+#: pyanaconda/ui/gui/spokes/custom.py:348
#, python-format
msgid "%d storage device selected"
msgid_plural "%d storage devices selected"
msgstr[0] ""
msgstr[1] ""
-#: pyanaconda/ui/gui/spokes/custom.py:320
+#: pyanaconda/ui/gui/spokes/custom.py:445
#: pyanaconda/ui/gui/spokes/network.py:554
msgid "Unknown"
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:340
+#: pyanaconda/ui/gui/spokes/custom.py:465
msgid ""
"The 'swap' area on your computer is used by the operating\n"
"system when running low on memory."
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:343
+#: pyanaconda/ui/gui/spokes/custom.py:468
msgid ""
"The 'boot' area on your computer is where files needed\n"
"to start the operating system are stored."
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:346
+#: pyanaconda/ui/gui/spokes/custom.py:471
msgid ""
"The 'root' area on your computer is where core system\n"
"files and applications are stored."
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:349
+#: pyanaconda/ui/gui/spokes/custom.py:474
msgid ""
"The 'home' area on your computer is where all your personal\n"
"data is stored."
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:352
+#: pyanaconda/ui/gui/spokes/custom.py:477
msgid "No one knows what this could possibly be for."
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:387
+#: pyanaconda/ui/gui/spokes/custom.py:513
msgid "This file system does not support labels."
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:396
+#: pyanaconda/ui/gui/spokes/custom.py:523
msgid "This file system may not be resized."
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:633
+#: pyanaconda/ui/gui/spokes/custom.py:780
msgid "BTRFS"
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:636
+#: pyanaconda/ui/gui/spokes/custom.py:783
msgid "LVM"
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:639
+#: pyanaconda/ui/gui/spokes/custom.py:786
msgid "RAID"
msgstr ""
-#: pyanaconda/ui/gui/spokes/custom.py:642
+#: pyanaconda/ui/gui/spokes/custom.py:789
msgid "Standard Partition"
msgstr ""
@@ -1565,7 +1561,7 @@ msgstr ""
msgid "You have no working NTP server configured"
msgstr ""
-#: pyanaconda/ui/gui/spokes/keyboard.py:148
+#: pyanaconda/ui/gui/spokes/keyboard.py:149
msgid "KEYBOARD"
msgstr ""
@@ -1814,7 +1810,7 @@ msgid "Error setting up software source"
msgstr ""
#: pyanaconda/ui/gui/spokes/source.py:601
-#: pyanaconda/ui/gui/spokes/storage.py:404
+#: pyanaconda/ui/gui/spokes/storage.py:405
msgid "Probing storage..."
msgstr ""
@@ -1877,23 +1873,23 @@ msgstr ""
msgid "INSTALLATION DESTINATION"
msgstr ""
-#: pyanaconda/ui/gui/spokes/storage.py:343
+#: pyanaconda/ui/gui/spokes/storage.py:344
msgid "No disks selected"
msgstr ""
-#: pyanaconda/ui/gui/spokes/storage.py:350
+#: pyanaconda/ui/gui/spokes/storage.py:351
msgid "Error checking storage configuration"
msgstr ""
-#: pyanaconda/ui/gui/spokes/storage.py:352
+#: pyanaconda/ui/gui/spokes/storage.py:353
msgid "Automatic partitioning selected"
msgstr ""
-#: pyanaconda/ui/gui/spokes/storage.py:354
+#: pyanaconda/ui/gui/spokes/storage.py:355
msgid "Custom partitioning selected"
msgstr ""
-#: pyanaconda/ui/gui/spokes/storage.py:465
+#: pyanaconda/ui/gui/spokes/storage.py:466
msgid "No disks selected; please select at least one disk to install to."
msgstr ""
@@ -1901,20 +1897,20 @@ msgstr ""
msgid "LANGUAGE"
msgstr ""
-#: pyanaconda/ui/gui/spokes/lib/accordion.py:120
+#: pyanaconda/ui/gui/spokes/lib/accordion.py:119
msgid "DATA"
msgstr ""
-#: pyanaconda/ui/gui/spokes/lib/accordion.py:125
+#: pyanaconda/ui/gui/spokes/lib/accordion.py:124
msgid "SYSTEM"
msgstr ""
-#: pyanaconda/ui/gui/spokes/lib/accordion.py:208
+#: pyanaconda/ui/gui/spokes/lib/accordion.py:207
#, python-format
msgid "You haven't created any mount points for your %s %s installation yet:"
msgstr ""
-#: pyanaconda/ui/gui/spokes/lib/accordion.py:223
+#: pyanaconda/ui/gui/spokes/lib/accordion.py:222
msgid "Or, create new mount points below with the '+' icon."
msgstr ""
@@ -2204,15 +2200,15 @@ msgstr ""
msgid "_CONTINUE"
msgstr ""
-#: pyanaconda/ui/gui/spokes/keyboard.glade:76
+#: pyanaconda/ui/gui/spokes/keyboard.glade:78
msgid "ADD A KEYBOARD LAYOUT"
msgstr ""
-#: pyanaconda/ui/gui/spokes/keyboard.glade:91
+#: pyanaconda/ui/gui/spokes/keyboard.glade:93
msgid "You may add a keyboard layout by selecting it below:"
msgstr ""
-#: pyanaconda/ui/gui/spokes/keyboard.glade:120
+#: pyanaconda/ui/gui/spokes/keyboard.glade:122
msgid "Name"
msgstr ""
@@ -2226,15 +2222,15 @@ msgstr ""
msgid "name"
msgstr ""
-#: pyanaconda/ui/gui/spokes/keyboard.glade:388
+#: pyanaconda/ui/gui/spokes/keyboard.glade:393
msgid "Test the selected layout below:"
msgstr ""
-#: pyanaconda/ui/gui/spokes/keyboard.glade:414
+#: pyanaconda/ui/gui/spokes/keyboard.glade:426
msgid "Alt + Shift to switch layouts."
msgstr ""
-#: pyanaconda/ui/gui/spokes/keyboard.glade:427
+#: pyanaconda/ui/gui/spokes/keyboard.glade:439
msgid "_Options..."
msgstr ""
diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py
index ed8040ff5..04b4066d9 100644
--- a/pyanaconda/bootloader.py
+++ b/pyanaconda/bootloader.py
@@ -620,6 +620,7 @@ class BootLoader(object):
return valid
def set_stage1_device(self, devices):
+ self.stage1_device = None
if not self.stage1_disk:
raise BootLoaderError("need stage1 disk to set stage1 device")
@@ -627,7 +628,6 @@ class BootLoader(object):
self.stage1_device = self.stage2_device
return
- self.stage1_device = None
for device in devices:
if self.stage1_disk not in device.disks:
continue
@@ -1546,7 +1546,7 @@ class GRUB2(GRUB):
self.stage2_device.format.sync(root=ROOT_PATH)
class EFIGRUB(GRUB2):
- packages = ["grub2-efi", "efibootmgr"]
+ packages = ["grub2-efi", "efibootmgr", "shim"]
can_dual_boot = False
@property
@@ -1606,7 +1606,7 @@ class EFIGRUB(GRUB2):
rc = self.efibootmgr("-c", "-w", "-L", productName,
"-d", boot_disk.path, "-p", boot_part_num,
"-l",
- self.efi_dir_as_efifs_dir + "\\grubx64.efi",
+ self.efi_dir_as_efifs_dir + "\\shim.efi",
root=ROOT_PATH,
stdout="/dev/tty5", stderr="/dev/tty5")
if rc:
diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index 1baf6bdd7..c2a4b2b13 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -49,6 +49,11 @@ productArch = product.productArch
bugzillaUrl = product.bugUrl
isFinal = product.isFinal
+# for use in device names, eg: "fedora", "rhel"
+shortProductName = productName.lower()
+if productName.count(" "):
+ shortProductName = ''.join(s[0] for s in shortProductName.split())
+
exceptionText = _("An unhandled exception has occurred. This "
"is most likely a bug. Please save a copy of "
"the detailed exception and file a bug report")
diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py
index cdef54215..8e519aaae 100644
--- a/pyanaconda/installclass.py
+++ b/pyanaconda/installclass.py
@@ -105,7 +105,7 @@ class BaseInstallClass(object):
# provided as a way for other products to specify their own.
return None
- def setDefaultPartitioning(self, storage, platform):
+ def setDefaultPartitioning(self, storage):
autorequests = [PartSpec(mountpoint="/", fstype=storage.defaultFSType,
size=1024, maxSize=50*1024, grow=True,
btr=True, lv=True, encrypted=True),
@@ -113,14 +113,21 @@ class BaseInstallClass(object):
size=500, grow=True, requiredSpace=50*1024,
btr=True, lv=True, encrypted=True)]
- bootreq = platform.setDefaultPartitioning()
- if bootreq:
- autorequests.extend(bootreq)
+ bootreqs = storage.platform.setDefaultPartitioning()
+ if bootreqs:
+ autorequests.extend(bootreqs)
swp = swap.swapSuggestion()
autorequests.append(PartSpec(fstype="swap", size=swp, grow=False,
lv=True, encrypted=True))
+ for autoreq in autorequests:
+ if autoreq.fstype is None:
+ if autoreq.mountpoint == "/boot":
+ autoreq.fstype = storage.defaultBootFSType
+ else:
+ autoreq.fstype = storage.defaultFSType
+
storage.autoPartitionRequests = autorequests
def configure(self, anaconda):
diff --git a/pyanaconda/installclasses/fedora.py b/pyanaconda/installclasses/fedora.py
index e2c15433a..ae1abf015 100644
--- a/pyanaconda/installclasses/fedora.py
+++ b/pyanaconda/installclasses/fedora.py
@@ -76,9 +76,7 @@ class InstallClass(BaseInstallClass):
def configure(self, anaconda):
BaseInstallClass.configure(self, anaconda)
- BaseInstallClass.setDefaultPartitioning(self,
- anaconda.storage,
- anaconda.platform)
+ BaseInstallClass.setDefaultPartitioning(self, anaconda.storage)
def setGroupSelection(self, anaconda):
BaseInstallClass.setGroupSelection(self, anaconda)
diff --git a/pyanaconda/installclasses/rhel.py b/pyanaconda/installclasses/rhel.py
index 25edbae41..765976649 100644
--- a/pyanaconda/installclasses/rhel.py
+++ b/pyanaconda/installclasses/rhel.py
@@ -55,9 +55,7 @@ class InstallClass(BaseInstallClass):
def configure(self, anaconda):
BaseInstallClass.configure(self, anaconda)
- BaseInstallClass.setDefaultPartitioning(self,
- anaconda.storage,
- anaconda.platform)
+ BaseInstallClass.setDefaultPartitioning(self, anaconda.storage)
def productMatches(self, oldprod):
if oldprod is None:
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index 5c8c42c69..e3d7fd186 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -26,6 +26,7 @@ from storage.devicelibs.mpath import MultipathConfigWriter, MultipathTopology
from storage.devicelibs import swap
from storage.formats import getFormat
from storage.partitioning import doPartitioning
+from storage.partitioning import growLVM
import storage.iscsi
import storage.fcoe
import storage.zfcp
@@ -236,7 +237,6 @@ class Authconfig(commands.authconfig.FC3_Authconfig):
class AutoPart(commands.autopart.F17_AutoPart):
def execute(self, storage, ksdata, instClass):
- from pyanaconda.platform import getPlatform
from pyanaconda.storage.partitioning import doAutoPartition
if not self.autopart:
@@ -244,7 +244,7 @@ class AutoPart(commands.autopart.F17_AutoPart):
# sets up default autopartitioning. use clearpart separately
# if you want it
- instClass.setDefaultPartitioning(storage, getPlatform())
+ instClass.setDefaultPartitioning(storage)
storage.doAutoPart = True
if self.encrypted:
@@ -539,6 +539,9 @@ class LogVol(commands.logvol.F17_LogVol):
for l in self.lvList:
l.execute(storage, ksdata, instClass)
+ if self.lvList:
+ growLVM(storage)
+
class LogVolData(commands.logvol.F17_LogVolData):
def execute(self, storage, ksdata, instClass):
devicetree = storage.devicetree
@@ -845,6 +848,9 @@ class Partition(commands.partition.F17_Partition):
for p in self.partitions:
p.execute(storage, ksdata, instClass)
+ if self.partitions:
+ doPartitioning(storage)
+
class PartitionData(commands.partition.F17_PartData):
def execute(self, storage, ksdata, instClass):
devicetree = storage.devicetree
@@ -1033,13 +1039,6 @@ class PartitionData(commands.partition.F17_PartData):
except KeyError:
pass
- if "format" in kwargs:
- # set weight based on fstype and mountpoint
- mpt = getattr(kwargs["format"], "mountpoint", None)
- fstype = kwargs["format"].type
- kwargs["weight"] = storage.platform.weight(fstype=fstype,
- mountpoint=mpt)
-
request = storage.newPartition(**kwargs)
storage.createDevice(request)
@@ -1600,5 +1599,4 @@ def doKickstartStorage(storage, ksdata, instClass):
ksdata.logvol.execute(storage, ksdata, instClass)
ksdata.btrfs.execute(storage, ksdata, instClass)
storage.setUpBootLoader()
- doPartitioning(storage)
diff --git a/pyanaconda/platform.py b/pyanaconda/platform.py
index 58cfa2dc4..6ec00374b 100644
--- a/pyanaconda/platform.py
+++ b/pyanaconda/platform.py
@@ -310,8 +310,6 @@ class S390(Platform):
"""Return the default platform-specific partitioning information."""
from storage.partspec import PartSpec
return [PartSpec(mountpoint="/boot", size=500,
-#TODO: need to pass this info in w/o using anaconda object
-# fstype=self.anaconda.storage.defaultBootFSType,
weight=self.weight(mountpoint="/boot"), lv=True,
singlePV=True)]
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py
index 914096234..70e772c36 100644
--- a/pyanaconda/storage/__init__.py
+++ b/pyanaconda/storage/__init__.py
@@ -26,6 +26,7 @@ import stat
import errno
import sys
import statvfs
+import copy
import nss.nss
import parted
@@ -37,6 +38,7 @@ from pykickstart.constants import *
from pyanaconda.flags import flags
from pyanaconda import tsort
from pyanaconda.errors import *
+from pyanaconda.bootloader import BootLoaderError
from errors import *
from devices import *
@@ -49,6 +51,11 @@ from devicelibs.dm import name_from_dm_node
from devicelibs.crypto import generateBackupPassphrase
from devicelibs.mpath import MultipathConfigWriter
from devicelibs.edd import get_edd_dict
+from devicelibs.mdraid import get_member_space
+from devicelibs.lvm import get_pv_space
+from .partitioning import SameSizeSet
+from .partitioning import TotalSizeSet
+from .partitioning import doPartitioning
from udev import *
import iscsi
import fcoe
@@ -296,31 +303,6 @@ class StorageDiscoveryConfig(object):
self.initializeDisks = ksdata.clearpart.initAll
self.zeroMbr = ksdata.zerombr.zerombr
- def writeKS(self, f):
- # clearpart
- if self.clearPartType is None or self.clearPartType == CLEARPART_TYPE_NONE:
- args = ["--none"]
- elif self.clearPartType == CLEARPART_TYPE_LINUX:
- args = ["--linux"]
- else:
- args = ["--all"]
-
- if self.clearPartDisks:
- args += ["--drives=%s" % ",".join(self.clearPartDisks)]
- if self.initializeDisks:
- args += ["--initlabel"]
-
- f.write("#clearpart %s\n" % " ".join(args))
-
- # ignoredisks
- if self.ignoreDiskInteractive:
- f.write("#ignoredisk --interactive\n")
- elif self.ignoredDisks:
- f.write("#ignoredisk --drives=%s\n" % ",".join(self.ignoredDisks))
- elif self.exclusiveDisks:
- f.write("#ignoredisk --only-use=%s\n" % ",".join(self.exclusiveDisks))
-
-
class Storage(object):
def __init__(self, data=None, platform=None):
""" Create a Storage instance.
@@ -348,10 +330,10 @@ class Storage(object):
self.autoPartAddBackupPassphrase = False
self.encryptionRetrofit = False
self.autoPartitionRequests = []
- self.shrinkPartitions = {}
self.eddDict = {}
self.__luksDevs = {}
+ self.size_sets = []
self.iscsi = iscsi.iscsi()
self.fcoe = fcoe.fcoe()
@@ -1090,6 +1072,15 @@ class Storage(object):
else:
name = "req%d" % self.nextID
+ if "weight" not in kwargs:
+ fmt = kwargs.get("format")
+ if fmt:
+ mountpoint = getattr(fmt, "mountpoint", None)
+
+ kwargs["weight"] = self.platform.weight(mountpoint=mountpoint,
+ fstype=fmt.type)
+
+
return PartitionDevice(name, *args, **kwargs)
def newMDArray(self, *args, **kwargs):
@@ -1097,7 +1088,8 @@ class Storage(object):
if kwargs.has_key("fmt_type"):
kwargs["format"] = getFormat(kwargs.pop("fmt_type"),
mountpoint=kwargs.pop("mountpoint",
- None))
+ None),
+ **kwargs.pop("fmt_args", {}))
if kwargs.has_key("minor"):
kwargs["minor"] = int(kwargs["minor"])
@@ -1128,7 +1120,7 @@ class Storage(object):
hostname = nd.hostname
break
- name = self.suggestContainerName(hostname=hostname, prefix="vg")
+ name = self.suggestContainerName(hostname=hostname)
if name in self.names:
raise ValueError("name already in use")
@@ -1141,7 +1133,8 @@ class Storage(object):
mountpoint = kwargs.pop("mountpoint", None)
if kwargs.has_key("fmt_type"):
kwargs["format"] = getFormat(kwargs.pop("fmt_type"),
- mountpoint=mountpoint)
+ mountpoint=mountpoint,
+ **kwargs.pop("fmt_args", {}))
if kwargs.has_key("name"):
name = kwargs.pop("name")
@@ -1150,8 +1143,7 @@ class Storage(object):
swap = True
else:
swap = False
- name = self.suggestDeviceName(prefix="lv",
- parent=vg,
+ name = self.suggestDeviceName(parent=vg,
swap=swap,
mountpoint=mountpoint)
@@ -1168,7 +1160,9 @@ class Storage(object):
name = args[0]
mountpoint = kwargs.pop("mountpoint", None)
- fmt_kwargs = {"mountpoint": mountpoint}
+
+ fmt_args = kwargs.pop("fmt_args", {})
+ fmt_args.update({"mountpoint": mountpoint})
if kwargs.pop("subvol", False):
dev_class = BTRFSSubVolumeDevice
@@ -1183,7 +1177,7 @@ class Storage(object):
# for btrfs this only needs to ensure the subvol name is not
# already in use within the parent volume
name = self.suggestDeviceName(mountpoint=mountpoint)
- fmt_kwargs["mountopts"] = "subvol=%s" % name
+ fmt_args["mountopts"] = "subvol=%s" % name
kwargs.pop("metaDataLevel", None)
kwargs.pop("dataLevel", None)
else:
@@ -1197,25 +1191,22 @@ class Storage(object):
hostname = nd.hostname
break
- name = self.suggestContainerName(prefix="btrfs",
- hostname=hostname)
- #if name and name.startswith("btrfs_"):
- # fmt_kwargs["label"] = name[6:]
- #elif name and not name.startswith("btrfs"):
- fmt_kwargs["label"] = name
-
- # do we want to prevent a subvol with a name that matches another dev?
- if name in self.names:
- raise ValueError("name already in use")
+ name = self.suggestContainerName(hostname=hostname)
+ if "label" not in fmt_args:
+ fmt_args["label"] = name
# discard fmt_type since it's btrfs always
kwargs.pop("fmt_type", None)
# this is to avoid auto-scheduled format create actions
device = dev_class(name, **kwargs)
- device.format = getFormat("btrfs", **fmt_kwargs)
+ device.format = getFormat("btrfs", **fmt_args)
return device
+ def newBTRFSSubVolume(self, *args, **kwargs):
+ kwargs["subvol"] = True
+ return self.newBTRFS(*args, **kwargs)
+
def createDevice(self, device):
""" Schedule creation of a device.
@@ -1306,15 +1297,19 @@ class Storage(object):
def suggestContainerName(self, hostname=None, prefix=""):
""" Return a reasonable, unused device name. """
+ if not prefix:
+ prefix = shortProductName
+
# try to create a device name incorporating the hostname
if hostname not in (None, "", 'localhost', 'localhost.localdomain'):
template = "%s_%s" % (prefix, hostname.split('.')[0].lower())
template = self.safeDeviceName(template)
- elif flags.imageInstall:
- template = "%s_image" % prefix
else:
template = prefix
+ if flags.imageInstall:
+ template = "%s_image" % template
+
names = self.names
name = template
if name in names:
@@ -1338,11 +1333,14 @@ class Storage(object):
body = ""
if mountpoint:
if mountpoint == "/":
- body = "_root"
+ body = "root"
else:
- body = mountpoint.replace("/", "_")
+ body = mountpoint[1:].replace("/", "_")
elif swap:
- body = "_swap"
+ body = "swap"
+
+ if prefix:
+ body = "_" + body
template = self.safeDeviceName(prefix + body)
names = self.names
@@ -1357,9 +1355,6 @@ class Storage(object):
# also include names of any lvs in the parent for the case of the
# temporary vg in the lvm dialogs, which can contain lvs that are
# not yet in the devicetree and therefore not in self.names
- if hasattr(parent, "lvs"):
- names.extend([full_name(d.lvname, parent) for d in parent.lvs])
-
if full_name(name, parent) in names or not body:
for i in range(100):
name = "%s%02d" % (template, i)
@@ -1626,69 +1621,6 @@ class Storage(object):
self.zfcp.write()
self.dasd.write()
- def writeKS(self, f):
- def useExisting(lst):
- foundCreateDevice = False
- foundCreateFormat = False
-
- for l in lst:
- if isinstance(l, ActionCreateDevice):
- foundCreateDevice = True
- elif isinstance(l, ActionCreateFormat):
- foundCreateFormat = True
-
- return (foundCreateFormat and not foundCreateDevice)
-
- log.warning("Storage.writeKS not completely implemented")
- f.write("# The following is the partition information you requested\n")
- f.write("# Note that any partitions you deleted are not expressed\n")
- f.write("# here so unless you clear all partitions first, this is\n")
- f.write("# not guaranteed to work\n")
-
- self.config.writeKS(f)
-
- # the various partitioning commands
- dict = {}
- actions = filter(lambda x: x.device.format.type != "luks",
- self.devicetree.findActions(type="create"))
-
- for action in actions:
- if dict.has_key(action.device.path):
- dict[action.device.path].append(action)
- else:
- dict[action.device.path] = [action]
-
- devices = self.devices
- edges = []
- for dev in devices:
- deps = [d for d in devices if dev.dependsOn(d)]
- for dep in deps:
- edges.append((devices.index(dep), devices.index(dev)))
-
- g = tsort.create_graph(range(len(devices)), edges)
- order = tsort.tsort(g)
- for idx in order:
- device = devices[idx]
- if device.format.type == "luks":
- continue
-
- # If there's no action for the given device, it must be one
- # we are reusing.
- if not dict.has_key(device.path):
- noformat = True
- preexisting = True
- else:
- noformat = False
- preexisting = useExisting(dict[device.path])
-
- device.writeKS(f, preexisting=preexisting, noformat=noformat)
- f.write("\n")
-
- self.iscsi.writeKS(f)
- self.fcoe.writeKS(f)
- self.zfcp.writeKS(f)
- f.write("\n")
-
def turnOnSwap(self, upgrading=None):
self.fsset.turnOnSwap(rootPath=ROOT_PATH,
upgrading=upgrading)
@@ -1732,7 +1664,10 @@ class Storage(object):
self.bootloader.stage1_disk = self.devicetree.resolveDevice(self.data.bootloader.bootDrive)
self.bootloader.stage2_device = self.bootDevice
- self.bootloader.set_stage1_device(self.devices)
+ try:
+ self.bootloader.set_stage1_device(self.devices)
+ except BootLoaderError as e:
+ log.debug("failed to set bootloader stage1 device: %s" % e)
@property
def bootDisk(self):
@@ -1750,6 +1685,10 @@ class Storage(object):
return dev
@property
+ def bootLoaderDevice(self):
+ return getattr(self.bootloader, "stage1_device", None)
+
+ @property
def bootFSTypes(self):
"""A list of all valid filesystem types for the boot partition."""
fstypes = []
@@ -1847,6 +1786,249 @@ class Storage(object):
return 0
+ def getFSType(self, mountpoint=None):
+ """ Return the default filesystem type based on mountpoint. """
+ fstype = self.defaultFSType
+ if not mountpoint:
+ # just return the default
+ pass
+ elif mountpoint.lower() == "swap":
+ fstype = "swap"
+ elif mountpoint == "/boot":
+ fstype = self.defaultBootFSType
+ elif mountpoint == "/boot/efi":
+ if iutil.isMactel():
+ fstype = "hfs+"
+ else:
+ fstype = "efi"
+
+ return fstype
+
+ def setContainerMembers(self, container, factory):
+ # set up member devices
+ container_size = 0
+ if container:
+ log.debug("using container %s with %d devices" % (container.name,
+ len(self.devicetree.getChildren(container))))
+ container_size = factory.container_size_func(container)
+ log.debug("raw container size reported as %d" % container_size)
+ disks = list(set([d for m in container.parents for d in m.disks]))
+ disks.sort(key=lambda d: d.name, cmp=self.compareDisks)
+ factory.disks = disks
+
+ device_space = factory.device_size
+ log.debug("device requires %d" % device_space)
+ container_size += device_space
+
+ # XXX TODO: multiple member devices per disk
+
+ # XXX Never try to reuse member devices. They can be converted by the
+ # user into free space.
+ if container:
+ members = container.parents[:]
+ for member in members[:]:
+ if isinstance(member, LUKSDevice):
+ member = member.slave
+
+ member.req_base_size = PartitionDevice.defaultSize
+ member.req_size = member.req_base_size
+ member.req_grow = True
+
+ for ss in self.size_sets[:]:
+ if member in ss.devices:
+ self.size_sets.remove(ss)
+ else:
+ # set up members as needed to accommodate the device
+ members = []
+ for disk in factory.disks:
+ if factory.encrypted:
+ luks_format = factory.member_format
+ member_format = "luks"
+ else:
+ member_format = factory.member_format
+
+ member = self.newPartition(parents=[disk], grow=True,
+ fmt_type=member_format)
+ self.createDevice(member)
+ if factory.encrypted:
+ fmt = getFormat(luks_format)
+ member = LUKSDevice("luks-%s" % member.name,
+ parents=[member], format=fmt)
+ self.createDevice(member)
+
+ members.append(member)
+
+ log.debug("adding a %s with size %d" % (factory.set_class.__name__,
+ container_size))
+ size_set = factory.set_class(members, container_size)
+ self.size_sets.append(size_set)
+
+ try:
+ doPartitioning(self)
+ except StorageError as e:
+ log.error("UI: failed to allocate partitions: %s" % e)
+ for device in members:
+ actions = self.devicetree.findActions(device=device)
+ for a in reversed(actions):
+ self.devicetree.cancelAction(a)
+ # FIXME: revert container to its original state?
+ return
+ else:
+ # fix the sizes of all allocated partitions
+ for partition in self.partitions:
+ partition.req_grow = False
+ partition.req_base_size = partition.size
+ partition.req_size = partition.size
+
+ return members
+
+ def getDeviceFactory(self, device_type, size, **kwargs):
+ disks = kwargs.get("disks", [])
+ encrypted = kwargs.get("encrypted", False)
+
+ # md, btrfs, lvm
+ raid_level = kwargs.get("level")
+
+ class_table = {AUTOPART_TYPE_LVM: LVMFactory,
+ AUTOPART_TYPE_BTRFS: BTRFSFactory}
+
+ factory_class = class_table.get(device_type, MDFactory)
+ log.debug("instantiating %s: %r, %s, %s, %s" % (factory_class,
+ self, size, [d.name for d in disks], raid_level))
+ return factory_class(self, size, disks, raid_level, encrypted)
+
+ def newDevice(self, device_type, size, **kwargs):
+ """ Schedule creation of a device based on a top-down specification.
+
+ Arguments:
+
+ device_type an AUTOPART_TYPE constant (lvm|btrfs|plain)
+ size device's requested size
+
+ Keyword arguments:
+
+ mountpoint the device's mountpoint
+ fstype the device's filesystem type, or swap
+ label filesystem label
+ disks the set of disks we can allocate from
+ encrypted boolean
+
+ level (btrfs/md only) RAID level
+
+ striped (lvm only) boolean
+ mirrored (lvm only) boolean
+
+ """
+ mountpoint = kwargs.get("mountpoint")
+ fstype = kwargs.get("fstype")
+ label = kwargs.get("label")
+ disks = kwargs.get("disks")
+ encrypted = kwargs.get("encrypted", self.data.autopart.encrypted)
+
+ # md, btrfs
+ raid_level = kwargs.get("level")
+
+ # lvm
+ striped = kwargs.get("striped")
+ mirrored = kwargs.get("mirrored")
+
+ if not fstype:
+ fstype = self.getFSType(mountpoint=mountpoint)
+ if fstype == "swap":
+ mountpoint = None
+
+ if fstype == "swap" and device_type == AUTOPART_TYPE_BTRFS:
+ device_type = AUTOPART_TYPE_PLAIN
+ elif device_type != AUTOPART_TYPE_PLAIN and \
+ mountpoint and mountpoint.startswith("/boot"):
+ device_type = AUTOPART_TYPE_PLAIN
+
+ fmt_args = {}
+ if label:
+ fmt_args["label"] = label
+
+ # TODO: striping, mirroring, &c
+ # TODO: non-partition members (pv-on-md)
+ if device_type == AUTOPART_TYPE_PLAIN:
+ device = self.newPartition(grow=True, maxsize=size,
+ fmt_type=fstype, mountpoint=mountpoint,
+ fmt_args=fmt_args)
+ self.createDevice(device)
+ try:
+ doPartitioning(self)
+ except StorageError as e:
+ log.error("failed to allocate partitions: %s" % e)
+ actions = self.devicetree.findActions(device=device)
+ for a in reversed(actions):
+ self.devicetree.cancelAction(a)
+ else:
+ # fix the sizes of all allocated partitions
+ for partition in self.partitions:
+ partition.req_grow = False
+ partition.req_base_size = partition.size
+ partition.req_size = partition.size
+
+ return
+
+ factory = self.getDeviceFactory(device_type, size, **kwargs)
+
+ for p in self.partitions:
+ log.debug("%r" % p)
+
+ container = None
+ containers = [c for c in factory.container_list if not c.exists]
+ if containers:
+ container = containers[0]
+
+ parents = self.setContainerMembers(container, factory)
+
+ # set up container
+ if not container and factory.new_container_attr:
+ log.debug("creating new container")
+ container = factory.new_container(parents=parents)
+ self.createDevice(container)
+
+ if container:
+ parents = [container]
+ log.debug("%r" % container)
+
+ # add the new device to the container
+ if factory.new_device_attr:
+ free = getattr(container, "freeSpace", size)
+ if free < size:
+ log.info("adjusting device size from %.2f to %.2f so it fits "
+ "in container" % (size, free))
+ size = free
+
+ log.debug("creating new device")
+ device = factory.new_device(parents=parents,
+ size=size,
+ fmt_type=fstype,
+ mountpoint=mountpoint,
+ fmt_args=fmt_args)
+ self.createDevice(device)
+
+ def copy(self):
+ new = copy.deepcopy(self)
+ # go through and re-get partedPartitions from the disks since they
+ # don't get deep-copied
+ for partition in new.partitions:
+ if not partition._partedPartition:
+ continue
+
+ # don't ask me why, but we have to update the refs in req_disks
+ req_disks = []
+ for disk in partition.req_disks:
+ req_disks.append(new.devicetree.getDeviceByID(disk.id))
+
+ partition.req_disks = req_disks
+
+ p = partition.disk.format.partedDisk.getPartitionByPath(partition.path)
+ partition.partedPartition = p
+
+ return new
+
+
def mountExistingSystem(fsset, rootDevice,
allowDirty=None, dirtyCB=None,
readOnly=None):
@@ -2788,3 +2970,112 @@ def parseFSTab(devicetree, chroot=None):
return (mounts, swaps)
+
+class DeviceFactory(object):
+ type_desc = None
+ member_format = None
+ new_container_attr = None
+ new_device_attr = None
+ container_list_attr = None
+
+ def __init__(self, storage, size, disks, raid_level, encrypted):
+ self.storage = storage
+ self.size = size
+ self.disks = disks
+ self.raid_level = raid_level
+ self.encrypted = encrypted
+
+ self.size_func_kwargs = {}
+
+ if raid_level in ("raid1", "raid10"):
+ self.set_class = SameSizeSet
+ else:
+ self.set_class = TotalSizeSet
+
+ @property
+ def container_list(self):
+ return getattr(self.storage, self.container_list_attr)
+
+ def new_container(self, *args, **kwargs):
+ return getattr(self.storage, self.new_container_attr)(*args, **kwargs)
+
+ def new_device(self, *args, **kwargs):
+ return getattr(self.storage, self.new_device_attr)(*args, **kwargs)
+
+ def container_size_func(self, container):
+ return container.size
+
+ @property
+ def device_size(self):
+ return self.size
+
+
+class BTRFSFactory(DeviceFactory):
+ type_desc = "btrfs"
+ member_format = "btrfs"
+ new_container_attr = "newBTRFS"
+ new_device_attr = "newBTRFSSubVolume"
+ container_list_attr = "btrfsVolumes"
+
+ def __init__(self, storage, size, disks, raid_level, encrypted):
+ super(BTRFSFactory, self).__init__(storage, size, disks, raid_level,
+ encrypted)
+ self.raid_level = raid_level or "single"
+
+ @property
+ def device_size(self):
+ # until we get/need something better
+ if self.raid_level in ("single", "raid0"):
+ return self.size
+ elif self.raid_level in ("raid1", "raid10"):
+ return self.size * len(self.disks)
+
+ def container_size_func(self, container):
+ if container.exists:
+ size = container.size
+ else:
+ size = sum([s._size for s in container.subvolumes])
+
+ return size
+
+class LVMFactory(DeviceFactory):
+ type_desc = "lvm"
+ member_format = "lvmpv"
+ new_container_attr = "newVG"
+ new_device_attr = "newLV"
+ container_list_attr = "vgs"
+
+ def __init__(self, storage, size, disks, raid_level, encrypted):
+ super(LVMFactory, self).__init__(storage, size, disks, raid_level,
+ encrypted)
+ if raid_level in ("raid1", "raid10"):
+ self.size_func_kwargs["mirrored"] = True
+ if raid_level in ("raid0", "raid10"):
+ self.size_func_kwargs["striped"] = True
+
+ @property
+ def device_size(self):
+ return get_pv_space(self.size, len(self.disks), **self.size_func_kwargs)
+
+ def container_size_func(self, container):
+ return container.size - container.freeSpace
+
+class MDFactory(DeviceFactory):
+ type_desc = "md"
+ member_format = "mdmember"
+ new_container_attr = None
+ new_device_attr = "newMD"
+
+ def __init__(self, storage, size, disks, raid_level, encrypted):
+ super(MDFactory, self).__init__(storage, size, disks, raid_level,
+ encrypted)
+ self.size_func_kwargs = {"level": raid_level}
+
+ @property
+ def container_list(self):
+ return []
+
+ @property
+ def device_size(self):
+ return get_member_space(self.size, len(self.disks),
+ **self.size_func_kwargs)
diff --git a/pyanaconda/storage/devicelibs/btrfs.py b/pyanaconda/storage/devicelibs/btrfs.py
index 3fe12188c..f114ba4ca 100644
--- a/pyanaconda/storage/devicelibs/btrfs.py
+++ b/pyanaconda/storage/devicelibs/btrfs.py
@@ -32,24 +32,21 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
import logging
log = logging.getLogger("storage")
-def btrfs(args, progress=None, capture=False):
+def btrfs(args, capture=False):
kwargs = {}
kwargs["stderr"] = "/dev/tty5"
if capture:
- # execWithCapture can't take a progress window so it gets ignored
exec_func = iutil.execWithCapture
else:
- exec_func = iutil.execWithPulseProgress
+ exec_func = iutil.execWithRedirect
kwargs["stdout"] = "/dev/tty5"
- kwargs["progress"] = progress
ret = exec_func("btrfs", args, **kwargs)
- if not capture and ret.rc:
- raise BTRFSError(ret.stderr)
-
+ if ret:
+ raise BTRFSError(ret)
return ret
-def create_volume(devices, label=None, data=None, metadata=None, progress=None):
+def create_volume(devices, label=None, data=None, metadata=None):
""" For now, data and metadata must be strings mkfs.btrfs understands. """
if not devices:
raise ValueError("no devices specified")
@@ -68,11 +65,10 @@ def create_volume(devices, label=None, data=None, metadata=None, progress=None):
args.extend(devices)
- ret = iutil.execWithPulseProgress("mkfs.btrfs", args,
- stdout="/dev/tty5", stderr="/dev/tty5",
- progress=progress)
- if ret.rc:
- raise BTRFSError(ret.stderr)
+ ret = iutil.execWithRedirect("mkfs.btrfs", args,
+ stdout="/dev/tty5", stderr="/dev/tty5")
+ if ret:
+ raise BTRFSError(ret)
return ret
@@ -82,21 +78,21 @@ def create_volume(devices, label=None, data=None, metadata=None, progress=None):
# remove device
-def create_subvolume(mountpoint, name, progress=None):
+def create_subvolume(mountpoint, name):
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
path = os.path.normpath("%s/%s" % (mountpoint, name))
args = ["subvol", "create", path]
- return btrfs(args, progress=progress)
+ return btrfs(args)
-def delete_subvolume(mountpoint, name, progress=None):
+def delete_subvolume(mountpoint, name):
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
path = os.path.normpath("%s/%s" % (mountpoint, name))
args = ["subvol", "delete", path]
- return btrfs(args, progress=progress)
+ return btrfs(args)
def create_snapshot(source, dest):
pass
diff --git a/pyanaconda/storage/devicelibs/lvm.py b/pyanaconda/storage/devicelibs/lvm.py
index 136168b91..fedba99df 100644
--- a/pyanaconda/storage/devicelibs/lvm.py
+++ b/pyanaconda/storage/devicelibs/lvm.py
@@ -36,6 +36,10 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
MAX_LV_SLOTS = 256
+# some of lvm's defaults that we have no way to ask it for
+LVM_PE_START = 1.0 # MB
+LVM_PE_SIZE = 4.0 # MB
+
def has_lvm():
if iutil.find_program_in_path("lvm"):
for line in open("/proc/devices").readlines():
@@ -143,6 +147,17 @@ def clampSize(size, pesize, roundup=None):
return long(round(float(size)/float(pesize)) * pesize)
+def get_pv_space(size, disks, pesize=LVM_PE_SIZE,
+ striped=False, mirrored=False):
+ """ Given specs for an LV, return total PV space required. """
+ # XXX default extent size should be something we can ask of lvm
+ # TODO: handle striped and mirrored
+ # this is adding one extent for the lv's metadata
+ space = clampSize(size, pesize, roundup=True) + \
+ (LVM_PE_START * disks) + \
+ pesize
+ return space
+
def lvm(args):
ret = iutil.execWithRedirect("lvm", args,
stdout = "/dev/tty5",
diff --git a/pyanaconda/storage/devicelibs/mdraid.py b/pyanaconda/storage/devicelibs/mdraid.py
index e383bdf75..d0b85945d 100644
--- a/pyanaconda/storage/devicelibs/mdraid.py
+++ b/pyanaconda/storage/devicelibs/mdraid.py
@@ -31,6 +31,10 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
import logging
log = logging.getLogger("storage")
+# these defaults were determined empirically
+MD_SUPERBLOCK_SIZE = 2.0 # MB
+MD_CHUNK_SIZE = 0.5 # MB
+
# raidlevels constants
RAID10 = 10
RAID6 = 6
@@ -124,6 +128,40 @@ def get_raid_max_spares(raidlevel, nummembers):
raise ValueError, "invalid raid level %d" % raidlevel
+def get_member_space(size, disks, level=None):
+ space = 0 # size of *each* member device
+
+ if isinstance(level, str):
+ level = raidLevel(level)
+
+ min_members = get_raid_min_members(level)
+ if disks < min_members:
+ raise ValueError("raid%d requires at least %d disks"
+ % (level, min_members))
+
+ if level == RAID0:
+ # you need the sum of the member sizes to equal your desired capacity
+ space = size / disks
+ elif level == RAID1:
+ # you need each member's size to equal your desired capacity
+ space = size
+ elif level in (RAID4, RAID5):
+ # you need the sum of all but one members' sizes to equal your desired
+ # capacity
+ space = size / (disks - 1)
+ elif level == RAID6:
+ # you need the sum of all but two members' sizes to equal your desired
+ # capacity
+ space = size / (disks - 2)
+ elif level == RAID10:
+ # you need the sum of the member sizes to equal twice your desired
+ # capacity
+ space = size / (disks / 2.0)
+
+ space += MD_SUPERBLOCK_SIZE
+
+ return space * disks
+
def mdadm(args):
ret = iutil.execWithRedirect("mdadm", args,
stdout = "/dev/tty5",
diff --git a/pyanaconda/storage/devices.py b/pyanaconda/storage/devices.py
index 8fef3d168..650ced060 100644
--- a/pyanaconda/storage/devices.py
+++ b/pyanaconda/storage/devices.py
@@ -274,9 +274,6 @@ class Device(object):
"parents": [p.name for p in self.parents]}
return d
- def writeKS(self, f, preexisting=False, noformat=False, s=None):
- return
-
def removeChild(self):
log_method_call(self, name=self.name, kids=self.kids)
self.kids -= 1
@@ -1219,34 +1216,6 @@ class PartitionDevice(StorageDevice):
"flags": self.partedPartition.getFlagsAsString()})
return d
- def writeKS(self, f, preexisting=False, noformat=False, s=None):
- args = []
-
- if self.isExtended:
- return
-
- if self.req_grow:
- args.append("--grow")
- if self.req_max_size:
- args.append("--maxsize=%s" % self.req_max_size)
- if self.req_primary:
- args.append("--asprimary")
- if self.req_size:
- args.append("--size=%s" % (self.req_size or self.defaultSize))
- if preexisting:
- if len(self.req_disks) == 1:
- args.append("--ondisk=%s" % self.req_disks[0].name)
- else:
- args.append("--onpart=%s" % self.name)
- if noformat:
- args.append("--noformat")
-
- f.write("#part ")
- self.format.writeKS(f)
- f.write(" %s" % " ".join(args))
- if s:
- f.write(" %s" % s)
-
def _setTargetSize(self, newsize):
if newsize != self.currentSize:
# change this partition's geometry in-memory so that other
@@ -1925,13 +1894,6 @@ class LUKSDevice(DMCryptDevice):
parents=parents, sysfsPath=sysfsPath,
uuid=None, exists=exists)
- def writeKS(self, f, preexisting=False, noformat=False, s=None):
- self.slave.writeKS(f, preexisting=preexisting, noformat=noformat, s=s)
- f.write(" ")
- self.format.writeKS(f)
- if s:
- f.write(" %s" % s)
-
@property
def size(self):
if not self.exists or not self.partedDevice:
@@ -2023,7 +1985,7 @@ class LVMVolumeGroupDevice(DMDevice):
# TODO: validate peSize if given
if not self.peSize:
- self.peSize = 32.0 # MB
+ self.peSize = lvm.LVM_PE_SIZE # MB
if not self.exists:
self.pvCount = len(self.parents)
@@ -2069,27 +2031,6 @@ class LVMVolumeGroupDevice(DMDevice):
"lvNames": [lv.name for lv in self.lvs]})
return d
- def writeKS(self, f, preexisting=False, noformat=False, s=None):
- args = ["--pesize=%s" % int(self.peSize * 1024)]
- pvs = []
-
- for pv in self.pvs:
- pvs.append("pv.%s" % pv.format.majorminor)
-
- if preexisting:
- args.append("--useexisting")
- if noformat:
- args.append("--noformat")
-
- if self.reserved_space:
- args.append("--reserved-space=%d" % self.reserved_space)
- elif self.reserved_percent:
- args.append("--reserved-percent=%d" % self.reserved_percent)
-
- f.write("#volgroup %s %s %s" % (self.name, " ".join(args), " ".join(pvs)))
- if s:
- f.write(" %s" % s)
-
@property
def mapName(self):
""" This device's device-mapper map name """
@@ -2492,32 +2433,6 @@ class LVMLogicalVolumeDevice(DMDevice):
return d
- def writeKS(self, f, preexisting=False, noformat=False, s=None):
- args = ["--name=%s" % self.lvname,
- "--vgname=%s" % self.vg.name]
-
- if self.req_grow:
- args.extend(["--grow", "--size=%s" % (self.req_size or 1)])
-
- if self.req_max_size > 0:
- args.append("--maxsize=%s" % self.req_max_size)
- else:
- if self.req_percent > 0:
- args.append("--percent=%s" % self.req_percent)
- elif self.req_size > 0:
- args.append("--size=%s" % self.req_size)
-
- if preexisting:
- args.append("--useexisting")
- if noformat:
- args.append("--noformat")
-
- f.write("#logvol ")
- self.format.writeKS(f)
- f.write(" %s" % " ".join(args))
- if s:
- f.write(" %s" % s)
-
@property
def mirrored(self):
return self.stripes > 1
@@ -2841,28 +2756,6 @@ class MDRaidArrayDevice(StorageDevice):
"metadataVersion": self.metadataVersion})
return d
- def writeKS(self, f, preexisting=False, noformat=False, s=None):
- args = ["--level=%s" % self.level,
- "--device=%s" % self.name]
- mems = []
-
- if self.spares > 0:
- args.append("--spares=%s" % self.spares)
- if preexisting:
- args.append("--useexisting")
- if noformat:
- args.append("--noformat")
-
- for mem in self.parents:
- mems.append("raid.%s" % mem.format.majorminor)
-
- f.write("#raid ")
- self.format.writeKS(f)
- f.write(" %s" % " ".join(args))
- f.write(" %s" % " ".join(mems))
- if s:
- f.write(" %s" % s)
-
@property
def mdadmConfEntry(self):
""" This array's mdadm.conf entry. """
diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py
index df0d15f81..49fb1740e 100644
--- a/pyanaconda/storage/devicetree.py
+++ b/pyanaconda/storage/devicetree.py
@@ -1742,6 +1742,8 @@ class DeviceTree(object):
self._devices.remove(device)
self._hidden.append(device)
lvm.lvm_cc_addFilterRejectRegexp(device.name)
+ for parent in device.parents:
+ parent.removeChild()
def unhide(self, device):
# the hidden list should be in leaves-first order
@@ -1752,7 +1754,9 @@ class DeviceTree(object):
hidden.id))
self._hidden.remove(hidden)
self._devices.append(hidden)
- lvm.lvm_cc_removeFilterRejectRegexp(device.name)
+ lvm.lvm_cc_removeFilterRejectRegexp(hidden.name)
+ for parent in device.parents:
+ parent.addChild()
def _setupLvs(self):
ret = False
@@ -2102,6 +2106,11 @@ class DeviceTree(object):
def getDevicesByInstance(self, device_class):
return [d for d in self._devices if isinstance(d, device_class)]
+ def getDeviceByID(self, id_num):
+ for device in self._devices:
+ if device.id == id_num:
+ return device
+
@property
def devices(self):
""" List of device instances """
diff --git a/pyanaconda/storage/fcoe.py b/pyanaconda/storage/fcoe.py
index fc2ad4017..d0614756a 100644
--- a/pyanaconda/storage/fcoe.py
+++ b/pyanaconda/storage/fcoe.py
@@ -141,10 +141,6 @@ class fcoe(object):
self._stabilize()
self.nics.append((nic, dcb, auto_vlan))
- def writeKS(self, f):
- # fixme plenty (including add ks support for fcoe in general)
- return
-
def write(self):
if not self.nics:
return
diff --git a/pyanaconda/storage/formats/__init__.py b/pyanaconda/storage/formats/__init__.py
index 3df470ebf..12ca5bf1c 100644
--- a/pyanaconda/storage/formats/__init__.py
+++ b/pyanaconda/storage/formats/__init__.py
@@ -437,7 +437,4 @@ class DeviceFormat(object):
(udev_device_get_major(dev), udev_device_get_minor(dev))
return self._majorminor
- def writeKS(self, f):
- return
-
collect_device_format_classes()
diff --git a/pyanaconda/storage/formats/biosboot.py b/pyanaconda/storage/formats/biosboot.py
index 34bd76019..861b59190 100644
--- a/pyanaconda/storage/formats/biosboot.py
+++ b/pyanaconda/storage/formats/biosboot.py
@@ -56,9 +56,5 @@ class BIOSBoot(DeviceFormat):
from pyanaconda import platform
return isinstance(platform.getPlatform(), platform.X86)
- def writeKS(self, f):
- f.write("biosboot --fstype=%s" % self.type)
-
-
register_device_format(BIOSBoot)
diff --git a/pyanaconda/storage/formats/disklabel.py b/pyanaconda/storage/formats/disklabel.py
index 338a88011..87dbea2d8 100644
--- a/pyanaconda/storage/formats/disklabel.py
+++ b/pyanaconda/storage/formats/disklabel.py
@@ -85,10 +85,13 @@ class DiskLabel(DeviceFormat):
"""
new = self.__class__.__new__(self.__class__)
memo[id(self)] = new
- shallow_copy_attrs = ('_partedDevice', '_partedDisk', '_origPartedDisk')
+ shallow_copy_attrs = ('_partedDevice', '_alignment', '_endAlignment')
+ duplicate_attrs = ('_partedDisk', '_origPartedDisk')
for (attr, value) in self.__dict__.items():
if attr in shallow_copy_attrs:
setattr(new, attr, copy.copy(value))
+ elif attr in duplicate_attrs:
+ setattr(new, attr, value.duplicate())
else:
setattr(new, attr, copy.deepcopy(value, memo))
diff --git a/pyanaconda/storage/formats/fs.py b/pyanaconda/storage/formats/fs.py
index eaf2405d3..b9cf3b370 100644
--- a/pyanaconda/storage/formats/fs.py
+++ b/pyanaconda/storage/formats/fs.py
@@ -868,16 +868,6 @@ class FS(DeviceFormat):
def sync(self, root="/"):
pass
- def writeKS(self, f):
- f.write("%s --fstype=%s" % (self.mountpoint, self.type))
-
- if self.label:
- f.write(" --label=\"%s\"" % self.label)
-
- if self.fsprofile:
- f.write(" --fsprofile=\"%s\"" % self.fsprofile)
-
-
class Ext2FS(FS):
""" ext2 filesystem. """
_type = "ext2"
@@ -1332,9 +1322,6 @@ class AppleBootstrapFS(HFS):
return (isinstance(platform.getPlatform(), platform.NewWorldPPC)
and self.utilsAvailable)
- def writeKS(self, f):
- f.write("appleboot --fstype=%s" % self.type)
-
register_device_format(AppleBootstrapFS)
@@ -1471,9 +1458,6 @@ class Iso9660FS(FS):
_migratable = False
_defaultMountOptions = ["ro"]
- def writeKS(self, f):
- return
-
register_device_format(Iso9660FS)
@@ -1492,9 +1476,6 @@ class NoDevFS(FS):
def _getExistingSize(self):
pass
- def writeKS(self, f):
- return
-
register_device_format(NoDevFS)
@@ -1535,9 +1516,6 @@ class BindFS(FS):
def _getExistingSize(self):
pass
- def writeKS(self, f):
- return
-
register_device_format(BindFS)
diff --git a/pyanaconda/storage/formats/luks.py b/pyanaconda/storage/formats/luks.py
index 8ddad642e..6bb8b6486 100644
--- a/pyanaconda/storage/formats/luks.py
+++ b/pyanaconda/storage/formats/luks.py
@@ -107,9 +107,6 @@ class LUKS(DeviceFormat):
"backup": self.add_backup_passphrase})
return s
- def writeKS(self, f):
- f.write(" --encrypted")
-
@property
def dict(self):
d = super(LUKS, self).dict
diff --git a/pyanaconda/storage/formats/lvmpv.py b/pyanaconda/storage/formats/lvmpv.py
index 994561e68..77bbacdbd 100644
--- a/pyanaconda/storage/formats/lvmpv.py
+++ b/pyanaconda/storage/formats/lvmpv.py
@@ -65,7 +65,7 @@ class LVMPhysicalVolume(DeviceFormat):
self.vgUuid = kwargs.get("vgUuid")
# liblvm may be able to tell us this at some point, even
# for not-yet-created devices
- self.peStart = kwargs.get("peStart", 1.0) # in MB
+ self.peStart = kwargs.get("peStart", lvm.LVM_PE_START) # in MB
self.inconsistentVG = False
@@ -141,8 +141,5 @@ class LVMPhysicalVolume(DeviceFormat):
return (self.exists and self.vgName and
os.path.isdir("/dev/mapper/%s" % self.vgName))
- def writeKS(self, f):
- f.write("pv.%s" % self.majorminor)
-
register_device_format(LVMPhysicalVolume)
diff --git a/pyanaconda/storage/formats/mdraid.py b/pyanaconda/storage/formats/mdraid.py
index 39164a4ee..c543434cf 100644
--- a/pyanaconda/storage/formats/mdraid.py
+++ b/pyanaconda/storage/formats/mdraid.py
@@ -110,9 +110,6 @@ class MDRaidMember(DeviceFormat):
def hidden(self):
return (self._hidden or self.biosraid)
- def writeKS(self, f):
- f.write("raid.%s" % self.majorminor)
-
# nodmraid -> Wether to use BIOS RAID or not
# Note the anaconda cmdline has not been parsed yet when we're first imported,
# so we can not use flags.dmraid here
diff --git a/pyanaconda/storage/formats/prepboot.py b/pyanaconda/storage/formats/prepboot.py
index f671e9563..884aa8fc4 100644
--- a/pyanaconda/storage/formats/prepboot.py
+++ b/pyanaconda/storage/formats/prepboot.py
@@ -83,9 +83,5 @@ class PPCPRePBoot(DeviceFormat):
from pyanaconda import platform
return isinstance(platform.getPlatform(), platform.IPSeriesPPC)
- def writeKS(self, f):
- f.write("prepboot --fstype=%s" % self.type)
-
-
register_device_format(PPCPRePBoot)
diff --git a/pyanaconda/storage/formats/swap.py b/pyanaconda/storage/formats/swap.py
index e961afe49..2267d680d 100644
--- a/pyanaconda/storage/formats/swap.py
+++ b/pyanaconda/storage/formats/swap.py
@@ -167,12 +167,5 @@ class SwapSpace(DeviceFormat):
self.exists = True
self.notifyKernel()
- def writeKS(self, f):
- f.write("swap")
-
- if self.label:
- f.write(" --label=\"%s\"" % self.label)
-
-
register_device_format(SwapSpace)
diff --git a/pyanaconda/storage/iscsi.py b/pyanaconda/storage/iscsi.py
index ce21a6d0f..3598ae05b 100644
--- a/pyanaconda/storage/iscsi.py
+++ b/pyanaconda/storage/iscsi.py
@@ -395,32 +395,6 @@ class iscsi(object):
self.stabilize(intf)
- def writeKS(self, f):
-
- if not self.initiatorSet:
- return
-
- nodes = ""
- for n in self.active_nodes():
- if n in self.ibftNodes:
- continue
- nodes += "iscsi --ipaddr %s --port %s --target %s" % (n.address, n.port, n.name)
- if n.iface != "default":
- nodes += " --iface %s" % self.ifaces[n.iface]
- auth = n.getAuth()
- if auth:
- nodes += " --user %s" % auth.username
- nodes += " --password %s" % auth.password
- if len(auth.reverse_username):
- nodes += " --reverse-user %s" % auth.reverse_username
- if len(auth.reverse_password):
- nodes += " --reverse-password %s" % auth.reverse_password
- nodes += "\n"
-
- if nodes:
- f.write("iscsiname %s\n" %(self.initiator,))
- f.write("%s" % nodes)
-
def write(self, storage):
if not self.initiatorSet:
return
diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py
index e9c06f6d0..7f34b85df 100644
--- a/pyanaconda/storage/partitioning.py
+++ b/pyanaconda/storage/partitioning.py
@@ -131,12 +131,6 @@ def _schedulePartitions(storage, disks):
if request.requiredSpace and request.requiredSpace > free:
continue
- if request.fstype is None:
- if request.mountpoint == "/boot":
- request.fstype = storage.defaultBootFSType
- else:
- request.fstype = storage.defaultFSType
-
elif request.fstype in ("prepboot", "efi", "biosboot", "hfs+") and \
stage1_device:
# there should never be a need for more than one of these
@@ -263,6 +257,7 @@ def _scheduleVolumes(storage, devs):
"singlePV": request.singlePV})
else:
kwargs.update({"parents": [container],
+ "size": request.size,
"subvol": True})
dev = new_volume(**kwargs)
@@ -270,19 +265,6 @@ def _scheduleVolumes(storage, devs):
# schedule the device for creation
storage.createDevice(dev)
-def scheduleShrinkActions(storage):
- """ Schedule actions to shrink partitions as per autopart selection. """
- for (path, size) in storage.shrinkPartitions.items():
- device = storage.devicetree.getDeviceByPath(path, preferLeaves=False)
- if not device or not isinstance(device, PartitionDevice):
- raise StorageError("device %s scheduled for shrink disappeared"
- % path)
- storage.devicetree.registerAction(ActionResizeFormat(device, size))
- storage.devicetree.registerAction(ActionResizeDevice(device, size))
- # aligning the partition's new end sector may have changed the size
- if device.targetSize != size:
- device.format.targetSize = device.targetSize
-
def doAutoPartition(storage, data):
log.debug("doAutoPart: %s" % storage.doAutoPart)
log.debug("encryptedAutoPart: %s" % storage.encryptedAutoPart)
@@ -299,9 +281,6 @@ def doAutoPartition(storage, data):
devs = []
if storage.doAutoPart:
- # XXX this doesn't belong on newui
- scheduleShrinkActions(storage)
-
disks = _getCandidateDisks(storage)
devs = _scheduleImplicitPartitions(storage, disks)
log.debug("candidate disks: %s" % disks)
@@ -321,6 +300,8 @@ def doAutoPartition(storage, data):
# grow LVs
growLVM(storage)
+
+ growBTRFS(storage)
except PartitioningWarning as e:
log.warning(str(e))
if errorHandler.cb(e) == ERROR_RAISE:
@@ -768,8 +749,7 @@ def doPartitioning(storage):
for part in storage.partitions:
part.req_bootable = False
- if part.exists or \
- (storage.deviceImmutable(part) and part.partedPartition):
+ if part.exists:
# if the partition is preexisting or part of a complex device
# then we shouldn't modify it
partitions.remove(part)
@@ -789,7 +769,7 @@ def doPartitioning(storage):
free = getFreeRegions(disks)
try:
allocatePartitions(storage, disks, partitions, free)
- growPartitions(disks, partitions, free)
+ growPartitions(disks, partitions, free, size_sets=storage.size_sets)
finally:
# The number and thus the name of partitions may have changed now,
# allocatePartitions() takes care of this for new partitions, but not
@@ -1081,13 +1061,14 @@ class Request(object):
self.device = device
self.growth = 0 # growth in sectors
self.max_growth = 0 # max growth in sectors
- self.done = not device.req_grow # can we grow this request more?
+ self.done = not getattr(device, "req_grow", True) # can we grow this
+ # request more?
self.base = 0 # base sectors
@property
def growable(self):
""" True if this request is growable. """
- return self.device.req_grow
+ return getattr(self.device, "req_grow", True)
@property
def id(self):
@@ -1188,6 +1169,8 @@ class Chunk(object):
for req in requests:
self.addRequest(req)
+ self.skip_list = []
+
def __repr__(self):
s = ("%(type)s instance --\n"
"device = %(device)s length = %(length)d size = %(size)d\n"
@@ -1212,6 +1195,22 @@ class Chunk(object):
if not req.done:
self.base += req.base
+ def reclaim(self, request, amount):
+ """ Reclaim units from a request and return them to the pool. """
+ log.debug("reclaim: %s %d (%d MB)" % (request, amount, self.lengthToSize(amount)))
+ if request.growth < amount:
+ log.error("tried to reclaim %d from request with %d of growth"
+ % (amount, request.growth))
+ raise ValueError("cannot reclaim more than request has grown")
+
+ request.growth -= amount
+ self.pool += amount
+
+ # put this request in the skip list so we don't try to grow it the
+ # next time we call growRequests to allocate the newly re-acquired pool
+ if request not in self.skip_list:
+ self.skip_list.append(request)
+
@property
def growth(self):
""" Sum of growth for all requests in this chunk. """
@@ -1291,7 +1290,7 @@ class Chunk(object):
log.debug("%d requests and %d (%dMB) left in chunk" %
(self.remaining, self.pool, self.lengthToSize(self.pool)))
for p in self.requests:
- if p.done:
+ if p.done or p in self.skip_list:
continue
if not uniform:
@@ -1336,6 +1335,10 @@ class Chunk(object):
if self.pool == 0:
break
+ # requests that were skipped over this time through are back on the
+ # table next time
+ self.skip_list = []
+
class DiskChunk(Chunk):
""" A free region on disk from which partitions will be allocated """
@@ -1549,7 +1552,121 @@ def getDiskChunks(disk, partitions, free):
return chunks
-def growPartitions(disks, partitions, free):
+class TotalSizeSet(object):
+ """ Set of device requests with a target combined size.
+
+ This will be handled by growing the requests until the desired combined
+ size has been achieved.
+ """
+ def __init__(self, devices, size):
+ self.devices = devices
+ self.size = size
+
+ self.requests = []
+
+ self.allocated = sum([d.req_base_size for d in self.devices])
+ log.debug("set.allocated = %d" % self.allocated)
+
+ def allocate(self, amount):
+ log.debug("allocating %d to TotalSizeSet with %d/%d (%d needed)"
+ % (amount, self.allocated, self.size, self.needed))
+ self.allocated += amount
+
+ @property
+ def needed(self):
+ return self.size - self.allocated
+
+ def deallocate(self, amount):
+ log.debug("deallocating %d from TotalSizeSet with %d/%d (%d needed)"
+ % (amount, self.allocated, self.size, self.needed))
+ self.allocated -= amount
+
+class SameSizeSet(object):
+ """ Set of device requests with a common target size. """
+ def __init__(self, devices, size, grow=False, max_size=None):
+ self.devices = devices
+ self.size = int(size / len(devices))
+ self.grow = grow
+ self.max_size = max_size
+
+ self.requests = []
+
+def manageSizeSets(size_sets, chunks):
+ growth_by_request = {}
+ requests_by_device = {}
+ chunks_by_request = {}
+ for chunk in chunks:
+ for request in chunk.requests:
+ requests_by_device[request.device] = request
+ chunks_by_request[request] = chunk
+ growth_by_request[request] = 0
+
+ for i in range(2):
+ reclaimed = dict([(chunk, 0) for chunk in chunks])
+ for ss in size_sets:
+ if isinstance(ss, TotalSizeSet):
+ # TotalSizeSet members are trimmed to achieve the requested
+ # total size
+ log.debug("set: %s %d/%d" % ([d.name for d in ss.devices],
+ ss.allocated, ss.size))
+
+ for device in ss.devices:
+ request = requests_by_device[device]
+ if request.done:
+ continue
+
+ chunk = chunks_by_request[request]
+ new_growth = request.growth - growth_by_request[request]
+ ss.allocate(chunk.lengthToSize(new_growth))
+
+ # decide how much to take back from each request
+ # We may assume that all requests have the same base size.
+ # We're shooting for a roughly equal distribution by trimming
+ # growth from the requests that have grown the most first.
+ requests = sorted([requests_by_device[d] for d in ss.devices],
+ key=lambda r: r.growth, reverse=True)
+ for request in requests:
+ chunk = chunks_by_request[request]
+ log.debug("%s" % request)
+ log.debug("needed: %d" % ss.needed)
+
+ if ss.needed < 0:
+ # it would be good to take back some from each device
+ # instead of taking all from the last one(s)
+ extra = min(-chunk.sizeToLength(ss.needed),
+ request.growth)
+ reclaimed[chunk] += extra
+ chunk.reclaim(request, extra)
+ ss.deallocate(chunk.lengthToSize(extra))
+
+ if ss.needed <= 0:
+ request.done = True
+
+ elif isinstance(ss, SameSizeSet):
+ # SameSizeSet members all have the same size as the smallest
+ # member
+ requests = [requests_by_device[d] for d in ss.devices]
+ min_growth = min([r.growth for r in requests])
+ for request in requests:
+ if request.growth > min_growth:
+ chunk = chunks_by_request[request]
+ extra = request.growth - min_growth
+ reclaimed[chunk] += extra
+ chunk.reclaim(request, extra)
+ request.done = True
+ elif request.growth == min_growth:
+ request.done = True
+
+ # store previous growth amounts so we know how much was allocated in
+ # the latest growRequests call
+ for request in growth_by_request.keys():
+ growth_by_request[request] = request.growth
+
+ for chunk in chunks:
+ if reclaimed[chunk] and not chunk.done:
+ chunk.growRequests()
+
+def growPartitions(disks, partitions, free, size_sets=None):
""" Grow all growable partition requests.
Partitions have already been allocated from chunks of free space on
@@ -1575,36 +1692,60 @@ def growPartitions(disks, partitions, free):
log.debug("no growable partitions")
return
+ if size_sets is None:
+ size_sets = []
+
log.debug("growable partitions are %s" % [p.name for p in all_growable])
+ #
+ # collect info about each disk and the requests it contains
+ #
+ chunks = []
for disk in disks:
- log.debug("growing partitions on %s" % disk.name)
sector_size = disk.format.partedDevice.sectorSize
- # find any extended partition on this disk
- extended_geometry = getattr(disk.format.extendedPartition,
- "geometry",
- None) # parted.Geometry
-
# list of free space regions on this disk prior to partition allocation
disk_free = [f for f in free if f.device.path == disk.path]
if not disk_free:
log.debug("no free space on %s" % disk.name)
continue
- chunks = getDiskChunks(disk, partitions, disk_free)
- log.debug("disk %s has %d chunks" % (disk.name, len(chunks)))
- # grow the partitions in each chunk as a group
+ disk_chunks = getDiskChunks(disk, partitions, disk_free)
+ log.debug("disk %s has %d chunks" % (disk.name, len(disk_chunks)))
+ chunks.extend(disk_chunks)
+
+ #
+ # grow the partitions in each chunk as a group
+ #
+ for chunk in chunks:
+ if not chunk.hasGrowable:
+ # no growable partitions in this chunk
+ continue
+
+ chunk.growRequests()
+
+ # adjust set members' growth amounts as needed
+ manageSizeSets(size_sets, chunks)
+
+ for disk in disks:
+ log.debug("growing partitions on %s" % disk.name)
for chunk in chunks:
+ if chunk.path != disk.path:
+ continue
+
if not chunk.hasGrowable:
# no growable partitions in this chunk
continue
- chunk.growRequests()
-
# recalculate partition geometries
disklabel = disk.format
start = chunk.geometry.start
+
+ # find any extended partition on this disk
+ extended_geometry = getattr(disklabel.extendedPartition,
+ "geometry",
+ None) # parted.Geometry
+
# align start sector as needed
if not disklabel.alignment.isAligned(chunk.geometry, start):
start = disklabel.alignment.alignUp(chunk.geometry, start)
@@ -1759,3 +1900,21 @@ def growLVM(storage):
# initial size.
req.device.size = chunk.lengthToSize(req.base + req.growth)
+def growBTRFS(storage):
+ """ Set up relative sizes for subvolumes after autopart for custom ui. """
+ for vol in storage.btrfsVolumes:
+ if vol.exists:
+ continue
+
+ requests = []
+ for subvol in vol.subvolumes:
+ request = Request(subvol)
+ request.base = subvol._size
+ requests.append(request)
+
+ chunk = Chunk(int(vol.size), requests=requests)
+ chunk.path = "btrfs %s" % vol.name
+ chunk.growRequests()
+
+ for req in chunk.requests:
+ req.device._size = chunk.lengthToSize(req.base + req.growth)
diff --git a/pyanaconda/storage/size.py b/pyanaconda/storage/size.py
index eb3e2e4ff..75ea933e0 100644
--- a/pyanaconda/storage/size.py
+++ b/pyanaconda/storage/size.py
@@ -139,27 +139,29 @@ class Size(Decimal):
return self
- def __str__(self):
+ def __str__(self, context=None):
return self.humanReadable()
def __repr__(self):
return "Size('%s')" % self
- def __add__(self, other):
- return Size(bytes=Decimal.__add__(self, other))
+ def __add__(self, other, context=None):
+ return Size(bytes=Decimal.__add__(self, other, context=context))
# needed to make sum() work with Size arguments
- def __radd__(self, other):
- return Size(bytes=Decimal.__radd__(self, other))
+ def __radd__(self, other, context=None):
+ return Size(bytes=Decimal.__radd__(self, other, context=context))
- def __sub__(self, other):
- return Size(bytes=Decimal.__sub__(self, other))
+ def __sub__(self, other, context=None):
+ # subtraction is implemented using __add__ and negation, so we'll
+ # be getting passed a Size
+ return Decimal.__sub__(self, other, context=context)
- def __mul__(self, other):
- return Size(bytes=Decimal.__mul__(self, other))
+ def __mul__(self, other, context=None):
+ return Size(bytes=Decimal.__mul__(self, other, context=context))
- def __div__(self, other):
- return Size(bytes=Decimal.__div__(self, other))
+ def __div__(self, other, context=None):
+ return Size(bytes=Decimal.__div__(self, other, context=context))
def _trimEnd(self, val):
""" Internal method to trim trailing zeros. """
diff --git a/pyanaconda/storage/zfcp.py b/pyanaconda/storage/zfcp.py
index e74c1e10e..3612dc07b 100644
--- a/pyanaconda/storage/zfcp.py
+++ b/pyanaconda/storage/zfcp.py
@@ -411,14 +411,6 @@ class ZFCP:
except ValueError as e:
log.warn(str(e))
- def writeKS(self, f):
- if len(self.fcpdevs) == 0:
- return
- for d in self.fcpdevs:
- f.write("zfcp --devnum %s --wwpn %s --fcplun %s\n" %(d.devnum,
- d.wwpn,
- d.fcplun))
-
def write(self):
if len(self.fcpdevs) == 0:
return
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index 10570a8cc..02f95e4ed 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -18,7 +18,7 @@
#
# Red Hat Author(s): Chris Lumens <clumens@redhat.com>
#
-import importlib, inspect, os, sys
+import importlib, inspect, os, sys, time
import meh.ui.gui
from pyanaconda.ui import UserInterface, common
@@ -27,6 +27,9 @@ from pyanaconda.ui.gui.utils import enlightbox
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
+import logging
+log = logging.getLogger("anaconda")
+
__all__ = ["GraphicalUserInterface", "UIObject"]
_screenshotIndex = 0
@@ -79,6 +82,18 @@ class GraphicalUserInterface(UserInterface):
def run(self):
from gi.repository import Gtk
+ if Gtk.main_level() > 0:
+ # Gtk main loop running. That means python-meh caught exception
+ # and runs its main loop. Do not crash Gtk by running another one
+ # from a different thread and just wait until python-meh is
+ # finished, then quit.
+ log.error("Unhandled exception caught, waiting for python-meh to "\
+ "exit")
+ while Gtk.main_level() > 0:
+ time.sleep(2)
+
+ sys.exit(0)
+
from pyanaconda.product import isFinal, productName, productVersion
# If we set these values on the very first window shown, they will get
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index c76146a72..9f3a72b07 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -32,13 +32,17 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
N_ = lambda x: x
P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z)
+from contextlib import contextmanager
+
from pykickstart.constants import *
from pyanaconda.product import productName, productVersion
from pyanaconda.storage.formats import device_formats
+from pyanaconda.storage.formats import getFormat
from pyanaconda.storage.size import Size
from pyanaconda.storage import Root
from pyanaconda.storage.partitioning import doPartitioning
+from pyanaconda.storage.partitioning import doAutoPartition
from pyanaconda.storage.errors import StorageError
from pyanaconda.ui.gui import GUIObject
@@ -51,10 +55,26 @@ from pyanaconda.ui.gui.categories.storage import StorageCategory
from gi.repository import Gtk
+import logging
+log = logging.getLogger("anaconda")
+
__all__ = ["CustomPartitioningSpoke"]
new_install_name = _("New %s %s Installation") % (productName, productVersion)
+class UIStorageFilter(logging.Filter):
+ def filter(self, record):
+ record.name = "storage.ui"
+ return True
+
+@contextmanager
+def ui_storage_logger():
+ storage_log = logging.getLogger("storage")
+ f = UIStorageFilter()
+ storage_log.addFilter(f)
+ yield
+ storage_log.removeFilter(f)
+
class AddDialog(GUIObject):
builderObjects = ["addDialog"]
mainWidgetName = "addDialog"
@@ -125,8 +145,98 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._current_selector = None
self._when_create_text = ""
+ self._devices = []
+ self._unused_devices = None # None indicates uninitialized
+ self._free_space = Size(bytes=0)
+
+ def _propagate_actions(self, actions):
+ """ Register actions from the UI with the main Storage instance. """
+ for ui_action in actions:
+ ui_device = ui_action.device
+ device = self.storage.devicetree.getDeviceByID(ui_device.id)
+ if device is None:
+ # This device does not exist in the main devicetree.
+ #
+ # That means it was created in the ui. If it is still present in
+ # the ui device list, schedule a create action for it. If not,
+ # just ignore it.
+ if not ui_action.isCreate or ui_device not in self._devices:
+ # this is a device that was created and then destroyed here
+ continue
+
+ args = [device]
+ if ui_action.isResize:
+ args.append(ui_device.targetSize)
+
+ if ui_action.isCreate and ui_action.isFormat:
+ args.append(ui_device.format)
+
+ if ui_action.isCreate and ui_action.isDevice:
+ # We're going to just move the already-defined device into the
+ # main devicetree, but first we need to replace its parents
+ # with the corresponding devices from the main devicetree
+ # instead of the ones from our local devicetree.
+ parents = [self.storage.devicetree.getDeviceByID(p.id)
+ for p in ui_device.parents]
+
+ if hasattr(ui_device, "partedPartition"):
+ # This cleans up any references to parted.Disk instances
+ # that only exist in our local devicetree.
+ ui_device.partedPartition = None
+
+ req_disks = [self.storage.devicetree.getDeviceByID(p.id)
+ for p in ui_device.req_disks]
+ ui_device.req_disks = req_disks
+
+ # If we somehow ended up with a different number of parents
+ # go ahead and take a dump on the floor.
+ assert(len(parents) == len(ui_device.parents))
+ ui_device.parents = parents
+ args = [ui_device]
+
+ log.debug("duplicating action '%s'" % ui_action)
+ action = ui_action.__class__(*args)
+ self.storage.devicetree.registerAction(action)
def apply(self):
+ ui_devicetree = self.__storage.devicetree
+
+ log.debug("converting custom spoke changes into actions")
+ for action in ui_devicetree.findActions():
+ log.debug("%s" % action)
+
+ # schedule actions for device removals, resizes
+ actions = ui_devicetree.findActions(type="destroy")
+ partition_destroys = [a for a in actions
+ if a.device.type == "partition"]
+ actions.extend(ui_devicetree.findActions(type="resize"))
+ self._propagate_actions(actions)
+
+ # register all disklabel create actions
+ actions = ui_devicetree.findActions(type="create", object="format")
+ disklabel_creates = [a for a in actions
+ if a.device.format.type == "disklabel"]
+ self._propagate_actions(disklabel_creates)
+
+ # register partition create actions, including formatting
+ actions = ui_devicetree.findActions(type="create")
+ partition_creates = [a for a in actions if a.device.type == "partition"]
+ self._propagate_actions(partition_creates)
+
+ if partition_creates or partition_destroys:
+ try:
+ doPartitioning(self.storage)
+ except Exception as e:
+ # TODO: error handling
+ raise
+
+ # register all other create actions
+ already_handled = disklabel_creates + partition_creates
+ actions = [a for a in ui_devicetree.findActions(type="create")
+ if a not in already_handled]
+ self._propagate_actions(actions)
+
+ # set up bootloader and check the configuration
self.storage.setUpBootLoader()
StorageChecker.run(self)
@@ -147,7 +257,6 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._configButton = self.builder.get_object("configureButton")
def initialize(self):
- from pyanaconda.storage.devices import DiskDevice
from pyanaconda.storage.formats.fs import FS
NormalSpoke.initialize(self)
@@ -198,40 +307,40 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
else:
return None
+ @property
def _clearpartDevices(self):
- return [d for d in self.storage.devicetree.devices if d.name in self.data.clearpart.drives]
-
- def _unusedDevices(self):
- from pyanaconda.storage.devices import DiskDevice
- return [d for d in self.storage.unusedDevices if d.disks and not isinstance(d, DiskDevice)]
+ return [d for d in self._devices if d.name in self.data.clearpart.drives]
- def _currentFreeSpace(self):
- """Add up all the free space on selected disks and return it as a Size."""
- totalFree = Size(bytes=0)
+ @property
+ def unusedDevices(self):
+ if self._unused_devices is None:
+ self._unused_devices = [d for d in self.__storage.unusedDevices
+ if d.disks and not d.isDisk]
- freeDisks = self.storage.getFreeSpace(disks=self._clearpartDevices())
- for tup in freeDisks.values():
- for chunk in tup:
- totalFree += chunk
+ return self._unused_devices
- return totalFree
+ def _setCurrentFreeSpace(self):
+ """Add up all the free space on selected disks and return it as a Size."""
+ freeDisks = self.__storage.getFreeSpace(clearPartType=CLEARPART_TYPE_NONE)
+ self._free_space = sum([f[0] for f in freeDisks.values()])
def _currentTotalSpace(self):
"""Add up the sizes of all selected disks and return it as a Size."""
totalSpace = 0
- for disk in self._clearpartDevices():
+ for disk in self._clearpartDevices:
totalSpace += disk.size
return Size(spec="%s MB" % totalSpace)
def _updateSpaceDisplay(self):
# Set up the free space/available space displays in the bottom left.
+ self._setCurrentFreeSpace()
self._availableSpaceLabel = self.builder.get_object("availableSpaceLabel")
self._totalSpaceLabel = self.builder.get_object("totalSpaceLabel")
self._summaryButton = self.builder.get_object("summary_button")
- self._availableSpaceLabel.set_text(str(self._currentFreeSpace()))
+ self._availableSpaceLabel.set_text(str(self._free_space))
self._totalSpaceLabel.set_text(str(self._currentTotalSpace()))
summaryLabel = self._summaryButton.get_children()[0]
@@ -245,7 +354,14 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
def refresh(self):
NormalSpoke.refresh(self)
+ self.__storage = self.storage.copy()
+ self.__storage.devicetree._actions = [] # keep things simple
+ self._devices = self.__storage.devices
self._do_refresh()
+
+ # update our free space number based on Storage
+ self._setCurrentFreeSpace()
+
self._updateSpaceDisplay()
def _do_refresh(self):
@@ -261,9 +377,13 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
# We can only have one page expanded at a time.
did_expand = False
- unused = self._unusedDevices()
- new_devices = [d for d in self.storage.devicetree.leaves if d not in unused and not d.exists]
- roots = self.storage.roots[:]
+ unused = [d for d in self.unusedDevices if d.isleaf and d in self._devices]
+ new_devices = [d for d in self._devices if not d.exists]
+
+ log.debug("ui: unused=%s" % [d.name for d in unused])
+ log.debug("ui: new_devices=%s" % [d.name for d in new_devices])
+
+ ui_roots = self.__storage.roots[:]
# If we've not yet run autopart, add an instance of CreateNewPage. This
# ensures it's only added once.
@@ -278,39 +398,44 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
label = self.builder.get_object("whenCreateLabel")
label.set_text(self._when_create_text % (productName, productVersion))
else:
- swaps = [d for d in new_devices if d in self.storage.swaps]
- mounts = dict([(d.format.mountpoint, d) for d in new_devices if getattr(d.format, "mountpoint", None)])
+ swaps = [d for d in new_devices if d.format.type == "swap"]
+ mounts = dict([(d.format.mountpoint, d) for d in new_devices
+ if getattr(d.format, "mountpoint", None)])
new_root = Root(mounts=mounts, swaps=swaps, name=new_install_name)
- roots.insert(0, new_root)
+ ui_roots.insert(0, new_root)
# Add in all the existing (or autopart-created) operating systems.
- for root in roots:
- if root.device and root.device not in self.storage.devices:
+ for root in ui_roots:
+ # don't make a page if none of the root's devices are left
+ if not [d for d in root.swaps + root.mounts.values()
+ if d in self._devices]:
continue
page = Page()
page.pageTitle = root.name
for device in root.swaps:
- if device not in self.storage.devices:
+ if device not in self._devices:
continue
- selector = page.addDevice("Swap", device.size, None, self.on_selector_clicked)
+ selector = page.addDevice("Swap",
+ Size(spec="%f MB" % device.size),
+ None, self.on_selector_clicked)
selector._device = device
selector._root = root
for (mountpoint, device) in root.mounts.iteritems():
- if device not in self.storage.devices:
+ if device not in self._devices:
continue
- selector = page.addDevice(self._mountpointName(mountpoint) or device.format.name, device.size, mountpoint, self.on_selector_clicked)
+ selector = page.addDevice(self._mountpointName(mountpoint) or device.format.name, Size(spec="%f MB" % device.size), mountpoint, self.on_selector_clicked)
selector._device = device
selector._root = root
page.show_all()
self._accordion.addPage(page, cb=self.on_page_clicked)
- if not did_expand and getattr(self._current_selector, "_root", None) and root.name == self._current_selector._root.name:
+ if not did_expand:
did_expand = True
self._accordion.expandPage(root.name)
@@ -320,14 +445,14 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
page.pageTitle = _("Unknown")
for u in unused:
- selector = page.addDevice(u.format.name, u.size, None, self.on_selector_clicked)
+ selector = page.addDevice(u.format.name, Size(spec="%f MB" % u.size), None, self.on_selector_clicked)
selector._device = u
selector._root = None
page.show_all()
self._accordion.addPage(page, cb=self.on_page_clicked)
- if not did_expand and self._current_selector and unused == self._current_selector._root:
+ if not did_expand:
did_expand = True
self._accordion.expandPage(page.pageTitle)
@@ -361,7 +486,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
device = selector._device
- if hasattr(device.format, "label") and labelEntry.get_text():
+ if labelEntry.get_text() and hasattr(device.format, "label"):
device.format.label = labelEntry.get_text()
def _populate_right_side(self, selector):
@@ -379,14 +504,16 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
selectedDeviceDescLabel.set_text(self._description(selector.props.name))
labelEntry.set_text(getattr(device.format, "label", "") or "")
- labelEntry.set_sensitive(getattr(device.format, "labelfsProg", "") != "")
+ can_label = getattr(device.format, "labelfsProg", "") != ""
+ labelEntry.set_sensitive(can_label)
if labelEntry.get_sensitive():
labelEntry.props.has_tooltip = False
else:
labelEntry.set_tooltip_text(_("This file system does not support labels."))
- sizeSpinner.set_range(device.minSize, device.maxSize)
+ sizeSpinner.set_range(device.minSize,
+ device.maxSize)
sizeSpinner.set_value(device.size)
sizeSpinner.set_sensitive(device.resizable)
@@ -400,15 +527,17 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
# FIXME: What do we do if we can't figure it out?
if device.type == "lvmlv":
typeCombo.set_active(1)
- elif device.type in ["dm-raid array", "mdarray"]:
+ elif device.type == "mdarray":
typeCombo.set_active(2)
elif device.type == "partition":
typeCombo.set_active(3)
+ elif device.type.startswith("btrfs"):
+ typeCombo.set_active(0)
# FIXME: What do we do if we can't figure it out?
model = fsCombo.get_model()
for i in range(0, len(model)):
- if model[i][0] == device.format.name:
+ if model[i][0] == device.format.type:
fsCombo.set_active(i)
break
@@ -439,47 +568,68 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
# create a device of the default type, using any disks, with an
# appropriate fstype and mountpoint
mountpoint = dialog.mountpoint
- size = dialog.size
- fstype = self.storage.defaultFSType
+ log.debug("requested size = %s ; available space = %s"
+ % (dialog.size, self._free_space))
+ size = min(dialog.size, self._free_space)
+ fstype = self.storage.getFSType(mountpoint)
+ encrypted = self.data.autopart.encrypted
+
+ # we're doing nothing here to ensure that bootable requests end up on
+ # the boot disk, but the weight from platform should take care of this
+
if mountpoint.lower() == "swap":
- fstype = "swap"
mountpoint = None
- elif mountpoint == "/boot":
- fstype = self.storage.defaultBootFSType
- elif mountpoint == "/boot/efi":
- if iutil.isMactel():
- fstype = "hfs+"
- else:
- fstype = "efi"
- elif not mountpoint or not mountpoint.startswith("/") or \
- mountpoint in self.storage.mountpoints.keys():
- return
- if not size:
- # no size specified, so use the default size
- size = None
-
- if self.data.autopart.type == AUTOPART_TYPE_PLAIN:
- # create a partition for this new filesystem
- size_mb = float(size.convertTo(spec="mb"))
- device = self.storage.newPartition(size=size_mb,
- fmt_type=fstype,
- mountpoint=mountpoint)
- self.storage.createDevice(device)
- try:
- doPartitioning(self.storage)
- except StorageError as e:
- actions = self.storage.devicetree.findActions(device=device)
- for a in reversed(actions):
- self.storage.devicetree.cancelAction(a)
- else:
- self._do_refresh()
+ # TODO: validate the mountpoint for sanity and uniqueness
+
+ device_type = self.data.autopart.type
+ if device_type == AUTOPART_TYPE_LVM and \
+ mountpoint == "/boot/efi":
+ device_type = AUTOPART_TYPE_PLAIN
+ elif device_type == AUTOPART_TYPE_BTRFS and \
+ (fstype == "swap" or \
+ (mountpoint and mountpoint.startswith("/boot"))):
+ device_type = AUTOPART_TYPE_PLAIN
+
+ disks = self._clearpartDevices
+
+ with ui_storage_logger():
+ self.__storage.newDevice(device_type,
+ size=float(size.convertTo(spec="mb")),
+ fstype=fstype,
+ mountpoint=mountpoint,
+ encrypted=encrypted,
+ disks=disks)
+ self._devices = self.__storage.devices
+ self._do_refresh()
+ self._updateSpaceDisplay()
def _destroy_device(self, device):
+ with ui_storage_logger():
+ self.__storage.destroyDevice(device)
+
+ self._devices = self.__storage.devices
+
+ # should this be in DeviceTree._removeDevice?
+ container = None
+ if hasattr(device, "vg"):
+ device.vg._removeLogVol(device)
+ container = device.vg
+ device_type = AUTOPART_TYPE_LVM
+ elif hasattr(device, "volume"):
+ device.volume._removeSubVolume(device.name)
+ container = device.volume
+ device_type = AUTOPART_TYPE_BTRFS
+
+ if container and not container.exists:
+ # adjust container to size of remaining devices
+ with ui_storage_logger():
+ # TODO: raid
+ factory = self.__storage.getDeviceFactory(device_type, 0)
+ parents = self.__storage.setContainerMembers(container, factory)
+
# if this device has parents with no other children, remove them too
- parents = device.parents[:]
- self.storage.destroyDevice(device)
- for parent in parents:
+ for parent in device.parents:
if parent.kids == 0 and not parent.isDisk:
self._destroy_device(parent)
@@ -488,8 +638,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
pass # unused device
elif device in root.swaps:
root.swaps.remove(device)
- elif hasattr(device.format, "mountpoint") and \
- device in root.mounts.values():
+ elif device in root.mounts.values():
mountpoints = [m for (m,d) in root.mounts.items() if d == device]
for mountpoint in mountpoints:
root.mounts.pop(mountpoint)
@@ -520,7 +669,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
# schedule actions to delete the thing.
dialog = ConfirmDeleteDialog(self.data)
with enlightbox(self.window, dialog.window):
- dialog.refresh(getattr(device.format, "mountpoint", None), device.name)
+ dialog.refresh(getattr(device.format, "mountpoint", ""),
+ device.name)
rc = dialog.run()
if rc == 0:
@@ -530,12 +680,6 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._remove_from_root(root, device)
self._destroy_device(device)
-
- # If the root is now empty, remove it. Devices from the Unused page
- # will have no root.
- if root and root in self.storage.roots and len(root.swaps + root.mounts.values()) == 0:
- self.storage.roots.remove(root)
-
self._update_ui_for_removals()
elif self._accordion.currentPage():
# This is a complete installed system. Thus, we first need to confirm
@@ -562,19 +706,14 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
for device in root.swaps + root.mounts.values():
self._destroy_device(device)
- # Remove the root entirely. This will cause the empty Page to be
- # deleted from the left hand side.
- if root in self.storage.roots:
- self.storage.roots.remove(root)
-
self._update_ui_for_removals()
def on_summary_clicked(self, button):
- free = self.storage.getFreeSpace()
+ free = self._free_space
dialog = SelectedDisksDialog(self.data)
with enlightbox(self.window, dialog.window):
- dialog.refresh(self._clearpartDevices(), free, showRemove=False)
+ dialog.refresh(self._clearpartDevices, free, showRemove=False)
dialog.run()
def on_configure_clicked(self, button):
@@ -608,20 +747,28 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._removeButton.set_sensitive(True)
- def on_create_clicked(self, button):
- from pyanaconda.storage import Root
- from pyanaconda.storage.devices import DiskDevice
- from pyanaconda.storage.partitioning import doAutoPartition
+ def _do_autopart(self):
+ # There are never any non-existent devices around when this runs.
+ log.debug("running automatic partitioning")
+ self.__storage.doAutoPart = True
+ with ui_storage_logger():
+ doAutoPartition(self.__storage, self.data)
+ self.__storage.doAutoPart = False
+ self._devices = self.__storage.devices
+ log.debug("finished automatic partitioning")
+ def on_create_clicked(self, button):
# Then do autopartitioning. We do not do any clearpart first. This is
# custom partitioning, so you have to make your own room.
- # FIXME: Handle all the autopart exns here.
- self.data.autopart.autopart = True
- self.data.autopart.execute(self.storage, self.data, self.instclass)
- self.data.autopart.autopart = False
+ self._do_autopart()
# Refresh the spoke to make the new partitions appear.
- self.refresh()
+ log.debug("refreshing ui")
+ self._do_refresh()
+ log.debug("finished refreshing ui")
+ log.debug("updating space display")
+ self._updateSpaceDisplay()
+ log.debug("finished updating space display")
self._accordion.expandPage(new_install_name)
# And then display the first filesystem on the RHS.
@@ -641,3 +788,5 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._optionsNotebook.set_current_page(2)
elif text == _("Standard Partition"):
self._optionsNotebook.hide()
+
+
diff --git a/pyanaconda/ui/gui/spokes/lib/accordion.py b/pyanaconda/ui/gui/spokes/lib/accordion.py
index c2da71ecb..593d4d545 100644
--- a/pyanaconda/ui/gui/spokes/lib/accordion.py
+++ b/pyanaconda/ui/gui/spokes/lib/accordion.py
@@ -23,7 +23,6 @@ import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
from pyanaconda.product import productName, productVersion
-from pyanaconda.storage.size import Size
from gi.repository.AnacondaWidgets import MountpointSelector
from gi.repository import Gtk
@@ -75,7 +74,7 @@ class Accordion(Gtk.Box):
def expandPage(self, pageTitle):
page = self._find_by_title(pageTitle)
- if page:
+ if page and not page.get_expanded():
page.emit("activate")
def removePage(self, pageTitle):
@@ -137,7 +136,7 @@ class Page(Gtk.Box):
def addDevice(self, name, size, mountpoint, cb):
selector = MountpointSelector()
- selector = MountpointSelector(name, str(Size(spec="%s MB" % size)), mountpoint or "")
+ selector = MountpointSelector(name, str(size).upper(), mountpoint or "")
selector.connect("button-press-event", self._onSelectorClicked, cb)
selector.connect("key-release-event", self._onSelectorClicked, cb)
selector.connect("focus-in-event", self._onSelectorClicked, cb)
@@ -182,7 +181,7 @@ class UnknownPage(Page):
def addDevice(self, name, size, mountpoint, cb):
selector = MountpointSelector()
- selector = MountpointSelector(name, str(Size(spec="%s MB" % size)), mountpoint or "")
+ selector = MountpointSelector(name, str(size).upper(), mountpoint or "")
selector.connect("button-press-event", self._onSelectorClicked, cb)
selector.connect("key-release-event", self._onSelectorClicked, cb)
selector.connect("focus-in-event", self._onSelectorClicked, cb)
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index d8cccd8c0..b560f5568 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -312,6 +312,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
self.data.clearpart.initAll = True
self.data.clearpart.type = self.clearPartType
self.storage.config.update(self.data)
+ self.storage.autoPartType = self.data.autopart.type
# If autopart is selected we want to remove whatever has been
# created/scheduled to make room for autopart.
@@ -515,8 +516,23 @@ class StorageSpoke(NormalSpoke, StorageChecker):
clearPartType=CLEARPART_TYPE_ALL)
disk_free = sum([f[0] for f in free_space.itervalues()])
fs_free = sum([f[1] for f in free_space.itervalues()])
+
+ # add in total size of new filesystems
+ new_free_mb = 0
+ for mountpoint, device in self.storage.mountpoints.items():
+ if mountpoint != "/" and not mountpoint.startswith("/usr") and \
+ not mountpoint.startswith("/var"):
+ # only count system mounts
+ continue
+
+ if device.format.exists:
+ new_free_mb += getattr(device.format, "free", 0)
+ else:
+ new_free_mb += device.size
+ new_free = Size(spec="%f MB" % new_free_mb)
+
required_space = self.payload.spaceRequired
- if disk_free >= required_space:
+ if disk_free + new_free >= required_space:
dialog = InstallOptions1Dialog(self.data)
elif sum([d.size for d in disks]) >= required_space:
dialog = InstallOptions2Dialog(self.data)