From 245cdf3d5d3cee56d8a5c128eaf9738a5dec8eda Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 7 Mar 2014 12:42:05 -0500 Subject: [PATCH] Add OSTreePayload support The biggest difference between RPM and OSTree is that the latter wants to own the bootloader configuration (and kernel installation) itself. This is ultimately how the atomic upgrades are implemented. To represent this, we introduce a new "handlesBootloaderConfiguration" property on the generic Payload class that other parts of the code will conditionalize on. So if we're doing an OSTreePayload install, we need to first install the bootloader, but with no configuration. The bootloader code knows about the storage configuration; most importantly, how to synthesize a root= kernel argument. Now we take these kernel arguments computed by bootloader code, and call into OSTree to do a "pull" (download over HTTP) and a "deployment" (create the hardlink farm, update the bootloader configuration). Finally, we run "ostree admin selinux-ensure-labeled" which will walk the whole disk and ensure that files outside of the root such as /ostree have labels. --- pyanaconda/bootloader.py | 52 ++++++++++++------ pyanaconda/install.py | 30 ++++++----- pyanaconda/kickstart.py | 8 +++ pyanaconda/packaging/__init__.py | 8 +++ pyanaconda/packaging/ostreepayload.py | 99 +++++++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 29 deletions(-) create mode 100644 pyanaconda/packaging/ostreepayload.py diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py index 0d54893..f2fe461 100644 --- a/pyanaconda/bootloader.py +++ b/pyanaconda/bootloader.py @@ -2361,6 +2361,34 @@ def writeSysconfigKernel(storage, version): f.write("HYPERVISOR_ARGS=logging=vga,serial,memory\n") f.close() +def writeBootLoaderFinal(storage, payload, instClass, ksdata): + """ Do the final write of the bootloader. """ + + # set up dracut/fips boot args + # XXX FIXME: do this from elsewhere? + #storage.bootloader.set_boot_args(keyboard=anaconda.keyboard, + # storage=anaconda.storage, + # language=anaconda.instLanguage, + # network=anaconda.network) + storage.bootloader.set_boot_args(storage=storage, + payload=payload, + keyboard=ksdata.keyboard) + + try: + storage.bootloader.write() + except BootLoaderError as e: + if errorHandler.cb(e) == ERROR_RAISE: + raise + +def getBootLoaderDefaultArgs(storage): + """ Return kernel args such as root=. + + This is used by the OSTree payload, which + requires the bootloader arguments at the time + of deployment. + """ + return storage.bootloader.boot_args + def writeBootLoader(storage, payload, instClass, ksdata): """ Write bootloader configuration to disk. @@ -2376,6 +2404,13 @@ def writeBootLoader(storage, payload, instClass, ksdata): stage2_device = storage.bootloader.stage2_device log.info("bootloader stage2 target device is %s" % stage2_device.name) + if payload.handlesBootloaderConfiguration: + if storage.bootloader.skip_bootloader: + log.info("skipping bootloader install per user request") + return + writeBootLoaderFinal(storage, payload, instClass, ksdata) + return + # get a list of installed kernel packages kernel_versions = payload.kernelVersionList if not kernel_versions: @@ -2420,19 +2455,4 @@ def writeBootLoader(storage, payload, instClass, ksdata): label=label, short=short) storage.bootloader.add_image(image) - # set up dracut/fips boot args - # XXX FIXME: do this from elsewhere? - #storage.bootloader.set_boot_args(keyboard=anaconda.keyboard, - # storage=anaconda.storage, - # language=anaconda.instLanguage, - # network=anaconda.network) - storage.bootloader.set_boot_args(storage=storage, - payload=payload, - keyboard=ksdata.keyboard) - - try: - storage.bootloader.write() - except BootLoaderError as e: - if errorHandler.cb(e) == ERROR_RAISE: - raise - + writeBootLoaderFinal(storage, payload, instClass, ksdata) diff --git a/pyanaconda/install.py b/pyanaconda/install.py index 5796086..ce92fe2 100644 --- a/pyanaconda/install.py +++ b/pyanaconda/install.py @@ -22,7 +22,7 @@ from pyanaconda.constants import ROOT_PATH, THREAD_PAYLOAD from blivet import turnOnFilesystems -from pyanaconda.bootloader import writeBootLoader +from pyanaconda.bootloader import writeBootLoader, writeBootLoaderFinal, getBootLoaderDefaultArgs from pyanaconda.progress import progress_report, progressQ from pyanaconda.users import createLuserConf, getPassAlgo, Users from pyanaconda import flags @@ -167,17 +167,21 @@ def doInstall(storage, payload, ksdata, instClass): # don't try to install packages from the install class' ignored list packages = [p for p in packages if p not in instClass.ignoredPackages] payload.preInstall(packages=packages, groups=payload.languageGroups()) - payload.install() - - if flags.flags.livecdInstall: - storage.write() - - with progress_report(_("Performing post-installation setup tasks")): - payload.postInstall() - - # Do bootloader. - if not flags.flags.dirInstall: - with progress_report(_("Installing bootloader")): - writeBootLoader(storage, payload, instClass, ksdata) + if payload.handlesBootloaderConfiguration: + writeBootLoaderFinal(storage, payload, instClass, ksdata) + payload.install(kargs=getBootLoaderDefaultArgs()) + else: + payload.install() + + if flags.flags.livecdInstall: + storage.write() + + with progress_report(_("Performing post-installation setup tasks")): + payload.postInstall() + + # Do bootloader. + if not flags.flags.dirInstall: + with progress_report(_("Installing bootloader")): + writeBootLoader(storage, payload, instClass, ksdata) progressQ.send_complete() diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index df7b5e6..8290d89 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -490,6 +490,13 @@ class Realm(commands.realm.F19_Realm): log.info("Joined realm %s", self.join_realm) +class OSTreeSetup(commands.ostreesetup.F20_OSTreeSetup): + def __init__(self, *args): + commands.ostreesetup.F20_OSTreeSetup.__init__(self, *args) + + def execute(self, *args): + # This is implemented in packaging/ostreepayload.py + pass class ClearPart(commands.clearpart.F17_ClearPart): def parse(self, args): @@ -1580,6 +1587,7 @@ commandMap = { "logvol": LogVol, "multipath": MultiPath, "network": Network, + "ostreesetup": OSTreeSetup, "part": Partition, "partition": Partition, "raid": Raid, diff --git a/pyanaconda/packaging/__init__.py b/pyanaconda/packaging/__init__.py index f0453c5..10b0fb2 100644 --- a/pyanaconda/packaging/__init__.py +++ b/pyanaconda/packaging/__init__.py @@ -128,6 +128,14 @@ class Payload(object): """ Reset the instance, not including ksdata. """ pass + @property + def handlesBootloaderConfiguration(self): + """ Set if the payload requires the bootloader be installed + but unconfigured before doing an install of the system. This + is used by the OSTreePayload subclass. + """ + return False + ### ### METHODS FOR WORKING WITH REPOSITORIES ### diff --git a/pyanaconda/packaging/ostreepayload.py b/pyanaconda/packaging/ostreepayload.py new file mode 100644 index 0000000..371c63e --- /dev/null +++ b/pyanaconda/packaging/ostreepayload.py @@ -0,0 +1,99 @@ +# ostreepayload.py +# Deploy OSTree trees to target +# +# Copyright (C) 2012,2014 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Colin Walters +# + +import shutil + +from . import * + +#try: +# from gi.repository import OSTree +#except ImportError: +# log.error("import of OSTree failed") +# OSTree = None + +from pyanaconda.constants import * +from pyanaconda.flags import flags + +from pyanaconda import iutil + +import logging +log = logging.getLogger("anaconda") + +from pyanaconda.errors import * +#from pyanaconda.progress import progress + +class OSTreePayload(ArchivePayload): + """ A OSTreePayload deploys a tree onto the target system. """ + def __init__(self, data): + if OSTree is None: + raise PayloadError("unsupported payload type") + + super(OSTreePayload, self).__init__(data) + + def setup(self, storage): + super(OSTreePayload, self).setup(storage) + + @property + def handlesBootloaderConfiguration(self): + return True + + @property + def kernelVersionList(self): + return [] + + def install(self, kargs=[]): + ostreesetup = self.data.ostreesetup + print "ostreesetup=%r" % (ostreesetup, ) + + # Initialize the filesystem - this will create the repo as well + iutil.execWithRedirect("ostree", ["admin", "--sysroot=" + ROOT_PATH, + "init-fs", ROOT_PATH]) + + # Set up the chosen remote + remote_args = ["--repo=" + ROOT_PATH + '/repo', + "remote", "add"] + if not self.ostreesetup.gpgVerify: + remote_args.push("--set=gpg-verify=false") + remote_args.extend([ostreesetup.remote, + ostreesetup.url]) + iutil.execWithRedirect("ostree", remote_args) + + for line in execReadlines("ostree", ["pull", ostreesetup.remote, ostreesetup.ref]): + progressQ.send_message(line) + + iutil.execWithRedirect("ostree", ["admin", "--sysroot=" + ROOT_PATH, + "os-init", ostreesetup.osname]) + + admin_deploy_args = ["admin", "--sysroot=" + ROOT_PATH, + "deploy", "--os=" + ostreesetup.osname] + + for karg in kargs: + admin_deploy_args.push('--karg=' + kargs) + + admin_deploy_args.push(ostreesetup.osname + ':' + ostreesetup.ref) + + for line in execReadlines("ostree", admin_deploy_args): + progressQ.send_message(line) + + # And finally, this command iterates over all + iutil.execWithRedirect("ostree", ["admin", "--sysroot=" + ROOT_PATH, + "selinux-ensure-labeled"]) -- 1.8.3.1