mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-12-25 17:27:01 +00:00
Merge pull request #121 from carlroth/master
Implement system upgrade for in-place re-partitioning
This commit is contained in:
@@ -284,7 +284,7 @@ class Runner(onl.install.InstallUtils.SubprocessMixin):
|
||||
l = [x for x in os.listdir(d) if x.endswith('.swi')]
|
||||
l = [os.path.join(d, x) for x in l]
|
||||
l.sort(key=swiSortKey)
|
||||
return l[-1]
|
||||
return l[-1] if l else None
|
||||
|
||||
pm = ProcMountsParser()
|
||||
parts = [x for x in pm.mounts if x.device == dev]
|
||||
@@ -294,6 +294,9 @@ class Runner(onl.install.InstallUtils.SubprocessMixin):
|
||||
self.log.info("found 'latest' swi %s", dst)
|
||||
else:
|
||||
dst = os.path.join(parts[0].dir, src)
|
||||
if dst is None:
|
||||
self.log.error("missing SWI")
|
||||
return None
|
||||
if not os.path.exists(dst):
|
||||
self.log.error("missing SWI: %s", dst)
|
||||
return None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""Run native ONIE tools
|
||||
"""Run native (on-disk) loader tools
|
||||
"""
|
||||
|
||||
import onl.install.ShellApp
|
||||
|
||||
7
packages/base/all/vendor-config-onl/src/bin/upgrade-shell
Executable file
7
packages/base/all/vendor-config-onl/src/bin/upgrade-shell
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""Run the upgrade image
|
||||
"""
|
||||
|
||||
import onl.install.ShellApp
|
||||
onl.install.ShellApp.Upgrader.main()
|
||||
1
packages/base/all/vendor-config-onl/src/boot.d/62.upgrade-system
Executable file
1
packages/base/all/vendor-config-onl/src/boot.d/62.upgrade-system
Executable file
@@ -0,0 +1 @@
|
||||
/sbin/onl-upgrade-system
|
||||
@@ -22,6 +22,9 @@ upgrade:
|
||||
package:
|
||||
dir: /lib/platform-config/current/onl/upgrade/onie
|
||||
|
||||
system:
|
||||
auto: advisory
|
||||
|
||||
firmware:
|
||||
auto: advisory
|
||||
package:
|
||||
|
||||
@@ -62,17 +62,26 @@ installer_mkchroot() {
|
||||
local rootdir
|
||||
rootdir=$1
|
||||
|
||||
local hasDevTmpfs
|
||||
if grep -q devtmpfs /proc/filesystems; then
|
||||
hasDevTmpfs=1
|
||||
fi
|
||||
|
||||
# special handling for /dev, which usually already has nested mounts
|
||||
installer_say "Setting up /dev"
|
||||
rm -fr "${rootdir}/dev"/*
|
||||
for dev in /dev/*; do
|
||||
if test -d "$dev"; then
|
||||
mkdir "${rootdir}${dev}"
|
||||
else
|
||||
cp -a "$dev" "${rootdir}${dev}"
|
||||
fi
|
||||
done
|
||||
mkdir -p "${rootdir}/dev/pts"
|
||||
if test "$hasDevTmpfs"; then
|
||||
:
|
||||
else
|
||||
for dev in /dev/*; do
|
||||
if test -d "$dev"; then
|
||||
mkdir "${rootdir}${dev}"
|
||||
else
|
||||
cp -a "$dev" "${rootdir}${dev}"
|
||||
fi
|
||||
done
|
||||
mkdir -p "${rootdir}/dev/pts"
|
||||
fi
|
||||
|
||||
installer_say "Setting up /run"
|
||||
rm -fr "${rootdir}/run"/*
|
||||
@@ -99,6 +108,10 @@ installer_mkchroot() {
|
||||
installer_say "Setting up mounts"
|
||||
mount -t proc proc "${rootdir}/proc"
|
||||
mount -t sysfs sysfs "${rootdir}/sys"
|
||||
if test "$hasDevTmpfs"; then
|
||||
mount -t devtmpfs devtmpfs "${rootdir}/dev"
|
||||
mkdir -p ${rootdir}/dev/pts
|
||||
fi
|
||||
mount -t devpts devpts "${rootdir}/dev/pts"
|
||||
|
||||
if test ${TMPDIR+set}; then
|
||||
|
||||
@@ -17,9 +17,32 @@ import time
|
||||
from InstallUtils import InitrdContext
|
||||
from InstallUtils import SubprocessMixin
|
||||
from InstallUtils import ProcMountsParser
|
||||
from ShellApp import Onie
|
||||
import ConfUtils, BaseInstall
|
||||
|
||||
class App(SubprocessMixin):
|
||||
class OnieHelper(Onie):
|
||||
"""Unpack the initrd, but keep it around."""
|
||||
|
||||
UMOUNT = False
|
||||
# leave self.onieDir mounted
|
||||
|
||||
ictx = None
|
||||
|
||||
def _runInitrdShell(self, initrd):
|
||||
with InitrdContext(initrd, log=self.log) as self.ictx:
|
||||
self.initrdDir = self.ictx.dir
|
||||
self.ictx.detach()
|
||||
|
||||
def shutdown(self):
|
||||
|
||||
self.ictx.attach()
|
||||
self.ictx.shutdown()
|
||||
|
||||
if self.dctx is not None:
|
||||
self.dctx.attach()
|
||||
self.dctx.shutdown()
|
||||
|
||||
class App(SubprocessMixin, object):
|
||||
|
||||
def __init__(self, url=None,
|
||||
debug=False, force=False,
|
||||
@@ -43,6 +66,8 @@ class App(SubprocessMixin):
|
||||
|
||||
self.nextUpdate = None
|
||||
|
||||
self.onieHelper = None
|
||||
|
||||
def run(self):
|
||||
|
||||
if self.url is not None:
|
||||
@@ -123,27 +148,27 @@ class App(SubprocessMixin):
|
||||
self.log.info("please reboot this system now.")
|
||||
return 0
|
||||
|
||||
def runLocal(self):
|
||||
def runLocalOrChroot(self):
|
||||
|
||||
self.log.info("getting installer configuration")
|
||||
if os.path.exists(ConfUtils.MachineConf.PATH):
|
||||
self.machineConf = ConfUtils.MachineConf()
|
||||
else:
|
||||
self.log.warn("missing /etc/machine.conf from ONIE runtime")
|
||||
self.machineConf = ConfUtils.MachineConf(path='/dev/null')
|
||||
self.installerConf = ConfUtils.InstallerConf()
|
||||
if self.machineConf is None:
|
||||
self.log.error("missing machine.conf")
|
||||
return 1
|
||||
if self.installerConf is None:
|
||||
self.log.error("missing installer.conf")
|
||||
return 1
|
||||
|
||||
##self.log.info("using native GRUB")
|
||||
##self.grubEnv = ConfUtils.GrubEnv(log=self.log.getChild("grub"))
|
||||
|
||||
pat = "/mnt/onie-boot/onie/initrd.img*"
|
||||
l = glob.glob(pat)
|
||||
if l:
|
||||
initrd = l[0]
|
||||
self.log.info("using native ONIE initrd+chroot GRUB (%s)", initrd)
|
||||
initrdDir = InitrdContext.mkChroot(initrd, log=self.log)
|
||||
self.grubEnv = ConfUtils.ChrootGrubEnv(initrdDir,
|
||||
bootDir="/mnt/onie-boot",
|
||||
self.onieHelper = OnieHelper(log=self.log)
|
||||
code = self.onieHelper.run()
|
||||
if code:
|
||||
self.log.warn("cannot find ONIE initrd")
|
||||
|
||||
if self.onieHelper.onieDir is not None:
|
||||
self.log.info("using native ONIE initrd+chroot GRUB (%s)", self.onieHelper.onieDir)
|
||||
self.grubEnv = ConfUtils.ChrootGrubEnv(self.onieHelper.initrdDir,
|
||||
bootDir=self.onieHelper.onieDir,
|
||||
path="/grub/grubenv",
|
||||
log=self.log.getChild("grub"))
|
||||
# direct access using ONIE initrd as a chroot
|
||||
@@ -216,6 +241,19 @@ class App(SubprocessMixin):
|
||||
self.log.info("Install finished.")
|
||||
return 0
|
||||
|
||||
def runLocal(self):
|
||||
|
||||
self.log.info("getting installer configuration")
|
||||
if os.path.exists(ConfUtils.MachineConf.PATH):
|
||||
self.machineConf = ConfUtils.MachineConf()
|
||||
else:
|
||||
self.log.warn("missing /etc/machine.conf from ONIE runtime")
|
||||
self.machineConf = ConfUtils.MachineConf(path='/dev/null')
|
||||
|
||||
self.installerConf = ConfUtils.InstallerConf()
|
||||
|
||||
return self.runLocalOrChroot()
|
||||
|
||||
def findPlatform(self):
|
||||
|
||||
plat = arch = None
|
||||
@@ -302,6 +340,10 @@ class App(SubprocessMixin):
|
||||
if installer is not None:
|
||||
installer.shutdown()
|
||||
|
||||
h, self.onieHelper = self.onieHelper, None
|
||||
if h is not None:
|
||||
h.shutdown()
|
||||
|
||||
def post_mortem(self):
|
||||
self.log.info("re-attaching to tty")
|
||||
fdno = os.open("/dev/console", os.O_RDWR)
|
||||
|
||||
@@ -390,7 +390,7 @@ class Base:
|
||||
for m in pm.mounts:
|
||||
if m.device.startswith(self.device):
|
||||
if not self.force:
|
||||
self.log.error("mount %s on %s will be erased by install",
|
||||
self.log.error("mount %s on %s will be erased by install (try --force)",
|
||||
m.dir, m.device)
|
||||
return 1
|
||||
else:
|
||||
@@ -676,9 +676,6 @@ class UbootInstaller(SubprocessMixin, Base):
|
||||
|
||||
self.device = self.im.getDevice()
|
||||
|
||||
code = self.assertUnmounted()
|
||||
if code: return code
|
||||
|
||||
self.rawLoaderDevice = None
|
||||
# set to a partition device for raw loader install,
|
||||
# default to None for FS-based install
|
||||
@@ -797,6 +794,9 @@ class UbootInstaller(SubprocessMixin, Base):
|
||||
self.log.error("not a block device: %s", self.device)
|
||||
return 1
|
||||
|
||||
code = self.assertUnmounted()
|
||||
if code: return code
|
||||
|
||||
code = self.maybeCreateLabel()
|
||||
if code: return code
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import os
|
||||
import logging
|
||||
import subprocess
|
||||
from InstallUtils import SubprocessMixin, ChrootSubprocessMixin, MountContext
|
||||
from cStringIO import StringIO
|
||||
|
||||
class ConfBase:
|
||||
|
||||
@@ -45,6 +46,14 @@ class ConfBase:
|
||||
def __setattr__(self, attr, val):
|
||||
self.__dict__['_data'][attr] = val
|
||||
|
||||
def dumps(self):
|
||||
"""Generate a serialized representation."""
|
||||
buf = StringIO()
|
||||
data = self.__dict__.get('_data', {})
|
||||
for key, val in data.iteritems():
|
||||
buf.write("%s=\"%s\"\n" % (key, val,))
|
||||
return buf.getvalue()
|
||||
|
||||
class ConfFileBase(ConfBase):
|
||||
|
||||
PATH = None
|
||||
|
||||
@@ -177,6 +177,25 @@ class SubprocessMixin:
|
||||
# don't believe it
|
||||
self.check_call(cmd, vmode=self.V1)
|
||||
|
||||
def cpR(self, srcRoot, dstRoot):
|
||||
srcRoot = os.path.abspath(srcRoot)
|
||||
dstRoot = os.path.abspath(dstRoot)
|
||||
dstRoot = os.path.join(dstRoot, os.path.split(srcRoot)[1])
|
||||
for r, dl, fl in os.walk(srcRoot):
|
||||
|
||||
for de in dl:
|
||||
src = os.path.join(r, de)
|
||||
subdir = src[len(srcRoot)+1:]
|
||||
dst = os.path.join(dstRoot, subdir)
|
||||
if not os.path.exists(dst):
|
||||
self.makedirs(dst)
|
||||
|
||||
for fe in fl:
|
||||
src = os.path.join(r, fe)
|
||||
subdir = src[len(srcRoot)+1:]
|
||||
dst = os.path.join(dstRoot, subdir)
|
||||
self.copy2(src, dst)
|
||||
|
||||
class TempdirContext(SubprocessMixin):
|
||||
|
||||
def __init__(self, prefix=None, suffix=None, chroot=None, log=None):
|
||||
@@ -219,6 +238,9 @@ class MountContext(SubprocessMixin):
|
||||
if not self.device and not self.label:
|
||||
raise ValueError("no device or label specified")
|
||||
|
||||
self._detachMounted = False
|
||||
self._detachHostDir = None
|
||||
|
||||
def __enter__(self):
|
||||
dev = self.device
|
||||
if dev is None:
|
||||
@@ -245,7 +267,7 @@ class MountContext(SubprocessMixin):
|
||||
self.mounted = True
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, tb):
|
||||
def shutdown(self):
|
||||
|
||||
mounted = False
|
||||
if self.mounted:
|
||||
@@ -263,8 +285,18 @@ class MountContext(SubprocessMixin):
|
||||
if self.hostDir is not None:
|
||||
self.rmdir(self.hostDir)
|
||||
|
||||
def __exit__(self, type, value, tb):
|
||||
self.shutdown()
|
||||
return False
|
||||
|
||||
def detach(self):
|
||||
self.mounted, self._detachMounted = False, self.mounted
|
||||
self.hostDir, self._detachHostdir = None, self.hostDir
|
||||
|
||||
def attach(self):
|
||||
self.mounted = self._detachMounted
|
||||
self.hostDir = self._detachHostdir
|
||||
|
||||
class BlkidEntry:
|
||||
|
||||
def __init__(self, device, **kwargs):
|
||||
@@ -665,6 +697,9 @@ class InitrdContext(SubprocessMixin):
|
||||
self.ilog.setLevel(logging.INFO)
|
||||
self.log = self.hlog
|
||||
|
||||
self._hasDevTmpfs = False
|
||||
self._detachInitrd = None
|
||||
|
||||
def _unpack(self):
|
||||
self.dir = self.mkdtemp(prefix="chroot-",
|
||||
suffix=".d")
|
||||
@@ -724,27 +759,28 @@ class InitrdContext(SubprocessMixin):
|
||||
else:
|
||||
self.unlink(dst)
|
||||
|
||||
for e in os.listdir("/dev"):
|
||||
src = os.path.join("/dev", e)
|
||||
dst = os.path.join(dev2, e)
|
||||
if os.path.islink(src):
|
||||
self.symlink(os.readlink(src), dst)
|
||||
elif os.path.isdir(src):
|
||||
self.mkdir(dst)
|
||||
elif os.path.isfile(src):
|
||||
self.copy2(src, dst)
|
||||
else:
|
||||
st = os.stat(src)
|
||||
if stat.S_ISBLK(st.st_mode):
|
||||
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
|
||||
self.log.debug("+ mknod %s b %d %d", dst, maj, min)
|
||||
os.mknod(dst, st.st_mode, st.st_rdev)
|
||||
elif stat.S_ISCHR(st.st_mode):
|
||||
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
|
||||
self.log.debug("+ mknod %s c %d %d", dst, maj, min)
|
||||
os.mknod(dst, st.st_mode, st.st_rdev)
|
||||
if not self._hasDevTmpfs:
|
||||
for e in os.listdir("/dev"):
|
||||
src = os.path.join("/dev", e)
|
||||
dst = os.path.join(dev2, e)
|
||||
if os.path.islink(src):
|
||||
self.symlink(os.readlink(src), dst)
|
||||
elif os.path.isdir(src):
|
||||
self.mkdir(dst)
|
||||
elif os.path.isfile(src):
|
||||
self.copy2(src, dst)
|
||||
else:
|
||||
self.log.debug("skipping device %s", src)
|
||||
st = os.stat(src)
|
||||
if stat.S_ISBLK(st.st_mode):
|
||||
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
|
||||
self.log.debug("+ mknod %s b %d %d", dst, maj, min)
|
||||
os.mknod(dst, st.st_mode, st.st_rdev)
|
||||
elif stat.S_ISCHR(st.st_mode):
|
||||
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
|
||||
self.log.debug("+ mknod %s c %d %d", dst, maj, min)
|
||||
os.mknod(dst, st.st_mode, st.st_rdev)
|
||||
else:
|
||||
self.log.debug("skipping device %s", src)
|
||||
|
||||
dst = os.path.join(self.dir, "dev/pts")
|
||||
if not os.path.exists(dst):
|
||||
@@ -757,6 +793,11 @@ class InitrdContext(SubprocessMixin):
|
||||
|
||||
def __enter__(self):
|
||||
|
||||
with open("/proc/filesystems") as fd:
|
||||
buf = fd.read()
|
||||
if "devtmpfs" in buf:
|
||||
self._hasDevTmpfs = True
|
||||
|
||||
if self.initrd is not None:
|
||||
|
||||
self.log.debug("extracting initrd %s", self.initrd)
|
||||
@@ -777,13 +818,23 @@ class InitrdContext(SubprocessMixin):
|
||||
cmd = ('mount', '-t', 'sysfs', 'sysfs', dst,)
|
||||
self.check_call(cmd, vmode=self.V1)
|
||||
|
||||
# maybe mount devtmpfs
|
||||
if self._hasDevTmpfs:
|
||||
dst = os.path.join(self.dir, "dev")
|
||||
cmd = ('mount', '-t', 'devtmpfs', 'devtmpfs', dst,)
|
||||
self.check_call(cmd, vmode=self.V1)
|
||||
|
||||
dst = os.path.join(self.dir, "dev/pts")
|
||||
if not os.path.exists(dst):
|
||||
self.mkdir(dst)
|
||||
|
||||
dst = os.path.join(self.dir, "dev/pts")
|
||||
cmd = ('mount', '-t', 'devpts', 'devpts', dst,)
|
||||
self.check_call(cmd, vmode=self.V1)
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, tb):
|
||||
def shutdown(self):
|
||||
|
||||
p = ProcMountsParser()
|
||||
dirs = [e.dir for e in p.mounts if e.dir.startswith(self.dir)]
|
||||
@@ -803,13 +854,21 @@ class InitrdContext(SubprocessMixin):
|
||||
else:
|
||||
self.log.debug("saving chroot in %s", self.dir)
|
||||
|
||||
def __exit__(self, type, value, tb):
|
||||
self.shutdown()
|
||||
return False
|
||||
|
||||
def detach(self):
|
||||
self.initrd, self._detachInitrd = None, self.initrd
|
||||
|
||||
def attach(self):
|
||||
self.initrd = self._detachInitrd
|
||||
|
||||
@classmethod
|
||||
def mkChroot(self, initrd, log=None):
|
||||
with InitrdContext(initrd=initrd, log=log) as ctx:
|
||||
def mkChroot(cls, initrd, log=None):
|
||||
with cls(initrd=initrd, log=log) as ctx:
|
||||
initrdDir = ctx.dir
|
||||
ctx.initrd = None
|
||||
ctx.detach()
|
||||
# save the unpacked directory, do not clean it up
|
||||
# (it's inside this chroot anyway)
|
||||
return initrdDir
|
||||
|
||||
@@ -16,8 +16,9 @@ from InstallUtils import BlkidParser
|
||||
import Fit
|
||||
|
||||
import onl.platform.current
|
||||
from onl.sysconfig import sysconfig
|
||||
|
||||
class AppBase(SubprocessMixin):
|
||||
class AppBase(SubprocessMixin, object):
|
||||
|
||||
@property
|
||||
def PROG(self):
|
||||
@@ -109,14 +110,20 @@ class AppBase(SubprocessMixin):
|
||||
sys.exit(code)
|
||||
|
||||
class Onie(AppBase):
|
||||
"""Application shell that uses the ONIE runtime."""
|
||||
|
||||
PROG = "onie-shell"
|
||||
|
||||
UNMOUNT = True
|
||||
# default, unmount directories once the initrd is extracted
|
||||
|
||||
def run(self):
|
||||
|
||||
self.pm = ProcMountsParser()
|
||||
self.blkid = BlkidParser(log=self.log.getChild("blkid"))
|
||||
self.mtd = ProcMtdParser(log=self.log.getChild("mtd"))
|
||||
self.dctx = None
|
||||
self.onieDir = None
|
||||
|
||||
def _g(d):
|
||||
pat = os.path.join(d, "onie/initrd.img*")
|
||||
@@ -134,22 +141,28 @@ class Onie(AppBase):
|
||||
|
||||
parts = [p for p in self.pm.mounts if p.device == dev]
|
||||
if parts:
|
||||
onieDir = parts[0]
|
||||
self.log.debug("found ONIE boot mounted at %s", onieDir)
|
||||
initrd = _g(onieDir)
|
||||
self.log.debug("found ONIE boot mounted at %s", parts[0].dir)
|
||||
initrd = _g(parts[0].dir)
|
||||
if initrd is None:
|
||||
self.log.warn("cannot find ONIE initrd on %s", onieDir)
|
||||
self.log.warn("cannot find ONIE initrd on %s", parts[0].dir)
|
||||
else:
|
||||
self.onieDir = parts[0].dir
|
||||
self.log.debug("found ONIE initrd at %s", initrd)
|
||||
return _runInitrdShell(initrd)
|
||||
return self._runInitrdShell(initrd)
|
||||
|
||||
with MountContext(dev, log=self.log) as ctx:
|
||||
initrd = _g(ctx.dir)
|
||||
# else, try to mount the directory containing the initrd
|
||||
with MountContext(dev, log=self.log) as self.dctx:
|
||||
initrd = _g(self.dctx.dir)
|
||||
if initrd is None:
|
||||
self.log.warn("cannot find ONIE initrd on %s", dev)
|
||||
else:
|
||||
self.log.debug("found ONIE initrd at %s", initrd)
|
||||
return self._runInitrdShell(initrd)
|
||||
try:
|
||||
return self._runInitrdShell(initrd)
|
||||
finally:
|
||||
if not self.UMOUNT:
|
||||
self.onieDir = self.dctx.hostDir
|
||||
self.dctx.detach()
|
||||
|
||||
self.log.warn("cannot find an ONIE initrd")
|
||||
return 1
|
||||
@@ -163,6 +176,7 @@ class Onie(AppBase):
|
||||
self.log.debug("cannot find ONIE initrd on %s (%s)",
|
||||
part.device, part.dir)
|
||||
else:
|
||||
self.onieDir = part.dir
|
||||
self.log.debug("found ONIE initrd at %s", initrd)
|
||||
return self._runInitrdShell(initrd)
|
||||
|
||||
@@ -181,6 +195,7 @@ class Onie(AppBase):
|
||||
return 1
|
||||
|
||||
class Loader(AppBase):
|
||||
"""Application shell that uses the (installed) loader runtime."""
|
||||
|
||||
PROG = "loader-shell"
|
||||
|
||||
@@ -297,6 +312,49 @@ class Loader(AppBase):
|
||||
self.log.error("invalid platform-config")
|
||||
return 1
|
||||
|
||||
class Upgrader(AppBase):
|
||||
"""Application shell that uses on-disk upgrade loader runtime."""
|
||||
|
||||
PROG = "upgrade-shell"
|
||||
|
||||
def runGrub(self):
|
||||
|
||||
d = sysconfig.upgrade.loader.package.dir
|
||||
for b in sysconfig.upgrade.loader.package.grub:
|
||||
p = os.path.join(d, b)
|
||||
if os.path.exists(p):
|
||||
self.log.debug("found upgrade initrd at %s", p)
|
||||
return self._runInitrdShell(p)
|
||||
|
||||
self.log.error("cannot find upgrade initrd")
|
||||
return 1
|
||||
|
||||
def runUboot(self):
|
||||
|
||||
d = sysconfig.upgrade.loader.package.dir
|
||||
for b in sysconfig.upgrade.loader.package.fit:
|
||||
p = os.path.join(d, b)
|
||||
if os.path.exists(p):
|
||||
self.log.debug("found upgrade FIT image %s", p)
|
||||
return self._runFitShell(p)
|
||||
|
||||
self.log.error("cannot find FIT image")
|
||||
return 1
|
||||
|
||||
def run(self):
|
||||
|
||||
self.platform = onl.platform.current.OnlPlatform()
|
||||
self.pc = self.platform.platform_config
|
||||
|
||||
if 'grub' in self.pc:
|
||||
return self.runGrub()
|
||||
|
||||
if 'flat_image_tree' in self.pc:
|
||||
return self.runUboot()
|
||||
|
||||
self.log.error("invalid platform-config")
|
||||
return 1
|
||||
|
||||
main = Onie.main
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
"""App.py
|
||||
|
||||
Application code for onl-install.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os, sys
|
||||
import json
|
||||
import tempfile
|
||||
import zipfile
|
||||
import shutil
|
||||
import argparse
|
||||
import fnmatch
|
||||
import subprocess
|
||||
|
||||
from onl.install.InstallUtils import InitrdContext
|
||||
from onl.install.InstallUtils import ProcMountsParser
|
||||
from onl.install.ConfUtils import MachineConf, InstallerConf
|
||||
from onl.install.ShellApp import Onie, Upgrader
|
||||
from onl.install.InstallUtils import SubprocessMixin
|
||||
import onl.install.App
|
||||
|
||||
from onl.sysconfig import sysconfig
|
||||
|
||||
from onl.mounts import OnlMountContextReadWrite
|
||||
|
||||
class UpgradeHelper(Upgrader):
|
||||
|
||||
def __init__(self, callback=None, log=None):
|
||||
super(UpgradeHelper, self).__init__(log=log)
|
||||
self.callback = callback
|
||||
|
||||
def _runInitrdShell(self, p):
|
||||
if self.callback is not None:
|
||||
self.callback(self, p)
|
||||
|
||||
class App(SubprocessMixin):
|
||||
|
||||
def __init__(self, force=False, log=None):
|
||||
|
||||
if log is not None:
|
||||
self.log = log
|
||||
else:
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
self.force = force
|
||||
|
||||
self.onieHelper = None
|
||||
|
||||
def _runInitrd(self, helper, path):
|
||||
with InitrdContext(initrd=path, log=self.log) as ctx:
|
||||
|
||||
tdir = os.path.join(ctx.dir, "tmp")
|
||||
abs_idir = tempfile.mkdtemp(dir=tdir,
|
||||
prefix="installer-", suffix=".d")
|
||||
chroot_idir = abs_idir[len(ctx.dir):]
|
||||
|
||||
self.onieHelper = onl.install.App.OnieHelper(log=self.log)
|
||||
code = self.onieHelper.run()
|
||||
if code:
|
||||
self.log.error("cannot find/unpack ONIE initrd")
|
||||
return code
|
||||
self.log.info("onie directory is %s", self.onieHelper.onieDir)
|
||||
self.log.info("initrd directory is %s", self.onieHelper.initrdDir)
|
||||
|
||||
src = os.path.join(self.onieHelper.initrdDir, "etc/machine.conf")
|
||||
dst = os.path.join(ctx.dir, "etc/machine.conf")
|
||||
self.log.debug("+ /bin/cp %s %s", src, dst)
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
h, self.onieHelper = self.onieHelper, None
|
||||
if h is not None:
|
||||
h.shutdown()
|
||||
|
||||
src = "/etc/fw_env.config"
|
||||
if os.path.exists(src):
|
||||
dst = os.path.join(ctx.dir, "etc/fw_env.config")
|
||||
self.log.debug("+ /bin/cp %s %s", src, dst)
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
srcRoot = "/etc/onl"
|
||||
dstRoot = os.path.join(ctx.dir, "etc")
|
||||
self.cpR(srcRoot, dstRoot)
|
||||
|
||||
# constitute an /etc/onl/installer.conf in place
|
||||
installerConf = InstallerConf(path="/dev/null")
|
||||
|
||||
with open("/etc/onl/loader/versions.json") as fd:
|
||||
data = json.load(fd)
|
||||
installerConf.onl_version = data['VERSION_ID']
|
||||
|
||||
installerConf.installer_dir = chroot_idir
|
||||
|
||||
abs_postinst = tempfile.mktemp(dir=abs_idir,
|
||||
prefix="postinst-", suffix=".sh")
|
||||
chroot_postinst = abs_postinst[len(ctx.dir):]
|
||||
installerConf.installer_postinst = chroot_postinst
|
||||
|
||||
# make an empty(ish) zip file (local path in installer_dir) for collateral
|
||||
zipPath = tempfile.mktemp(dir=abs_idir,
|
||||
prefix="install-", suffix=".zip")
|
||||
with zipfile.ZipFile(zipPath, "w") as zf:
|
||||
pass
|
||||
installerConf.installer_zip = os.path.split(zipPath)[1]
|
||||
|
||||
# finalize the local installer.conf
|
||||
dst = os.path.join(ctx.dir, "etc/onl/installer.conf")
|
||||
with open(dst, "w") as fd:
|
||||
fd.write(installerConf.dumps())
|
||||
|
||||
# populate installer_dir with the contents of the loader upgrade
|
||||
# See also Loader_Upgrade_x86_64.do_upgrade
|
||||
# Here the initrd filename is as per the installer.zip;
|
||||
# it is renamed on install to the grub directory
|
||||
sdir = sysconfig.upgrade.loader.package.dir
|
||||
|
||||
# get kernels for grub installs:
|
||||
pats = ["kernel-*",]
|
||||
for f in os.listdir(sdir):
|
||||
for pat in pats:
|
||||
if fnmatch.fnmatch(f, pat):
|
||||
src = os.path.join(sdir, f)
|
||||
dst = os.path.join(abs_idir, f)
|
||||
self.log.debug("+ /bin/cp %s %s", src, dst)
|
||||
shutil.copy2(src, dst)
|
||||
try:
|
||||
l = sysconfig.upgrade.loader.package.grub
|
||||
except AttributeError:
|
||||
l = []
|
||||
for f in l:
|
||||
src = os.path.join(sdir, f)
|
||||
if os.path.exists(src):
|
||||
dst = os.path.join(abs_idir, f)
|
||||
self.log.debug("+ /bin/cp %s %s", src, dst)
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
# get FIT files from powerpc installs:
|
||||
try:
|
||||
l = sysconfig.upgrade.loader.package.fit
|
||||
except AttributeError:
|
||||
l = []
|
||||
for f in l:
|
||||
src = os.path.join(sdir, f)
|
||||
if os.path.exists(src):
|
||||
dst = os.path.join(abs_idir, f)
|
||||
self.log.debug("+ /bin/cp %s %s", src, dst)
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
with OnlMountContextReadWrite('ONL-BOOT', logger=self.log) as octx:
|
||||
src = os.path.join(octx.directory, "boot-config")
|
||||
dst = os.path.join(abs_idir, "boot-config")
|
||||
self.log.debug("+ /bin/cp %s %s", src, dst)
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
# chroot to the onl-install script
|
||||
##cmd = ('chroot', ctx.dir,
|
||||
## '/bin/sh', '-i')
|
||||
if self.log.level < logging.INFO:
|
||||
cmd = ('chroot', ctx.dir, "/usr/bin/onl-install", "--verbose", "--force",)
|
||||
else:
|
||||
cmd = ('chroot', ctx.dir, "/usr/bin/onl-install", "--force",)
|
||||
try:
|
||||
self.check_call(cmd)
|
||||
except subprocess.CalledProcessError, what:
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
"""XXX roth -- migrate this to onl.install.App.App
|
||||
|
||||
XXX roth -- assume TMPDIR=/tmp.
|
||||
"""
|
||||
|
||||
pm = ProcMountsParser()
|
||||
|
||||
# resize /tmp to be large enough for the initrd, see tmpfs
|
||||
# nonsense in installer.sh.in
|
||||
tflags = None
|
||||
tdev = os.stat('/tmp').st_dev
|
||||
pdir = None
|
||||
for m in pm.mounts:
|
||||
if m.fsType in ('ramfs', 'tmpfs',):
|
||||
dev = os.stat(m.dir).st_dev
|
||||
if dev == tdev:
|
||||
self.log.info("found tmpfs/ramfs %s (%s)", dev, m.flags)
|
||||
pdir = m.dir
|
||||
tflags = m.flags
|
||||
|
||||
# XXX glean this from install.sh.in (installer_tmpfs_kmin)
|
||||
if pdir is None:
|
||||
self.check_call(('mount',
|
||||
'-o', 'size=1048576k',
|
||||
'-t', 'tmpfs',
|
||||
'tmpfs', '/tmp',))
|
||||
else:
|
||||
self.check_call(('mount',
|
||||
'-o', 'remount,size=1048576k',
|
||||
pdir,))
|
||||
|
||||
for m in pm.mounts:
|
||||
if m.dir.startswith('/mnt/onl'):
|
||||
if not self.force:
|
||||
self.log.error("directory %s is still mounted (try --force)", m.dir)
|
||||
return 1
|
||||
self.log.warn("unmounting %s (--force)", m.dir)
|
||||
self.check_call(('umount', m.dir,))
|
||||
|
||||
upgrader = UpgradeHelper(callback=self._runInitrd, log=self.log)
|
||||
try:
|
||||
code = upgrader.run()
|
||||
except:
|
||||
self.log.exception("upgrader failed")
|
||||
code = 1
|
||||
upgrader.shutdown()
|
||||
return code
|
||||
|
||||
def shutdown(self):
|
||||
|
||||
h, self.onieHelper = self.onieHelper, None
|
||||
if h is not None:
|
||||
h.shutdown()
|
||||
|
||||
@classmethod
|
||||
def main(cls):
|
||||
|
||||
logging.basicConfig()
|
||||
logger = logging.getLogger("onl-install")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# send to ONIE log
|
||||
hnd = logging.FileHandler("/dev/console")
|
||||
logger.addHandler(hnd)
|
||||
logger.propagate = False
|
||||
|
||||
onie_verbose = 'onie_verbose' in os.environ
|
||||
installer_debug = 'installer_debug' in os.environ
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('-v', '--verbose', action='store_true',
|
||||
default=onie_verbose,
|
||||
help="Enable verbose logging")
|
||||
ap.add_argument('-D', '--debug', action='store_true',
|
||||
default=installer_debug,
|
||||
help="Enable python debugging")
|
||||
ap.add_argument('-F', '--force', action='store_true',
|
||||
help="Unmount filesystems before install")
|
||||
ops = ap.parse_args()
|
||||
|
||||
if ops.verbose:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
app = cls(force=ops.force,
|
||||
log=logger)
|
||||
try:
|
||||
code = app.run()
|
||||
except:
|
||||
logger.exception("runner failed")
|
||||
code = 1
|
||||
if ops.debug:
|
||||
app.post_mortem()
|
||||
|
||||
app.shutdown()
|
||||
sys.exit(code)
|
||||
|
||||
main = App.main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/python
|
||||
############################################################
|
||||
#
|
||||
# ONL System Upgrade
|
||||
#
|
||||
############################################################
|
||||
import os
|
||||
import sys
|
||||
import fnmatch
|
||||
from onl.upgrade import ubase
|
||||
from onl.sysconfig import sysconfig
|
||||
from onl.mounts import OnlMountManager, OnlMountContextReadOnly, OnlMountContextReadWrite
|
||||
|
||||
from onl.install.SystemInstall import App
|
||||
|
||||
class SystemUpgrade(ubase.BaseUpgrade):
|
||||
name="system"
|
||||
Name="System"
|
||||
title="System Compatibility Version Check"
|
||||
atype="A Compatible System"
|
||||
|
||||
current_version_key="Current System Compatibility Version"
|
||||
next_version_key="Next System Compatibility Version"
|
||||
|
||||
def auto_upgrade_default(self):
|
||||
return sysconfig.upgrade.system.auto
|
||||
|
||||
def init_versions(self):
|
||||
|
||||
#
|
||||
# Current loader version file.
|
||||
# If this file doesn't exist then in-place upgrade is not supported.
|
||||
#
|
||||
ETC_LOADER_VERSIONS_JSON = sysconfig.upgrade.loader.versions
|
||||
|
||||
# Upgrade Loader Version file.
|
||||
NEXT_LOADER_VERSIONS_JSON = os.path.join(sysconfig.upgrade.loader.package.dir, "manifest.json")
|
||||
|
||||
VKEY = "SYSTEM_COMPATIBILITY_VERSION"
|
||||
|
||||
self.current_version = self.load_json(ETC_LOADER_VERSIONS_JSON, VKEY, None)
|
||||
|
||||
self.next_version = self.load_json(NEXT_LOADER_VERSIONS_JSON,
|
||||
"version", {}).get(VKEY, None)
|
||||
|
||||
def prepare_upgrade(self):
|
||||
pass
|
||||
|
||||
def summarize(self):
|
||||
self.logger.info("Current System Compatibility Version: %s",
|
||||
self.current_version)
|
||||
self.logger.info(" Next System Compatibility Version: %s",
|
||||
self.next_version)
|
||||
self.logger.info("")
|
||||
|
||||
|
||||
def upgrade_notes(self):
|
||||
return """
|
||||
* One or more reboots will be required to complete this upgrade.
|
||||
"""
|
||||
|
||||
def do_upgrade(self, forced=False):
|
||||
app = App(force=True, log=self.logger)
|
||||
try:
|
||||
code = app.run()
|
||||
except:
|
||||
self.logger.exception("upgrade failed")
|
||||
code = 1
|
||||
app.shutdown()
|
||||
if code:
|
||||
self.abort("System upgrade failed.")
|
||||
else:
|
||||
self.logger.info("Upgrade succeeded, rebooting")
|
||||
self.reboot()
|
||||
|
||||
if __name__ == '__main__':
|
||||
klass = SystemUpgrade
|
||||
klass().main()
|
||||
7
packages/base/all/vendor-config-onl/src/sbin/onl-install-system
Executable file
7
packages/base/all/vendor-config-onl/src/sbin/onl-install-system
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""Re-install ONL using the ONL installer infrastructure.
|
||||
"""
|
||||
|
||||
import onl.install.SystemInstall
|
||||
onl.install.SystemInstall.main()
|
||||
3
packages/base/all/vendor-config-onl/src/sbin/onl-upgrade-system
Executable file
3
packages/base/all/vendor-config-onl/src/sbin/onl-upgrade-system
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
from onl.upgrade.system import SystemUpgrade
|
||||
SystemUpgrade().main()
|
||||
@@ -1098,7 +1098,6 @@ if __name__ == '__main__':
|
||||
ap.add_argument("--repo-package-dir", default=os.environ.get('ONLPM_OPTION_REPO_PACKAGE_DIR', 'packages'))
|
||||
ap.add_argument("--packagedirs", nargs='+', metavar='PACKAGEDIR')
|
||||
ap.add_argument("--subdir", default=os.getcwd())
|
||||
ap.add_argument("--extract", metavar='PACKAGE')
|
||||
ap.add_argument("--extract-dir", nargs=2, metavar=('PACKAGE', 'DIR'), action='append')
|
||||
ap.add_argument("--force", action='store_true')
|
||||
ap.add_argument("--list", action='store_true');
|
||||
|
||||
Reference in New Issue
Block a user